Skip to content

Bump minimum PHP to 8.1, modernize all dependencies, improve CI pipelines#1088

Open
maksimovic wants to merge 28 commits intobshaffer:mainfrom
maksimovic:php85
Open

Bump minimum PHP to 8.1, modernize all dependencies, improve CI pipelines#1088
maksimovic wants to merge 28 commits intobshaffer:mainfrom
maksimovic:php85

Conversation

@maksimovic
Copy link
Copy Markdown

@maksimovic maksimovic commented Mar 17, 2026

Hello.

The company I work for is in need of upgrading its codebase to PHP 8.5 and upon investgating the compatibility of this package, we've encountered conflicts between both direct and transitive dependencies that couldn't be mitigated.

The decision has been made to propose upgrade that will move away PHP 7.x compatibility, which would then unlock better compatibilty for both today & future upgrades.

Note that the goal was to make some kind of a MVP, not to modernize the entire codebase, so there's still room for improvements that have been unlocked by setting the minimum version to 8.1. The decision to start with 8.1 has been made because it just recently became unsupported, compared to 8.0 which reached EOL 2 years ago.

Summary

In short: modernized the entire project for PHP 8.1+ with updated dependencies, rewritten storage backends, and a fully containerized CI pipeline.

PHP & Dependencies

  • Minimum PHP bumped from 5.4 to 8.1, CI matrix covers 8.1–8.5
  • PHPUnit 9.x → 10.5 (attributes, static providers, phpunit.xml v10)
  • firebase/php-jwt → ^7.0
  • aws/aws-sdk-php → ^3.0 (DynamoDB storage rewritten for v3)
  • predis/predis → ^2.0
  • yoast/phpunit-polyfills removed (unnecessary with PHPUnit 10)
  • phpstan/phpstan added as dev dependency with config (phpstan.neon)

Storage Backends

  • Mongo.php removed — dead code using PHP 5.x MongoClient. Modern MongoDB.php (using mongodb/mongodb library) remains
  • CouchbaseDB.php rewritten — from SDK 1.x (Couchbase class) to SDK 4.x (Couchbase\Cluster, Couchbase\Collection, Couchbase\ClusterOptions). Added missing unsetAccessToken method
  • Cassandra.php rewritten — from thobbs/phpcassa (Thrift) to mroosz/php-cassandra ^1.2 (CQL native)
  • DynamoDB.php modernized — AWS SDK v3, fixed deprecated 'self::isNotEmpty' callables → first-class callable syntax, fixed null client_id crash in public key methods
  • All PHP 8.x deprecations fixed (null-to-string coercion, null array offsets, setAccessible)

CI Pipeline

  • Custom Docker images (.docker/Dockerfile) based on php:*-cli-alpine with mongodb, redis, couchbase, pdo_mysql, pdo_pgsql pre-compiled. Built and pushed to GHCR via .github/workflows/docker.yml (weekly + on Dockerfile changes)
  • Layer caching via GitHub Actions cache backend for faster rebuilds
  • All services tested: Redis, MongoDB, MariaDB, PostgreSQL, Cassandra, DynamoDB Local, Couchbase Server
  • Cassandra & DynamoDB started as background containers (overlaps with setup instead of blocking)
  • Couchbase Server (couchbase/server:community-7.6.2) with cluster init and bucket creation via REST API
  • DynamoDB Local replaces AWS credentials/Travis CI logic
  • Bootstrap uses env vars for all service hostnames (MYSQL_HOST, POSTGRES_HOST, REDIS_HOST, MONGODB_HOST, CASSANDRA_HOST, DYNAMODB_ENDPOINT, CB_*)
  • Bootstrap Postgres setup uses PDO instead of shell commands (eliminates psql version mismatch errors)
  • PHPStan runs in container image, Couchbase stub provided for local analysis
  • composer test, composer test:coverage, composer analyze scripts added
  • PHPUnit configured to show details on deprecations, warnings, and skipped tests

Test Fixes

  • JwtAccessTokenTest was checking instanceof PublicKey (nonexistent class) instead of PublicKeyInterface — all backends were silently skipping
  • AccessTokenTest called getMessage() on non-NullStorage objects — fixed guard logic
  • 2048-bit RSA test keys (required by php-jwt 7.x)

Test Results

  • 416 tests, 2135 assertions, 11 skips (interface gaps in MongoDB/Redis/Cassandra/Couchbase — not bugs)
  • 0 failures, 0 errors, 0 deprecations

Validations

  • All PHP versions 8.1–8.5 pass in CI
  • PHPStan level 0 passes
  • All storage backends tested: Memory, SQLite, MySQL, PostgreSQL, MongoDB, Redis, Cassandra, DynamoDB, Couchbase
  • Docker images build successfully for all PHP versions

- Require PHP ^8.1, CI matrix now tests 8.1-8.5
- Upgrade phpunit/phpunit ^9.6 to ^10.5 (convert @dataProvider to
  #[DataProvider] attributes, static data providers, update phpunit.xml)
- Upgrade aws/aws-sdk-php ^2.8 to ^3.0 (rewrite DynamoDB storage for
  v3 API: Marshaler, Result, lazy credential handling)
- Upgrade predis/predis ^1.1 to ^2.0 (fix getPayload() compat)
- Upgrade firebase/php-jwt ^6.4 to ^6.4 || ^7.0 (v7 requires 2048-bit
  RSA keys; all test keypairs regenerated)
- Replace Cassandra storage: swap abandoned thobbs/phpcassa (Thrift) for
  mroosz/php-cassandra ^1.2 (CQL native protocol, PHP 8.1+)
- Fix CI: bump to actions/checkout@v5, drop unmaintained retry action,
  fix MariaDB service config, add health checks, add Cassandra service,
  add pdo_pgsql extension
…ocal to CI

Remove Mongo.php (dead PHP 5.x MongoClient code; MongoDB.php remains).
Rewrite CouchbaseDB.php for Couchbase SDK 4.x (Cluster/Collection API).
Replace AWS credentials/Travis logic with DynamoDB Local in CI.
…ix DynamoDB health check

Use bash /dev/tcp for DynamoDB Local health check since the image
has no curl or wget. Narrow php-jwt to ^7.0 and remove the polyfills
package which is unnecessary on PHPUnit 10.
Add composer test, test:coverage, and analyze scripts. Enable verbose
PHPUnit output for deprecations, warnings, and skip reasons. CI now
uses the composer scripts.
Move Cassandra and DynamoDB out of services block so they start in
parallel with PHP setup and composer install instead of blocking the
entire job. Cassandra takes ~70s to boot; this now overlaps with the
~20s of checkout + PHP setup + composer install.
Eliminates psql client/server version mismatch errors (daticulocale)
and createdb failures when the database already exists from the Docker
service POSTGRES_DB env var.
- Replace deprecated 'self::isNotEmpty' callable strings with
  first-class callable syntax in DynamoDB storage
- Use 127.0.0.1 instead of localhost for MySQL to force TCP connection
  to Docker service (localhost uses Unix socket on Linux)
- Add mongodb/mongodb to require-dev so MongoDB tests run in CI
- Check extension_loaded('mongodb') before class_exists to avoid
  fatal errors when library is present but extension is not
Start couchbase:community-7.6.5 as a background container, initialize
the cluster with kv service, create the oauth2test bucket, and install
ext-couchbase via shivammathur/setup-php. This eliminates the last
remaining test skips (Couchbase).
…eyInterface)

The instanceof check used the non-existent PublicKey class instead of
PublicKeyInterface, causing all storage backends to skip. Also add
NullStorage guard to prevent errors from unavailable backends.
The couchbase extension has no pre-built binary in setup-php and
compiling from PECL hangs indefinitely. Remove the Couchbase container,
cluster setup, and extension from CI. Couchbase tests remain as skips.
DynamoDB rejects empty AttributeValue strings. The getEncryptionAlgorithm
method defaulted client_id to null, causing 'S' => null which is invalid.
Coalesce null to '0' (the sentinel key used for global keys) in all three
public key methods.
- Add .docker/Dockerfile based on php:*-cli-alpine with mongodb, redis,
  couchbase, pdo_mysql, pdo_pgsql, intl extensions pre-compiled
- Add docker.yml workflow to build and push images to GHCR for PHP
  8.1-8.4, triggered on Dockerfile changes and weekly schedule
- Update tests.yml to use container images for PHP 8.1-8.4 (with all
  services including Couchbase), PHP 8.5 remains on setup-php
- Make all service hostnames configurable via env vars in Bootstrap
  (MYSQL_HOST, POSTGRES_HOST, REDIS_HOST, MONGODB_HOST, CASSANDRA_HOST)
php:8.5-cli-alpine exists, so all 5 PHP versions now use the same
custom container image path. No more special-cased setup-php job.
Uses GitHub Actions cache backend (type=gha) with per-PHP-version
scopes so subsequent rebuilds only recompile changed layers.
The couchbase extension's classes weren't registering because apk del
removed shared libraries needed at runtime. Use scanelf to discover
all shared lib dependencies of compiled extensions and install them
before removing build deps.
The ext-couchbase 4.x C extension only provides low-level functions
(Couchbase\Extension\*) and exception classes. The high-level classes
(Cluster, ClusterOptions, Collection) come from the couchbase/couchbase
composer package.

- Install couchbase/couchbase:^4.4 in CI after composer install
- Update Bootstrap check to look for ClusterOptions class
- Add scanelf to Dockerfile to auto-detect runtime shared lib deps
- Keep couchbase/couchbase out of require-dev since it needs ext-couchbase
… bug

CouchbaseDB was the only storage missing unsetAccessToken. Also fix
AccessTokenTest calling getMessage() on non-NullStorage objects when
the method check fails.
@maksimovic maksimovic changed the title Php85 Bump minimum PHP to 8.1, modernize all dependencies, improve CI pipelines Mar 17, 2026
@maksimovic
Copy link
Copy Markdown
Author

Also: I've built Docker images and referenced them in the CI setup, otherwise tests would fail because they don't exist in this repository. I have no problems with keeping them there, but that can also be updated later (workflow will need to be triggered after merge to master & update to point to this repository)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant