├── .editorconfig ├── .env.dist ├── .github ├── dependabot.yml ├── settings.yml └── workflows │ └── dependabot-auto-merge.yml ├── .gitignore ├── LICENSE ├── README.md ├── app.yaml ├── bin └── console ├── composer.json ├── composer.lock ├── config ├── bundles.php ├── packages │ ├── dev │ │ └── routing.yaml │ ├── firebase.yaml │ ├── framework.yaml │ ├── routing.yaml │ ├── sensio_framework_extra.yaml │ └── test │ │ └── framework.yaml ├── preload.php ├── routes.yaml ├── routes │ └── annotations.yaml └── services.yaml ├── public └── index.php ├── src ├── Command │ ├── CreateUserCommand.php │ ├── FcmSendMessageCommand.php │ ├── GetDatabaseSnapshot.php │ ├── ListRemoteConfigVersionsCommand.php │ ├── ResetProjectCommand.php │ └── UploadFileCommand.php ├── Controller │ ├── .gitignore │ ├── DatabaseController.php │ ├── HomeController.php │ └── UserController.php └── Kernel.php └── symfony.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [**.{yml,yaml}] 16 | indent_size = 2 17 | -------------------------------------------------------------------------------- /.env.dist: -------------------------------------------------------------------------------- 1 | # This file is a "template" of which env vars need to be defined for your application 2 | # Copy this file to .env file for development, create environment variables when deploying to production 3 | # https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration 4 | 5 | ###> app ### 6 | FIREBASE_CREDENTIALS= 7 | ###< app ### 8 | 9 | ###> symfony/framework-bundle ### 10 | APP_ENV=dev 11 | APP_SECRET= 12 | #TRUSTED_PROXIES=127.0.0.1,127.0.0.2 13 | #TRUSTED_HOSTS=localhost,example.com 14 | ###< symfony/framework-bundle ### 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: composer 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/settings.yml: -------------------------------------------------------------------------------- 1 | # https://probot.github.io/apps/settings/ 2 | _extends: beste/.github 3 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: "Dependabot auto-merge" 2 | 3 | on: [pull_request_target] # yamllint disable-line rule:truthy 4 | 5 | permissions: 6 | contents: "write" 7 | 8 | jobs: 9 | dependabot: 10 | runs-on: "ubuntu-latest" 11 | if: "${{ github.actor == 'dependabot[bot]' }}" 12 | steps: 13 | - name: "Dependabot metadata" 14 | id: "metadata" 15 | uses: "dependabot/fetch-metadata@v1.3.4" 16 | with: 17 | github-token: "${{ secrets.GITHUB_TOKEN }}" 18 | - name: "Enable auto-merge for Dependabot PRs" 19 | if: "${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }}" 20 | run: "gh pr merge --auto --rebase \"$PR_URL\"" 21 | env: 22 | PR_URL: "${{github.event.pull_request.html_url}}" 23 | GITHUB_TOKEN: "${{secrets.GITHUB_TOKEN}}" 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ###> app ### 2 | credentials.json 3 | ###< app ### 4 | 5 | ###> symfony/framework-bundle ### 6 | /.env 7 | /public/bundles/ 8 | /var/ 9 | /vendor/ 10 | ###< symfony/framework-bundle ### 11 | 12 | .phpunit.result.cache 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Jérôme Gamez, https://github.com/jeromegamez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Firebase Admin SDK Examples 2 | 3 | Example Symfony 6 Project using https://github.com/kreait/firebase-php 4 | 5 | [![Sponsor](https://img.shields.io/static/v1?logo=GitHub&label=Sponsor&message=%E2%9D%A4&color=ff69b4)](https://github.com/sponsors/jeromegamez) 6 | 7 | ## How to use 8 | 9 | ```bash 10 | git clone git@github.com:jeromegamez/firebase-php-examples.git 11 | cd firebase-php-examples 12 | composer install 13 | cp .env.dist .env 14 | # Set FIREBASE_CREDENTIALS with the path to your credentials file 15 | bin/console list app 16 | ``` 17 | 18 | ## Running the server 19 | 20 | ```bash 21 | # Show available routes 22 | bin/console debug:router 23 | # Start the server 24 | bin/console server:start 25 | 26 | ``` 27 | 28 | ## Available commands 29 | 30 | ``` 31 | bin/console app:create-user # Creates a Firebase user 32 | bin/console app:fcm:send-message # Send an FCM message 33 | bin/console app:remote-config:list-versions # Lists all remote config versions 34 | bin/console app:reset-project # Reset parts of a Firebase project to its initial state 35 | bin/console app:upload-file # Upload a file to Firebase storage 36 | ``` 37 | 38 | ## Deploying to Google Cloud Engine 39 | 40 | You can deploy the application to Google Cloud Engine with the following command: 41 | 42 | ```bash 43 | gcloud app deploy 44 | ``` 45 | 46 | Make sure to remove/override the credentials setting when running on GCE - the SDK SDK will 47 | autodiscover the credentials on the GCE environment to connect to the Firebase services. 48 | -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | runtime: php 2 | env: flex 3 | 4 | runtime_config: 5 | document_root: public 6 | front_controller_file: index.php 7 | 8 | env_variables: 9 | APP_ENV: "prod" 10 | 11 | manual_scaling: 12 | instances: 1 13 | resources: 14 | cpu: 1 15 | memory_gb: 0.5 16 | disk_size_gb: 10 17 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | load(__DIR__.'/../.env'); 23 | } 24 | 25 | $input = new ArgvInput(); 26 | $env = $input->getParameterOption(['--env', '-e'], $_SERVER['APP_ENV'] ?? 'dev', true); 27 | $debug = (bool) ($_SERVER['APP_DEBUG'] ?? ('prod' !== $env)) && !$input->hasParameterOption('--no-debug', true); 28 | 29 | if ($debug) { 30 | umask(0000); 31 | 32 | if (class_exists(Debug::class)) { 33 | Debug::enable(); 34 | } 35 | } 36 | 37 | $kernel = new Kernel($env, $debug); 38 | $application = new Application($kernel); 39 | $application->run($input); 40 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gamez/firebase-examples", 3 | "license": "MIT", 4 | "description": "Symfony 5 example application integrating https://github.com/kreait/firebase-php", 5 | "require": { 6 | "php": "^7.4|^8.0", 7 | "ext-iconv": "*", 8 | "ext-json": "*", 9 | "kreait/firebase-bundle": "^4.0", 10 | "sensio/framework-extra-bundle": "^5.4|^6.2", 11 | "symfony/console": "^5.4|^6.0", 12 | "symfony/dotenv": "^5.4|^6.0", 13 | "symfony/flex": "^1.18|^2.0.1", 14 | "symfony/yaml": "^5.4|^6.0" 15 | }, 16 | "require-dev": { 17 | "symfony/maker-bundle": "^1.22" 18 | }, 19 | "config": { 20 | "preferred-install": { 21 | "*": "dist" 22 | }, 23 | "sort-packages": true, 24 | "allow-plugins": { 25 | "symfony/flex": true 26 | } 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "App\\": "src/" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "App\\Tests\\": "tests/" 36 | } 37 | }, 38 | "replace": { 39 | "symfony/polyfill-iconv": "*", 40 | "symfony/polyfill-php72": "*", 41 | "symfony/polyfill-php71": "*", 42 | "symfony/polyfill-php70": "*", 43 | "symfony/polyfill-php56": "*" 44 | }, 45 | "scripts": { 46 | "auto-scripts": { 47 | "cache:clear": "symfony-cmd", 48 | "assets:install --symlink --relative %PUBLIC_DIR%": "symfony-cmd" 49 | }, 50 | "post-install-cmd": [ 51 | "chmod -R ug+w $APP_DIR/var", 52 | "@auto-scripts" 53 | ], 54 | "post-update-cmd": [ 55 | "@auto-scripts" 56 | ] 57 | }, 58 | "conflict": { 59 | "symfony/symfony": "*" 60 | }, 61 | "extra": { 62 | "symfony": { 63 | "id": "01CAGV79KEF5QRC8SQ9XGRH6B7", 64 | "allow-contrib": false 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /config/bundles.php: -------------------------------------------------------------------------------- 1 | ['all' => true], 5 | Kreait\Firebase\Symfony\Bundle\FirebaseBundle::class => ['all' => true], 6 | Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], 7 | Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], 8 | ]; 9 | -------------------------------------------------------------------------------- /config/packages/dev/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /config/packages/firebase.yaml: -------------------------------------------------------------------------------- 1 | kreait_firebase: 2 | projects: 3 | my_project: 4 | # Remove/Override the following line if running on GCE 5 | credentials: '%env(FIREBASE_CREDENTIALS)%' 6 | # database_uri: 'https://my-project-default-rtdb.firebaseio.com' 7 | # or, depending on your Firebase project configuration 8 | # database_uri: 'https://my-project-default-rtdb.europe-west1.firebasedatabase.app' 9 | verifier_cache: cache.app 10 | auth_token_cache: cache.app 11 | -------------------------------------------------------------------------------- /config/packages/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | secret: '%env(APP_SECRET)%' 3 | #default_locale: en 4 | #csrf_protection: true 5 | #http_method_override: true 6 | 7 | # Enables session support. Note that the session will ONLY be started if you read or write from it. 8 | # Remove or comment this section to explicitly disable session support. 9 | session: 10 | handler_id: ~ 11 | 12 | #esi: true 13 | #fragments: true 14 | php_errors: 15 | log: true 16 | 17 | cache: 18 | # Put the unique name of your app here: the prefix seed 19 | # is used to compute stable namespaces for cache keys. 20 | #prefix_seed: your_vendor_name/app_name 21 | 22 | # The app cache caches to the filesystem by default. 23 | # Other options include: 24 | 25 | # Redis 26 | #app: cache.adapter.redis 27 | #default_redis_provider: redis://localhost 28 | 29 | # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) 30 | #app: cache.adapter.apcu 31 | -------------------------------------------------------------------------------- /config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: ~ 4 | -------------------------------------------------------------------------------- /config/packages/sensio_framework_extra.yaml: -------------------------------------------------------------------------------- 1 | sensio_framework_extra: 2 | router: 3 | annotations: false 4 | -------------------------------------------------------------------------------- /config/packages/test/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: true 3 | session: 4 | storage_id: session.storage.mock_file 5 | -------------------------------------------------------------------------------- /config/preload.php: -------------------------------------------------------------------------------- 1 | bootEnv(dirname(__DIR__).'/.env'); 11 | 12 | if ($_SERVER['APP_DEBUG']) { 13 | umask(0000); 14 | 15 | Debug::enable(); 16 | } 17 | 18 | if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? false) { 19 | Request::setTrustedProxies(explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOST); 20 | } 21 | 22 | if ($trustedHosts = $_SERVER['TRUSTED_HOSTS'] ?? false) { 23 | Request::setTrustedHosts([$trustedHosts]); 24 | } 25 | 26 | $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); 27 | $request = Request::createFromGlobals(); 28 | $response = $kernel->handle($request); 29 | $response->send(); 30 | $kernel->terminate($request, $response); 31 | -------------------------------------------------------------------------------- /src/Command/CreateUserCommand.php: -------------------------------------------------------------------------------- 1 | auth = $auth; 20 | 21 | parent::__construct(); 22 | } 23 | 24 | protected function configure() 25 | { 26 | $this->setDescription('Creates a Firebase user'); 27 | } 28 | 29 | protected function execute(InputInterface $input, OutputInterface $output): int 30 | { 31 | $io = new SymfonyStyle($input, $output); 32 | 33 | $io->note('All properties are optional, you can omit them by pressing .'); 34 | 35 | $properties = [ 36 | 'displayName' => $io->ask('Display name'), 37 | 'photo' => $io->ask('Photo URL'), 38 | 'email' => $email = $io->ask('Email'), 39 | 'emailverified' => $email && $io->confirm('Is the email address verified?', false), 40 | 'phoneNumber' => $io->ask('Phone Number'), 41 | ]; 42 | 43 | $properties = array_filter($properties, static function ($value) { 44 | return null !== $value; 45 | }); 46 | 47 | $user = $this->auth->createUser($properties); 48 | 49 | $io->success('The following user has been stored:'); 50 | $io->writeln(json_encode($user, JSON_PRETTY_PRINT)); 51 | 52 | return 0; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Command/FcmSendMessageCommand.php: -------------------------------------------------------------------------------- 1 | messaging = $messaging; 23 | 24 | parent::__construct(); 25 | } 26 | 27 | protected function configure() 28 | { 29 | $this->setDescription('Send an FCM message'); 30 | } 31 | 32 | protected function execute(InputInterface $input, OutputInterface $output) 33 | { 34 | $io = new SymfonyStyle($input, $output); 35 | 36 | $target = $io->choice('Please select a target', ['Topic', 'Condition', 'Registration Token']); 37 | $validator = static function ($answer) use ($target) { 38 | if (empty($answer)) { 39 | throw new InvalidArgumentException('The '.$target.' must not be empty'); 40 | } 41 | 42 | return $answer; 43 | }; 44 | 45 | switch ($target) { 46 | case 'Topic': 47 | $topic = $io->ask('Please enter the name of the topic', null, $validator); 48 | $message = CloudMessage::withTarget(MessageTarget::TOPIC, $topic); 49 | break; 50 | case 'Condition': 51 | $condition = $io->ask('Please enter the condition', null, $validator); 52 | $message = CloudMessage::withTarget(MessageTarget::CONDITION, $condition); 53 | break; 54 | case 'Registration Token': 55 | $registrationToken = $io->ask('Please enter the registration token', null, $validator); 56 | $message = CloudMessage::withTarget(MessageTarget::TOKEN, $registrationToken); 57 | break; 58 | default: 59 | throw new InvalidArgumentException("Invalid message target {$target}"); 60 | } 61 | 62 | $message = $message->withNotification([ 63 | 'title' => $io->ask('Please enter the title of your message'), 64 | 'body' => $io->ask('Please enter the body of your message'), 65 | ]); 66 | 67 | $responseData = $this->messaging->send($message); 68 | 69 | $io->success('The message has been sent and the API returned the following:'); 70 | $io->writeln(json_encode($responseData, JSON_PRETTY_PRINT)); 71 | 72 | return 0; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Command/GetDatabaseSnapshot.php: -------------------------------------------------------------------------------- 1 | database = $database; 21 | 22 | parent::__construct(); 23 | } 24 | 25 | protected function configure() 26 | { 27 | $this->setDescription('Returns a full snapshot of the Realtime Database'); 28 | } 29 | 30 | protected function execute(InputInterface $input, OutputInterface $output): int 31 | { 32 | $io = new SymfonyStyle($input, $output); 33 | 34 | $io->writeln(json_encode($this->database->getReference()->getValue(), JSON_PRETTY_PRINT)); 35 | 36 | return 0; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Command/ListRemoteConfigVersionsCommand.php: -------------------------------------------------------------------------------- 1 | remoteConfig = $remoteConfig; 21 | 22 | parent::__construct(); 23 | } 24 | 25 | protected function configure() 26 | { 27 | $this 28 | ->setDescription('Lists all remote config versions') 29 | ->addOption('limit', null, InputOption::VALUE_REQUIRED, 'Limit the number of retrieved versions') 30 | ; 31 | } 32 | 33 | protected function execute(InputInterface $input, OutputInterface $output) 34 | { 35 | $io = new SymfonyStyle($input, $output); 36 | 37 | $headers = ['#', 'Updated at', 'Updated by', 'Type', 'Origin']; 38 | $rows = []; 39 | 40 | $query = array_filter([ 41 | 'limit' => $input->getOption('limit'), 42 | ]); 43 | 44 | foreach ($this->remoteConfig->listVersions($query) as $version) { 45 | $rows[] = [ 46 | $version->versionNumber(), 47 | $version->updatedAt()->format('Y-m-d H:i:s'), 48 | $version->user()->name() ?? 'Service Account', 49 | $version->updateType(), 50 | $version->updateOrigin(), 51 | ]; 52 | } 53 | 54 | $io->table($headers, $rows); 55 | 56 | return 0; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Command/ResetProjectCommand.php: -------------------------------------------------------------------------------- 1 | auth = $auth; 27 | $this->database = $database; 28 | $this->storage = $storage; 29 | } 30 | 31 | protected function configure() 32 | { 33 | $this->setDescription('Reset parts of a Firebase project to its initial state'); 34 | } 35 | 36 | protected function execute(InputInterface $input, OutputInterface $output) 37 | { 38 | $io = new SymfonyStyle($input, $output); 39 | 40 | if ($io->confirm('Reset realtime database rules?', false)) { 41 | $this->database->updateRules(RuleSet::default()); 42 | $io->success('Realtime database rules have been reset.'); 43 | } 44 | 45 | if ($io->confirm('Empty realtime database?', false)) { 46 | $this->database->getReference('/')->remove(); 47 | $io->success('Realtime database has been emptied'); 48 | } 49 | 50 | if ($io->confirm('Empty cloud storage?', false)) { 51 | foreach ($this->storage->getBucket()->objects() as $object) { 52 | $object->delete(); 53 | } 54 | $io->success('Cloud Storage has been emptied!'); 55 | } 56 | 57 | if ($io->confirm('Delete all users?', false)) { 58 | $userCount = 0; 59 | 60 | foreach ($this->auth->listUsers() as $user) { 61 | $this->auth->deleteUser($user->uid); 62 | $io->writeln('Deleted user with ID '.$user->uid); 63 | ++$userCount; 64 | } 65 | 66 | $io->success("{$userCount} users have been deleted"); 67 | } 68 | 69 | return 0; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Command/UploadFileCommand.php: -------------------------------------------------------------------------------- 1 | setDescription('Upload a file to Firebase storage') 26 | ->addArgument('source', InputArgument::REQUIRED, 'Local path to file') 27 | ->addArgument('target', InputArgument::REQUIRED, 'Remote path') 28 | ->addOption('bucket', null, InputOption::VALUE_OPTIONAL, 'If omitted, the default bucket will be used.') 29 | ->addOption('public', null, InputOption::VALUE_NONE, 'Whether the file should be publicly available') 30 | ; 31 | } 32 | 33 | public function __construct(Storage $storage, Filesystem $filesystem) 34 | { 35 | parent::__construct(); 36 | 37 | $this->storage = $storage; 38 | $this->filesystem = $filesystem; 39 | } 40 | 41 | protected function execute(InputInterface $input, OutputInterface $output): int 42 | { 43 | $io = new SymfonyStyle($input, $output); 44 | 45 | $source = $input->getArgument('source'); 46 | $target = $input->getArgument('target'); 47 | $bucketName = $input->hasOption('bucket') ? $input->getOption('bucket') : null; 48 | $isPublic = (bool) $input->getOption('public'); 49 | 50 | $bucket = $this->storage->getBucket($bucketName); 51 | 52 | $uploadOptions = array_filter([ 53 | 'name' => $target, 54 | 'predefinedAcl' => $isPublic ? 'publicRead' : null, 55 | ]); 56 | 57 | $uploadedFile = $bucket->upload(fopen($source, 'rb'), $uploadOptions); 58 | 59 | $io->success('File uploaded'); 60 | 61 | $io->title('GCS URI'); 62 | $io->writeln($uploadedFile->gcsUri()); 63 | 64 | $io->title('Signed URL (valid for 1h)'); 65 | $io->writeln($uploadedFile->signedUrl((new DateTimeImmutable())->modify('+1 hour'))); 66 | 67 | if ($isPublic) { 68 | $io->title('Public URL'); 69 | $io->writeln("https://{$bucket->name()}.storage.googleapis.com/{$uploadedFile->name()}"); 70 | } 71 | 72 | return 0; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Controller/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeromegamez/firebase-php-examples/490a8fc33254cac6280bccf4686bd9e5e0674e0e/src/Controller/.gitignore -------------------------------------------------------------------------------- /src/Controller/DatabaseController.php: -------------------------------------------------------------------------------- 1 | database = $firebase; 17 | } 18 | 19 | /** 20 | * @Route("/database", name="database") 21 | */ 22 | public function index(): JsonResponse 23 | { 24 | $reference = '/'; 25 | $snapshot = $this->database->getReference($reference)->getSnapshot(); 26 | 27 | return $this->json([ 28 | 'reference' => $reference, 29 | 'num_children' => $snapshot->numChildren(), 30 | 'children' => $snapshot->hasChildren() ? array_keys($snapshot->getValue()) : null, 31 | ]); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Controller/HomeController.php: -------------------------------------------------------------------------------- 1 | json([ 16 | 'links' => [ 17 | 'database' => $this->generateUrl('database'), 18 | 'users' => $this->generateUrl('users'), 19 | ], 20 | ]); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Controller/UserController.php: -------------------------------------------------------------------------------- 1 | auth = $firebase; 17 | } 18 | 19 | /** 20 | * @Route("/users", name="users") 21 | */ 22 | public function index(): JsonResponse 23 | { 24 | return $this->json([ 25 | 'data' => iterator_to_array($this->auth->listUsers()), 26 | ]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Kernel.php: -------------------------------------------------------------------------------- 1 | import('../config/{packages}/*.yaml'); 17 | $container->import('../config/{packages}/'.$this->environment.'/*.yaml'); 18 | 19 | if (is_file(\dirname(__DIR__).'/config/services.yaml')) { 20 | $container->import('../config/services.yaml'); 21 | $container->import('../config/{services}_'.$this->environment.'.yaml'); 22 | } elseif (is_file($path = \dirname(__DIR__).'/config/services.php')) { 23 | (require $path)($container->withPath($path), $this); 24 | } 25 | } 26 | 27 | protected function configureRoutes(RoutingConfigurator $routes): void 28 | { 29 | $routes->import('../config/{routes}/'.$this->environment.'/*.yaml'); 30 | $routes->import('../config/{routes}/*.yaml'); 31 | 32 | if (is_file(\dirname(__DIR__).'/config/routes.yaml')) { 33 | $routes->import('../config/routes.yaml'); 34 | } elseif (is_file($path = \dirname(__DIR__).'/config/routes.php')) { 35 | (require $path)($routes->withPath($path), $this); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /symfony.lock: -------------------------------------------------------------------------------- 1 | { 2 | "beste/clock": { 3 | "version": "1.0.0" 4 | }, 5 | "beste/json": { 6 | "version": "1.0.0" 7 | }, 8 | "doctrine/annotations": { 9 | "version": "1.0", 10 | "recipe": { 11 | "repo": "github.com/symfony/recipes", 12 | "branch": "master", 13 | "version": "1.0", 14 | "ref": "cb4152ebcadbe620ea2261da1a1c5a9b8cea7672" 15 | } 16 | }, 17 | "doctrine/inflector": { 18 | "version": "v1.3.0" 19 | }, 20 | "doctrine/lexer": { 21 | "version": "v1.0.1" 22 | }, 23 | "fig/http-message-util": { 24 | "version": "1.1.2" 25 | }, 26 | "firebase/php-jwt": { 27 | "version": "v5.0.0" 28 | }, 29 | "google/auth": { 30 | "version": "v1.3.0" 31 | }, 32 | "google/cloud-core": { 33 | "version": "v1.19.1" 34 | }, 35 | "google/cloud-storage": { 36 | "version": "v1.3.4" 37 | }, 38 | "google/crc32": { 39 | "version": "v0.1.0" 40 | }, 41 | "guzzlehttp/guzzle": { 42 | "version": "6.3.2" 43 | }, 44 | "guzzlehttp/promises": { 45 | "version": "v1.3.1" 46 | }, 47 | "guzzlehttp/psr7": { 48 | "version": "1.4.2" 49 | }, 50 | "kreait/firebase-bundle": { 51 | "version": "1.0.0" 52 | }, 53 | "kreait/firebase-php": { 54 | "version": "4.4.0" 55 | }, 56 | "kreait/firebase-tokens": { 57 | "version": "1.7.1" 58 | }, 59 | "lcobucci/clock": { 60 | "version": "2.0.0" 61 | }, 62 | "lcobucci/jwt": { 63 | "version": "3.2.2" 64 | }, 65 | "monolog/monolog": { 66 | "version": "1.23.0" 67 | }, 68 | "mtdowling/jmespath.php": { 69 | "version": "2.4.0" 70 | }, 71 | "nikic/php-parser": { 72 | "version": "v4.0.1" 73 | }, 74 | "psr/cache": { 75 | "version": "1.0.1" 76 | }, 77 | "psr/container": { 78 | "version": "1.0.0" 79 | }, 80 | "psr/event-dispatcher": { 81 | "version": "1.0.0" 82 | }, 83 | "psr/http-client": { 84 | "version": "1.0.1" 85 | }, 86 | "psr/http-factory": { 87 | "version": "1.0.1" 88 | }, 89 | "psr/http-message": { 90 | "version": "1.0.1" 91 | }, 92 | "psr/log": { 93 | "version": "1.0.2" 94 | }, 95 | "psr/simple-cache": { 96 | "version": "1.0.1" 97 | }, 98 | "ralouphie/getallheaders": { 99 | "version": "2.0.5" 100 | }, 101 | "riverline/multipart-parser": { 102 | "version": "2.0.5" 103 | }, 104 | "rize/uri-template": { 105 | "version": "0.3.2" 106 | }, 107 | "sensio/framework-extra-bundle": { 108 | "version": "5.2", 109 | "recipe": { 110 | "repo": "github.com/symfony/recipes", 111 | "branch": "master", 112 | "version": "5.2", 113 | "ref": "fb7e19da7f013d0d422fa9bce16f5c510e27609b" 114 | } 115 | }, 116 | "stella-maris/clock": { 117 | "version": "0.1.4" 118 | }, 119 | "symfony/cache": { 120 | "version": "v4.0.8" 121 | }, 122 | "symfony/cache-contracts": { 123 | "version": "v1.1.5" 124 | }, 125 | "symfony/config": { 126 | "version": "v4.0.8" 127 | }, 128 | "symfony/console": { 129 | "version": "3.3", 130 | "recipe": { 131 | "repo": "github.com/symfony/recipes", 132 | "branch": "master", 133 | "version": "3.3", 134 | "ref": "e3868d2f4a5104f19f844fe551099a00c6562527" 135 | } 136 | }, 137 | "symfony/dependency-injection": { 138 | "version": "v4.0.8" 139 | }, 140 | "symfony/deprecation-contracts": { 141 | "version": "v2.1.2" 142 | }, 143 | "symfony/dotenv": { 144 | "version": "v4.0.8" 145 | }, 146 | "symfony/error-handler": { 147 | "version": "v4.4.1" 148 | }, 149 | "symfony/event-dispatcher": { 150 | "version": "v4.0.8" 151 | }, 152 | "symfony/event-dispatcher-contracts": { 153 | "version": "v1.1.5" 154 | }, 155 | "symfony/filesystem": { 156 | "version": "v4.0.8" 157 | }, 158 | "symfony/finder": { 159 | "version": "v4.0.8" 160 | }, 161 | "symfony/flex": { 162 | "version": "1.0", 163 | "recipe": { 164 | "repo": "github.com/symfony/recipes", 165 | "branch": "master", 166 | "version": "1.0", 167 | "ref": "cc1afd81841db36fbef982fe56b48ade6716fac4" 168 | } 169 | }, 170 | "symfony/framework-bundle": { 171 | "version": "3.3", 172 | "recipe": { 173 | "repo": "github.com/symfony/recipes", 174 | "branch": "master", 175 | "version": "3.3", 176 | "ref": "8a2f7fa50a528f0aad1d7a87ae3730c981b367ce" 177 | } 178 | }, 179 | "symfony/http-foundation": { 180 | "version": "v4.0.8" 181 | }, 182 | "symfony/http-kernel": { 183 | "version": "v4.0.8" 184 | }, 185 | "symfony/maker-bundle": { 186 | "version": "1.0", 187 | "recipe": { 188 | "repo": "github.com/symfony/recipes", 189 | "branch": "master", 190 | "version": "1.0", 191 | "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f" 192 | } 193 | }, 194 | "symfony/polyfill-ctype": { 195 | "version": "v1.8.0" 196 | }, 197 | "symfony/polyfill-intl-grapheme": { 198 | "version": "v1.17.1" 199 | }, 200 | "symfony/polyfill-intl-normalizer": { 201 | "version": "v1.17.1" 202 | }, 203 | "symfony/polyfill-mbstring": { 204 | "version": "v1.7.0" 205 | }, 206 | "symfony/polyfill-php80": { 207 | "version": "v1.17.0" 208 | }, 209 | "symfony/polyfill-php81": { 210 | "version": "v1.23.0" 211 | }, 212 | "symfony/routing": { 213 | "version": "4.0", 214 | "recipe": { 215 | "repo": "github.com/symfony/recipes", 216 | "branch": "master", 217 | "version": "4.0", 218 | "ref": "cda8b550123383d25827705d05a42acf6819fe4e" 219 | } 220 | }, 221 | "symfony/service-contracts": { 222 | "version": "v1.1.5" 223 | }, 224 | "symfony/string": { 225 | "version": "v5.1.2" 226 | }, 227 | "symfony/var-dumper": { 228 | "version": "v4.4.1" 229 | }, 230 | "symfony/var-exporter": { 231 | "version": "v4.2.8" 232 | }, 233 | "symfony/yaml": { 234 | "version": "v4.0.8" 235 | } 236 | } 237 | --------------------------------------------------------------------------------