Repository: GoogleCloudPlatform/pgadapter
Pull request #2718 (2025-01-08 14:41:43)

docs: add PHP snippets
Adds PHP sample snippets for the Getting Started guide.


Patch:
diff --git a/.github/workflows/snippets.yaml b/.github/workflows/snippets.yaml
index 871c46dd..bb118436 100644
--- a/.github/workflows/snippets.yaml
+++ b/.github/workflows/snippets.yaml
@@ -80,3 +80,18 @@ jobs:
       - name: Run dotnet snippets
         working-directory: ./samples/snippets/dotnet-snippets
         run: dotnet test
+  php-snippets:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - uses: shivammathur/setup-php@v2
+        with:
+          php-version: '8.3'
+          tools: composer
+      - run: php --version
+      - name: Install dependencies
+        working-directory: ./samples/snippets/php-snippets
+        run: composer install
+      - name: Run PHP snippets
+        working-directory: ./samples/snippets/php-snippets/test
+        run: ../vendor/bin/phpunit SamplesTest.php
diff --git a/.gitignore b/.gitignore
index ec2e7eaf..e9bc04d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,5 +37,6 @@ samples/**/ruby/**/Gemfile.lock
 
 samples/**/vendor
 samples/**/composer.lock
+samples/**/.phpunit.result.cache
 src/test/**/vendor
 src/test/**/composer.lock
diff --git a/README.md b/README.md
index c2bee9f8..ec182829 100644
--- a/README.md
+++ b/README.md
@@ -273,7 +273,7 @@ The following list contains the most frequently used startup options for PGAdapt
     Docker container, as the connections from the host machine will not be seen as a connection from
     localhost in the container.
 
---allow_shutdown_command
+--allow_shutdown_statement
   * Enables the use of the custom SQL command `SHUTDOWN [SMART | FAST | IMMEDIATE]`. This command can
     be used to shut down PGAdapter by just sending it a SQL statement. This option should only be enabled
     when PGAdapter runs in a trusted environment, for example as a side-car container. The default
diff --git a/benchmarks/latency-comparison/golang/go.mod b/benchmarks/latency-comparison/golang/go.mod
index d67b48b8..384d8e11 100644
--- a/benchmarks/latency-comparison/golang/go.mod
+++ b/benchmarks/latency-comparison/golang/go.mod
@@ -2,24 +2,24 @@ module cloud.google.com/pgadapter-latency-benchmark
 
 go 1.22
 
-toolchain go1.23.4
+toolchain go1.23.5
 
 require (
 	cloud.google.com/go/spanner v1.73.0
-	github.com/docker/docker v27.4.1+incompatible
+	github.com/docker/docker v27.5.0+incompatible
 	github.com/jackc/pgx/v4 v4.18.3
 	github.com/jackc/pgx/v5 v5.7.2
 	github.com/montanaflynn/stats v0.7.0
 	github.com/testcontainers/testcontainers-go v0.35.0
 	golang.org/x/oauth2 v0.25.0
-	google.golang.org/api v0.216.0
+	google.golang.org/api v0.217.0
 )
 
 require (
 	cel.dev/expr v0.16.2 // indirect
 	cloud.google.com/go v0.116.0 // indirect
-	cloud.google.com/go/auth v0.13.0 // indirect
-	cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
+	cloud.google.com/go/auth v0.14.0 // indirect
+	cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
 	cloud.google.com/go/compute/metadata v0.6.0 // indirect
 	cloud.google.com/go/monitoring v1.21.2 // indirect
 	dario.cat/mergo v1.0.0 // indirect
@@ -50,7 +50,7 @@ require (
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
-	github.com/google/s2a-go v0.1.8 // indirect
+	github.com/google/s2a-go v0.1.9 // indirect
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
 	github.com/googleapis/gax-go/v2 v2.14.1 // indirect
@@ -93,17 +93,17 @@ require (
 	go.opentelemetry.io/otel/sdk v1.31.0 // indirect
 	go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect
 	go.opentelemetry.io/otel/trace v1.31.0 // indirect
-	golang.org/x/crypto v0.31.0 // indirect
-	golang.org/x/net v0.33.0 // indirect
+	golang.org/x/crypto v0.32.0 // indirect
+	golang.org/x/net v0.34.0 // indirect
 	golang.org/x/sync v0.10.0 // indirect
-	golang.org/x/sys v0.28.0 // indirect
+	golang.org/x/sys v0.29.0 // indirect
 	golang.org/x/text v0.21.0 // indirect
 	golang.org/x/time v0.9.0 // indirect
 	golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
 	google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
-	google.golang.org/grpc v1.69.2 // indirect
-	google.golang.org/protobuf v1.36.1 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 // indirect
+	google.golang.org/grpc v1.69.4 // indirect
+	google.golang.org/protobuf v1.36.2 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/benchmarks/latency-comparison/golang/go.sum b/benchmarks/latency-comparison/golang/go.sum
index 4d2924bb..87c4b4a3 100644
--- a/benchmarks/latency-comparison/golang/go.sum
+++ b/benchmarks/latency-comparison/golang/go.sum
@@ -137,6 +137,8 @@ cloud.google.com/go/auth v0.11.0 h1:Ic5SZz2lsvbYcWT5dfjNWgw6tTlGi2Wc8hyQSC9BstA=
 cloud.google.com/go/auth v0.11.0/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
 cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs=
 cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q=
+cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM=
+cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A=
 cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
 cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
 cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI=
@@ -147,6 +149,8 @@ cloud.google.com/go/auth/oauth2adapt v0.2.5 h1:2p29+dePqsCHPP1bqDJcKj4qxRyYCcbzK
 cloud.google.com/go/auth/oauth2adapt v0.2.5/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
 cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU=
 cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
+cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
+cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
 cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0=
 cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8=
 cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8=
@@ -797,6 +801,8 @@ github.com/docker/docker v27.4.0+incompatible h1:I9z7sQ5qyzO0BfAb9IMOawRkAGxhYsi
 github.com/docker/docker v27.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker v27.4.1+incompatible h1:ZJvcY7gfwHn1JF48PfbyXg7Jyt9ZCWDW+GGXOIxEwp4=
 github.com/docker/docker v27.4.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U=
+github.com/docker/docker v27.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
 github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
 github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -950,6 +956,8 @@ github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
 github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
 github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
 github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
+github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
+github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -1303,6 +1311,8 @@ golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
 golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
 golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
 golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
+golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
+golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1434,6 +1444,8 @@ golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
 golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
 golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
 golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
+golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
+golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1600,6 +1612,8 @@ golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
 golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
 golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
+golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1842,6 +1856,8 @@ google.golang.org/api v0.215.0 h1:jdYF4qnyczlEz2ReWIsosNLDuzXyvFHJtI5gcr0J7t0=
 google.golang.org/api v0.215.0/go.mod h1:fta3CVtuJYOEdugLNWm6WodzOS8KdFckABwN4I40hzY=
 google.golang.org/api v0.216.0 h1:xnEHy+xWFrtYInWPy8OdGFsyIfWJjtVnO39g7pz2BFY=
 google.golang.org/api v0.216.0/go.mod h1:K9wzQMvWi47Z9IU7OgdOofvZuw75Ge3PPITImZR/UyI=
+google.golang.org/api v0.217.0 h1:GYrUtD289o4zl1AhiTZL0jvQGa2RDLyC+kX1N/lfGOU=
+google.golang.org/api v0.217.0/go.mod h1:qMc2E8cBAbQlRypBTBWHklNJlaZZJBwDv81B1Iu8oSI=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -2039,6 +2055,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:
 google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 h1:3UsHvIr4Wc2aW4brOaSCmcxh9ksica6fHEr8P1XhkYw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -2096,6 +2114,8 @@ google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
 google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
 google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU=
 google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
+google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
+google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -2122,6 +2142,8 @@ google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8i
 google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
 google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
+google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/benchmarks/latency-comparison/java/pom.xml b/benchmarks/latency-comparison/java/pom.xml
index 5fc6302a..3e6638d3 100644
--- a/benchmarks/latency-comparison/java/pom.xml
+++ b/benchmarks/latency-comparison/java/pom.xml
@@ -50,7 +50,7 @@
       <dependency>
         <groupId>io.opentelemetry</groupId>
         <artifactId>opentelemetry-bom</artifactId>
-        <version>1.45.0</version>
+        <version>1.46.0</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
@@ -87,7 +87,7 @@
     <dependency>
       <groupId>org.postgresql</groupId>
       <artifactId>postgresql</artifactId>
-      <version>42.7.4</version>
+      <version>42.7.5</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
diff --git a/benchmarks/tpcc/pom.xml b/benchmarks/tpcc/pom.xml
index 883e12ab..9ef3d5c0 100644
--- a/benchmarks/tpcc/pom.xml
+++ b/benchmarks/tpcc/pom.xml
@@ -28,7 +28,7 @@
       <dependency>
         <groupId>io.opentelemetry</groupId>
         <artifactId>opentelemetry-bom</artifactId>
-        <version>1.45.0</version>
+        <version>1.46.0</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
diff --git a/build/DockerfilePsql13 b/build/DockerfilePsql13
index f145efc7..29ecaf16 100644
--- a/build/DockerfilePsql13
+++ b/build/DockerfilePsql13
@@ -1,3 +1,3 @@
-FROM alpine:3.21.1
+FROM alpine:3.21.2
 RUN apk --no-cache add postgresql13-client
 ENTRYPOINT [ "psql" ]
diff --git a/build/DockerfilePsql14 b/build/DockerfilePsql14
index 2262a9b3..010033ce 100644
--- a/build/DockerfilePsql14
+++ b/build/DockerfilePsql14
@@ -1,3 +1,3 @@
-FROM alpine:3.21.1
+FROM alpine:3.21.2
 RUN apk --no-cache add postgresql14-client
 ENTRYPOINT [ "psql" ]
diff --git a/docs/command_line_arguments.md b/docs/command_line_arguments.md
index cc1784ca..3bb497fd 100644
--- a/docs/command_line_arguments.md
+++ b/docs/command_line_arguments.md
@@ -70,7 +70,7 @@
     Docker container, as the connections from the host machine will not be seen as a connection from
     localhost in the container.
 
---allow_shutdown_command
+--allow_shutdown_statement
   * Enables the use of the custom SQL command `SHUTDOWN [SMART | FAST | IMMEDIATE]`. This command can
     be used to shut down PGAdapter by just sending it a SQL statement. This option should only be enabled
     when PGAdapter runs in a trusted environment, for example as a side-car container. The default
diff --git a/pom.xml b/pom.xml
index b864db04..5f51204b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -78,14 +78,14 @@
       <dependency>
         <groupId>io.opentelemetry</groupId>
         <artifactId>opentelemetry-bom</artifactId>
-        <version>1.45.0</version>
+        <version>1.46.0</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>libraries-bom</artifactId>
-        <version>26.52.0</version>
+        <version>26.53.0</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
@@ -194,10 +194,15 @@
       <artifactId>commons-cli</artifactId>
       <version>1.9.0</version>
     </dependency>
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+      <version>1.17.2</version>
+    </dependency>
     <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-csv</artifactId>
-      <version>1.12.0</version>
+      <version>1.13.0</version>
     </dependency>
     <dependency>
       <groupId>org.apache.commons</groupId>
@@ -212,7 +217,7 @@
     <dependency>
       <groupId>org.bouncycastle</groupId>
       <artifactId>bcpkix-jdk18on</artifactId>
-      <version>1.79</version>
+      <version>1.80</version>
     </dependency>
     <dependency>
       <groupId>org.codehaus.mojo</groupId>
diff --git a/samples/golang/gorm/go.mod b/samples/golang/gorm/go.mod
index af153fd8..69d1dd87 100644
--- a/samples/golang/gorm/go.mod
+++ b/samples/golang/gorm/go.mod
@@ -2,7 +2,7 @@ module gorm
 
 go 1.22
 
-toolchain go1.23.4
+toolchain go1.23.5
 
 require (
 	github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20250101102758-b554b94f49f2
@@ -10,7 +10,7 @@ require (
 	github.com/jackc/pgtype v1.14.4
 	github.com/shopspring/decimal v1.4.0
 	github.com/testcontainers/testcontainers-go v0.35.0
-	github.com/testcontainers/testcontainers-go/modules/postgres v0.34.0
+	github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0
 	gorm.io/datatypes v1.2.5
 	gorm.io/driver/postgres v1.5.11
 	gorm.io/gorm v1.25.12
diff --git a/samples/golang/gorm/go.sum b/samples/golang/gorm/go.sum
index ca5f7269..80c79023 100644
--- a/samples/golang/gorm/go.sum
+++ b/samples/golang/gorm/go.sum
@@ -250,6 +250,8 @@ github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X
 github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4=
 github.com/testcontainers/testcontainers-go/modules/postgres v0.34.0 h1:c51aBXT3v2HEBVarmaBnsKzvgZjC5amn0qsj8Naqi50=
 github.com/testcontainers/testcontainers-go/modules/postgres v0.34.0/go.mod h1:EWP75ogLQU4M4L8U+20mFipjV4WIR9WtlMXSB6/wiuc=
+github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 h1:eEGx9kYzZb2cNhRbBrNOCL/YPOM7+RMJiy3bB+ie0/I=
+github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0/go.mod h1:hfH71Mia/WWLBgMD2YctYcMlfsbnT0hflweL1dy8Q4s=
 github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
 github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
 github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
diff --git a/samples/golang/pgx/go.mod b/samples/golang/pgx/go.mod
index d17ab8f4..54e6ae88 100644
--- a/samples/golang/pgx/go.mod
+++ b/samples/golang/pgx/go.mod
@@ -2,7 +2,7 @@ module pgadapter-pgx-sample
 
 go 1.22
 
-toolchain go1.23.4
+toolchain go1.23.5
 
 require (
 	github.com/GoogleCloudPlatform/pgadapter/wrappers/golang v0.0.0-20250101102758-b554b94f49f2
diff --git a/samples/java/cloud-run/pom.xml b/samples/java/cloud-run/pom.xml
index ff1e8c26..4b297ea5 100644
--- a/samples/java/cloud-run/pom.xml
+++ b/samples/java/cloud-run/pom.xml
@@ -47,7 +47,7 @@
     <dependency>
       <groupId>org.postgresql</groupId>
       <artifactId>postgresql</artifactId>
-      <version>42.7.4</version>
+      <version>42.7.5</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
diff --git a/samples/java/hibernate/pom.xml b/samples/java/hibernate/pom.xml
index 5c5b4723..78396d95 100644
--- a/samples/java/hibernate/pom.xml
+++ b/samples/java/hibernate/pom.xml
@@ -45,7 +45,7 @@
     <dependency>
       <groupId>org.postgresql</groupId>
       <artifactId>postgresql</artifactId>
-      <version>42.7.4</version>
+      <version>42.7.5</version>
     </dependency>
     <!-- Add Spanner Hibernate tools for access to the batch compatible bit-reversed sequence generator. -->
     <dependency>
diff --git a/samples/java/jdbc/pom.xml b/samples/java/jdbc/pom.xml
index 038c6ea9..96e0bc97 100644
--- a/samples/java/jdbc/pom.xml
+++ b/samples/java/jdbc/pom.xml
@@ -51,7 +51,7 @@
     <dependency>
       <groupId>org.postgresql</groupId>
       <artifactId>postgresql</artifactId>
-      <version>42.7.4</version>
+      <version>42.7.5</version>
     </dependency>
     <dependency>
       <groupId>com.google.cloud</groupId>
diff --git a/samples/java/liquibase/pom.xml b/samples/java/liquibase/pom.xml
index 49eb9381..b9d3ed55 100644
--- a/samples/java/liquibase/pom.xml
+++ b/samples/java/liquibase/pom.xml
@@ -62,7 +62,7 @@
         <plugin>
           <groupId>org.liquibase</groupId>
           <artifactId>liquibase-maven-plugin</artifactId>
-          <version>4.30.0</version>
+          <version>4.31.0</version>
           <configuration>
             <propertyFile>liquibase.properties</propertyFile>
             <outputChangeLogFile>changelog-master.xml</outputChangeLogFile>
diff --git a/samples/nodejs/knex/package.json b/samples/nodejs/knex/package.json
index 9764c1f3..4a3a9f97 100644
--- a/samples/nodejs/knex/package.json
+++ b/samples/nodejs/knex/package.json
@@ -6,7 +6,7 @@
   "devDependencies": {
     "@types/node": "^22.0.0",
     "ts-node": "10.9.2",
-    "typescript": "5.7.2"
+    "typescript": "5.7.3"
   },
   "dependencies": {
     "pg": "^8.9.0",
diff --git a/samples/python/django/requirements.txt b/samples/python/django/requirements.txt
index 7bf1c505..c0c42206 100644
--- a/samples/python/django/requirements.txt
+++ b/samples/python/django/requirements.txt
@@ -1,6 +1,6 @@
-psycopg-binary==3.2.3
+psycopg-binary==3.2.4
 pytz==2024.2
-Django==5.1.4
+Django==5.1.5
 google==3.0.0
 google.auth==2.37.0
 testcontainers==4.9.0
diff --git a/samples/python/psycopg3/requirements.txt b/samples/python/psycopg3/requirements.txt
index 17d04770..bca1dd7c 100644
--- a/samples/python/psycopg3/requirements.txt
+++ b/samples/python/psycopg3/requirements.txt
@@ -1,4 +1,4 @@
-psycopg[binary]~=3.2.3
+psycopg[binary]~=3.2.4
 testcontainers~=4.9.0
 requests==2.32.3
 google~=3.0.0
diff --git a/samples/python/sqlalchemy2-sample/requirements.txt b/samples/python/sqlalchemy2-sample/requirements.txt
index cd4fedd2..96c28ad0 100644
--- a/samples/python/sqlalchemy2-sample/requirements.txt
+++ b/samples/python/sqlalchemy2-sample/requirements.txt
@@ -1,6 +1,6 @@
-psycopg[binary]==3.2.3
+psycopg[binary]==3.2.4
 pytz==2024.2
-sqlalchemy==2.0.36
+sqlalchemy==2.0.37
 google~=3.0.0
 google.auth~=2.37.0
 testcontainers~=4.9.0
diff --git a/samples/ruby/activerecord/config/environment.rb b/samples/ruby/activerecord/config/environment.rb
index 3de1ad7f..3b25156b 100644
--- a/samples/ruby/activerecord/config/environment.rb
+++ b/samples/ruby/activerecord/config/environment.rb
@@ -6,6 +6,7 @@
 # license that can be found in the LICENSE file or at
 # https://opensource.org/licenses/MIT.
 
+require "logger" # https://github.com/rails/rails/issues/54260
 require 'active_record'
 require 'bundler'
 
diff --git a/samples/sidecar-proxy/Dockerfile b/samples/sidecar-proxy/Dockerfile
index 7f51bd34..a5765505 100644
--- a/samples/sidecar-proxy/Dockerfile
+++ b/samples/sidecar-proxy/Dockerfile
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM golang:1.23.4 as builder
+FROM golang:1.23.5 as builder
 WORKDIR /app
 RUN go mod init example-app
 RUN go get github.com/jackc/pgx/v4
diff --git a/samples/snippets/golang-snippets/go.mod b/samples/snippets/golang-snippets/go.mod
index 060b7392..5737bc8e 100644
--- a/samples/snippets/golang-snippets/go.mod
+++ b/samples/snippets/golang-snippets/go.mod
@@ -2,10 +2,10 @@ module golang-snippets
 
 go 1.22
 
-toolchain go1.23.4
+toolchain go1.23.5
 
 require (
-	github.com/docker/docker v27.4.1+incompatible
+	github.com/docker/docker v27.5.0+incompatible
 	github.com/jackc/pgx/v5 v5.7.2
 	github.com/testcontainers/testcontainers-go v0.35.0
 )
diff --git a/samples/snippets/golang-snippets/go.sum b/samples/snippets/golang-snippets/go.sum
index 055e64b6..84209e05 100644
--- a/samples/snippets/golang-snippets/go.sum
+++ b/samples/snippets/golang-snippets/go.sum
@@ -47,6 +47,8 @@ github.com/docker/docker v27.4.0+incompatible h1:I9z7sQ5qyzO0BfAb9IMOawRkAGxhYsi
 github.com/docker/docker v27.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker v27.4.1+incompatible h1:ZJvcY7gfwHn1JF48PfbyXg7Jyt9ZCWDW+GGXOIxEwp4=
 github.com/docker/docker v27.4.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U=
+github.com/docker/docker v27.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
 github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
 github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/samples/snippets/php-snippets/composer.json b/samples/snippets/php-snippets/composer.json
new file mode 100644
index 00000000..303d1278
--- /dev/null
+++ b/samples/snippets/php-snippets/composer.json
@@ -0,0 +1,10 @@
+{
+  "require": {
+    "ext-pgsql": "*",
+    "ext-pdo": "*",
+    "testcontainers/testcontainers": "^0.2.0"
+  },
+  "require-dev": {
+    "phpunit/phpunit": "^11.5"
+  }
+}
diff --git a/samples/snippets/php-snippets/samples/add_column.php b/samples/snippets/php-snippets/samples/add_column.php
new file mode 100644
index 00000000..63663940
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/add_column.php
@@ -0,0 +1,28 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_add_column]
+function add_column(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+    
+    $connection->exec("ALTER TABLE albums ADD COLUMN marketing_budget bigint");
+    print("Added marketing_budget column\n");
+
+    $connection = null;
+}
+// [END spanner_add_column]
diff --git a/samples/snippets/php-snippets/samples/albums_data.txt b/samples/snippets/php-snippets/samples/albums_data.txt
new file mode 100644
index 00000000..6580d0aa
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/albums_data.txt
@@ -0,0 +1,5 @@
+1	1	Total Junk
+1	2	Go, Go, Go
+2	1	Green
+2	2	Forever Hold Your Peace
+2	3	Terrified
diff --git a/samples/snippets/php-snippets/samples/create_connection.php b/samples/snippets/php-snippets/samples/create_connection.php
new file mode 100644
index 00000000..b69d07de
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/create_connection.php
@@ -0,0 +1,35 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_create_connection]
+function create_connection(string $host, string $port, string $database): void
+{
+    // Connect to Spanner through PGAdapter using the PostgreSQL PDO driver.
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+
+    // Execute a query on Spanner through PGAdapter.
+    $statement = $connection->query("select 'Hello world!' as hello");
+    $rows = $statement->fetchAll();
+
+    printf("Greeting from Cloud Spanner PostgreSQL: %s\n", $rows[0][0]);
+
+    // Cleanup resources.
+    $rows = null;
+    $statement = null;
+    $connection = null;
+}
+// [END spanner_create_connection]
diff --git a/samples/snippets/php-snippets/samples/create_tables.php b/samples/snippets/php-snippets/samples/create_tables.php
new file mode 100644
index 00000000..de0e571a
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/create_tables.php
@@ -0,0 +1,45 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_create_database]
+function create_tables(string $host, string $port, string $database): void
+{
+    // Connect to Spanner through PGAdapter using the PostgreSQL PDO driver.
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+    
+    // Create two tables in one batch.
+    $connection->exec("start batch ddl");
+    $connection->exec("create table singers ("
+        ."  singer_id   bigint primary key not null,"
+        ."  first_name  character varying(1024),"
+        ."  last_name   character varying(1024),"
+        ."  singer_info bytea,"
+        ."  full_name   character varying(2048) generated "
+        ."  always as (first_name || ' ' || last_name) stored"
+        .")");
+    $connection->exec("create table albums ("
+        ."  singer_id     bigint not null,"
+        ."  album_id      bigint not null,"
+        ."  album_title   character varying(1024),"
+        ."  primary key (singer_id, album_id)"
+        .") interleave in parent singers on delete cascade");
+    $connection->exec("run batch");
+    print("Created Singers & Albums tables in database: [{$database}]\n");
+
+    $connection = null;
+}
+// [END spanner_create_database]
diff --git a/samples/snippets/php-snippets/samples/data_boost.php b/samples/snippets/php-snippets/samples/data_boost.php
new file mode 100644
index 00000000..34522a60
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/data_boost.php
@@ -0,0 +1,42 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_data_boost]
+function data_boost(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+
+    // This enables Data Boost for all partitioned queries on this
+    // connection.
+    $connection->exec("set spanner.data_boost_enabled=true");
+
+    // Run a partitioned query. This query will use Data Boost.
+    $statement = $connection->query(
+        "run partitioned query "
+        ."select singer_id, first_name, last_name "
+        ."from singers"
+    );
+    $rows = $statement->fetchAll();
+    foreach ($rows as $singer) {
+        printf("%s\t%s\t%s\n", $singer["singer_id"], $singer["first_name"], $singer["last_name"]);
+    }
+
+    $rows = null;
+    $statement = null;
+    $connection = null;
+}
+// [END spanner_data_boost]
diff --git a/samples/snippets/php-snippets/samples/ddl_batch.php b/samples/snippets/php-snippets/samples/ddl_batch.php
new file mode 100644
index 00000000..5b424e9d
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/ddl_batch.php
@@ -0,0 +1,48 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_ddl_batch]
+function ddl_batch(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+
+    // Executing multiple DDL statements as one batch is
+    // more efficient than executing each statement
+    // individually.
+    $connection->exec("start batch ddl");
+    $connection->exec("CREATE TABLE venues ("
+        ."  venue_id    bigint not null primary key,"
+        ."  name        varchar(1024),"
+        ."  description jsonb"
+        .")");
+    $connection->exec("CREATE TABLE concerts ("
+        ."  concert_id bigint not null primary key ,"
+        ."  venue_id   bigint not null,"
+        ."  singer_id  bigint not null,"
+        ."  start_time timestamptz,"
+        ."  end_time   timestamptz,"
+        ."  constraint fk_concerts_venues foreign key"
+        ."    (venue_id) references venues (venue_id),"
+        ."  constraint fk_concerts_singers foreign key"
+        ."    (singer_id) references singers (singer_id)"
+        .")");
+    $connection->exec("run batch");
+    print("Added venues and concerts tables\n");
+
+    $connection = null;
+}
+// [END spanner_ddl_batch]
diff --git a/samples/snippets/php-snippets/samples/partitioned_dml.php b/samples/snippets/php-snippets/samples/partitioned_dml.php
new file mode 100644
index 00000000..3f5ca0dc
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/partitioned_dml.php
@@ -0,0 +1,40 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_partitioned_dml]
+function execute_partitioned_dml(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+
+    // Change the DML mode that is used by this connection to Partitioned
+    // DML. Partitioned DML is designed for bulk updates and deletes.
+    // See https://cloud.google.com/spanner/docs/dml-partitioned for more
+    // information.
+    $connection->exec("set spanner.autocommit_dml_mode='partitioned_non_atomic'");
+
+    // The following statement will use Partitioned DML.
+    $rowcount = $connection->exec(
+        "update albums "
+        ."set marketing_budget=0 "
+        ."where marketing_budget is null"
+    );
+    printf("Updated at least %d albums\n", $rowcount);
+
+    $statement = null;
+    $connection = null;
+}
+// [END spanner_partitioned_dml]
diff --git a/samples/snippets/php-snippets/samples/query_data.php b/samples/snippets/php-snippets/samples/query_data.php
new file mode 100644
index 00000000..60566057
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/query_data.php
@@ -0,0 +1,37 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_query_data]
+function query_data(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+
+    $statement = $connection->query("SELECT singer_id, album_id, album_title "
+        ."FROM albums "
+        ."ORDER BY singer_id, album_id"
+    );
+    $rows = $statement->fetchAll();
+    foreach ($rows as $album)
+    {
+        printf("%s\t%s\t%s\n", $album["singer_id"], $album["album_id"], $album["album_title"]);
+    }
+
+    $rows = null;
+    $statement = null;
+    $connection = null;
+}
+// [END spanner_query_data]
diff --git a/samples/snippets/php-snippets/samples/query_data_with_new_column.php b/samples/snippets/php-snippets/samples/query_data_with_new_column.php
new file mode 100644
index 00000000..640a392d
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/query_data_with_new_column.php
@@ -0,0 +1,38 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_query_data_with_new_column]
+function query_data_with_new_column(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+
+    $statement = $connection->query(
+        "SELECT singer_id, album_id, marketing_budget "
+        ."FROM albums "
+        ."ORDER BY singer_id, album_id"
+    );
+    $rows = $statement->fetchAll();
+    foreach ($rows as $album)
+    {
+        printf("%s\t%s\t%s\n", $album["singer_id"], $album["album_id"], $album["marketing_budget"]);
+    }
+
+    $rows = null;
+    $statement = null;
+    $connection = null;
+}
+// [END spanner_query_data_with_new_column]
diff --git a/samples/snippets/php-snippets/samples/query_data_with_parameter.php b/samples/snippets/php-snippets/samples/query_data_with_parameter.php
new file mode 100644
index 00000000..6116e2e9
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/query_data_with_parameter.php
@@ -0,0 +1,38 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_query_with_parameter]
+function query_data_with_parameter(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+
+    $statement = $connection->prepare("SELECT singer_id, first_name, last_name "
+                        ."FROM singers "
+                        ."WHERE last_name = ?"
+    );
+    $statement->execute(["Garcia"]);
+    $rows = $statement->fetchAll();
+    foreach ($rows as $singer)
+    {
+        printf("%s\t%s\t%s\n", $singer["singer_id"], $singer["first_name"], $singer["last_name"]);
+    }
+
+    $rows = null;
+    $statement = null;
+    $connection = null;
+}
+// [END spanner_query_with_parameter]
diff --git a/samples/snippets/php-snippets/samples/read_only_transaction.php b/samples/snippets/php-snippets/samples/read_only_transaction.php
new file mode 100644
index 00000000..2f91ebdc
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/read_only_transaction.php
@@ -0,0 +1,61 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_read_only_transaction]
+function read_only_transaction(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+    
+    // Start a transaction.
+    $connection->beginTransaction();
+    // Change the current transaction to a read-only transaction.
+    // This statement can only be executed at the start of a transaction.
+    $connection->exec("set transaction read only");
+    
+    // The following two queries use the same read-only transaction.
+    $statement = $connection->query(
+        "select singer_id, album_id, album_title "
+        ."from albums "
+        ."order by singer_id, album_id"
+    );
+    $rows = $statement->fetchAll();
+    foreach ($rows as $album)
+    {
+        printf("%s\t%s\t%s\n", $album["singer_id"], $album["album_id"], $album["album_title"]);
+    }
+
+    $statement = $connection->query(
+        "select singer_id, album_id, album_title "
+        ."from albums "
+        ."order by album_title"
+    );
+    $rows = $statement->fetchAll();
+    foreach ($rows as $album)
+    {
+        printf("%s\t%s\t%s\n", $album["singer_id"], $album["album_id"], $album["album_title"]);
+    }
+
+    # Read-only transactions must also be committed or rolled back to mark
+    # the end of the transaction. There is no semantic difference between
+    # rolling back or committing a read-only transaction.
+    $connection->commit();
+
+    $rows = null;
+    $statement = null;
+    $connection = null;
+}
+// [END spanner_read_only_transaction]
diff --git a/samples/snippets/php-snippets/samples/sample_runner.php b/samples/snippets/php-snippets/samples/sample_runner.php
new file mode 100644
index 00000000..3123bc47
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/sample_runner.php
@@ -0,0 +1,25 @@
+<?php
+
+require 'create_tables.php';
+
+/**
+ * @throws Exception if the number of command line arguments is not equal to 4
+ */
+function parse_arguments(): array {
+    global $argc, $argv;
+    
+    if (!$argc == 4) {
+        throw new Exception(sprintf("Invalid number of arguments: %d\nExpected: 4", $argc));
+    }
+    $host = $argv[1];
+    $port = $argv[2];
+    $database = $argv[3];
+    return [$host, $port, $database];
+}
+
+try {
+    [$host, $port, $database] = parse_arguments();
+    create_tables($host, $port, $database);
+} catch (Exception $e) {
+    printf("Failed to run sample: %s\n", $e);
+}
diff --git a/samples/snippets/php-snippets/samples/singers_data.txt b/samples/snippets/php-snippets/samples/singers_data.txt
new file mode 100644
index 00000000..4af568a7
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/singers_data.txt
@@ -0,0 +1,5 @@
+1	Marc	Richards
+2	Catalina	Smith
+3	Alice	Trentor
+4	Lea	Martin
+5	David	Lomond
diff --git a/samples/snippets/php-snippets/samples/statement_timeout.php b/samples/snippets/php-snippets/samples/statement_timeout.php
new file mode 100644
index 00000000..5898b388
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/statement_timeout.php
@@ -0,0 +1,50 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_statement_timeout]
+function query_with_timeout(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+
+    // Set the statement timeout that should be used for all statements
+    // on this connection to 5 seconds.
+    // Supported time units are 's' (seconds), 'ms' (milliseconds),
+    // 'us' (microseconds), and 'ns' (nanoseconds).
+    $connection->exec("set statement_timeout='5s'");
+
+    try {
+        $statement = $connection->query("SELECT singer_id, album_id, album_title "
+            . "FROM albums "
+            . "WHERE album_title in ("
+            . "  SELECT first_name "
+            . "  FROM singers "
+            . "  WHERE last_name LIKE '%a%'"
+            . "     OR last_name LIKE '%m%'"
+            . ")");
+        $rows = $statement->fetchAll();
+        foreach ($rows as $album) {
+            printf("%s\t%s\t%s\n", $album["singer_id"], $album["album_id"], $album["album_title"]);
+        }
+    } catch (Exception $exception) {
+        printf("Error occurred during query execution: %s", $exception->getMessage());
+    }
+
+    $rows = null;
+    $statement = null;
+    $connection = null;
+}
+// [END spanner_statement_timeout]
diff --git a/samples/snippets/php-snippets/samples/tags.php b/samples/snippets/php-snippets/samples/tags.php
new file mode 100644
index 00000000..3b05df2c
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/tags.php
@@ -0,0 +1,73 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_transaction_and_statement_tag]
+function tags(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+    
+    // Start a read/write transaction.
+    $connection->beginTransaction();
+    
+    // Set the TRANSACTION_TAG session variable to set a transaction tag
+    // for the current transaction.
+    $connection->exec("set spanner.transaction_TAG='example-tx-tag'");
+
+    // Set the STATEMENT_TAG session variable to set the request tag
+    // that should be included with the next SQL statement.
+    $connection->exec("set spanner.statement_tag='query-marketing-budget'");
+
+    $singer_id = 1;
+    $album_id = 1;
+    $statement = $connection->prepare(
+        "select marketing_budget "
+        ."from albums "
+        ."where singer_id = ? "
+        ."  and album_id  = ?"
+    );
+    $statement->execute([1, 1]);
+    $marketing_budget = $statement->fetchAll()[0][0];
+    $statement->closeCursor();
+
+    # Reduce the marketing budget by 10% if it is more than 1,000.
+    $max_marketing_budget = 1000;
+    $reduction = 0.1;
+    if ($marketing_budget > $max_marketing_budget) {
+        // Make sure the marketing_budget remains an int.
+        $marketing_budget -= intval($marketing_budget * $reduction);
+        // Set a statement tag for the update statement.
+        $connection->exec("set spanner.statement_tag='reduce-marketing-budget'");
+        $update_statement = $connection->prepare(
+            "update albums set marketing_budget = :budget "
+            ."where singer_id = :singer_id "
+            ."  and album_id  = :album_id"
+        );
+        $update_statement->execute([
+            "budget" => $marketing_budget,
+            "singer_id" => $singer_id,
+            "album_id" => $album_id,
+        ]);
+    } else {
+        print("Marketing budget already less than or equal to 1,000\n");
+    }
+    // Commit the transaction.
+    $connection->commit();
+    print("Reduced marketing budget\n");
+
+    $connection = null;
+}
+// [END spanner_transaction_and_statement_tag]
diff --git a/samples/snippets/php-snippets/samples/update_data_with_copy.php b/samples/snippets/php-snippets/samples/update_data_with_copy.php
new file mode 100644
index 00000000..93e3e2cd
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/update_data_with_copy.php
@@ -0,0 +1,39 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_update_data]
+function update_data_with_copy(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+
+    // Instruct PGAdapter to use insert-or-update for COPY statements.
+    // This enables us to use COPY to update data.
+    $connection->exec("set spanner.copy_upsert=true");
+
+    // COPY uses mutations to insert or update existing data in Spanner.
+    $connection->pgsqlCopyFromArray(
+        "albums",
+        ["1\t1\t100000", "2\t2\t500000"],
+        "\t",
+        "\\\\N",
+        "singer_id, album_id, marketing_budget",
+    );
+    print("Updated 2 albums\n");
+
+    $connection = null;
+}
+// [END spanner_update_data]
diff --git a/samples/snippets/php-snippets/samples/update_data_with_transaction.php b/samples/snippets/php-snippets/samples/update_data_with_transaction.php
new file mode 100644
index 00000000..c35e6aa5
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/update_data_with_transaction.php
@@ -0,0 +1,73 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_dml_getting_started_update]
+function update_data_with_transaction(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+    
+    // Start a read/write transaction.
+    $connection->beginTransaction();
+    // Transfer marketing budget from one album to another.
+    // We do it in a transaction to ensure that the transfer is atomic.
+    
+    // Create a prepared statement that we can use to execute the same
+    // SQL string multiple times with different parameter values.
+    $select_marketing_budget_statement = $connection->prepare(
+        "SELECT marketing_budget "
+        ."from albums "
+        ."WHERE singer_id = ? "
+        ."and album_id = ?"
+    );
+    // Get the marketing budget of Album #2.
+    $select_marketing_budget_statement->execute([2, 2]);
+    $album2_budget = $select_marketing_budget_statement->fetchAll()[0][0];
+    $select_marketing_budget_statement->closeCursor();
+        
+    $transfer = 200000;
+    if ($album2_budget > $transfer) {
+        // Get the marketing budget of Album #1.
+        $select_marketing_budget_statement->execute([1, 1]);
+        $album1_budget = $select_marketing_budget_statement->fetchAll()[0][0];
+        $select_marketing_budget_statement->closeCursor();
+        // Transfer the marketing budgets and write the update back
+        // to the database.
+        $album1_budget += $transfer;
+        $album2_budget -= $transfer;
+        // PHP PDO also supports named query parameters.
+        $update_statement = $connection->prepare(
+            "update albums "
+                ."set marketing_budget = :budget "
+                ."where singer_id = :singer_id "
+                ."and   album_id = :album_id"
+        );
+        // Start a DML batch. This batch will become part of the current transaction.
+        // $connection->exec("start batch dml");
+        // Update the marketing budget of both albums.
+        $update_statement->execute(["budget" => $album1_budget, "singer_id" => 1, "album_id" => 1]);
+        $update_statement->execute(["budget" => $album2_budget, "singer_id" => 2, "album_id" => 2]);
+        // $connection->exec("run batch");
+    } else {
+        print("Insufficient budget to transfer\n");
+    }
+    // Commit the transaction.
+    $connection->commit();
+    print("Transferred marketing budget from Album 2 to Album 1\n");
+
+    $connection = null;
+}
+// [END spanner_dml_getting_started_update]
diff --git a/samples/snippets/php-snippets/samples/write_data_with_copy.php b/samples/snippets/php-snippets/samples/write_data_with_copy.php
new file mode 100644
index 00000000..6824feac
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/write_data_with_copy.php
@@ -0,0 +1,40 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_copy_from_stdin]
+function write_data_with_copy(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+    
+    $dir = dirname(__FILE__);
+    
+    $connection->pgsqlCopyFromFile(
+        "singers",
+        sprintf("%s/singers_data.txt", $dir),
+        "\t",
+        "\\\\N",
+        "singer_id, first_name, last_name");
+    print("Copied 5 singers\n");
+
+    $connection->pgsqlCopyFromFile(
+        "albums",
+        sprintf("%s/albums_data.txt", $dir));
+    print("Copied 5 albums\n");
+
+    $connection = null;
+}
+// [END spanner_copy_from_stdin]
diff --git a/samples/snippets/php-snippets/samples/write_data_with_dml.php b/samples/snippets/php-snippets/samples/write_data_with_dml.php
new file mode 100644
index 00000000..183a55b7
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/write_data_with_dml.php
@@ -0,0 +1,38 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_dml_getting_started_insert]
+function write_data_with_dml(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+    
+    $sql = "INSERT INTO singers (singer_id, first_name, last_name)"
+                        ." VALUES (?, ?, ?), (?, ?, ?), "
+                        ."        (?, ?, ?), (?, ?, ?)";
+    $statement = $connection->prepare($sql);
+    $statement->execute([
+        12, "Melissa", "Garcia",
+        13, "Russel", "Morales",
+        14, "Jacqueline", "Long",
+        15, "Dylan", "Shaw"
+    ]);
+    printf("%d records inserted\n", $statement->rowCount());
+
+    $statement = null;
+    $connection = null;
+}
+// [END spanner_dml_getting_started_insert]
diff --git a/samples/snippets/php-snippets/samples/write_data_with_dml_batch.php b/samples/snippets/php-snippets/samples/write_data_with_dml_batch.php
new file mode 100644
index 00000000..70c52163
--- /dev/null
+++ b/samples/snippets/php-snippets/samples/write_data_with_dml_batch.php
@@ -0,0 +1,45 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START spanner_dml_batch]
+function write_data_with_dml_batch(string $host, string $port, string $database): void
+{
+    $dsn = sprintf("pgsql:host=%s;port=%s;dbname=%s", $host, $port, $database);
+    $connection = new PDO($dsn);
+
+    // Use START BATCH DML / RUN BATCH to run a batch of DML statements.
+    // Create a prepared statement for the DML that should be executed.
+    $sql = "INSERT INTO singers (singer_id, first_name, last_name) VALUES (?, ?, ?)";
+    $statement = $connection->prepare($sql);
+    // Start a DML batch.
+    $connection->exec("START BATCH DML");
+    
+    $statement->execute([16, "Sarah", "Wilson"]);
+    $statement->execute([17, "Ethan", "Miller"]);
+    $statement->execute([18, "Maya", "Patel"]);
+    
+    // Run the DML batch. Use the 'query(..)' method, as the update counts are returned as a row
+    // containing an array with the update count of each statement in the batch.
+    $statement = $connection->query("RUN BATCH");
+    $result = $statement->fetchAll();
+    $update_count = $result[0][0];
+    
+    printf("%s records inserted\n", $update_count);
+
+    $statement = null;
+    $connection = null;
+}
+// [END spanner_dml_batch]
diff --git a/src/main/java/com/google/cloud/spanner/connection/ConnectionOptionsHelper.java b/src/main/java/com/google/cloud/spanner/connection/PGAdapterConnectionOptionsHelper.java
similarity index 96%
rename from src/main/java/com/google/cloud/spanner/connection/ConnectionOptionsHelper.java
rename to src/main/java/com/google/cloud/spanner/connection/PGAdapterConnectionOptionsHelper.java
index d11e53d2..fdd78f96 100644
--- a/src/main/java/com/google/cloud/spanner/connection/ConnectionOptionsHelper.java
+++ b/src/main/java/com/google/cloud/spanner/connection/PGAdapterConnectionOptionsHelper.java
@@ -24,9 +24,9 @@ import java.util.logging.Level;
 
 /** Simple helper class to get access to a package-private method in the ConnectionOptions. */
 @InternalApi
-public class ConnectionOptionsHelper {
+public class PGAdapterConnectionOptionsHelper {
   /** Private constructor to prevent instantiation. */
-  private ConnectionOptionsHelper() {}
+  private PGAdapterConnectionOptionsHelper() {}
 
   // TODO: Remove when Builder.setCredentials(..) has been made public.
   public static Builder setCredentials(Builder connectionOptionsBuilder, Credentials credentials) {
diff --git a/src/main/java/com/google/cloud/spanner/connection/ResultSetHelper.java b/src/main/java/com/google/cloud/spanner/connection/PGAdapterResultSetHelper.java
similarity index 93%
rename from src/main/java/com/google/cloud/spanner/connection/ResultSetHelper.java
rename to src/main/java/com/google/cloud/spanner/connection/PGAdapterResultSetHelper.java
index ef56904d..69a7b533 100644
--- a/src/main/java/com/google/cloud/spanner/connection/ResultSetHelper.java
+++ b/src/main/java/com/google/cloud/spanner/connection/PGAdapterResultSetHelper.java
@@ -18,10 +18,10 @@ import com.google.api.core.InternalApi;
 import com.google.cloud.spanner.ResultSet;
 
 @InternalApi
-public class ResultSetHelper {
+public class PGAdapterResultSetHelper {
 
   /** Private constructor to prevent instantiation. */
-  private ResultSetHelper() {}
+  private PGAdapterResultSetHelper() {}
 
   /**
    * Converts the given {@link ResultSet} to a {@link DirectExecuteResultSet}. A {@link
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/ConnectionHandler.java b/src/main/java/com/google/cloud/spanner/pgadapter/ConnectionHandler.java
index fa6d940b..b14aeadb 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/ConnectionHandler.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/ConnectionHandler.java
@@ -38,7 +38,7 @@ import com.google.cloud.spanner.connection.AbstractStatementParser.ParsedStateme
 import com.google.cloud.spanner.connection.AbstractStatementParser.StatementType;
 import com.google.cloud.spanner.connection.Connection;
 import com.google.cloud.spanner.connection.ConnectionOptions;
-import com.google.cloud.spanner.connection.ConnectionOptionsHelper;
+import com.google.cloud.spanner.connection.PGAdapterConnectionOptionsHelper;
 import com.google.cloud.spanner.connection.SavepointSupport;
 import com.google.cloud.spanner.pgadapter.error.PGException;
 import com.google.cloud.spanner.pgadapter.error.PGExceptionFactory;
@@ -203,15 +203,16 @@ public class ConnectionHandler implements Runnable {
     String uri = buildConnectionURL(database, options, getServer().getProperties());
     ConnectionOptions.Builder connectionOptionsBuilder = ConnectionOptions.newBuilder().setUri(uri);
     connectionOptionsBuilder =
-        ConnectionOptionsHelper.maybeAddGrpcLogInterceptor(
+        PGAdapterConnectionOptionsHelper.maybeAddGrpcLogInterceptor(
             connectionOptionsBuilder, options.isLogGrpcMessages());
-    connectionOptionsBuilder = ConnectionOptionsHelper.useDirectExecutor(connectionOptionsBuilder);
+    connectionOptionsBuilder =
+        PGAdapterConnectionOptionsHelper.useDirectExecutor(connectionOptionsBuilder);
     if (credentials != null) {
       connectionOptionsBuilder =
-          ConnectionOptionsHelper.setCredentials(connectionOptionsBuilder, credentials);
+          PGAdapterConnectionOptionsHelper.setCredentials(connectionOptionsBuilder, credentials);
     } else if (options.getCredentials() != null) {
       connectionOptionsBuilder =
-          ConnectionOptionsHelper.setCredentials(
+          PGAdapterConnectionOptionsHelper.setCredentials(
               connectionOptionsBuilder, options.getCredentials());
     }
     SessionPoolOptions sessionPoolOptions =
diff --git a/src/main/java/com/google/cloud/spanner/pgadapter/statements/BackendConnection.java b/src/main/java/com/google/cloud/spanner/pgadapter/statements/BackendConnection.java
index 61998a03..9f2d72fa 100644
--- a/src/main/java/com/google/cloud/spanner/pgadapter/statements/BackendConnection.java
+++ b/src/main/java/com/google/cloud/spanner/pgadapter/statements/BackendConnection.java
@@ -48,7 +48,7 @@ import com.google.cloud.spanner.connection.AbstractStatementParser.ParsedStateme
 import com.google.cloud.spanner.connection.AbstractStatementParser.StatementType;
 import com.google.cloud.spanner.connection.AutocommitDmlMode;
 import com.google.cloud.spanner.connection.Connection;
-import com.google.cloud.spanner.connection.ResultSetHelper;
+import com.google.cloud.spanner.connection.PGAdapterResultSetHelper;
 import com.google.cloud.spanner.connection.StatementResult;
 import com.google.cloud.spanner.connection.StatementResult.ClientSideStatementType;
 import com.google.cloud.spanner.connection.TransactionRetryListener;
@@ -374,7 +374,7 @@ public class BackendConnection {
           try {
             result.set(
                 new QueryResult(
-                    ResultSetHelper.toDirectExecuteResultSet(
+                    PGAdapterResultSetHelper.toDirectExecuteResultSet(
                         spannerConnection
                             .getDatabaseClient()
                             .singleUse()
@@ -415,7 +415,7 @@ public class BackendConnection {
           // We handle one very specific use case here to prevent unnecessary problems: If the user
           // has started a DML batch and is then analyzing an update statement (probably a prepared
           // statement), then we use a separate transaction for that.
-          if (spannerConnection.isDmlBatchActive()) {
+          if (spannerConnection.isDmlBatchActive() && !spannerConnection.isInTransaction()) {
             final Statement statementToAnalyze = statement;
             resultSet =
                 spannerConnection
diff --git a/wrappers/golang/go.mod b/wrappers/golang/go.mod
index 2ff711b5..5a870d75 100644
--- a/wrappers/golang/go.mod
+++ b/wrappers/golang/go.mod
@@ -2,10 +2,10 @@ module github.com/GoogleCloudPlatform/pgadapter/wrappers/golang
 
 go 1.22
 
-toolchain go1.23.4
+toolchain go1.23.5
 
 require (
-	github.com/docker/docker v27.4.1+incompatible
+	github.com/docker/docker v27.5.0+incompatible
 	github.com/testcontainers/testcontainers-go v0.35.0
 	golang.org/x/oauth2 v0.25.0
 )
diff --git a/wrappers/golang/go.sum b/wrappers/golang/go.sum
index 17b8acb0..9b305682 100644
--- a/wrappers/golang/go.sum
+++ b/wrappers/golang/go.sum
@@ -25,6 +25,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
 github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
 github.com/docker/docker v27.4.1+incompatible h1:ZJvcY7gfwHn1JF48PfbyXg7Jyt9ZCWDW+GGXOIxEwp4=
 github.com/docker/docker v27.4.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U=
+github.com/docker/docker v27.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
 github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
 github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=


Test patch:
diff --git a/.ci/e2e-expected/backslash-dt.txt b/.ci/e2e-expected/backslash-dt.txt
index c795abd6..3f62a128 100644
--- a/.ci/e2e-expected/backslash-dt.txt
+++ b/.ci/e2e-expected/backslash-dt.txt
@@ -18,6 +18,7 @@
  testdb_e2e_psql_v?? | information_schema | database_options                 | VIEW       |                              |                      |                           |                          |                        |                    |          |               |                   |                  |               |                 |
  testdb_e2e_psql_v?? | information_schema | enabled_roles                    | VIEW       |                              |                      |                           |                          |                        |                    |          |               |                   |                  |               |                 |
  testdb_e2e_psql_v?? | information_schema | index_columns                    | VIEW       |                              |                      |                           |                          |                        |                    |          |               |                   |                  |               |                 |
+ testdb_e2e_psql_v?? | information_schema | index_options                    | VIEW       |                              |                      |                           |                          |                        |                    |          |               |                   |                  |               |                 | 
  testdb_e2e_psql_v?? | information_schema | indexes                          | VIEW       |                              |                      |                           |                          |                        |                    |          |               |                   |                  |               |                 |
  testdb_e2e_psql_v?? | information_schema | information_schema_catalog_name  | VIEW       |                              |                      |                           |                          |                        |                    |          |               |                   |                  |               |                 |
  testdb_e2e_psql_v?? | information_schema | key_column_usage                 | VIEW       |                              |                      |                           |                          |                        |                    |          |               |                   |                  |               |                 |
@@ -79,5 +80,6 @@
  testdb_e2e_psql_v?? | spanner_sys        | txn_stats_total_10minute         | VIEW       |                              |                      |                           |                          |                        |                    |          |               |                   |                  |               |                 | 
  testdb_e2e_psql_v?? | spanner_sys        | txn_stats_total_hour             | VIEW       |                              |                      |                           |                          |                        |                    |          |               |                   |                  |               |                 | 
  testdb_e2e_psql_v?? | spanner_sys        | txn_stats_total_minute           | VIEW       |                              |                      |                           |                          |                        |                    |          |               |                   |                  |               |                 | 
-(78 rows)
+ testdb_e2e_psql_v?? | spanner_sys        | user_split_points                | VIEW       |                              |                      |                           |                          |                        |                    |          |               |                   |                  |               |                 | 
+(80 rows)
 
diff --git a/samples/snippets/php-snippets/test/SamplesTest.php b/samples/snippets/php-snippets/test/SamplesTest.php
new file mode 100644
index 00000000..13fda330
--- /dev/null
+++ b/samples/snippets/php-snippets/test/SamplesTest.php
@@ -0,0 +1,162 @@
+<?php
+
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+require '../vendor/autoload.php';
+require '../samples/create_tables.php';
+require '../samples/create_connection.php';
+require '../samples/write_data_with_dml.php';
+require '../samples/write_data_with_dml_batch.php';
+require '../samples/write_data_with_copy.php';
+require '../samples/query_data.php';
+require '../samples/query_data_with_parameter.php';
+require '../samples/statement_timeout.php';
+require '../samples/add_column.php';
+require '../samples/ddl_batch.php';
+require '../samples/update_data_with_copy.php';
+require '../samples/query_data_with_new_column.php';
+require '../samples/update_data_with_transaction.php';
+require '../samples/tags.php';
+require '../samples/read_only_transaction.php';
+require '../samples/data_boost.php';
+require '../samples/partitioned_dml.php';
+
+use PHPUnit\Framework\TestCase;
+use Testcontainers\Container\Container;
+use Testcontainers\Wait\WaitForLog;
+
+class SamplesTest extends TestCase
+{
+    function test_run_samples(): void
+    {
+        [$pg_adapter, $port] = start_pg_adapter();
+        $host = "localhost";
+        $database = "test-database";
+
+        ob_start();
+        create_tables($host, $port, $database);
+        $this->assertEquals("Created Singers & Albums tables in database: [test-database]\n", ob_get_clean());
+
+        ob_start();
+        create_connection($host, $port, $database);
+        $this->assertEquals("Greeting from Cloud Spanner PostgreSQL: Hello world!\n", ob_get_clean());
+
+        ob_start();
+        write_data_with_dml($host, $port, $database);
+        $this->assertEquals("4 records inserted\n", ob_get_clean());
+
+        ob_start();
+        write_data_with_dml_batch($host, $port, $database);
+        $this->assertEquals("{1,1,1} records inserted\n", ob_get_clean());
+
+        ob_start();
+        write_data_with_copy($host, $port, $database);
+        $this->assertEquals("Copied 5 singers\nCopied 5 albums\n", ob_get_clean());
+
+        ob_start();
+        query_data($host, $port, $database);
+        $this->assertEquals("1\t1\tTotal Junk\n"
+            ."1\t2\tGo, Go, Go\n"
+            ."2\t1\tGreen\n"
+            ."2\t2\tForever Hold Your Peace\n"
+            ."2\t3\tTerrified\n",
+            ob_get_clean());
+
+        ob_start();
+        query_data_with_parameter($host, $port, $database);
+        $this->assertEquals("12\tMelissa\tGarcia\n", ob_get_clean());
+
+        ob_start();
+        query_with_timeout($host, $port, $database);
+        $this->assertEquals("", ob_get_clean());
+
+        ob_start();
+        add_column($host, $port, $database);
+        $this->assertEquals("Added marketing_budget column\n", ob_get_clean());
+
+        ob_start();
+        ddl_batch($host, $port, $database);
+        $this->assertEquals("Added venues and concerts tables\n", ob_get_clean());
+
+        ob_start();
+        update_data_with_copy($host, $port, $database);
+        $this->assertEquals("Updated 2 albums\n", ob_get_clean());
+
+        ob_start();
+        query_data_with_new_column($host, $port, $database);
+        $this->assertEquals("1\t1\t100000\n"
+            ."1\t2\t\n"
+            ."2\t1\t\n"
+            ."2\t2\t500000\n"
+            ."2\t3\t\n",
+            ob_get_clean());
+
+        ob_start();
+        update_data_with_transaction($host, $port, $database);
+        $this->assertEquals("Transferred marketing budget from Album 2 to Album 1\n", ob_get_clean());
+
+        ob_start();
+        tags($host, $port, $database);
+        $this->assertEquals("Reduced marketing budget\n", ob_get_clean());
+        
+        ob_start();
+        read_only_transaction($host, $port, $database);
+        $this->assertEquals(
+            "1\t1\tTotal Junk\n"
+            ."1\t2\tGo, Go, Go\n"
+            ."2\t1\tGreen\n"
+            ."2\t2\tForever Hold Your Peace\n"
+            ."2\t3\tTerrified\n"
+            ."2\t2\tForever Hold Your Peace\n"
+            ."1\t2\tGo, Go, Go\n"
+            ."2\t1\tGreen\n"
+            ."2\t3\tTerrified\n"
+            ."1\t1\tTotal Junk\n",
+            ob_get_clean());
+
+        ob_start();
+        data_boost($host, $port, $database);
+        $this->assertStringContainsString("17\tEthan\tMiller\n", ob_get_clean());
+
+        ob_start();
+        execute_partitioned_dml($host, $port, $database);
+        $this->assertEquals("Updated at least 3 albums\n", ob_get_clean());
+        
+        $pg_adapter->stop();
+    }
+    
+}
+
+function start_pg_adapter(): array
+{
+    // The 'pgadapter-emulator' Docker image is a combined Docker image of PGAdapter + the Spanner emulator.
+    // This Docker image automatically creates any Spanner instance and database that you try to connect to,
+    // which means that you don't have to manually create the instance/database before connecting to it.
+    $pg_adapter = Container::make("gcr.io/cloud-spanner-pg-adapter/pgadapter-emulator");
+    // Map port '5432' in the container to a random port on the host.
+    $pg_adapter->withPort("0", "5432");
+    $pg_adapter->withWait(new WaitForLog('Server started on port'));
+    $pg_adapter->run();
+
+    // Get the mapped host port of port '5432' in the container and use that port number to connect
+    // to PGAdapter using the PHP PDO driver.
+    $reflected_pg_adapter = new ReflectionObject($pg_adapter);
+    $inspected_data = $reflected_pg_adapter->getProperty('inspectedData');
+    $inspected_data->setAccessible(true);
+    $ports = $inspected_data->getValue($pg_adapter)[0]['NetworkSettings']['Ports'];
+    $port = $ports['5432/tcp'][0]['HostPort'];
+
+    return [$pg_adapter, $port];
+}
diff --git a/samples/snippets/php-snippets/test/config.xml b/samples/snippets/php-snippets/test/config.xml
new file mode 100644
index 00000000..93bd90a4
--- /dev/null
+++ b/samples/snippets/php-snippets/test/config.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+  
+</configuration>
\ No newline at end of file
diff --git a/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsHelperTest.java b/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsHelperTest.java
index 7ce9d201..496cff79 100644
--- a/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsHelperTest.java
+++ b/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsHelperTest.java
@@ -39,7 +39,7 @@ public class ConnectionOptionsHelperTest {
     ConnectionOptions.Builder builder = mock(ConnectionOptions.Builder.class);
     GoogleCredentials credentials = mock(GoogleCredentials.class);
 
-    ConnectionOptionsHelper.setCredentials(builder, credentials);
+    PGAdapterConnectionOptionsHelper.setCredentials(builder, credentials);
 
     verify(builder).setCredentials(credentials);
   }
diff --git a/src/test/java/com/google/cloud/spanner/connection/ResultSetHelperTest.java b/src/test/java/com/google/cloud/spanner/connection/ResultSetHelperTest.java
index 2bb3b85e..23a7240b 100644
--- a/src/test/java/com/google/cloud/spanner/connection/ResultSetHelperTest.java
+++ b/src/test/java/com/google/cloud/spanner/connection/ResultSetHelperTest.java
@@ -14,7 +14,7 @@
 
 package com.google.cloud.spanner.connection;
 
-import static com.google.cloud.spanner.connection.ResultSetHelper.toDirectExecuteResultSet;
+import static com.google.cloud.spanner.connection.PGAdapterResultSetHelper.toDirectExecuteResultSet;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/ITPsqlTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/ITPsqlTest.java
index 2b2fb163..8a9d1ebb 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/ITPsqlTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/ITPsqlTest.java
@@ -944,6 +944,7 @@ public class ITPsqlTest implements IntegrationTest {
           ZoneId.of("Asia/Tehran"),
           // Changed the use of DST multiple times. Changed offset on 2024-02-29.
           ZoneId.of("Asia/Almaty"),
+          ZoneId.of("Asia/Choibalsan"),
           ZoneId.of("Asia/Qostanay"),
           // Rankin_Inlet and Resolute did not observe DST in 1970-1979, but not all databases
           // agree.
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/JdbcSimpleModeMockServerTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/JdbcSimpleModeMockServerTest.java
index 8ca4788b..6ed9b3f5 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/JdbcSimpleModeMockServerTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/JdbcSimpleModeMockServerTest.java
@@ -32,6 +32,7 @@ import com.google.spanner.admin.database.v1.UpdateDatabaseDdlRequest;
 import com.google.spanner.v1.CommitRequest;
 import com.google.spanner.v1.ExecuteSqlRequest;
 import com.google.spanner.v1.ExecuteSqlRequest.QueryMode;
+import com.google.spanner.v1.ResultSetStats;
 import com.google.spanner.v1.RollbackRequest;
 import com.google.spanner.v1.TypeCode;
 import io.grpc.Status;
@@ -873,4 +874,36 @@ public class JdbcSimpleModeMockServerTest extends AbstractMockServerTest {
       }
     }
   }
+
+  @Test
+  public void testPrepareInDmlBatch() throws SQLException {
+    String sql = "insert into test (id, value) values ($1, $2)";
+    mockSpanner.putStatementResult(
+        StatementResult.query(
+            com.google.cloud.spanner.Statement.of(sql),
+            com.google.spanner.v1.ResultSet.newBuilder()
+                .setMetadata(
+                    createParameterTypesMetadata(ImmutableList.of(TypeCode.INT64, TypeCode.STRING)))
+                .setStats(ResultSetStats.getDefaultInstance())
+                .build()));
+    mockSpanner.putStatementResult(
+        StatementResult.update(
+            com.google.cloud.spanner.Statement.newBuilder(sql)
+                .bind("p1")
+                .to(1L)
+                .bind("p2")
+                .to("one")
+                .build(),
+            1L));
+
+    try (Connection connection = DriverManager.getConnection(createUrl());
+        Statement statement = connection.createStatement()) {
+      statement.execute("begin");
+      statement.execute("start batch dml");
+      statement.execute("prepare foo as insert into test (id, value) values ($1, $2)");
+      statement.execute("execute foo (1, 'one')");
+      statement.execute("run batch");
+      statement.execute("commit");
+    }
+  }
 }
diff --git a/src/test/java/com/google/cloud/spanner/pgadapter/php/pdo/PdoMockServerTest.java b/src/test/java/com/google/cloud/spanner/pgadapter/php/pdo/PdoMockServerTest.java
index 75d9a17e..439666b2 100644
--- a/src/test/java/com/google/cloud/spanner/pgadapter/php/pdo/PdoMockServerTest.java
+++ b/src/test/java/com/google/cloud/spanner/pgadapter/php/pdo/PdoMockServerTest.java
@@ -815,6 +815,34 @@ public class PdoMockServerTest extends AbstractMockServerTest {
     assertEquals(2, request.getStatementsCount());
   }
 
+  @Test
+  public void testBatchDmlInTransaction() throws Exception {
+    String sql = "insert into my_table (id, value) values ($1, $2)";
+    mockSpanner.putStatementResult(
+        StatementResult.query(
+            Statement.of(sql),
+            ResultSet.newBuilder()
+                .setMetadata(
+                    createParameterTypesMetadata(ImmutableList.of(TypeCode.INT64, TypeCode.STRING)))
+                .setStats(ResultSetStats.getDefaultInstance())
+                .build()));
+    mockSpanner.putStatementResult(
+        StatementResult.update(
+            Statement.newBuilder(sql).bind("p1").to(1L).bind("p2").to("One").build(), 1L));
+    mockSpanner.putStatementResult(
+        StatementResult.update(
+            Statement.newBuilder(sql).bind("p1").to(2L).bind("p2").to("Two").build(), 1L));
+
+    String actualOutput = execute("batch_dml_in_transaction");
+    String expectedOutput = "Inserted two rows\n";
+    assertEquals(expectedOutput, actualOutput);
+
+    assertEquals(1, mockSpanner.countRequestsOfType(ExecuteBatchDmlRequest.class));
+    ExecuteBatchDmlRequest request =
+        mockSpanner.getRequestsOfType(ExecuteBatchDmlRequest.class).get(0);
+    assertEquals(2, request.getStatementsCount());
+  }
+
   @Test
   public void testBatchDdl() throws Exception {
     addDdlResponseToSpannerAdmin();
diff --git a/src/test/php/pdo/pdo_test.php b/src/test/php/pdo/pdo_test.php
index 2a3cd813..da0f62c7 100644
--- a/src/test/php/pdo/pdo_test.php
+++ b/src/test/php/pdo/pdo_test.php
@@ -286,6 +286,23 @@ function batch_dml($dsn): void
     $connection = null;
 }
 
+function batch_dml_in_transaction($dsn): void
+{
+    $connection = new PDO($dsn);
+    $connection->beginTransaction();
+    $connection->exec("START BATCH DML");
+    $statement = $connection->prepare("insert into my_table (id, value) values (:id, :value)");
+    $statement->execute(["id" => 1, "value" => "One"]);
+    $statement->execute(["id" => 2, "value" => "Two"]);
+    $connection->exec("RUN BATCH");
+    $connection->commit();
+
+    print("Inserted two rows\n");
+
+    $statement = null;
+    $connection = null;
+}
+
 function batch_ddl($dsn): void
 {
     $connection = new PDO($dsn);
diff --git a/src/test/ruby/activerecord/application.rb b/src/test/ruby/activerecord/application.rb
index 80796fd0..636d67d8 100644
--- a/src/test/ruby/activerecord/application.rb
+++ b/src/test/ruby/activerecord/application.rb
@@ -6,6 +6,7 @@
 # license that can be found in the LICENSE file or at
 # https://opensource.org/licenses/MIT.
 
+require "logger" # https://github.com/rails/rails/issues/54260
 require 'active_record'
 require 'io/console'
 require_relative 'config/environment'
diff --git a/src/test/ruby/activerecord/config/environment.rb b/src/test/ruby/activerecord/config/environment.rb
index 8433cd8b..ed0c0674 100644
--- a/src/test/ruby/activerecord/config/environment.rb
+++ b/src/test/ruby/activerecord/config/environment.rb
@@ -6,6 +6,7 @@
 # license that can be found in the LICENSE file or at
 # https://opensource.org/licenses/MIT.
 
+require "logger" # https://github.com/rails/rails/issues/54260
 require 'active_record'
 require 'bundler'