Skip to content

PHP 8.5 compatibility: zero deprecations on 8.1-8.5, CI matrix, docs (PHP85-21) - #5

Open
alenarmona wants to merge 30 commits into
masterfrom
cakephp2-php85
Open

alenarmona wants to merge 30 commits into
masterfrom
cakephp2-php85

Conversation

@alenarmona

@alenarmona alenarmona commented Oct 16, 2026 •

Copy link
Copy Markdown

Jira: PHP85-21

Summary

Creates the cakephp2-php85 branch our applications will consume on PHP 8.5 containers. The framework now runs with zero deprecation notices on PHP 8.1 through 8.5 while staying compatible with PHP 8.0+ (no 8.5-only syntax, composer still requires ^8.0).

Upstream sync

The 14 upstream commits from kamilwylegala/cakephp2-php8 not yet in our master, up to kamilwylegala#87. Includes the CSRF hardening (kamilwylegala#76) and upstream's PHP 8.4/8.5 fixes (kamilwylegala#75, kamilwylegala#83, kamilwylegala#86). Cherry-picked; the resulting tree is identical to upstream master.

CI and tooling

  • Test matrix now runs PHP 8.0, 8.1, 8.4 and 8.5 (intl installed, mcrypt dropped).
  • The test app's core.php reports E_ALL, so any deprecation fails the suite. Previously deprecations were masked, which is why CI looked green on 8.1.
  • docker/web image moved to php:8.5-apache.
  • README: Zumba branch section, composer update procedure, upstream sync procedure, changelog.

Framework changes

PHP 8.5

  • Security::cipher(): reproduce the pre-8.5 float→int wrap-around of Security.cipherSeed without the deprecated cast. Verified identical output for the production-style seed, so existing cookies still decrypt.
  • limit()/offset() in DboSource, Sqlite, Postgres, Sqlserver clamp out-of-range values instead of casting.
  • ConsoleLog no longer calls posix_isatty(null).
  • ReflectionProperty::setAccessible() only called on PHP < 8.1.
  • Null array offsets replaced by explicit checks (I18n context key, Helper, CakeResponse, ConsoleOptionParser, RequestHandlerComponent, Set, Configure::consume, CakeSession::check).
  • libxml_disable_entity_loader() calls removed; deprecated mhash() path removed.

PHP 8.4

  • trigger_error(..., E_USER_ERROR) replaced by exceptions (ConfigureException, CakeException, RuntimeException in bootstrap scripts) or ErrorHandler::handleFatalError(). All were fatal paths.

PHP 8.2 (dynamic properties)

  • Properties declared on Scaffold, ConsoleOptionParser, HelpFormatter, MemcachedEngine, CakeTestCase, CakeTestFixture, CakeFixtureManager, ControllerTestDispatcher, PDOExceptionWithQueryString and ~100 test classes.
  • ConnectionManager instantiates DATABASE_CONFIG through CakeDatabaseConfig, a #[\AllowDynamicProperties] subclass, so ConnectionManager::create() stops emitting deprecations. instanceof DATABASE_CONFIG still holds.
  • CakeRequest allows dynamic properties (tests and plugins decorate it).

PHP 8.1

  • Remaining "passing null to non-nullable internal parameter" cases fixed across the framework with (string) casts or null checks.

Other

  • Security::encrypt()/decrypt() fall back to OpenSSL when ext-mcrypt is missing.
  • SessionHandlerAdapter methods marked #[\ReturnTypeWillChange].
  • CakeRoute: no false-to-array conversion for named rules. CakeSchema: null name no longer passed to strtolower().
  • MysqlTest and ApplicationControllerTest restore global state when skipped (they were leaking debug and Session config into later tests).

Container requirements

  • ext-intl is required: upstream replaced strftime() with php81_bc/strftime, which hard-requires intl.
  • ext-mcrypt is no longer needed.

Test evidence

Full suite (4073 tests) run locally with SQLite:

PHP Result
8.5.8 Clean except 6 CakeTime tests (local build has no ext-intl) and 2 CakeSocket tests (DNS).
8.3.11 Clean.

CI covers the intl and locale cases (locale-gen es_ES), so watch the 8.5 jobs on the first run.

Follow-ups

  • Reference dev-cakephp2-php85 as 2.10.24 from the admin and public repos (separate dependency-bump tickets).
  • Offer the framework fixes upstream to reduce future sync work.

🤖 Generated with Claude Code

pbarabe and others added 30 commits October 15, 2026 16:43
Utility\I18n's protected _bindTextDomain method was sometimes assigning an array key
to an array element that evaluated as false. This was trigging deprecation error
'Automatic conversion of false to array is deprecated'.

This modification checks whether the array element is false and explicitly converts it
to an array if it is.

closes kamilwylegala#70

(cherry picked from commit 0f45d97)
(cherry picked from commit b993fa8)
…ilwylegala#69)

* Intl extension requirement.

* CakeTime migrated to Intl.

* FormHelper migration strftime to Intl.

* Readme update.

(cherry picked from commit b918df8)
…ala#76)

* Backported patch, fixing potential CSRF circumvention with custom HTTP methods.

Upstream: cakephp/cakephp@0f818a2

* Fix unit tests for SecurityComponent

---------

Co-authored-by: Markus Bauer <markus.bauer@cispa.saarland>
(cherry picked from commit c0fb45e)
(cherry picked from commit 42e9fa1)
* Remove constructs that deprecate with PHP 8.4

* Fix SecurityComponent typing

---------

Co-authored-by: Markus Bauer <markus.bauer@cispa.saarland>
(cherry picked from commit 2220f35)
(cherry picked from commit 7e1da9a)
(cherry picked from commit 789a752)
* SessionHandlerAdapter for Cake's interface.

* Removed deprecated E_STRICT.

(cherry picked from commit 5b6a103)
* Fixing deprecation in PHP 8.5

* Removed ancient code sniffer.

* Skip DNS resoultion failure.

(cherry picked from commit d9f6508)
The condition read

    $options['readFile'] && strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0

and && binds tighter than ||, so the https branch was never gated by the
option: Xml::build($input, array('readFile' => false)) still went out and
fetched an input beginning with https://. Only the http:// branch was
actually disabled.

RequestHandlerComponent passes readFile => false when it decodes an XML
request body, so an application that accepts XML input can be made to
issue an outbound https request to a host of the caller's choosing.

Parenthesised so both schemes are gated as the option intends, and the
existing readFile url test gains its https twin.

(cherry picked from commit b024bc1)
…(PHP85-21)

Branch cakephp2-php85 syncs every upstream fix up to kamilwylegala kamilwylegala#87
(cherry-picked, includes the CSRF fix kamilwylegala#76) and makes the framework run
without deprecation notices on PHP 8.5 while staying compatible with 8.0+.

CI / tooling
- Test matrix now covers PHP 8.0, 8.1, 8.4 and 8.5; intl installed, mcrypt dropped.
- Test app core.php reports E_ALL so any deprecation fails the suite.
- docker/web image moved to php:8.5-apache.
- README: Zumba branch section, update procedure, changelog.

PHP 8.5
- Security::cipher(): reproduce the pre-8.5 float->int wrap-around of
  Security.cipherSeed without the deprecated cast (same seed value).
- DboSource/Sqlite/Postgres/Sqlserver limit(): clamp out-of-range values
  before sprintf('%u').
- ConsoleLog: stop calling posix_isatty(null).
- ReflectionProperty::setAccessible() only on PHP < 8.1.
- Null array offsets replaced by explicit checks (I18n context key,
  Helper, CakeResponse, ConsoleOptionParser, RequestHandlerComponent,
  Set, Configure::consume, CakeSession::check).
- libxml_disable_entity_loader() calls removed; mhash() path removed.
- Tests: no enabling of session.use_trans_sid or session.referer_check.

PHP 8.4
- trigger_error(E_USER_ERROR) replaced by exceptions or
  ErrorHandler::handleFatalError().
- str_getcsv escape parameter passed explicitly in tests.

PHP 8.2
- Declare properties on Scaffold, ConsoleOptionParser, HelpFormatter,
  MemcachedEngine, CakeTestCase, CakeTestFixture, CakeFixtureManager,
  ControllerTestDispatcher, PDOExceptionWithQueryString and ~100 test classes.
- ConnectionManager instantiates DATABASE_CONFIG through CakeDatabaseConfig,
  a #[\AllowDynamicProperties] subclass, so create() adds connections silently.
- CakeRequest allows dynamic properties.

PHP 8.1
- Remaining "passing null to non-nullable parameter" cases fixed with
  (string) casts or null checks across the framework.

Other
- Security::encrypt()/decrypt() fall back to OpenSSL without ext-mcrypt.
- SessionHandlerAdapter methods marked #[\ReturnTypeWillChange].
- CakeRoute: no false-to-array conversion for named rules.
- CakeSchema: null name no longer passed to strtolower().
- MysqlTest/ApplicationControllerTest restore global state when skipped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- RedisEngine::delete() uses Redis::del(); Redis::delete() is deprecated
  in phpredis 6 and the deprecation, converted to an exception inside
  Cache::delete(), skipped the settings reset and left keys behind,
  which also made RedisEngineTest::testExpiry fail.
- MemcacheEngineTest only mocks Memcache::set(); stubbing every method
  reproduces the extension's implicitly nullable parameters, which
  PHP 8.4 reports as deprecated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Any PHPUnit mock of the Memcache extension class repeats its implicitly
nullable parameters, which PHP 8.4+ reports as deprecated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Postgres::describe(): columns without a default passed null to preg_replace().
- CakeTestFixture: imported fixtures passed a null table name to strpos().

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…is deprecated

PHP 8.4 already reports enabling session.use_trans_sid as deprecated. Running
the test there raised the deprecation mid-request and left the setting in the
Session config for every later session-based test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…able

The other SSL tests already skip on SocketException; these two errored
instead and failed every CI job during a runner network outage.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s missing

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A local test run had emptied the directory and the deletion slipped into
an earlier commit. Git does not track empty directories, so CI checkouts
had no app/tmp/cache/views and every view-cache test failed on all
PHP versions since Oct 17.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

5 participants