2121use Google \Cloud \Core \Exception \AbortedException ;
2222use Google \Cloud \Spanner \Session \Session ;
2323use Google \Cloud \Spanner \Session \SessionPoolInterface ;
24+ use Google \Cloud \Spanner \V1 \RequestOptions ;
25+ use Google \Cloud \Spanner \V1 \TransactionOptions ;
26+ use Google \Protobuf \Duration ;
2427
2528/**
2629 * Manages interaction with Cloud Spanner inside a Transaction.
4750 * ```
4851 * use Google\Cloud\Spanner\SpannerClient;
4952 *
50- * $spanner = new SpannerClient();
53+ * $spanner = new SpannerClient(['projectId' => 'my-project'] );
5154 *
5255 * $database = $spanner->connect('my-instance', 'my-database');
5356 *
@@ -68,69 +71,58 @@ class Transaction implements TransactionalReadInterface
6871 use MutationTrait;
6972 use TransactionalReadTrait;
7073
71- /**
72- * @var CommitStats
73- */
74- private $ commitStats = [];
75-
76- /**
77- * @var array
78- */
79- private $ mutations = [];
80-
81- /**
82- * @var bool
83- */
84- private $ isRetry = false ;
85-
86- private ValueMapper $ mapper ;
74+ private array $ commitStats = [];
75+ private array $ mutations = [];
76+ private bool $ isRetry ;
77+ private array |RequestOptions $ requestOptions ;
8778
8879 /**
8980 * @param Operation $operation The Operation instance.
9081 * @param Session $session The session to use for spanner interactions.
91- * @param string $transactionId [optional] The Transaction ID. If no ID is
92- * provided, the Transaction will be a Single-Use Transaction.
93- * @param bool $isRetry Whether the transaction will automatically retry or not.
94- * @param string $tag A transaction tag. Requests made using this transaction will
95- * use this as the transaction tag.
96- * @param array $options [optional] {
82+ * @param string $transactionId The Transaction ID. If no ID is provided, the Transaction will
83+ * be a Single-Use Transaction.
84+ * @param array $options {
9785 * Configuration Options.
9886 *
99- * @type array $begin The begin Transaction options.
100- * [Refer](https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.v1#transactionoptions)
101- * @type int $isolationLevel level of Isolation for this transaction instance
102- * **Defaults to** IsolationLevel::ISOLATION_LEVEL_UNSPECIFIED
87+ * @type bool $isRetry Whether the transaction will automatically retry or not.
88+ * @type string $tag A transaction tag. Requests made using this transaction will
89+ * use this as the transaction tag.
90+ * @type array $begin The begin Transaction options, for using inline begin transactions.
91+ * See {@see V1\TransactionOptions}.
92+ * @type array $singleUse The singleUse Transaction options. See {@see V1\TransactionOptions}.
93+ * @type array $requestOptions See {@see V1\RequestOptions}.
94+ * @type array $transactionOptions See {@see V1\TransactionOptions}.
10395 * }
10496 * @param ValueMapper $mapper Consumed internally for properly map mutation data.
10597 * @throws \InvalidArgumentException if a tag is specified on a single-use transaction.
10698 */
10799 public function __construct (
108- Operation $ operation ,
109- Session $ session ,
110- $ transactionId = null ,
111- $ isRetry = false ,
112- $ tag = null ,
113- $ options = [],
114- $ mapper = null
100+ private Operation $ operation ,
101+ private Session $ session ,
102+ private string |null $ transactionId = null ,
103+ array $ options = [],
104+ private ValueMapper |null $ mapper = null
115105 ) {
116- $ this ->operation = $ operation ;
117- $ this ->session = $ session ;
118- $ this ->transactionId = $ transactionId ;
119- $ this ->isRetry = $ isRetry ;
120-
121106 $ this ->type = ($ transactionId || isset ($ options ['begin ' ]))
122107 ? self ::TYPE_PRE_ALLOCATED
123108 : self ::TYPE_SINGLE_USE ;
124109
125- if ($ this ->type == self ::TYPE_SINGLE_USE && isset ($ tag )) {
110+ if ($ this ->type == self ::TYPE_SINGLE_USE && isset ($ options [ ' tag ' ] )) {
126111 throw new \InvalidArgumentException (
127112 'Cannot set a transaction tag on a single-use transaction. '
128113 );
129114 }
130- $ this ->tag = $ tag ;
131115
132116 $ this ->context = SessionPoolInterface::CONTEXT_READWRITE ;
133- $ this ->options = $ options ;
117+ $ this ->tag = $ options ['tag ' ] ?? null ;
118+ $ this ->isRetry = $ options ['isRetry ' ] ?? false ;
119+ $ this ->transactionSelector = array_intersect_key (
120+ (array ) $ options ,
121+ array_flip (['singleUse ' , 'begin ' ])
122+ );
123+ $ this ->requestOptions = $ options ['requestOptions ' ] ?? [];
124+ $ this ->transactionOptions = $ options ['transactionOptions ' ] ?? new TransactionOptions ();
125+
134126 if (!is_null ($ mapper )) {
135127 $ this ->mapper = $ mapper ;
136128 }
@@ -149,7 +141,7 @@ public function __construct(
149141 *
150142 * @return array The commit stats
151143 */
152- public function getCommitStats ()
144+ public function getCommitStats (): array
153145 {
154146 return $ this ->commitStats ;
155147 }
@@ -244,7 +236,7 @@ public function getCommitStats()
244236 * @return int The number of rows modified.
245237 * @throws ValidationException
246238 */
247- public function executeUpdate ($ sql , array $ options = [])
239+ public function executeUpdate (string $ sql , array $ options = []): int
248240 {
249241 if (isset ($ options ['transaction ' ]['begin ' ]['excludeTxnFromChangeStreams ' ])) {
250242 throw new ValidationException (
@@ -257,7 +249,7 @@ public function executeUpdate($sql, array $options = [])
257249 if (isset ($ options ['transaction ' ]['begin ' ]['readOnly ' ])) {
258250 // isolationLevel can only be used with read/write transactions
259251 throw new ValidationException (
260- 'The isolation level can only be applied to read/write transactions. ' .
252+ 'The isolation level can only be applied to read/write transactions. ' .
261253 'Single use transactions are not read/write ' ,
262254 );
263255 }
@@ -354,7 +346,7 @@ public function executeUpdate($sql, array $options = [])
354346 * @return BatchDmlResult
355347 * @throws \InvalidArgumentException If any statement is missing the `sql` key.
356348 */
357- public function executeUpdateBatch (array $ statements , array $ options = [])
349+ public function executeUpdateBatch (array $ statements , array $ options = []): BatchDmlResult
358350 {
359351 $ options = $ this ->buildUpdateOptions ($ options );
360352 return $ this ->operation
@@ -385,7 +377,7 @@ public function executeUpdateBatch(array $statements, array $options = [])
385377 * @param array $options [optional] Configuration Options.
386378 * @return void
387379 */
388- public function rollback (array $ options = [])
380+ public function rollback (array $ options = []): void
389381 {
390382 if ($ this ->state !== self ::STATE_ACTIVE ) {
391383 throw new \BadMethodCallException ('The transaction cannot be rolled back because it is not active ' );
@@ -432,28 +424,27 @@ public function rollback(array $options = [])
432424 * Please note, the `requestTag` setting will be ignored as it is not supported for commit requests.
433425 * }
434426 * @return Timestamp The commit timestamp.
435- * @throws \BadMethodCall If the transaction is not active or already used.
427+ * @throws \BadMethodCallException If the transaction is not active or already used.
436428 * @throws AbortedException If the commit is aborted for any reason.
437429 */
438- public function commit (array $ options = [])
430+ public function commit (array $ options = []): Timestamp
439431 {
440432 if ($ this ->state !== self ::STATE_ACTIVE ) {
441433 throw new \BadMethodCallException ('The transaction cannot be committed because it is not active ' );
442434 }
443435
444436 // For commit, A transaction ID is mandatory for non-single-use transactions,
445- // and the `begin` option is not supported.
446- if (empty ($ this ->transactionId ) && isset ($ this ->options ['begin ' ])) {
447- // Since the begin option is not supported in commit, unset it.
448- unset($ this ->options ['begin ' ]);
449-
450- // A transaction ID is mandatory for non-single-use transactions.
451- if ($ this ->type !== self ::TYPE_SINGLE_USE ) {
452- // Execute the beginTransaction RPC.
453- $ transaction = $ this ->operation ->transaction ($ this ->session , $ this ->options );
454- // Set the transaction ID of the current transaction.
455- $ this ->transactionId = $ transaction ->id ();
456- }
437+ // and the `begin` option is not supported (because `begin` is only used in "inline begin transactions")
438+ if (empty ($ this ->transactionId ) && isset ($ this ->transactionSelector ['begin ' ])) {
439+ $ operationTransactionOptions = array_filter ([
440+ 'requestOptions ' => $ this ->requestOptions ,
441+ 'transactionOptions ' => $ this ->transactionOptions ,
442+ 'singleUse ' => $ this ->transactionSelector ['singleUse ' ] ?? null ,
443+ ]);
444+ // Execute the beginTransaction RPC.
445+ $ transaction = $ this ->operation ->transaction ($ this ->session , $ operationTransactionOptions );
446+ // Set the transaction ID of the current transaction.
447+ $ this ->transactionId = $ transaction ->id ();
457448 }
458449
459450 if (!$ this ->singleUseState ()) {
@@ -476,6 +467,7 @@ public function commit(array $options = [])
476467
477468 $ t = $ this ->transactionOptions ($ options );
478469
470+ // @TODO find out what this is and clean it up
479471 $ options [$ t [1 ]] = $ t [0 ];
480472
481473 $ res = $ this ->operation ->commitWithResponse ($ this ->session , $ this ->pluck ('mutations ' , $ options ), $ options );
@@ -499,7 +491,7 @@ public function commit(array $options = [])
499491 *
500492 * @return int
501493 */
502- public function state ()
494+ public function state (): int
503495 {
504496 return $ this ->state ;
505497 }
@@ -522,7 +514,7 @@ public function state()
522514 *
523515 * @return bool
524516 */
525- public function isRetry ()
517+ public function isRetry (): bool
526518 {
527519 return $ this ->isRetry ;
528520 }
@@ -548,14 +540,17 @@ private function buildUpdateOptions(array $options): array
548540 $ this ->seqno ++;
549541
550542 $ options ['transactionType ' ] = $ this ->context ;
551- if (empty ($ this ->transactionId ) && isset ($ this ->options ['begin ' ])) {
552- $ options ['begin ' ] = $ this ->options ['begin ' ];
543+ if (empty ($ this ->transactionId ) && isset ($ this ->transactionSelector ['begin ' ])) {
544+ $ options ['begin ' ] = $ this ->transactionSelector ['begin ' ];
553545 } else {
554546 $ options ['transactionId ' ] = $ this ->transactionId ;
555547 }
548+
556549 $ selector = $ this ->transactionSelector ($ options );
557550 $ options ['transaction ' ] = $ selector [0 ];
558551
559- return $ this ->addLarHeader ($ options );
552+ $ options ['headers ' ]['spanner-route-to-leader ' ] = ['true ' ];
553+
554+ return $ options ;
560555 }
561556}
0 commit comments