Can we CRUD and perform custom queries in Google Firestore with PHP?

I’m trying to connect PHP - codeigniter with google Firebase as my android app has authentication with firebase, my Web application will use the same Auth

its Firestore not real time database

I want to perform CRUD operation through PHP

I used the following links and code,

I can authenticate using the login and verify_id_token functions

But can’t read collections

– please read comment in AuthController->get_user_info()

I’m using

GitHub - kreait/firebase-php: Unofficial Firebase Admin SDK for PHP kreait/firebase-php

Google Cloud google\Cloud\Firestore\FirestoreClient;

this is my Helper.php

<?php

use Kreait\Firebase\Factory;
use Kreait\Firebase\Auth;
use Google\Cloud\Firestore\FirestoreClient;

function firebase_auth() {
    static $auth = null;

    if ($auth === null) {
        $serviceAccount = __DIR__ . '/../config/somthing.json'; // Adjust path as needed
        $factory = (new Factory)->withServiceAccount($serviceAccount);
        $auth = $factory->createAuth(); // Use the Factory from Kreait\Firebase
    }

    return $auth;
}

function create_firestore(string $projectId = null) {
    static $firestore = null;
    if ($firestore === null) {
        try {
            $serviceAccount = __DIR__ . '/../config/somthing.json';
            $firestore = new FirestoreClient([
                'projectId' => $projectId, // Set project ID if needed
            ]); // Directly create FirestoreClient
        } catch (Exception $e) {
            // Handle the exception, e.g., log the error or throw a custom exception
            echo "Error creating Firestore client: " . $e->getMessage();
        }
    }

    return $firestore;
}

This is my Authcontroller

<?php

use Monolog\Handler\StreamHandler;
use Monolog\Logger;

class AuthController extends CI_Controller {
    private $logger;

    public function __construct() {
        parent::__construct();
        $this->logger = new Logger('FirestoreLogger');
        $this->logger->pushHandler(new StreamHandler(APPPATH . 'logs/firestore_debug.log', Logger::DEBUG));
    }
    public function verify_id_token($idToken) {
        try {
            $auth = firebase_auth();
            $verifiedIdToken = $auth->verifyIdToken($idToken);
            return $verifiedIdToken->claims()->get('sub'); // Return the UID
        } catch (\Kreait\Firebase\Exception\AuthException $e) {
            echo "Token verification failed: " . $e->getMessage();
            return null; // Token verification failed
        } catch (\Kreait\Firebase\Exception\FirebaseException $e) {
            echo "Firebase error: " . $e->getMessage();
            return null; // Token verification failed
        }
    }

    public function login() {
        $email = 'nuked.hacker@gmail.com'; 
        $password = 'abc123';

        try {
            $auth = firebase_auth();

            // Attempt to sign in the user
            $signInResult = $auth->signInWithEmailAndPassword($email, $password);
            
            // Retrieve the ID token
            $idToken = $signInResult->idToken();

            // Store the ID token in session or return it to the client
            $_SESSION['idToken'] = $idToken; // Example of storing in session

            echo "User signed in successfully. ID Token: " . $idToken;

        } catch (\Kreait\Firebase\Exception\AuthException $e) {
            echo "Authentication failed: " . $e->getMessage();
        } catch (\Kreait\Firebase\Exception\FirebaseException $e) {
            echo "Firebase error: " . $e->getMessage();
        }
    }

    public function get_user_info($uid) {
        // Retrieve the ID token from session or request
        $idToken = isset($_SESSION['idToken']) ? $_SESSION['idToken'] : null;

        // Verify the ID token
        $verifiedUid = $this->verify_id_token($idToken);

        if ($verifiedUid === null) {
            echo "User is not authenticated.";
            return;
        }

        // Check UID authorization
        if ($verifiedUid !== $uid) {
            echo "You are not authorized to access this user data.";
            return;
        }

        try {
            // Initialize Firestore
            $firestore = create_firestore('tracingapp-13543');

            // Attempt to access document
            $collection = $firestore->collection('users');
            $document = $collection->document($uid);
            $userDocument = $document->snapshot();

//when i use above line it shows me site can't be reached

            if ($userDocument->exists()) {
                $userData = $userDocument->data();
                echo "User Info: ";
                print_r($userData);
            } else {
                echo "User does not exist.";
            }
        } catch (\Kreait\Firebase\Exception\FirebaseException $e) {
            $this->logger->error("Firestore Error: " . $e->getMessage());
            echo "An error occurred. Check logs for details.";
        }
    }
}

what am i doing wrong

$userDocument = $document->snapshot();

this line says site isn’t working
there is no error or debug any where, (apache logs, CI logs, monologs)

When print_r above code before this line, i.e $document i get an object

But when i use snapshot, it says site isn’t working

Yes I can open firebase console

Your issue likely stems from a misconfiguration or a missing dependency in your Firestore setup. Here’s a step-by-step guide to resolve this:

1. Check Firestore Configuration

  • Ensure that the service account JSON file (somthing.json) has the correct permissions for Firestore access.
    • It should include the roles/datastore.user role or equivalent.
  • Verify the projectId in your create_firestore method matches your Firestore project.

2. Update create_firestore Method

Your create_firestore function is missing a critical part where it uses the service account to authenticate with Firestore. Update it as follows:

function create_firestore(string $projectId = null) {
    static $firestore = null;
    if ($firestore === null) {
        try {
            $serviceAccount = __DIR__ . '/../config/somthing.json';
            $firestore = new FirestoreClient([
                'keyFilePath' => $serviceAccount, // Add this line for service account authentication
                'projectId' => $projectId,
            ]);
        } catch (Exception $e) {
            echo "Error creating Firestore client: " . $e->getMessage();
        }
    }
    return $firestore;
}

3. Debugging Firestore Snapshot

When calling $document->snapshot(), ensure:

  1. Your network allows outbound requests to Firestore’s APIs.
  2. You have the required dependencies installed.

To debug further:

  • Add a try-catch block directly around the snapshot call to capture potential exceptions:
try {
    $userDocument = $document->snapshot();
    if ($userDocument->exists()) {
        $userData = $userDocument->data();
        echo "User Info: ";
        print_r($userData);
    } else {
        echo "User does not exist.";
    }
} catch (\Google\Cloud\Core\Exception\GoogleException $e) {
    echo "Firestore Error: " . $e->getMessage();
}

4. Verify Dependencies

Ensure all required packages are installed:

composer require google/cloud-firestore kreait/firebase-php monolog/monolog

5. Test Firestore Connection

Create a standalone script to test Firestore outside of CodeIgniter. This will help isolate the issue:

require 'vendor/autoload.php';

use Google\Cloud\Firestore\FirestoreClient;

$serviceAccount = __DIR__ . '/config/somthing.json'; // Adjust path as needed
$firestore = new FirestoreClient([
    'keyFilePath' => $serviceAccount,
    'projectId' => 'tracingapp-13543',
]);

try {
    $collection = $firestore->collection('users');
    $document = $collection->document('test-user-id'); // Replace with a real document ID
    $snapshot = $document->snapshot();

    if ($snapshot->exists()) {
        echo "Document data: ";
        print_r($snapshot->data());
    } else {
        echo "No such document!";
    }
} catch (Exception $e) {
    echo "Error: " . $e->getMessage();
}

Run this script:

php test_firestore.php

6. Network and Environment

  • Ensure your Docker container or server has network access to Firestore endpoints.
    • Check if your firewall or network policies block outgoing requests.
  • Verify the PHP environment meets Firestore’s requirements:
    • PHP version >= 7.3
    • ext-json, ext-curl enabled

7. Review Logs

If no errors appear in Apache or CodeIgniter logs:

  • Enable Google Cloud Firestore logs:
putenv('GOOGLE_CLOUD_ENABLE_LOGGING=true');
  • Log output might appear in your default PHP logs or in a dedicated directory.

8. Use Firestore Emulator (Optional)

If network or API access remains an issue, test with the Firestore emulator locally:

  1. Install the Firebase CLI and start the emulator:
firebase emulators:start --only firestore
  1. Modify your FirestoreClient initialization to connect to the emulator:
$firestore = new FirestoreClient([
    'keyFilePath' => $serviceAccount,
    'projectId' => 'your-emulated-project-id',
    'apiEndpoint' => 'localhost:8080',
]);

9. Possible Causes for site isn’t working

  • PHP Error: If snapshot() throws an uncaught exception, it might crash the script.
  • Memory Limit: Large data or unoptimized queries might hit PHP memory limits. Increase it in php.ini:
memory_limit = 256M
  • Timeout: If Firestore APIs are slow or blocked, the script might time out. Increase the execution time in php.ini:
max_execution_time = 300

By addressing these steps, you should be able to debug and resolve the issue with Firestore integration in your CodeIgniter application. Let me know if you encounter specific roadblocks!