Firestore/src/FirestoreClient.php
public function runTransaction(callable $callable, array $options = [])
    {
        $options += [
            'maxRetries' => self::MAX_RETRIES,
            'begin' => [],
            'commit' => [],
            'rollback' => []
        ];

        $retryableErrors = [
            AbortedException::class
        ];

        $delayFn = function () {
            return [
                'seconds' => 0,
                'nanos' => 0
            ];
        };

        $retryFn = function (\Exception $e) use ($retryableErrors) {
            return in_array(get_class($e), $retryableErrors);
        };

        // Track the Transaction ID outside the retry function.
        // If the transaction is retried after an abort, the previous transaction
        // must be provided to the subsequent `beginTransaction` rpc.
        // It also provides a convenient indication to the user whether the
        // transaction is retried or not.
        $transactionId = null;

        $retry = new Retry($options['maxRetries'], $delayFn, $retryFn);

        return $retry->execute(function (
            callable $callable,
            array $options
        ) use (&$transactionId) {
            $database = $this->databaseName($this->projectId, $this->database);

            $beginTransaction = $this->connection->beginTransaction(array_filter([
                'database' => $database,
                'retryTransaction' => $transactionId
            ]) + $options['begin']);

            $transactionId = $beginTransaction['transaction'];

            $transaction = new Transaction(
                $this->connection,
                $this->valueMapper,
                $database,
                $transactionId
            );

            try {
                $res = $callable($transaction);

                if (!$transaction->writer()->isEmpty()) {
                    $transaction->writer()->commit([
                        'transaction' => $transactionId
                    ] + $options['commit']);
                } else {
                    // trigger rollback if no writes exist.
                    $transaction->writer()->rollback($options['rollback']);
                }

                return $res;
            } catch (\Exception $e) {
                $transaction->writer()->rollback($options['rollback']);

                throw $e;
            }
        }, [
            $callable,
            $options
        ]);
    }
public function runTransaction(callable $callable, array $options = [])
    {
        $options += [
            'maxRetries' => self::MAX_RETRIES,
            'begin' => [],
            'commit' => [],
            'rollback' => []
        ];

        $retryableErrors = [
            AbortedException::class
        ];

        $delayFn = function () {
            return [
                'seconds' => 0,
                'nanos' => 0
            ];
        };

        $retryFn = function (\Exception $e) use ($retryableErrors) {
            return in_array(get_class($e), $retryableErrors);
        };

        // Track the Transaction ID outside the retry function.
        // If the transaction is retried after an abort, the previous transaction
        // must be provided to the subsequent `beginTransaction` rpc.
        // It also provides a convenient indication to the user whether the
        // transaction is retried or not.
        $transactionId = null;

        $retry = new Retry($options['maxRetries'], $delayFn, $retryFn);

        return $retry->execute(function (
            callable $callable,
            array $options
        ) use (&$transactionId) {
            $database = $this->databaseName($this->projectId, $this->database);

            $beginTransaction = $this->connection->beginTransaction(array_filter([
                'database' => $database,
                'retryTransaction' => $transactionId
            ]) + $options['begin']);

            $transactionId = $beginTransaction['transaction'];

            $transaction = new Transaction(
                $this->connection,
                $this->valueMapper,
                $database,
                $transactionId
            );

            try {
                $res = $callable($transaction);

                if (!$transaction->writer()->isEmpty()) {
                    $transaction->writer()->commit([
                        'transaction' => $transactionId
                    ] + $options['commit']);
                } else {
                    // trigger rollback if no writes exist.
                    $transaction->writer()->rollback($options['rollback']);
                }

                return $res;
            } catch (\Exception $e) {
                $transaction->writer()->rollback($options['rollback']);

                throw $e;
            }
        }, [
            $callable,
            $options
        ]);
    }
public function runTransaction ( callable $ callable , array $ options = [ ] ) { $ options += [ 'maxRetries' => self :: MAX_RETRIES , 'begin' => [ ] , 'commit' => [ ] , 'rollback' => [ ] ] ; $ retryableErrors = [ AbortedException :: class ] ; $ delayFn = function ( ) { return [ 'seconds' => 0 , 'nanos' => 0 ] ; } ; $ retryFn = function ( \ Exception $ e ) use ( $ retryableErrors ) { return in_array ( get_class ( $ e ) , $ retryableErrors ) ; } ; // Track the Transaction ID outside the retry function. // If the transaction is retried after an abort, the previous transaction // must be provided to the subsequent `beginTransaction` rpc. // It also provides a convenient indication to the user whether the // transaction is retried or not. $ transactionId = null ; $ retry = new Retry ( $ options [ 'maxRetries' ] , $ delayFn , $ retryFn ) ; return $ retry -> execute ( function ( callable $ callable , array $ options ) use ( & $ transactionId ) { $ database = $ this -> databaseName ( $ this -> projectId , $ this -> database ) ; $ beginTransaction = $ this -> connection -> beginTransaction ( array_filter ( [ 'database' => $ database , 'retryTransaction' => $ transactionId ] ) + $ options [ 'begin' ] ) ; $ transactionId = $ beginTransaction [ 'transaction' ] ; $ transaction = new Transaction ( $ this -> connection , $ this -> valueMapper , $ database , $ transactionId ) ; try { $ res = $ callable ( $ transaction ) ; if ( ! $ transaction -> writer ( ) -> isEmpty ( ) ) { $ transaction -> writer ( ) -> commit ( [ 'transaction' => $ transactionId ] + $ options [ 'commit' ] ) ; } else { // trigger rollback if no writes exist. $ transaction -> writer ( ) -> rollback ( $ options [ 'rollback' ] ) ; } return $ res ; } catch ( \ Exception $ e ) { $ transaction -> writer ( ) -> rollback ( $ options [ 'rollback' ] ) ; throw $ e ; } } , [ $ callable , $ options ] ) ; }
Executes a function in a Firestore transaction.

Transactions offer atomic operations, guaranteeing that either all
writes will be applied, or none will be applied. The Google Cloud PHP
Firestore client also handles automatic retry in cases where transactions
fail due to a retryable error.

Transactions will be committed once the provided callable has finished
execution. Thrown exceptions will prevent commit and trigger a rollback,
and will bubble up to your level to be handled in whatever fashion is
appropriate.

This method returns the return value of the given transaction callable.

Example:
```
use Google\Cloud\Firestore\Transaction;

$transferAmount = 500.00;
$from = $firestore->document('users/john');
$to = $firestore->document('users/dave');

$toNewBalance = $firestore->runTransaction(function (Transaction $t) use ($from, $to, $transferAmount) {
$fromSnapshot = $t->snapshot($from);
$toSnapshot = $t->snapshot($to);

$fromNewBalance = $fromSnapshot['balance'] - $transferAmount;
$toNewBalance = $toSnapshot['balance'] + $transferAmount;

// If the transaction cannot be completed, throwing any exception
// will trigger a rollback operation.
if ($fromNewBalance < 0) {
throw new \Exception('User 1 has insufficient funds!');
}

$t->update($from, [
['path' => 'balance', 'value' => $fromNewBalance]
])->update($to, [
['path' => 'balance', 'value' => $toNewBalance]
]);

return $toNewBalance;
});
```

@codingStandardsIgnoreStart
@see https://cloud.google.com/firestore/docs/reference/rpc/google.firestore.v1beta1#google.firestore.v1beta1.Firestore.BeginTransaction BeginTransaction
@see https://cloud.google.com/firestore/docs/reference/rpc/google.firestore.v1beta1#google.firestore.v1beta1.Firestore.Commit Commit
@see https://cloud.google.com/firestore/docs/reference/rpc/google.firestore.v1beta1#google.firestore.v1beta1.Firestore.Rollback Rollback
@codingStandardsIgnoreEnd

@param callable $callable A callable function, allowing atomic operations
against the Firestore API. Function signature should be of form:
`function (Transaction $t)`.
@param array $options {
Configuration Options.

@type array $begin Configuration options for BeginTransaction.
@type array $commit Configuration options for Commit.
@type array $rollback Configuration options for rollback.
@type int $maxRetries The maximum number of times to retry failures.
**Defaults to** `5`.
}
@return mixed
Executes a function in a Firestore transaction .