From d8e226979b0305f7463817f28148f5342659775b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Sat, 22 Nov 2025 00:16:54 +0100 Subject: [PATCH 1/6] add php 8.5 support --- .github/workflows/test-unit.yml | 6 ++++-- composer.json | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index cd74b69c1..5f46c756f 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -5,6 +5,7 @@ on: push: schedule: - cron: '0 0/2 * * *' + workflow_dispatch: jobs: smoke-test: @@ -83,13 +84,13 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] + php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] type: ['Phpunit', 'Phpunit Lowest'] include: - php: 'latest' type: 'Phpunit Burn' env: - LOG_COVERAGE: "${{ fromJSON('{true: \"1\", false: \"\"}')[matrix.php == '8.4' && matrix.type == 'Phpunit' && (github.event_name == 'pull_request' || (github.event_name == 'push' && (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master')))] }}" + LOG_COVERAGE: "${{ fromJSON('{true: \"1\", false: \"\"}')[matrix.php == '8.5' && matrix.type == 'Phpunit' && (github.event_name == 'pull_request' || (github.event_name == 'push' && (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master')))] }}" services: mysql: image: mysql:${{ matrix.type == 'Phpunit Lowest' && 'latest' || '8.0' }} @@ -145,6 +146,7 @@ jobs: if [ "${{ matrix.type }}" != "StaticAnalysis" ]; then composer remove --no-interaction --no-update phpstan/\* --dev; fi if [ -n "$LOG_COVERAGE" ]; then composer require --no-interaction --no-install phpunit/phpcov; fi composer update --ansi --prefer-dist --no-interaction --no-progress --optimize-autoloader + if [ "${{ matrix.type }}" = "Phpunit Lowest" ] && [ "${{ matrix.php >= 8.5 }}" != "false" ]; then composer require --no-interaction --no-update phpunit/phpunit:^11.3 --dev; fi if [ "${{ matrix.type }}" = "Phpunit Lowest" ]; then composer update --ansi --prefer-dist --prefer-lowest --prefer-stable --no-interaction --no-progress --optimize-autoloader; fi if [ "${{ matrix.type }}" = "Phpunit Burn" ]; then sed -i 's~public function runBare(): void~public function runBare(): void { gc_collect_cycles(); $memDiffs = array_fill(0, '"$(if [ \"$GITHUB_EVENT_NAME\" == \"schedule\" ]; then echo 64; else echo 4; fi)"', 0); $emitter = Event\\Facade::emitter(); for ($i = -1; $i < count($memDiffs); ++$i) { $this->_runBare(); if ($this->inIsolation) { $dispatcher = \\Closure::bind(static fn () => $emitter->dispatcher, null, Event\\DispatchingEmitter::class)(); if ($i === -1) { $dispatcherEvents = $dispatcher->flush()->asArray(); } else { $dispatcher->flush(); } foreach ($dispatcherEvents as $event) { $dispatcher->dispatch($event); } } gc_collect_cycles(); $mem = memory_get_usage(); if ($i !== -1) { $memDiffs[$i] = $mem - $memPrev; } $memPrev = $mem; rsort($memDiffs); if (array_sum($memDiffs) >= 4096 * 1024 || $memDiffs[2] > 0) { $e = new AssertionFailedError("Memory leak detected! (" . implode(" + ", array_map(static fn ($v) => number_format($v / 1024, 3, ".", " "), array_filter($memDiffs))) . " KB, " . ($i + 2) . " iterations)"); $this->status = TestStatus::failure($e->getMessage()); $emitter->testFailed($this->valueObjectForEvents(), Event\\Code\\ThrowableBuilder::from($e), Event\\Code\\ComparisonFailureBuilder::from($e)); $this->onNotSuccessfulTest($e); } } } private function _runBare(): void~' vendor/phpunit/phpunit/src/Framework/TestCase.php && cat vendor/phpunit/phpunit/src/Framework/TestCase.php | grep '_runBare('; fi diff --git a/composer.json b/composer.json index 094de5700..d219ec5e9 100644 --- a/composer.json +++ b/composer.json @@ -33,14 +33,14 @@ ], "homepage": "https://github.com/atk4/data", "require": { - "php": ">=7.4 <8.5", + "php": ">=7.4 <8.6", "atk4/core": "dev-develop", "doctrine/dbal": "~3.5.1 || ~3.6.0 || ~3.7.0 || ~3.8.0 || ~3.9.0 || ~3.10.0 || ~4.0.0 || ~4.1.0 || ~4.2.0 || ~4.3.0 || ~4.4.0", "mvorisek/atk4-hintable": "~1.9.0", "symfony/polyfill-intl-idn": "^1.30" }, "require-release": { - "php": ">=7.4 <8.5", + "php": ">=7.4 <8.6", "atk4/core": "~6.1.0", "doctrine/dbal": "~3.5.1 || ~3.6.0 || ~3.7.0 || ~3.8.0 || ~3.9.0 || ~3.10.0 || ~4.0.0 || ~4.1.0 || ~4.2.0 || ~4.3.0 || ~4.4.0", "mvorisek/atk4-hintable": "~1.9.0", From ce6abf575c03102da139625536031b61a9006110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Sun, 23 Nov 2025 00:52:16 +0100 Subject: [PATCH 2/6] DEBUG no MSSQL --- .github/workflows/test-unit.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index 5f46c756f..97f5e2c51 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -108,11 +108,6 @@ jobs: POSTGRES_PASSWORD: atk4_pass POSTGRES_DB: atk4_test options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 - mssql: - image: mcr.microsoft.com/mssql/server:${{ matrix.type == 'Phpunit Lowest' && 'latest' || '2017-latest' }} - env: - ACCEPT_EULA: Y - SA_PASSWORD: atk4_pass oracle: image: gvenzl/${{ matrix.type == 'Phpunit Lowest' && 'oracle-free:slim-faststart' || 'oracle-xe:18-slim-faststart' }} env: @@ -230,16 +225,6 @@ jobs: php -d opcache.enable_cli=1 vendor/bin/phpunit --exclude-group none $(if [ -n "$LOG_COVERAGE" ]; then echo --coverage-text; else echo --no-coverage; fi) --fail-on-warning --fail-on-risky $(if vendor/bin/phpunit --version | grep -q '^PHPUnit 9\.'; then echo -v; else echo --fail-on-notice --fail-on-deprecation --display-notices --display-deprecations --display-warnings --display-errors --display-incomplete --display-skipped; fi) if [ -n "$LOG_COVERAGE" ]; then mv coverage/phpunit.cov coverage/phpunit-postgres.cov; fi - - name: "Run tests: MSSQL" - if: success() || failure() - env: - DB_DSN: "sqlsrv:host=mssql;dbname=master;driverOptions[TrustServerCertificate]=1" - DB_USER: sa - DB_PASSWORD: atk4_pass - run: | - php -d opcache.enable_cli=1 vendor/bin/phpunit --exclude-group none $(if [ -n "$LOG_COVERAGE" ]; then echo --coverage-text; else echo --no-coverage; fi) --fail-on-warning --fail-on-risky $(if vendor/bin/phpunit --version | grep -q '^PHPUnit 9\.'; then echo -v; else echo --fail-on-notice --fail-on-deprecation --display-notices --display-deprecations --display-warnings --display-errors --display-incomplete --display-skipped; fi) - if [ -n "$LOG_COVERAGE" ]; then mv coverage/phpunit.cov coverage/phpunit-mssql.cov; fi - - name: "Run tests: Oracle - PDO (only for coverage or cron)" if: (success() || failure()) && (env.LOG_COVERAGE || github.event_name == 'schedule') env: From d0f27c48ad3f35317d2ffccda6b42d51db4373db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Sun, 23 Nov 2025 00:52:36 +0100 Subject: [PATCH 3/6] DEBUG no SQLite 3.25.3 --- .github/workflows/test-unit.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index 97f5e2c51..23b688253 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -159,13 +159,6 @@ jobs: php -d opcache.enable_cli=1 vendor/bin/phpunit --exclude-group none $(if [ -n "$LOG_COVERAGE" ]; then echo --coverage-text; else echo --no-coverage; fi) --fail-on-warning --fail-on-risky $(if vendor/bin/phpunit --version | grep -q '^PHPUnit 9\.'; then echo -v; else echo --fail-on-notice --fail-on-deprecation --display-notices --display-deprecations --display-warnings --display-errors --display-incomplete --display-skipped; fi) if [ -n "$LOG_COVERAGE" ]; then mv coverage/phpunit.cov coverage/phpunit-sqlite.cov; fi - - name: "Run tests: SQLite 3.25.3" - if: success() || failure() - run: | - apk add sqlite-dev=3.25.3-r0 --repository=https://dl-cdn.alpinelinux.org/alpine/v3.6/main - php -d opcache.enable_cli=1 vendor/bin/phpunit --exclude-group none $(if [ -n "$LOG_COVERAGE" ]; then echo --coverage-text; else echo --no-coverage; fi) --fail-on-warning --fail-on-risky $(if vendor/bin/phpunit --version | grep -q '^PHPUnit 9\.'; then echo -v; else echo --fail-on-notice --fail-on-deprecation --display-notices --display-deprecations --display-warnings --display-errors --display-incomplete --display-skipped; fi) - if [ -n "$LOG_COVERAGE" ]; then mv coverage/phpunit.cov coverage/phpunit-sqlite325.cov; fi - # remove once SQLite v3.46.0 or higher is available in stable Alpine release # TODO https://github.com/atk4/data/blob/df7dbb9136/tests/ScopeTest.php#L334 - name: "Run tests: SQLite edge" From 1128f9ab801b53feb19615370b2bd455d5bfef24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Sat, 22 Nov 2025 02:16:13 +0100 Subject: [PATCH 4/6] fix depr for all vendors --- src/Persistence/Sql/Connection.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Persistence/Sql/Connection.php b/src/Persistence/Sql/Connection.php index dd483ecec..1e1268e8c 100644 --- a/src/Persistence/Sql/Connection.php +++ b/src/Persistence/Sql/Connection.php @@ -281,6 +281,12 @@ protected static function connectFromDsn(array $dsn): DbalDriverConnection (static::class)::createDbalConfiguration() ); + // backport https://github.com/doctrine/dbal/pull/7173 + // TODO remove once DBAL 3.10+ is supported + if (\PHP_VERSION_ID >= 8_05_00) { + @$dbalConnection->getWrappedConnection(); // @phpstan-ignore method.deprecated (https://github.com/doctrine/dbal/issues/5199) + } + return self::isDbal3x() ? $dbalConnection->getWrappedConnection() // @phpstan-ignore method.notFound : \Closure::bind(static fn () => $dbalConnection->connect(), null, DbalConnection::class)(); From a3690fda7f82f99c6d61f1ce1d588c87cf19f4ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Sun, 23 Nov 2025 02:36:28 +0100 Subject: [PATCH 5/6] HOTFIX all remaining warnings (from dbal /w mysqli) --- src/Persistence/Sql/Expression.php | 6 +++--- src/Schema/Migrator.php | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Persistence/Sql/Expression.php b/src/Persistence/Sql/Expression.php index b00ea3c0f..2ba2615b5 100644 --- a/src/Persistence/Sql/Expression.php +++ b/src/Persistence/Sql/Expression.php @@ -559,9 +559,9 @@ static function ($matches) use ($params, &$numParams, &$i, &$j) { protected function _executeStatement(Statement $statement, bool $fromExecuteStatement) { if ($fromExecuteStatement) { - $result = $statement->executeStatement(); + $result = @$statement->executeStatement(); } else { - $result = $statement->executeQuery(); + $result = @$statement->executeQuery(); } return $result; @@ -590,7 +590,7 @@ protected function _execute(?object $connection, bool $fromExecuteStatement) $platform = $this->connection->getDatabasePlatform(); try { - $statement = $connection->prepare($sql); + $statement = @$connection->prepare($sql); foreach ($params as $key => $val) { if ($val === null) { diff --git a/src/Schema/Migrator.php b/src/Schema/Migrator.php index aba9892b6..ad54047c9 100644 --- a/src/Schema/Migrator.php +++ b/src/Schema/Migrator.php @@ -147,7 +147,7 @@ public function getCreatedTableNames(): array public function create(): self { - $this->createSchemaManager()->createTable($this->table); + @$this->createSchemaManager()->createTable($this->table); $this->createdTableNames[] = $this->table->getName(); return $this; @@ -184,7 +184,7 @@ public function drop(bool $dropForeignKeysFirst = false): self } } - $schemaManager->dropTable($this->table->getQuotedName($this->getDatabasePlatform())); + @$schemaManager->dropTable($this->table->getQuotedName($this->getDatabasePlatform())); $this->createdTableNames = array_values(array_diff($this->createdTableNames, [$this->table->getName()])); @@ -407,7 +407,7 @@ public function isTableExists(string $tableName): bool ->field($this->getConnection()->expr('1')) ->table($tableName) ->render(); - $this->getConnection()->getConnection()->executeQuery($sql); + @$this->getConnection()->getConnection()->executeQuery($sql); return true; } catch (TableNotFoundException $e) { @@ -549,7 +549,7 @@ public function createIndex(array $fields, bool $isUnique): void $mssqlNullable === false ? ['atk4-not-null'] : [] ); - $this->createSchemaManager()->createIndex($index, $platform->quoteIdentifier($tableName)); + @$this->createSchemaManager()->createIndex($index, $platform->quoteIdentifier($tableName)); } /** @@ -594,6 +594,6 @@ public function createForeignKey($relation): void $foreignTableIdentifier = $this->fixAbstractAssetName(new Identifier('0.0'), $foreignField->getOwner()->table); \Closure::bind(static fn () => $foreignKey->_foreignTableName = $foreignTableIdentifier, null, ForeignKeyConstraint::class)(); - $this->createSchemaManager()->createForeignKey($foreignKey, $platform->quoteIdentifier($localField->getOwner()->table)); + @$this->createSchemaManager()->createForeignKey($foreignKey, $platform->quoteIdentifier($localField->getOwner()->table)); } } From 164daf46bb1e21159c5d357eb0c25913682ce026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Sun, 23 Nov 2025 02:37:42 +0100 Subject: [PATCH 6/6] improve hack --- src/Persistence/Sql/Connection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Persistence/Sql/Connection.php b/src/Persistence/Sql/Connection.php index 1e1268e8c..c3c091b77 100644 --- a/src/Persistence/Sql/Connection.php +++ b/src/Persistence/Sql/Connection.php @@ -283,7 +283,7 @@ protected static function connectFromDsn(array $dsn): DbalDriverConnection // backport https://github.com/doctrine/dbal/pull/7173 // TODO remove once DBAL 3.10+ is supported - if (\PHP_VERSION_ID >= 8_05_00) { + if (\PHP_VERSION_ID >= 8_05_00 && ($dsn['driver'] === 'pdo_mysql' || $dsn['driver'] === 'mysqli')) { @$dbalConnection->getWrappedConnection(); // @phpstan-ignore method.deprecated (https://github.com/doctrine/dbal/issues/5199) }