From 3a1f0804242c1a23b9bef8cd4f7cd9ce2eac2257 Mon Sep 17 00:00:00 2001 From: Patrick Barabe Date: Wed, 22 May 2024 09:00:49 -0700 Subject: [PATCH 01/30] Fix deprecation error in I18n (#71) 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 #70 (cherry picked from commit 0f45d97c71030d8507086b953789537327dd1fa9) --- lib/Cake/I18n/I18n.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/Cake/I18n/I18n.php b/lib/Cake/I18n/I18n.php index 814c2cfe08..56a92f52bd 100644 --- a/lib/Cake/I18n/I18n.php +++ b/lib/Cake/I18n/I18n.php @@ -437,7 +437,13 @@ protected function _bindTextDomain($domain) { } if ($translations !== false) { - $this->_domains[$domain][$this->_lang][$this->category] = $translations; + if ($this->_domains[$domain][$this->_lang] === false) { + $this->_domains[$domain][$this->_lang] = [ + $this->category => $translations + ]; + } else { + $this->_domains[$domain][$this->_lang][$this->category] = $translations; + } $this->_noLocale = false; break 2; } @@ -445,7 +451,13 @@ protected function _bindTextDomain($domain) { } if (empty($this->_domains[$domain][$this->_lang][$this->category])) { - $this->_domains[$domain][$this->_lang][$this->category] = array(); + if ($this->_domains[$domain][$this->_lang] === false) { + $this->_domains[$domain][$this->_lang] = [ + $this->category => [] + ]; + } else { + $this->_domains[$domain][$this->_lang][$this->category] = []; + } return $domain; } From 48372333468e318f8610a903da35a8fa380c991e Mon Sep 17 00:00:00 2001 From: Kamil Wylegala Date: Wed, 22 May 2024 18:01:40 +0200 Subject: [PATCH 02/30] Update README.md (cherry picked from commit b993fa8d0c47ce8b9e281c1a1a8efea314d8c839) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 24cea1b044..cae994b585 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,10 @@ It means that composer will look at `master` branch of repository configured und ## Changelog +### 2024-05-22 + +- Fix deprecation error in I18n: `Automatic conversion of false to array is deprecated` + ### 2024-02-02 - `str_len` deprecation warning fix in CakeResponse (passing null instead of `string`) From 8773173fb23f860c22c704d49f9d2b779e1284c5 Mon Sep 17 00:00:00 2001 From: Kamil Wylegala Date: Fri, 24 May 2024 22:27:19 +0200 Subject: [PATCH 03/30] False to array conversion fix. (#73) (cherry picked from commit eed5974b24c270e80d0d600f2ddbfe0f29a8fcf5) --- README.md | 4 ++++ lib/Cake/Model/Model.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cae994b585..517cd6059e 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,10 @@ It means that composer will look at `master` branch of repository configured und ## Changelog +### 2024-05-24 + +- Fix deprecation error in Model: `Automatic conversion of false to array is deprecated` + ### 2024-05-22 - Fix deprecation error in I18n: `Automatic conversion of false to array is deprecated` diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index aef3c1085f..b597cf2d0b 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -1979,7 +1979,7 @@ protected function _doSave($data = null, $options = array()) { $this->_clearCache(); $this->validationErrors = array(); $this->whitelist = $_whitelist; - $this->data = false; + $this->data = []; return $success; } From 7343bc8526d4ceceecb56a92df54a7d78c32eccd Mon Sep 17 00:00:00 2001 From: Kamil Wylegala Date: Wed, 5 Jun 2024 22:21:39 +0200 Subject: [PATCH 04/30] Intl recommendation + `PHP81_BC\strftime` + Symfony ICU Polyfill (#69) * Intl extension requirement. * CakeTime migrated to Intl. * FormHelper migration strftime to Intl. * Readme update. (cherry picked from commit b918df80082ae8a546da9b47af0d15a68b183daf) --- README.md | 14 +++++++------ composer.json | 8 ++++++-- lib/Cake/Test/Case/Utility/CakeTimeTest.php | 22 ++++++++++----------- lib/Cake/Utility/CakeTime.php | 5 ++--- lib/Cake/View/Helper/FormHelper.php | 6 ++---- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 517cd6059e..4e1257b4ba 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,6 @@ Unfortunately branch 2.x in original repository was taken down. -## Incoming breaking changes - -The `strftime` function is widely used in the framework codebase. We plan to replace it with `IntlDateFormatter::format()`, this method comes from the `Intl` extension. CakePHP2 doesn't use it, so it would be a **breaking change**. We'd like to start rolling it out in early **May 2024**. - -If you have any concerns, let's discuss it under: https://github.com/kamilwylegala/cakephp2-php8/issues/65 - ## Why I created this fork? 🤔 CakePHP 2 stopped getting updates in the end of 2019 (AFAIR). Unfortunately in my case it's too expensive to migrate to newer versions of CakePHP. I started migrating to Symfony framework, but I still use ORM from CakePHP (and actually I like it). So in order to keep up with the newest PHP versions I decided to create fork of the framework. @@ -38,6 +32,10 @@ Here are steps I took to migrate my project through all versions to PHP 8.1, may - ~~Due to lack of tests ☝️~~ - **you also need to rely** on tests in your application after integrating with this fork. - If after integration you spot any issues related to framework please let me know by creating an issue or pull request with fix. +### Breaking changes + +- In order to get rid of `strftime()` deprecation notices, it's required to switch to `IntlDateFormatter` class. This class is available in `intl` extension. Fork doesn't require it explicitly but to be able to use its functions Symfony ICU Polyfill is installed. To provide `strftime` behavior compatibility, `PHP81_BC\strftime` is used. `PHP81_BC` doesn't fully cover strftime, your code should work but there is a chance you'll get slightly different results. Discussed (here)[https://github.com/kamilwylegala/cakephp2-php8/pull/64] and (here)[https://github.com/kamilwylegala/cakephp2-php8/issues/65]. + ## Installation This repository **is not** available in packagist, therefore your project's `composer.json` must be changed to point to custom repository. @@ -61,6 +59,10 @@ It means that composer will look at `master` branch of repository configured und ## Changelog +### 2024-06-05 + +- Removed usage of `strftime`, replaced with Intl extension. + ### 2024-05-24 - Fix deprecation error in Model: `Automatic conversion of false to array is deprecated` diff --git a/composer.json b/composer.json index 8fbb39241c..8557694a05 100644 --- a/composer.json +++ b/composer.json @@ -18,11 +18,15 @@ "source": "https://github.com/cakephp/cakephp" }, "require": { - "php": "^8.0" + "php": "^8.0", + "ext-mbstring": "*", + "symfony/polyfill-intl-icu": "^1.29", + "php81_bc/strftime": "^0.7.5" }, "suggest": { "ext-openssl": "You need to install ext-openssl or ext-mcrypt to use AES-256 encryption", - "ext-mcrypt": "You need to install ext-openssl or ext-mcrypt to use AES-256 encryption" + "ext-mcrypt": "You need to install ext-openssl or ext-mcrypt to use AES-256 encryption", + "ext-intl": "Required to use IntlDateFormatter instead of strftime, if not Symfony polyfill will be used." }, "require-dev": { "phpunit/phpunit": "^9.5", diff --git a/lib/Cake/Test/Case/Utility/CakeTimeTest.php b/lib/Cake/Test/Case/Utility/CakeTimeTest.php index f9ce1158c0..3ff7fb1fdb 100644 --- a/lib/Cake/Test/Case/Utility/CakeTimeTest.php +++ b/lib/Cake/Test/Case/Utility/CakeTimeTest.php @@ -279,9 +279,7 @@ public function testTimeAgoInWordsWithFormat() { $this->assertEquals('on 2007-09-25', $result); $result = $this->Time->timeAgoInWords('2007-9-25', '%x'); - // @codingStandardsIgnoreStart - $this->assertEquals('on ' . @strftime('%x', strtotime('2007-9-25')), $result); - // @codingStandardsIgnoreEnd + $this->assertEquals('on ' . PHP81_BC\strftime('%x', strtotime('2007-9-25')), $result); $result = $this->Time->timeAgoInWords( strtotime('+2 weeks +2 days'), @@ -306,7 +304,7 @@ public function testTimeAgoInWordsWithFormat() { array('end' => '1 month', 'format' => '%x') ); // @codingStandardsIgnoreStart - $this->assertEquals('on ' . @strftime('%x', strtotime('+2 months +2 days')), $result); + $this->assertEquals('on ' . PHP81_BC\strftime('%x', strtotime('+2 months +2 days')), $result); // @codingStandardsIgnoreEnd } @@ -476,11 +474,11 @@ public function testNiceShort() { * @return void */ public function testNiceShortI18n() { - $restore = setlocale(LC_ALL, 0); - setlocale(LC_ALL, 'es_ES'); + $restore = Locale::getDefault(); + Locale::setDefault('es_ES'); $time = strtotime('2015-01-07 03:05:00'); $this->assertEquals('ene 7th 2015, 03:05', $this->Time->niceShort($time)); - setlocale(LC_ALL, $restore); + Locale::setDefault($restore); } /** @@ -1166,6 +1164,7 @@ public function testConvertPercentE() { * @return void */ public function testI18nFormat() { + $resetLocale = Locale::getDefault(); App::build(array( 'Locale' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Locale' . DS) ), App::RESET); @@ -1173,13 +1172,14 @@ public function testI18nFormat() { $time = strtotime('Thu Jan 14 13:59:28 2010'); + Locale::setDefault('en_US'); $result = $this->Time->i18nFormat($time); $expected = '14/01/10'; $this->assertEquals($expected, $result); $result = $this->Time->i18nFormat($time, '%c'); // @codingStandardsIgnoreStart - $expected = 'jue 14 ene 2010 13:59:28 ' . mb_convert_encoding(@strftime('%Z', $time), 'UTF-8', 'ISO-8859-1'); + $expected = 'jue 14 ene 2010 13:59:28 ' . mb_convert_encoding(PHP81_BC\strftime('%Z', $time), 'UTF-8', 'ISO-8859-1'); // @codingStandardsIgnoreEnd $this->assertEquals($expected, $result); @@ -1194,9 +1194,7 @@ public function testI18nFormat() { $this->assertEquals($expected, $result); $result = $this->Time->i18nFormat($time, '%c'); - // @codingStandardsIgnoreStart - $expected = 'mié 13 ene 2010 13:59:28 ' . mb_convert_encoding(@strftime('%Z', $time), 'UTF-8', 'ISO-8859-1'); - // @codingStandardsIgnoreEnd + $expected = 'mié 13 ene 2010 13:59:28 ' . mb_convert_encoding(PHP81_BC\strftime('%Z', $time), 'UTF-8', 'ISO-8859-1'); $this->assertEquals($expected, $result); $result = $this->Time->i18nFormat($time, 'Time is %r, and date is %x'); @@ -1206,6 +1204,8 @@ public function testI18nFormat() { $result = $this->Time->i18nFormat('invalid date', '%x', 'Date invalid'); $expected = 'Date invalid'; $this->assertEquals($expected, $result); + + Locale::setDefault($resetLocale); } public function testI18nFormatTimezoneConversionToUTC() { diff --git a/lib/Cake/Utility/CakeTime.php b/lib/Cake/Utility/CakeTime.php index 75fae995e3..fb9f1cad03 100644 --- a/lib/Cake/Utility/CakeTime.php +++ b/lib/Cake/Utility/CakeTime.php @@ -1167,9 +1167,8 @@ public static function listTimezones($filter = null, $country = null, $options = * @return string formatted string with correct encoding. */ protected static function _strftime($format, $timestamp) { - // @codingStandardsIgnoreStart - $format = @strftime($format, $timestamp); - // @codingStandardsIgnoreEnd + $format = PHP81_BC\strftime($format, $timestamp); + $encoding = Configure::read('App.encoding'); if (!empty($encoding) && $encoding === 'UTF-8') { if (function_exists('mb_check_encoding')) { diff --git a/lib/Cake/View/Helper/FormHelper.php b/lib/Cake/View/Helper/FormHelper.php index 89b7f11274..032771bfb3 100644 --- a/lib/Cake/View/Helper/FormHelper.php +++ b/lib/Cake/View/Helper/FormHelper.php @@ -2767,9 +2767,7 @@ protected function _getDateTimeValue($value, $timeFormat) { } if (is_numeric($value)) { - // @codingStandardsIgnoreStart - $value = @strftime('%Y-%m-%d %H:%M:%S', $value); - // @codingStandardsIgnoreEnd + $value = PHP81_BC\strftime('%Y-%m-%d %H:%M:%S', $value); } $meridian = 'am'; $pos = strpos($value, '-'); @@ -3024,7 +3022,7 @@ protected function _generateOptions($name, $options = array()) { } else { for ($m = 1; $m <= 12; $m++) { // @codingStandardsIgnoreStart - $data[sprintf("%02s", $m)] = @strftime("%m", mktime(1, 1, 1, $m, 1, 1999)); + $data[sprintf("%02s", $m)] = PHP81_BC\strftime("%m", mktime(1, 1, 1, $m, 1, 1999)); // @codingStandardsIgnoreEnd } } From da10e2802353288dc94e22414a70eb490354ae32 Mon Sep 17 00:00:00 2001 From: Markus Bauer Date: Wed, 24 Jul 2024 18:13:57 +0200 Subject: [PATCH 05/30] Fix potential CSRF circumvention with custom HTTP methods (#76) * Backported patch, fixing potential CSRF circumvention with custom HTTP methods. Upstream: https://github.com/cakephp/cakephp/commit/0f818a23a876c01429196bf7623e1e94a50230f0 * Fix unit tests for SecurityComponent --------- Co-authored-by: Markus Bauer (cherry picked from commit c0fb45e79e17734b8db0245e06e83eca815b25ae) --- lib/Cake/Controller/Component/SecurityComponent.php | 2 +- .../Test/Case/Controller/Component/SecurityComponentTest.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Controller/Component/SecurityComponent.php b/lib/Cake/Controller/Component/SecurityComponent.php index 308f27c738..9b3134e470 100644 --- a/lib/Cake/Controller/Component/SecurityComponent.php +++ b/lib/Cake/Controller/Component/SecurityComponent.php @@ -227,7 +227,7 @@ class SecurityComponent extends Component { public function startup(Controller $controller) { $this->request = $controller->request; $this->_action = $controller->request->params['action']; - $hasData = ($controller->request->data || $controller->request->is(array('put', 'post', 'delete', 'patch'))); + $hasData = ($controller->request->data || !$controller->request->is(['head', 'get', 'options'])); try { $this->_methodsRequired($controller); $this->_secureRequired($controller); diff --git a/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php b/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php index 0fba140b28..e49e65c0a1 100644 --- a/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php @@ -162,6 +162,7 @@ class SecurityComponentTest extends CakeTestCase { */ public function setUp() : void { parent::setUp(); + $_SERVER['REQUEST_METHOD'] = 'GET'; $request = $this->getMock('CakeRequest', array('here'), array('posts/index', false)); $request->addParams(array('controller' => 'posts', 'action' => 'index')); @@ -321,7 +322,7 @@ public function testRequireSecureFail() { * @return void */ public function testRequireSecureSucceed() { - $_SERVER['REQUEST_METHOD'] = 'Secure'; + $_SERVER['REQUEST_METHOD'] = 'GET'; $this->Controller->request['action'] = 'posted'; $_SERVER['HTTPS'] = 'on'; $this->Controller->Security->requireSecure('posted'); From 0b67e9f7271dd9d0baee4382f0aabdc73f924129 Mon Sep 17 00:00:00 2001 From: Kamil Wylegala Date: Wed, 24 Jul 2024 18:14:39 +0200 Subject: [PATCH 06/30] Update README.md (cherry picked from commit 42e9fa1aadf4b109b23d576aab20ec34cc47d20d) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 4e1257b4ba..e7626aa30f 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,10 @@ It means that composer will look at `master` branch of repository configured und ## Changelog +### 2024-07-24 + +- Csrf vulnerabity fix back ported from Cake PHP 3 + ### 2024-06-05 - Removed usage of `strftime`, replaced with Intl extension. From 20f887560388d2f13d4515ca3b5bc695889734d8 Mon Sep 17 00:00:00 2001 From: Markus Bauer Date: Wed, 24 Jul 2024 21:55:01 +0200 Subject: [PATCH 07/30] Remove constructs that deprecate with PHP 8.4 (#75) * Remove constructs that deprecate with PHP 8.4 * Fix SecurityComponent typing --------- Co-authored-by: Markus Bauer (cherry picked from commit 2220f35b1b048fd0e79e2893ab853d5b472e1507) --- lib/Cake/Console/Command/Task/FixtureTask.php | 2 +- lib/Cake/Console/Command/Task/ModelTask.php | 4 ++-- .../Controller/Component/Auth/BaseAuthorize.php | 2 +- .../Component/Auth/ControllerAuthorize.php | 2 +- lib/Cake/Controller/Component/AuthComponent.php | 2 +- .../Controller/Component/SecurityComponent.php | 4 ++-- lib/Cake/Error/exceptions.php | 2 +- lib/Cake/Model/Datasource/DboSource.php | 14 +++++++------- lib/Cake/Model/Model.php | 2 +- lib/Cake/Test/Case/Console/ShellTest.php | 2 +- lib/Cake/View/JsonView.php | 2 +- lib/Cake/View/View.php | 2 +- lib/Cake/View/XmlView.php | 2 +- 13 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/Cake/Console/Command/Task/FixtureTask.php b/lib/Cake/Console/Command/Task/FixtureTask.php index 7b2ee52e7d..70a55ed6ec 100644 --- a/lib/Cake/Console/Command/Task/FixtureTask.php +++ b/lib/Cake/Console/Command/Task/FixtureTask.php @@ -248,7 +248,7 @@ public function bake($model, $useTable = false, $importOptions = array()) { $this->_Schema = new CakeSchema(); $data = $this->_Schema->read(array('models' => false, 'connection' => $this->connection)); if (!isset($data['tables'][$useTable])) { - $this->err("Warning: Could not find the '${useTable}' table for ${model}."); + $this->err("Warning: Could not find the '{$useTable}' table for {$model}."); return null; } diff --git a/lib/Cake/Console/Command/Task/ModelTask.php b/lib/Cake/Console/Command/Task/ModelTask.php index f70cf656d5..3473e3f410 100644 --- a/lib/Cake/Console/Command/Task/ModelTask.php +++ b/lib/Cake/Console/Command/Task/ModelTask.php @@ -170,7 +170,7 @@ public function inOptions($options, $prompt = null, $default = null) { while (!$valid) { $len = strlen(count($options) + 1); foreach ($options as $i => $option) { - $this->out(sprintf("%${len}d. %s", $i + 1, $option)); + $this->out(sprintf("%{$len}d. %s", $i + 1, $option)); } if (empty($prompt)) { $prompt = __d('cake_console', 'Make a selection from the choices above'); @@ -901,7 +901,7 @@ public function listAll($useDbConfig = null) { $this->out(__d('cake_console', 'Possible Models based on your current database:')); $len = strlen($count + 1); for ($i = 0; $i < $count; $i++) { - $this->out(sprintf("%${len}d. %s", $i + 1, $this->_modelNames[$i])); + $this->out(sprintf("%{$len}d. %s", $i + 1, $this->_modelNames[$i])); } } return $this->_tables; diff --git a/lib/Cake/Controller/Component/Auth/BaseAuthorize.php b/lib/Cake/Controller/Component/Auth/BaseAuthorize.php index 1e6a0d6886..feefb88c29 100644 --- a/lib/Cake/Controller/Component/Auth/BaseAuthorize.php +++ b/lib/Cake/Controller/Component/Auth/BaseAuthorize.php @@ -89,7 +89,7 @@ abstract public function authorize($user, CakeRequest $request); * @return mixed * @throws CakeException */ - public function controller(Controller $controller = null) { + public function controller(?Controller $controller = null) { if ($controller) { if (!$controller instanceof Controller) { throw new CakeException(__d('cake_dev', '$controller needs to be an instance of Controller')); diff --git a/lib/Cake/Controller/Component/Auth/ControllerAuthorize.php b/lib/Cake/Controller/Component/Auth/ControllerAuthorize.php index c55ef3d3ea..62b3065067 100644 --- a/lib/Cake/Controller/Component/Auth/ControllerAuthorize.php +++ b/lib/Cake/Controller/Component/Auth/ControllerAuthorize.php @@ -43,7 +43,7 @@ class ControllerAuthorize extends BaseAuthorize { * @return mixed * @throws CakeException */ - public function controller(Controller $controller = null) { + public function controller(?Controller $controller = null) { if ($controller) { if (!method_exists($controller, 'isAuthorized')) { throw new CakeException(__d('cake_dev', '$controller does not implement an %s method.', 'isAuthorized()')); diff --git a/lib/Cake/Controller/Component/AuthComponent.php b/lib/Cake/Controller/Component/AuthComponent.php index a652e950ab..f21ac5cacd 100644 --- a/lib/Cake/Controller/Component/AuthComponent.php +++ b/lib/Cake/Controller/Component/AuthComponent.php @@ -453,7 +453,7 @@ protected function _setDefaults() { * @param CakeRequest|null $request The request to authenticate for. If empty, the current request will be used. * @return bool True if $user is authorized, otherwise false */ - public function isAuthorized($user = null, CakeRequest $request = null) { + public function isAuthorized($user = null, ?CakeRequest $request = null) { if (empty($user) && !$this->user()) { return false; } diff --git a/lib/Cake/Controller/Component/SecurityComponent.php b/lib/Cake/Controller/Component/SecurityComponent.php index 9b3134e470..e9d779e76a 100644 --- a/lib/Cake/Controller/Component/SecurityComponent.php +++ b/lib/Cake/Controller/Component/SecurityComponent.php @@ -344,7 +344,7 @@ public function requireAuth() { * @link https://book.cakephp.org/2.0/en/core-libraries/components/security-component.html#handling-blackhole-callbacks * @throws BadRequestException */ - public function blackHole(Controller $controller, $error = '', SecurityException $exception = null) { + public function blackHole(Controller $controller, $error = '', ?SecurityException $exception = null) { if (!$this->blackHoleCallback) { $this->_throwException($exception); } @@ -822,7 +822,7 @@ protected function _expireTokens($tokens) { * @param string $method Method to execute * @param array $params Parameters to send to method * @return mixed Controller callback method's response - * @throws BadRequestException When a the blackholeCallback is not callable. + * @throws BadRequestException When the blackholeCallback is not callable. */ protected function _callback(Controller $controller, $method, $params = array()) { if (!is_callable(array($controller, $method))) { diff --git a/lib/Cake/Error/exceptions.php b/lib/Cake/Error/exceptions.php index 6608f62218..373d01a1bd 100644 --- a/lib/Cake/Error/exceptions.php +++ b/lib/Cake/Error/exceptions.php @@ -297,7 +297,7 @@ class PrivateActionException extends CakeException { protected $_messageTemplate = 'Private Action %s::%s() is not directly accessible.'; //@codingStandardsIgnoreStart - public function __construct($message, $code = 404, Exception $previous = null) { + public function __construct($message, $code = 404, ?Exception $previous = null) { parent::__construct($message, $code, $previous); } //@codingStandardsIgnoreEnd diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index 0322668ee9..da3ffb911f 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -527,7 +527,7 @@ protected function _execute($sql, $params = array(), $prepareOptions = array()) * @param PDOStatement $query the query to extract the error from if any * @return string Error message with error number */ - public function lastError(PDOStatement $query = null) { + public function lastError(?PDOStatement $query = null) { if ($query) { $error = $query->errorInfo(); } else { @@ -2761,7 +2761,7 @@ public function fields(Model $Model, $alias = null, $fields = array(), $quote = * @param Model $Model A reference to the Model instance making the query * @return string SQL fragment */ - public function conditions($conditions, $quoteValues = true, $where = true, Model $Model = null) { + public function conditions($conditions, $quoteValues = true, $where = true, ?Model $Model = null) { $clause = $out = ''; if ($where) { @@ -2804,7 +2804,7 @@ public function conditions($conditions, $quoteValues = true, $where = true, Mode * @param Model $Model A reference to the Model instance making the query * @return string SQL fragment */ - public function conditionKeysToString($conditions, $quoteValues = true, Model $Model = null) { + public function conditionKeysToString($conditions, $quoteValues = true, ?Model $Model = null) { $out = array(); $data = $columnType = null; @@ -2909,7 +2909,7 @@ public function conditionKeysToString($conditions, $quoteValues = true, Model $M * @param Model $Model Model object initiating the query * @return string */ - protected function _parseKey($key, $value, Model $Model = null) { + protected function _parseKey($key, $value, ?Model $Model = null) { $operatorMatch = '/^(((' . implode(')|(', $this->_sqlOps); $operatorMatch .= ')\\x20?)|<[>=]?(?![^>]+>)\\x20?|[>=!]{1,3}(?!<)\\x20?)/is'; $bound = (strpos($key, '?') !== false || (is_array($value) && strpos($key, ':') !== false)); @@ -3083,7 +3083,7 @@ public function limit($limit, $offset = null) { * @param Model $Model Model reference (used to look for virtual field) * @return string ORDER BY clause */ - public function order($keys, $direction = 'ASC', Model $Model = null) { + public function order($keys, $direction = 'ASC', ?Model $Model = null) { if (!is_array($keys)) { $keys = array($keys); } @@ -3166,7 +3166,7 @@ public function order($keys, $direction = 'ASC', Model $Model = null) { * @param Model $Model The model to get group by fields for. * @return string Group By clause or null. */ - public function group($fields, Model $Model = null) { + public function group($fields, ?Model $Model = null) { if (empty($fields)) { return null; } @@ -3196,7 +3196,7 @@ public function group($fields, Model $Model = null) { * @param Model $Model A reference to the Model instance making the query * @return string|null HAVING clause or null */ - public function having($fields, $quoteValues = true, Model $Model = null) { + public function having($fields, $quoteValues = true, ?Model $Model = null) { if (!$fields) { return null; } diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index b597cf2d0b..3ac6f6d6f5 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -3913,7 +3913,7 @@ protected function _clearCache($type = null) { * If null a new ModelValidator instance will be made using current model object * @return ModelValidator */ - public function validator(ModelValidator $instance = null) { + public function validator(?ModelValidator $instance = null) { if ($instance) { $this->_validator = $instance; } elseif (!$this->_validator) { diff --git a/lib/Cake/Test/Case/Console/ShellTest.php b/lib/Cake/Test/Case/Console/ShellTest.php index 2617320e6f..1886eac575 100644 --- a/lib/Cake/Test/Case/Console/ShellTest.php +++ b/lib/Cake/Test/Case/Console/ShellTest.php @@ -607,7 +607,7 @@ public function testCreateFileNonInteractive() { $this->Shell->interactive = false; - $contents = "Shell->createFile($file, $contents); $this->assertTrue($result); $this->assertTrue(file_exists($file)); diff --git a/lib/Cake/View/JsonView.php b/lib/Cake/View/JsonView.php index 2284233a59..d5412c46f1 100644 --- a/lib/Cake/View/JsonView.php +++ b/lib/Cake/View/JsonView.php @@ -65,7 +65,7 @@ class JsonView extends View { * * @param Controller $controller Controller instance. */ - public function __construct(Controller $controller = null) { + public function __construct(?Controller $controller = null) { parent::__construct($controller); if (isset($controller->response) && $controller->response instanceof CakeResponse) { $controller->response->type('json'); diff --git a/lib/Cake/View/View.php b/lib/Cake/View/View.php index 085928d2e8..2b7410f660 100644 --- a/lib/Cake/View/View.php +++ b/lib/Cake/View/View.php @@ -330,7 +330,7 @@ class View extends CakeObject { * * @param Controller $controller A controller object to pull View::_passedVars from. */ - public function __construct(Controller $controller = null) { + public function __construct(?Controller $controller = null) { if (is_object($controller)) { $count = count($this->_passedVars); for ($j = 0; $j < $count; $j++) { diff --git a/lib/Cake/View/XmlView.php b/lib/Cake/View/XmlView.php index a8f4fcb16b..eb6267b13e 100644 --- a/lib/Cake/View/XmlView.php +++ b/lib/Cake/View/XmlView.php @@ -65,7 +65,7 @@ class XmlView extends View { * * @param Controller $controller Controller instance. */ - public function __construct(Controller $controller = null) { + public function __construct(?Controller $controller = null) { parent::__construct($controller); if (isset($controller->response) && $controller->response instanceof CakeResponse) { From b0260596003a0d683bbc50545ed7d74a2e1c568c Mon Sep 17 00:00:00 2001 From: Kamil Wylegala Date: Wed, 24 Jul 2024 21:55:59 +0200 Subject: [PATCH 08/30] Update README.md (cherry picked from commit 7e1da9a5cab3bc4489e9c92eabcad3826d228188) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e7626aa30f..fb21491472 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ It means that composer will look at `master` branch of repository configured und ### 2024-07-24 - Csrf vulnerabity fix back ported from Cake PHP 3 +- Explicit type hint definition of nullable parameters. ### 2024-06-05 From c00fe46f9b3856ce40085f6a720f483bb2fb7987 Mon Sep 17 00:00:00 2001 From: Kamil Wylegala Date: Sat, 21 Sep 2024 12:53:44 +0200 Subject: [PATCH 09/30] Added PDOException wrapper to avoid creating dynamic property. (#77) (cherry picked from commit 61c8f9ad257749254d5ee85625870b641c59beba) --- README.md | 4 ++++ lib/Cake/Model/Datasource/DboSource.php | 8 +++++--- .../Datasource/PDOExceptionWithQueryString.php | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 lib/Cake/Model/Datasource/PDOExceptionWithQueryString.php diff --git a/README.md b/README.md index fb21491472..773536179a 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,10 @@ It means that composer will look at `master` branch of repository configured und ## Changelog +### 2024-09-21 + +- Added wrapper for PDOException to avoid creating dynamic property `queryString`. + ### 2024-07-24 - Csrf vulnerabity fix back ported from Cake PHP 3 diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index da3ffb911f..d356492df2 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -17,6 +17,7 @@ */ App::uses('DataSource', 'Model/Datasource'); +App::uses('PDOExceptionWithQueryString', 'Model/Datasource'); App::uses('CakeText', 'Utility'); App::uses('View', 'View'); @@ -512,12 +513,13 @@ protected function _execute($sql, $params = array(), $prepareOptions = array()) } return $query; } catch (PDOException $e) { + $wrapperException = new PDOExceptionWithQueryString($e); if (isset($query->queryString)) { - $e->queryString = $query->queryString; + $wrapperException->queryString = $query->queryString; } else { - $e->queryString = $sql; + $wrapperException->queryString = $sql; } - throw $e; + throw $wrapperException; } } diff --git a/lib/Cake/Model/Datasource/PDOExceptionWithQueryString.php b/lib/Cake/Model/Datasource/PDOExceptionWithQueryString.php new file mode 100644 index 0000000000..dd59c9e06e --- /dev/null +++ b/lib/Cake/Model/Datasource/PDOExceptionWithQueryString.php @@ -0,0 +1,17 @@ +getMessage(), 0, $e->getPrevious()); + + $this->code = $e->code; + } +} From 9fc1597614e2a4d456b060a6274a0b644a9e705b Mon Sep 17 00:00:00 2001 From: Kamil Wylegala Date: Sat, 21 Sep 2024 13:04:20 +0200 Subject: [PATCH 10/30] Fixed errorInfo in pdo exception wrapper. (#78) (cherry picked from commit cb3382f6d2713ff9d4fe4cf4498ee312f2efa63e) --- lib/Cake/Model/Datasource/PDOExceptionWithQueryString.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Cake/Model/Datasource/PDOExceptionWithQueryString.php b/lib/Cake/Model/Datasource/PDOExceptionWithQueryString.php index dd59c9e06e..fdc2cf825d 100644 --- a/lib/Cake/Model/Datasource/PDOExceptionWithQueryString.php +++ b/lib/Cake/Model/Datasource/PDOExceptionWithQueryString.php @@ -12,6 +12,7 @@ class PDOExceptionWithQueryString extends PDOException { public function __construct(PDOException $e) { parent::__construct($e->getMessage(), 0, $e->getPrevious()); + $this->errorInfo = $e->errorInfo; $this->code = $e->code; } } From 97562d6fb583a0d22ce050f24811e3972b58e013 Mon Sep 17 00:00:00 2001 From: Kamil Wylegala Date: Sat, 16 Nov 2024 18:07:14 +0100 Subject: [PATCH 11/30] inflector fix. (#80) (cherry picked from commit 789a752cf6a099913fd56b80f6ef05bf4b10d166) --- README.md | 4 ++++ lib/Cake/Utility/Inflector.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 773536179a..d5d8e2584d 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,10 @@ It means that composer will look at `master` branch of repository configured und ## Changelog +### 2024-11-16 + +- Inflector fix: str_place with null. + ### 2024-09-21 - Added wrapper for PDOException to avoid creating dynamic property `queryString`. diff --git a/lib/Cake/Utility/Inflector.php b/lib/Cake/Utility/Inflector.php index d0ec62f470..a14fbb1e89 100644 --- a/lib/Cake/Utility/Inflector.php +++ b/lib/Cake/Utility/Inflector.php @@ -500,7 +500,7 @@ public static function underscore($camelCasedWord) { */ public static function humanize($lowerCaseAndUnderscoredWord) { if (!($result = static::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) { - $result = explode(' ', str_replace('_', ' ', $lowerCaseAndUnderscoredWord)); + $result = explode(' ', str_replace('_', ' ', $lowerCaseAndUnderscoredWord ?? "")); foreach ($result as &$word) { $word = mb_strtoupper(mb_substr($word, 0, 1)) . mb_substr($word, 1); } From 725ba6cf8dc3be8e6d62724fc445a97f23311ec7 Mon Sep 17 00:00:00 2001 From: Kamil Wylegala Date: Tue, 4 Feb 2025 22:29:07 +0100 Subject: [PATCH 12/30] Php8.4 (#83) * SessionHandlerAdapter for Cake's interface. * Removed deprecated E_STRICT. (cherry picked from commit 5b6a1031bf7cfd3b246b814f56d637067a92772d) --- .github/workflows/phpcs.yml | 54 ------------------- README.md | 5 ++ lib/Cake/Error/ErrorHandler.php | 4 -- lib/Cake/Model/Datasource/CakeSession.php | 9 ++-- .../Datasource/SessionHandlerAdapter.php | 43 +++++++++++++++ 5 files changed, 51 insertions(+), 64 deletions(-) delete mode 100644 .github/workflows/phpcs.yml create mode 100644 lib/Cake/Model/Datasource/SessionHandlerAdapter.php diff --git a/.github/workflows/phpcs.yml b/.github/workflows/phpcs.yml deleted file mode 100644 index 174620946a..0000000000 --- a/.github/workflows/phpcs.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: PHP Coding Standard - -on: - push: - branches: - - 'master' - pull_request: - branches: - - '*' - -permissions: - contents: read - -jobs: - phpcs: - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - php-version: - - '7.4' - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install PHP with extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - ini-values: assert.exception=1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On - tools: composer - - - name: Composer get cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache Composer - uses: actions/cache@v3 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }} - restore-keys: | - ${{ runner.os }}-php${{ matrix.php-version }}-composer- - - - name: Install Composer Packages - run: composer install --no-ansi --no-interaction --no-progress --ignore-platform-req=php - - - name: Install CodeSniffer Rule - run: vendors/bin/phpcs --config-set installed_paths vendors/cakephp/cakephp-codesniffer - - - name: Check CodeSniffer - run: vendors/bin/phpcs -p --extensions=php --standard=ruleset.xml ./lib/Cake diff --git a/README.md b/README.md index d5d8e2584d..de79003fbd 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,11 @@ It means that composer will look at `master` branch of repository configured und ## Changelog +### 2025-02-04 + +- Fixes for PHP 8.4: `session_set_save_handler` accepts object, removed `E_STRICT` reference. +- Removed github action with php code sniffer. It's quite painful to work with. Need to migrate to something newer, that will affect code base as little as possible. + ### 2024-11-16 - Inflector fix: str_place with null. diff --git a/lib/Cake/Error/ErrorHandler.php b/lib/Cake/Error/ErrorHandler.php index de892f6be0..30873d7268 100644 --- a/lib/Cake/Error/ErrorHandler.php +++ b/lib/Cake/Error/ErrorHandler.php @@ -302,10 +302,6 @@ public static function mapErrorCode($code) { $error = 'Notice'; $log = LOG_NOTICE; break; - case E_STRICT: - $error = 'Strict'; - $log = LOG_NOTICE; - break; case E_DEPRECATED: case E_USER_DEPRECATED: $error = 'Deprecated'; diff --git a/lib/Cake/Model/Datasource/CakeSession.php b/lib/Cake/Model/Datasource/CakeSession.php index e67731429b..3cf7f24de5 100644 --- a/lib/Cake/Model/Datasource/CakeSession.php +++ b/lib/Cake/Model/Datasource/CakeSession.php @@ -23,6 +23,8 @@ App::uses('Hash', 'Utility'); App::uses('Security', 'Utility'); +App::uses('SessionHandlerAdapter', 'Model/Datasource'); + /** * Session class for CakePHP. @@ -591,12 +593,7 @@ protected static function _configureSession() { $handler = static::_getHandler($sessionConfig['handler']['engine']); if (!function_exists('session_status') || session_status() !== PHP_SESSION_ACTIVE) { session_set_save_handler( - array($handler, 'open'), - array($handler, 'close'), - array($handler, 'read'), - array($handler, 'write'), - array($handler, 'destroy'), - array($handler, 'gc') + new SessionHandlerAdapter($handler) ); } } diff --git a/lib/Cake/Model/Datasource/SessionHandlerAdapter.php b/lib/Cake/Model/Datasource/SessionHandlerAdapter.php new file mode 100644 index 0000000000..4737011848 --- /dev/null +++ b/lib/Cake/Model/Datasource/SessionHandlerAdapter.php @@ -0,0 +1,43 @@ +cakeSessionHandler->close(); + } + + public function destroy(string $id) + { + return $this->cakeSessionHandler->destroy($id); + } + + public function gc(int $max_lifetime) + { + return $this->cakeSessionHandler->gc($max_lifetime); + } + + public function open(string $path, string $name) + { + //Cake interface ignores these parameters. + return $this->cakeSessionHandler->open(); + } + + public function read(string $id) + { + return $this->cakeSessionHandler->read($id); + } + + public function write(string $id, string $data) + { + return $this->cakeSessionHandler->write($id, $data); + } +} From 1a9bdf84af81b7cb887e405e4e10c7a5840baa23 Mon Sep 17 00:00:00 2001 From: Kamil Wylegala Date: Sun, 22 Feb 2026 20:37:52 +0100 Subject: [PATCH 13/30] Fixing deprecation in PHP 8.5 (#86) * Fixing deprecation in PHP 8.5 * Removed ancient code sniffer. * Skip DNS resoultion failure. (cherry picked from commit d9f65086d1badde3398af2c0865fc5fe82fc7564) --- composer.json | 5 +---- lib/Cake/Model/Datasource/Database/Mysql.php | 13 +++++++++++-- lib/Cake/Test/Case/Network/Http/HttpSocketTest.php | 1 + 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 8557694a05..426391415f 100644 --- a/composer.json +++ b/composer.json @@ -29,8 +29,7 @@ "ext-intl": "Required to use IntlDateFormatter instead of strftime, if not Symfony polyfill will be used." }, "require-dev": { - "phpunit/phpunit": "^9.5", - "cakephp/cakephp-codesniffer": "^1.0.0" + "phpunit/phpunit": "^9.5" }, "config": { "vendor-dir": "vendors/", @@ -41,10 +40,8 @@ ], "scripts": { "check": [ - "@cs-check", "@test" ], - "cs-check": "./vendors/bin/phpcs -p --extensions=php --standard=CakePHP ./lib/Cake", "test": "./lib/Cake/Console/cake test core AllTests --stderr --verbose" } } diff --git a/lib/Cake/Model/Datasource/Database/Mysql.php b/lib/Cake/Model/Datasource/Database/Mysql.php index 000a19ea95..5fb953c8e8 100644 --- a/lib/Cake/Model/Datasource/Database/Mysql.php +++ b/lib/Cake/Model/Datasource/Database/Mysql.php @@ -161,14 +161,23 @@ public function connect() { $config = $this->config; $this->connected = false; + // PDO::MYSQL_ATTR_* constants are deprecated in PHP 8.5; Pdo\Mysql class is available since PHP 8.1 + if (class_exists('Pdo\Mysql')) { + $mysqlAttrUseBufferedQuery = Pdo\Mysql::ATTR_USE_BUFFERED_QUERY; + $mysqlAttrInitCommand = Pdo\Mysql::ATTR_INIT_COMMAND; + } else { + $mysqlAttrUseBufferedQuery = PDO::MYSQL_ATTR_USE_BUFFERED_QUERY; + $mysqlAttrInitCommand = PDO::MYSQL_ATTR_INIT_COMMAND; + } + $flags = $config['flags'] + array( PDO::ATTR_PERSISTENT => $config['persistent'], - PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, + $mysqlAttrUseBufferedQuery => true, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ); if (!empty($config['encoding'])) { - $flags[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES ' . $config['encoding']; + $flags[$mysqlAttrInitCommand] = 'SET NAMES ' . $config['encoding']; } if (!empty($config['ssl_key']) && !empty($config['ssl_cert'])) { $flags[PDO::MYSQL_ATTR_SSL_KEY] = $config['ssl_key']; diff --git a/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php b/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php index 07dfb6c87a..9acdb5e0d9 100644 --- a/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php +++ b/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php @@ -1845,6 +1845,7 @@ public function testVerifyPeer() { } catch (SocketException $e) { $message = $e->getMessage(); $this->skipIf(strpos($message, 'Invalid HTTP') !== false, 'Invalid HTTP Response received, skipping.'); + $this->skipIf(strpos($message, 'getaddrinfo') !== false, 'Could not resolve host, network unavailable, skipping.'); $this->assertStringContainsString('Failed to enable crypto', $message); } } From 4220ed41773ab87f8a48404fa7482c61f802f49f Mon Sep 17 00:00:00 2001 From: Andras Iklody Date: Fri, 18 Sep 2026 10:09:50 +0200 Subject: [PATCH 14/30] Fix readFile bypass for https urls in Xml::build() (#87) 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 b024bc1ff7daee820dbe5719f0ed1826152c5307) --- lib/Cake/Test/Case/Utility/XmlTest.php | 11 +++++++++++ lib/Cake/Utility/Xml.php | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Test/Case/Utility/XmlTest.php b/lib/Cake/Test/Case/Utility/XmlTest.php index 8fa7fd9ef5..1aacb51fcf 100644 --- a/lib/Cake/Test/Case/Utility/XmlTest.php +++ b/lib/Cake/Test/Case/Utility/XmlTest.php @@ -201,6 +201,17 @@ public function testBuildFromUrlWhenDisabled() { Xml::build($xml, array('readFile' => false)); } +/** + * Test that the readFile option disables https url parsing too. + * + * @return void + */ + public function testBuildFromHttpsUrlWhenDisabled() { + $this->expectException(XmlException::class); + $xml = 'https://www.google.com'; + Xml::build($xml, array('readFile' => false)); + } + /** * data provider function for testBuildInvalidData * diff --git a/lib/Cake/Utility/Xml.php b/lib/Cake/Utility/Xml.php index 84a67ea5d9..33f93f944b 100644 --- a/lib/Cake/Utility/Xml.php +++ b/lib/Cake/Utility/Xml.php @@ -107,7 +107,7 @@ public static function build($input, $options = array()) { return static::_loadXml($input, $options); } elseif ($options['readFile'] && file_exists($input)) { return static::_loadXml(file_get_contents($input), $options); - } elseif ($options['readFile'] && strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) { + } elseif ($options['readFile'] && (strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0)) { try { $socket = new HttpSocket(array('request' => array('redirect' => 10))); $response = $socket->get($input); From 67db9ab1a937442b0ab1a685d084a1b4d388560f Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Fri, 16 Oct 2026 12:18:46 -0300 Subject: [PATCH 15/30] PHP 8.5 compatibility: zero deprecations on 8.1-8.5, CI matrix, docs (PHP85-21) Branch cakephp2-php85 syncs every upstream fix up to kamilwylegala #87 (cherry-picked, includes the CSRF fix #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 --- .github/workflows/tests.yml | 14 ++++- README.md | 63 +++++++++++++++++++ app/Config/core.php | 2 +- app/Console/cake.php | 2 +- app/webroot/index.php | 2 +- app/webroot/test.php | 2 +- docker/web/Dockerfile | 10 +-- lib/Cake/Cache/Engine/MemcachedEngine.php | 8 +++ lib/Cake/Console/Command/SchemaShell.php | 2 +- lib/Cake/Console/Command/Task/CommandTask.php | 2 +- lib/Cake/Console/Command/Task/ExtractTask.php | 4 +- lib/Cake/Console/Command/Task/FixtureTask.php | 2 +- lib/Cake/Console/Command/Task/ModelTask.php | 4 +- lib/Cake/Console/Command/Task/TestTask.php | 2 +- lib/Cake/Console/ConsoleInputOption.php | 4 +- lib/Cake/Console/ConsoleOptionParser.php | 11 +++- lib/Cake/Console/HelpFormatter.php | 7 +++ lib/Cake/Console/Shell.php | 2 +- lib/Cake/Console/TaskCollection.php | 2 +- .../Console/Templates/skel/Console/cake.php | 2 +- .../Console/Templates/skel/webroot/index.php | 2 +- .../Console/Templates/skel/webroot/test.php | 2 +- lib/Cake/Console/cake.php | 2 +- .../Component/Auth/BasicAuthenticate.php | 4 +- .../Component/Auth/DigestAuthenticate.php | 2 +- .../Component/RequestHandlerComponent.php | 2 +- lib/Cake/Controller/ComponentCollection.php | 2 +- lib/Cake/Controller/Scaffold.php | 35 +++++++++++ lib/Cake/Core/Configure.php | 21 ++++--- lib/Cake/Error/ErrorHandler.php | 2 +- lib/Cake/I18n/I18n.php | 21 +++++-- lib/Cake/Log/Engine/ConsoleLog.php | 4 +- lib/Cake/Model/Behavior/TranslateBehavior.php | 12 ++-- lib/Cake/Model/BehaviorCollection.php | 2 +- lib/Cake/Model/CakeDatabaseConfig.php | 27 ++++++++ lib/Cake/Model/CakeSchema.php | 2 +- lib/Cake/Model/ConnectionManager.php | 25 +++++++- lib/Cake/Model/Datasource/CakeSession.php | 7 ++- .../Model/Datasource/Database/Postgres.php | 4 +- lib/Cake/Model/Datasource/Database/Sqlite.php | 8 +-- .../Model/Datasource/Database/Sqlserver.php | 4 +- lib/Cake/Model/Datasource/DboSource.php | 29 ++++++++- .../PDOExceptionWithQueryString.php | 7 +++ .../Datasource/SessionHandlerAdapter.php | 6 ++ lib/Cake/Network/CakeRequest.php | 7 ++- lib/Cake/Network/CakeResponse.php | 8 +-- lib/Cake/Network/Email/CakeEmail.php | 3 +- lib/Cake/Network/Http/HttpResponse.php | 4 +- lib/Cake/Network/Http/HttpSocket.php | 2 +- lib/Cake/Routing/Route/CakeRoute.php | 4 +- lib/Cake/Test/Case/Cache/CacheTest.php | 9 +++ .../Test/Case/Cache/Engine/ApcEngineTest.php | 4 ++ .../Case/Cache/Engine/MemcacheEngineTest.php | 4 ++ .../Case/Cache/Engine/RedisEngineTest.php | 4 ++ .../Case/Cache/Engine/WincacheEngineTest.php | 4 ++ .../Case/Cache/Engine/XcacheEngineTest.php | 4 ++ .../Test/Case/Configure/IniReaderTest.php | 4 ++ .../Test/Case/Configure/PhpReaderTest.php | 4 ++ .../Case/Console/Command/AclShellTest.php | 4 ++ .../Case/Console/Command/ApiShellTest.php | 4 ++ .../Case/Console/Command/BakeShellTest.php | 4 ++ .../Console/Command/CommandListShellTest.php | 4 ++ .../Console/Command/CompletionShellTest.php | 4 ++ .../Case/Console/Command/SchemaShellTest.php | 14 +++++ .../Console/Command/Task/CommandTaskTest.php | 4 ++ .../Command/Task/ControllerTaskTest.php | 4 ++ .../Console/Command/Task/DbConfigTaskTest.php | 4 ++ .../Console/Command/Task/ExtractTaskTest.php | 19 ++++++ .../Console/Command/Task/FixtureTaskTest.php | 4 ++ .../Console/Command/Task/ModelTaskTest.php | 4 ++ .../Console/Command/Task/PluginTaskTest.php | 26 +++++++- .../Console/Command/Task/ProjectTaskTest.php | 4 ++ .../Console/Command/Task/TemplateTaskTest.php | 4 ++ .../Console/Command/Task/TestTaskTest.php | 19 ++++++ .../Console/Command/Task/ViewTaskTest.php | 4 ++ .../Case/Console/Command/TestShellTest.php | 4 ++ .../Case/Console/ConsoleErrorHandlerTest.php | 8 ++- .../Test/Case/Console/ConsoleOutputTest.php | 4 ++ .../Helper/ProgressShellHelperTest.php | 9 +++ .../Console/Helper/TableShellHelperTest.php | 9 +++ lib/Cake/Test/Case/Console/ShellTest.php | 4 ++ .../Test/Case/Console/TaskCollectionTest.php | 4 ++ .../Controller/ApplicationControllerTest.php | 12 ++++ .../Controller/Component/Acl/DbAclTest.php | 4 ++ .../Controller/Component/Acl/PhpAclTest.php | 9 +++ .../Controller/Component/AclComponentTest.php | 4 ++ .../Component/Auth/ActionsAuthorizeTest.php | 19 ++++++ .../Component/Auth/BasicAuthenticateTest.php | 14 +++++ .../Auth/BlowfishAuthenticateTest.php | 14 +++++ .../Auth/ControllerAuthorizeTest.php | 14 +++++ .../Component/Auth/CrudAuthorizeTest.php | 14 +++++ .../Component/Auth/DigestAuthenticateTest.php | 19 ++++++ .../Component/Auth/FormAuthenticateTest.php | 14 +++++ .../Component/AuthComponentTest.php | 14 +++++ .../Component/CookieComponentTest.php | 4 ++ .../Component/FlashComponentTest.php | 9 +++ .../Component/PaginatorComponentTest.php | 14 +++++ .../Component/RequestHandlerComponentTest.php | 8 ++- .../Component/SecurityComponentTest.php | 4 ++ .../Component/SessionComponentTest.php | 4 ++ .../Controller/ComponentCollectionTest.php | 4 ++ .../Test/Case/Controller/ComponentTest.php | 4 ++ .../Test/Case/Controller/ScaffoldTest.php | 4 ++ lib/Cake/Test/Case/Core/CakeObjectTest.php | 4 ++ .../Test/Case/Error/ExceptionRendererTest.php | 1 + .../Test/Case/Log/Engine/ConsoleLogTest.php | 10 ++- .../Behavior/ContainableBehaviorTest.php | 29 +++++++++ .../Model/Behavior/TreeBehaviorAfterTest.php | 4 ++ .../Model/Behavior/TreeBehaviorNumberTest.php | 4 ++ .../Model/Behavior/TreeBehaviorScopedTest.php | 14 +++++ .../Model/Behavior/TreeBehaviorUuidTest.php | 4 ++ lib/Cake/Test/Case/Model/CakeSchemaTest.php | 4 ++ .../Case/Model/Datasource/CakeSessionTest.php | 4 +- .../Case/Model/Datasource/DataSourceTest.php | 9 +++ .../Model/Datasource/Database/MysqlTest.php | 26 +++++++- .../Datasource/Database/PostgresTest.php | 4 ++ .../Datasource/Database/SqlserverTest.php | 9 +++ .../Case/Model/Datasource/DboSourceTest.php | 13 +++- .../Datasource/Session/CacheSessionTest.php | 4 ++ .../Session/DatabaseSessionTest.php | 4 ++ lib/Cake/Test/Case/Model/ModelTestBase.php | 4 ++ .../Test/Case/Network/CakeRequestTest.php | 9 +++ lib/Cake/Test/Case/Network/CakeSocketTest.php | 4 ++ .../Test/Case/Network/Email/CakeEmailTest.php | 9 +++ .../Case/Network/Email/DebugTransportTest.php | 4 ++ .../Case/Network/Email/MailTransportTest.php | 4 ++ .../Case/Network/Email/SmtpTransportTest.php | 9 +++ .../Case/Network/Http/HttpResponseTest.php | 4 ++ lib/Cake/Test/Case/Routing/DispatcherTest.php | 39 ++++++++++++ .../Case/TestSuite/CakeTestFixtureTest.php | 14 +++++ .../Case/TestSuite/ControllerTestCaseTest.php | 4 ++ .../Fixture/CakeFixtureManagerTest.php | 8 ++- .../Case/TestSuite/HtmlCoverageReportTest.php | 4 ++ .../TestSuite/Stub/ConsoleOutputStubTest.php | 4 ++ lib/Cake/Test/Case/Utility/CakeNumberTest.php | 4 ++ lib/Cake/Test/Case/Utility/CakeTextTest.php | 4 ++ lib/Cake/Test/Case/Utility/CakeTimeTest.php | 6 +- lib/Cake/Test/Case/Utility/DebuggerTest.php | 3 +- .../Case/Utility/ObjectCollectionTest.php | 28 +++++++++ lib/Cake/Test/Case/Utility/ValidationTest.php | 17 +++-- lib/Cake/Test/Case/Utility/XmlTest.php | 4 ++ .../Test/Case/View/Helper/CacheHelperTest.php | 9 +++ .../Test/Case/View/Helper/FlashHelperTest.php | 9 +++ .../Test/Case/View/Helper/FormHelperTest.php | 46 +++++++++++++- .../Test/Case/View/Helper/HtmlHelperTest.php | 4 ++ .../View/Helper/JqueryEngineHelperTest.php | 9 +++ .../Test/Case/View/Helper/JsHelperTest.php | 18 ++++++ .../View/Helper/MootoolsEngineHelperTest.php | 9 +++ .../Case/View/Helper/NumberHelperTest.php | 4 ++ .../Case/View/Helper/PaginatorHelperTest.php | 9 +++ .../View/Helper/PrototypeEngineHelperTest.php | 9 +++ .../Test/Case/View/Helper/RssHelperTest.php | 9 +++ .../Case/View/Helper/SessionHelperTest.php | 9 +++ .../Test/Case/View/Helper/TextHelperTest.php | 9 +++ .../Test/Case/View/Helper/TimeHelperTest.php | 4 ++ .../Test/Case/View/HelperCollectionTest.php | 9 +++ lib/Cake/Test/Case/View/HelperTest.php | 9 +++ lib/Cake/Test/Case/View/MediaViewTest.php | 4 ++ lib/Cake/Test/Case/View/ScaffoldViewTest.php | 9 +++ lib/Cake/Test/Case/View/ThemeViewTest.php | 14 +++++ lib/Cake/Test/Case/View/ViewTest.php | 29 +++++++++ lib/Cake/TestSuite/CakeTestCase.php | 7 +++ lib/Cake/TestSuite/ControllerTestCase.php | 7 +++ .../TestSuite/Fixture/CakeFixtureManager.php | 7 +++ .../TestSuite/Fixture/CakeTestFixture.php | 15 +++++ lib/Cake/Utility/CakeNumber.php | 2 +- lib/Cake/Utility/CakeText.php | 14 ++--- lib/Cake/Utility/Debugger.php | 4 +- lib/Cake/Utility/File.php | 6 +- lib/Cake/Utility/Hash.php | 2 +- lib/Cake/Utility/Inflector.php | 2 +- lib/Cake/Utility/Security.php | 48 ++++++++++---- lib/Cake/Utility/Set.php | 4 +- lib/Cake/Utility/Validation.php | 2 +- lib/Cake/Utility/Xml.php | 18 ++---- lib/Cake/View/Helper.php | 1 + lib/Cake/View/Helper/FormHelper.php | 15 ++--- lib/Cake/View/Helper/HtmlHelper.php | 2 +- .../View/Helper/PrototypeEngineHelper.php | 2 +- lib/Cake/View/HelperCollection.php | 2 +- lib/Cake/basics.php | 10 +-- 181 files changed, 1390 insertions(+), 166 deletions(-) create mode 100644 lib/Cake/Model/CakeDatabaseConfig.php diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 853c59f66b..9e492210e9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,6 +29,14 @@ jobs: db-type: sqlite - php-version: '8.1' db-type: mysql + - php-version: '8.4' + db-type: mysql + - php-version: '8.5' + db-type: mysql + - php-version: '8.5' + db-type: pgsql + - php-version: '8.5' + db-type: sqlite services: mysql: @@ -61,13 +69,13 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install PHP with extensions uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-version }} - extensions: apcu, memcache, memcached, redis, mcrypt, pdo_mysql, pdo_pgsql, pdo_sqlite + extensions: apcu, memcache, memcached, redis, intl, pdo_mysql, pdo_pgsql, pdo_sqlite ini-values: | assert.exception=1, zend.assertions=1, @@ -105,7 +113,7 @@ jobs: run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache Composer - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-php${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }} diff --git a/README.md b/README.md index de79003fbd..698138b171 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,57 @@ Unfortunately branch 2.x in original repository was taken down. +## Zumba fork: PHP 8.5 branch (`cakephp2-php85`) + +This repository (`zumba/cakephp2-php8`) is Zumba's fork of [kamilwylegala/cakephp2-php8](https://github.com/kamilwylegala/cakephp2-php8). +The `cakephp2-php85` branch is the version our applications use on PHP 8.5 containers. It contains: + +- Every upstream fix up to and including the CSRF hardening (upstream #76), the PHP 8.4 fixes (#75, #83) and the PHP 8.5 fixes (#86). +- A CI matrix that runs the framework test suite on PHP 8.0, 8.1, 8.4 and 8.5. The test bootstrap converts every deprecation into a failure, so a green build means no deprecation notices on those versions. +- A local `docker/web` image based on `php:8.5-apache`. + +The code stays compatible with PHP 8.0+; no PHP 8.5-only syntax is used. + +### Requirements on PHP 8.5 + +- `ext-intl` must be installed in the container. `strftime()` was replaced by `IntlDateFormatter` (via `php81_bc/strftime`), which requires it. +- `ext-mcrypt` is no longer needed; `Security::encrypt()`/`decrypt()` use OpenSSL. + +### Using the branch in an application + +Point `composer.json` at this repository and at the branch: + +``` +{ + "require": { + "cakephp/cakephp": "dev-cakephp2-php85 as 2.10.24" + }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/zumba/cakephp2-php8" + } + ] +} +``` + +### Update procedure + +Updating an application to the latest state of the branch is a standard composer update: + +``` +composer update cakephp/cakephp +``` + +Then run the application's own test suite under PHP 8.5. Commit the resulting `composer.lock`. + +### Syncing new upstream fixes into the branch + +1. `git fetch upstream` (where `upstream` is `https://github.com/kamilwylegala/cakephp2-php8.git`). +2. Bring the new upstream commits onto a feature branch cut from `cakephp2-php85` (merge or cherry-pick). +3. Run the test suite on PHP 8.5 (locally via `docker compose up` or by pushing and letting CI run the matrix). +4. Open a PR into `cakephp2-php85`. + ## Why I created this fork? 🤔 CakePHP 2 stopped getting updates in the end of 2019 (AFAIR). Unfortunately in my case it's too expensive to migrate to newer versions of CakePHP. I started migrating to Symfony framework, but I still use ORM from CakePHP (and actually I like it). So in order to keep up with the newest PHP versions I decided to create fork of the framework. @@ -59,6 +110,18 @@ It means that composer will look at `master` branch of repository configured und ## Changelog +### 2026-10-16 (Zumba, branch `cakephp2-php85`) + +- Synced all upstream fixes up to `kamilwylegala/cakephp2-php8` #87 (includes the CSRF fix #76 and the PHP 8.4/8.5 fixes #75, #83, #86). +- CI matrix now runs PHP 8.0, 8.1, 8.4 and 8.5; the test `app/Config/core.php` reports `E_ALL` so any deprecation fails the suite. +- PHP 8.5: `Security::cipher()` no longer casts the out-of-range `Security.cipherSeed` float to int (same seed value, no deprecation); `limit()`/`offset()` clamp out-of-range values instead of casting; `ConsoleLog` no longer calls `posix_isatty(null)`; `ReflectionProperty::setAccessible()` only on PHP < 8.1; null array offsets in `I18n`, `Helper`, `CakeResponse`, `ConsoleOptionParser`, `RequestHandlerComponent`, `Set`, `Configure`, `CakeSession` replaced by explicit checks; `libxml_disable_entity_loader()` calls removed. +- PHP 8.4: `trigger_error(..., E_USER_ERROR)` replaced by exceptions (`ConfigureException`, `CakeException`, `RuntimeException` in the webroot/console bootstrap scripts) or `ErrorHandler::handleFatalError()`. +- PHP 8.2: undeclared (dynamic) properties declared on `Scaffold`, `ConsoleOptionParser`, `HelpFormatter`, `MemcachedEngine`, `CakeTestCase`, `CakeTestFixture`, `CakeFixtureManager`, `ControllerTestDispatcher` and ~100 test classes; `ConnectionManager::create()` stores runtime connections on a `#[\AllowDynamicProperties]` subclass of `DATABASE_CONFIG`; `CakeRequest` allows dynamic properties. +- PHP 8.1: remaining "passing null to non-nullable internal parameter" cases fixed across the framework (strlen/strpos/strtolower/preg_*/trim/substr/… with `(string)` casts or null checks). +- `Security::encrypt()`/`decrypt()` fall back to OpenSSL when ext-mcrypt is missing. +- `SessionHandlerAdapter` methods marked `#[\ReturnTypeWillChange]`. +- Local `docker/web` image moved to `php:8.5-apache` (with `intl`, without `mcrypt`). + ### 2025-02-04 - Fixes for PHP 8.4: `session_set_save_handler` accepts object, removed `E_STRICT` reference. diff --git a/app/Config/core.php b/app/Config/core.php index da5555a340..adc85d98cc 100644 --- a/app/Config/core.php +++ b/app/Config/core.php @@ -53,7 +53,7 @@ */ Configure::write('Error', array( 'handler' => 'ErrorHandler::handleError', - 'level' => E_ALL & ~E_DEPRECATED, + 'level' => E_ALL, 'trace' => true )); diff --git a/app/Console/cake.php b/app/Console/cake.php index d4a6aeaf01..4eed898d09 100644 --- a/app/Console/cake.php +++ b/app/Console/cake.php @@ -40,7 +40,7 @@ } if (!include $dispatcher) { - trigger_error('Could not locate CakePHP core files.', E_USER_ERROR); + throw new RuntimeException('Could not locate CakePHP core files.'); } unset($dispatcher); diff --git a/app/webroot/index.php b/app/webroot/index.php index 18c098d8e0..7a9679b60c 100644 --- a/app/webroot/index.php +++ b/app/webroot/index.php @@ -106,7 +106,7 @@ $failed = true; } if (!empty($failed)) { - trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); + throw new RuntimeException("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory."); } App::uses('Dispatcher', 'Routing'); diff --git a/app/webroot/test.php b/app/webroot/test.php index 2f8f8d3c0c..a3274584c6 100644 --- a/app/webroot/test.php +++ b/app/webroot/test.php @@ -100,7 +100,7 @@ } } if (!empty($failed)) { - trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/test.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); + throw new RuntimeException("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/test.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory."); } if (Configure::read('debug') < 1) { diff --git a/docker/web/Dockerfile b/docker/web/Dockerfile index d9c90b56e1..7fc1c4785e 100644 --- a/docker/web/Dockerfile +++ b/docker/web/Dockerfile @@ -1,12 +1,12 @@ -FROM php:8.2-apache +FROM php:8.5-apache RUN apt-get update && apt-get install -y --no-install-recommends \ - && apt-get install -y libpq-dev libzip-dev unzip openssl libmcrypt-dev libmemcached-dev locales \ + && apt-get install -y libpq-dev libzip-dev unzip openssl libmcrypt-dev libmemcached-dev libicu-dev locales \ && rm -rf /var/lib/apt/lists/* -RUN docker-php-ext-install pdo_mysql pdo_pgsql zip \ - && pecl install apcu redis memcached mcrypt xdebug \ - && docker-php-ext-enable redis memcached mcrypt \ +RUN docker-php-ext-install pdo_mysql pdo_pgsql zip intl \ + && pecl install apcu redis memcached xdebug \ + && docker-php-ext-enable redis memcached \ && docker-php-ext-enable xdebug \ && echo "extension=apcu.so" >> /usr/local/etc/php/php.ini \ && echo "apc.enable_cli = 1" >> /usr/local/etc/php/php.ini diff --git a/lib/Cake/Cache/Engine/MemcachedEngine.php b/lib/Cake/Cache/Engine/MemcachedEngine.php index 655eb5d1cf..691d658e70 100644 --- a/lib/Cake/Cache/Engine/MemcachedEngine.php +++ b/lib/Cake/Cache/Engine/MemcachedEngine.php @@ -54,6 +54,14 @@ class MemcachedEngine extends CacheEngine { */ public $settings = array(); +/** + * Contains the compiled group names + * (prefixed with the global configuration prefix) + * + * @var array + */ + protected $_compiledGroupNames = array(); + /** * List of available serializer engines * diff --git a/lib/Cake/Console/Command/SchemaShell.php b/lib/Cake/Console/Command/SchemaShell.php index 6a704bda81..7ebfaef96c 100644 --- a/lib/Cake/Console/Command/SchemaShell.php +++ b/lib/Cake/Console/Command/SchemaShell.php @@ -62,7 +62,7 @@ public function startup() { $name = $this->params['name'] = $this->args[0]; } - if (strpos($name, '.')) { + if ($name !== null && strpos($name, '.')) { list($this->params['plugin'], $splitName) = pluginSplit($name); $name = $this->params['name'] = $splitName; } diff --git a/lib/Cake/Console/Command/Task/CommandTask.php b/lib/Cake/Console/Command/Task/CommandTask.php index 99c888f8e8..f4bd84774e 100644 --- a/lib/Cake/Console/Command/Task/CommandTask.php +++ b/lib/Cake/Console/Command/Task/CommandTask.php @@ -133,7 +133,7 @@ public function subCommands($commandName) { public function getShell($commandName) { list($pluginDot, $name) = pluginSplit($commandName, true); - if (in_array(strtolower($pluginDot), array('app.', 'core.'))) { + if (in_array(strtolower((string)$pluginDot), array('app.', 'core.'))) { $commandName = $name; $pluginDot = ''; } diff --git a/lib/Cake/Console/Command/Task/ExtractTask.php b/lib/Cake/Console/Command/Task/ExtractTask.php index eaa691e318..fd94873f38 100644 --- a/lib/Cake/Console/Command/Task/ExtractTask.php +++ b/lib/Cake/Console/Command/Task/ExtractTask.php @@ -173,7 +173,7 @@ public function execute() { $this->_extractCore = !(strtolower($this->params['extract-core']) === 'no'); } else { $response = $this->in(__d('cake_console', 'Would you like to extract the messages from the CakePHP core?'), array('y', 'n'), 'n'); - $this->_extractCore = strtolower($response) === 'y'; + $this->_extractCore = strtolower((string)$response) === 'y'; } if (!empty($this->params['exclude-plugins']) && $this->_isExtractingApp()) { @@ -221,7 +221,7 @@ public function execute() { } else { $this->out(); $response = $this->in(__d('cake_console', 'Would you like to merge all domain and category strings into the default.pot file?'), array('y', 'n'), 'n'); - $this->_merge = strtolower($response) === 'y'; + $this->_merge = strtolower((string)$response) === 'y'; } if (empty($this->_files)) { diff --git a/lib/Cake/Console/Command/Task/FixtureTask.php b/lib/Cake/Console/Command/Task/FixtureTask.php index 70a55ed6ec..23b021042f 100644 --- a/lib/Cake/Console/Command/Task/FixtureTask.php +++ b/lib/Cake/Console/Command/Task/FixtureTask.php @@ -203,7 +203,7 @@ public function importOptions($modelName) { if (!isset($options['records']) && $this->interactive) { $prompt = __d('cake_console', "Would you like to build this fixture with data from %s's table?", $modelName); $fromTable = $this->in($prompt, array('y', 'n'), 'n'); - if (strtolower($fromTable) === 'y') { + if (strtolower((string)$fromTable) === 'y') { $options['fromTable'] = true; } } diff --git a/lib/Cake/Console/Command/Task/ModelTask.php b/lib/Cake/Console/Command/Task/ModelTask.php index 3473e3f410..f26912ec85 100644 --- a/lib/Cake/Console/Command/Task/ModelTask.php +++ b/lib/Cake/Console/Command/Task/ModelTask.php @@ -426,7 +426,7 @@ public function fieldValidation($fieldName, $metaData, $primaryKey = 'id') { $this->hr(); $optionText = ''; - for ($i = 1, $m = $defaultChoice / 2; $i <= $m; $i++) { + for ($i = 1, $m = (int)($defaultChoice / 2); $i <= $m; $i++) { $line = sprintf("%2d. %s", $i, $this->_validations[$i]); $optionText .= $line . str_repeat(" ", 31 - strlen($line)); if ($m + $i !== $defaultChoice) { @@ -710,7 +710,7 @@ public function confirmAssociations(Model $model, $associations) { $prompt = "{$model->name} {$type} {$assoc['alias']}?"; $response = $this->in($prompt, array('y', 'n'), 'y'); - if (strtolower($response) === 'n') { + if (strtolower((string)$response) === 'n') { unset($associations[$type][$i]); } elseif ($type === 'hasMany') { unset($associations['hasOne'][$i]); diff --git a/lib/Cake/Console/Command/Task/TestTask.php b/lib/Cake/Console/Command/Task/TestTask.php index a4c4e936b0..dd410c977d 100644 --- a/lib/Cake/Console/Command/Task/TestTask.php +++ b/lib/Cake/Console/Command/Task/TestTask.php @@ -456,7 +456,7 @@ protected function _addFixture($name) { public function getUserFixtures() { $proceed = $this->in(__d('cake_console', 'Bake could not detect fixtures, would you like to add some?'), array('y', 'n'), 'n'); $fixtures = array(); - if (strtolower($proceed) === 'y') { + if (strtolower((string)$proceed) === 'y') { $fixtureList = $this->in(__d('cake_console', "Please provide a comma separated list of the fixtures names you'd like to use.\nExample: 'app.comment, app.post, plugin.forums.post'")); $fixtureListTrimmed = str_replace(' ', '', $fixtureList); $fixtures = explode(',', $fixtureListTrimmed); diff --git a/lib/Cake/Console/ConsoleInputOption.php b/lib/Cake/Console/ConsoleInputOption.php index 139c983539..2a7096e5a9 100644 --- a/lib/Cake/Console/ConsoleInputOption.php +++ b/lib/Cake/Console/ConsoleInputOption.php @@ -90,7 +90,7 @@ public function __construct($name, $short = null, $help = '', $boolean = false, $this->_default = $default; $this->_choices = $choices; } - if (strlen($this->_short) > 1) { + if (strlen((string)$this->_short) > 1) { throw new ConsoleException( __d('cake_console', 'Short option "%s" is invalid, short options must be one letter.', $this->_short) ); @@ -204,7 +204,7 @@ public function xml(SimpleXmlElement $parent) { $option = $parent->addChild('option'); $option->addAttribute('name', '--' . $this->_name); $short = ''; - if (strlen($this->_short)) { + if (strlen((string)$this->_short)) { $short = '-' . $this->_short; } $option->addAttribute('short', $short); diff --git a/lib/Cake/Console/ConsoleOptionParser.php b/lib/Cake/Console/ConsoleOptionParser.php index f313c41c96..f80e8e85c8 100644 --- a/lib/Cake/Console/ConsoleOptionParser.php +++ b/lib/Cake/Console/ConsoleOptionParser.php @@ -86,6 +86,13 @@ class ConsoleOptionParser { */ protected $_description = null; +/** + * Tokens left to parse while parse() is running. + * + * @var array + */ + protected $_tokens = array(); + /** * Epilog text - displays after options when help is generated * @@ -473,7 +480,7 @@ public function subcommands() { * @throws ConsoleException When an invalid parameter is encountered. */ public function parse($argv, $command = null) { - if (isset($this->_subcommands[$command]) && $this->_subcommands[$command]->parser()) { + if ($command !== null && isset($this->_subcommands[$command]) && $this->_subcommands[$command]->parser()) { return $this->_subcommands[$command]->parser()->parse($argv); } $params = $args = array(); @@ -521,7 +528,7 @@ public function parse($argv, $command = null) { * @return string Generated help. */ public function help($subcommand = null, $format = 'text', $width = 72) { - if (isset($this->_subcommands[$subcommand]) && + if ($subcommand !== null && isset($this->_subcommands[$subcommand]) && $this->_subcommands[$subcommand]->parser() instanceof self ) { $subparser = $this->_subcommands[$subcommand]->parser(); diff --git a/lib/Cake/Console/HelpFormatter.php b/lib/Cake/Console/HelpFormatter.php index 0c4259aae3..6cf2f1d081 100644 --- a/lib/Cake/Console/HelpFormatter.php +++ b/lib/Cake/Console/HelpFormatter.php @@ -37,6 +37,13 @@ class HelpFormatter { */ protected $_maxArgs = 6; +/** + * Option parser being formatted. + * + * @var ConsoleOptionParser + */ + protected $_parser = null; + /** * The maximum number of options shown when generating usage. * diff --git a/lib/Cake/Console/Shell.php b/lib/Cake/Console/Shell.php index 2e9cebf257..dd0663f2f2 100644 --- a/lib/Cake/Console/Shell.php +++ b/lib/Cake/Console/Shell.php @@ -851,7 +851,7 @@ protected function _checkUnitTest() { * @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::shortPath */ public function shortPath($file) { - $shortPath = str_replace(ROOT, null, $file); + $shortPath = str_replace(ROOT, '', $file); $shortPath = str_replace('..' . DS, '', $shortPath); return str_replace(DS . DS, DS, $shortPath); } diff --git a/lib/Cake/Console/TaskCollection.php b/lib/Cake/Console/TaskCollection.php index 5d2e7a7a9a..50fd73610b 100644 --- a/lib/Cake/Console/TaskCollection.php +++ b/lib/Cake/Console/TaskCollection.php @@ -87,7 +87,7 @@ public function load($task, $settings = array()) { if (!$exists) { throw new MissingTaskException(array( 'class' => $taskClass, - 'plugin' => substr($plugin, 0, -1) + 'plugin' => substr((string)$plugin, 0, -1) )); } diff --git a/lib/Cake/Console/Templates/skel/Console/cake.php b/lib/Cake/Console/Templates/skel/Console/cake.php index 280613a271..34d90895ff 100644 --- a/lib/Cake/Console/Templates/skel/Console/cake.php +++ b/lib/Cake/Console/Templates/skel/Console/cake.php @@ -41,7 +41,7 @@ } if (!include $dispatcher) { - trigger_error('Could not locate CakePHP core files.', E_USER_ERROR); + throw new RuntimeException('Could not locate CakePHP core files.'); } unset($dispatcher); diff --git a/lib/Cake/Console/Templates/skel/webroot/index.php b/lib/Cake/Console/Templates/skel/webroot/index.php index 4f9a70955d..fd0172a528 100644 --- a/lib/Cake/Console/Templates/skel/webroot/index.php +++ b/lib/Cake/Console/Templates/skel/webroot/index.php @@ -99,7 +99,7 @@ $failed = true; } if (!empty($failed)) { - trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); + throw new RuntimeException("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory."); } App::uses('Dispatcher', 'Routing'); diff --git a/lib/Cake/Console/Templates/skel/webroot/test.php b/lib/Cake/Console/Templates/skel/webroot/test.php index caa2933165..166f9c3453 100644 --- a/lib/Cake/Console/Templates/skel/webroot/test.php +++ b/lib/Cake/Console/Templates/skel/webroot/test.php @@ -84,7 +84,7 @@ } } if (!empty($failed)) { - trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); + throw new RuntimeException("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory."); } if (Configure::read('debug') < 1) { diff --git a/lib/Cake/Console/cake.php b/lib/Cake/Console/cake.php index b825e02364..52aa35a3ef 100644 --- a/lib/Cake/Console/cake.php +++ b/lib/Cake/Console/cake.php @@ -41,7 +41,7 @@ } elseif (file_exists($rootInstall)) { include $rootInstall; } else { - trigger_error('Could not locate CakePHP core files.', E_USER_ERROR); + throw new RuntimeException('Could not locate CakePHP core files.'); } unset($rootInstall, $composerInstall); diff --git a/lib/Cake/Controller/Component/Auth/BasicAuthenticate.php b/lib/Cake/Controller/Component/Auth/BasicAuthenticate.php index b19f23c934..aa350f6720 100644 --- a/lib/Cake/Controller/Component/Auth/BasicAuthenticate.php +++ b/lib/Cake/Controller/Component/Auth/BasicAuthenticate.php @@ -84,9 +84,9 @@ public function authenticate(CakeRequest $request, CakeResponse $response) { public function getUser(CakeRequest $request) { $username = env('PHP_AUTH_USER'); $pass = env('PHP_AUTH_PW'); - if (!strlen($username)) { + if (!strlen((string)$username)) { $httpAuthorization = $request->header('Authorization'); - if (strlen($httpAuthorization) > 0 && strpos($httpAuthorization, 'Basic') !== false) { + if (strlen((string)$httpAuthorization) > 0 && strpos($httpAuthorization, 'Basic') !== false) { list($username, $pass) = explode(':', base64_decode(substr($httpAuthorization, 6))); } } diff --git a/lib/Cake/Controller/Component/Auth/DigestAuthenticate.php b/lib/Cake/Controller/Component/Auth/DigestAuthenticate.php index 04637994fd..175f94d07c 100644 --- a/lib/Cake/Controller/Component/Auth/DigestAuthenticate.php +++ b/lib/Cake/Controller/Component/Auth/DigestAuthenticate.php @@ -102,7 +102,7 @@ public function __construct(ComponentCollection $collection, $settings) { $this->settings['nonce'] = uniqid(''); } if (empty($this->settings['opaque'])) { - $this->settings['opaque'] = md5($this->settings['realm']); + $this->settings['opaque'] = md5((string)$this->settings['realm']); } } diff --git a/lib/Cake/Controller/Component/RequestHandlerComponent.php b/lib/Cake/Controller/Component/RequestHandlerComponent.php index 513af4029e..72ffebd644 100644 --- a/lib/Cake/Controller/Component/RequestHandlerComponent.php +++ b/lib/Cake/Controller/Component/RequestHandlerComponent.php @@ -686,7 +686,7 @@ public function respondAs($type, $options = array()) { $cType = $this->response->getMimeType($type); } if (is_array($cType)) { - if (isset($cType[$options['index']])) { + if ($options['index'] !== null && isset($cType[$options['index']])) { $cType = $cType[$options['index']]; } diff --git a/lib/Cake/Controller/ComponentCollection.php b/lib/Cake/Controller/ComponentCollection.php index a9500dab2d..911f953778 100644 --- a/lib/Cake/Controller/ComponentCollection.php +++ b/lib/Cake/Controller/ComponentCollection.php @@ -110,7 +110,7 @@ public function load($component, $settings = array()) { if (!class_exists($componentClass)) { throw new MissingComponentException(array( 'class' => $componentClass, - 'plugin' => substr($plugin, 0, -1) + 'plugin' => substr((string)$plugin, 0, -1) )); } $this->_loaded[$alias] = new $componentClass($this, $settings); diff --git a/lib/Cake/Controller/Scaffold.php b/lib/Cake/Controller/Scaffold.php index 33999ecb7d..2c7692d785 100644 --- a/lib/Cake/Controller/Scaffold.php +++ b/lib/Cake/Controller/Scaffold.php @@ -96,6 +96,41 @@ class Scaffold { */ public $scaffoldTitle = null; +/** + * Model instance being scaffolded. + * + * @var Model + */ + public $ScaffoldModel = null; + +/** + * Name of the model class being scaffolded. + * + * @var string + */ + public $modelClass = null; + +/** + * Key of the model being scaffolded. + * + * @var string + */ + public $modelKey = null; + +/** + * URL to redirect to after a scaffold action completes. + * + * @var array|string + */ + public $redirect = null; + +/** + * Actions that are scaffolded. + * + * @var array|bool|string + */ + public $scaffoldActions = null; + /** * Construct and set up given controller with given parameters. * diff --git a/lib/Cake/Core/Configure.php b/lib/Cake/Core/Configure.php index 901606cd23..7c7c26448c 100644 --- a/lib/Cake/Core/Configure.php +++ b/lib/Cake/Core/Configure.php @@ -70,11 +70,10 @@ public static function bootstrap($boot = true) { static::_appDefaults(); if (!include CONFIG . 'core.php') { - trigger_error(__d('cake_dev', - "Can't find application core file. Please create %s, and make sure it is readable by PHP.", - CONFIG . 'core.php'), - E_USER_ERROR - ); + throw new ConfigureException(__d('cake_dev', + "Can't find application core file. Please create %s, and make sure it is readable by PHP.", + CONFIG . 'core.php' + )); } App::init(); App::$bootstrapping = false; @@ -96,11 +95,10 @@ public static function bootstrap($boot = true) { static::_setErrorHandlers($error, $exception); if (!include CONFIG . 'bootstrap.php') { - trigger_error(__d('cake_dev', - "Can't find application bootstrap file. Please create %s, and make sure it is readable by PHP.", - CONFIG . 'bootstrap.php'), - E_USER_ERROR - ); + throw new ConfigureException(__d('cake_dev', + "Can't find application bootstrap file. Please create %s, and make sure it is readable by PHP.", + CONFIG . 'bootstrap.php' + )); } restore_error_handler(); @@ -209,6 +207,9 @@ public static function read($var = null) { * @return array|null */ public static function consume($var) { + if ($var === null) { + return null; + } $simple = strpos($var, '.') === false; if ($simple && !isset(static::$_values[$var])) { return null; diff --git a/lib/Cake/Error/ErrorHandler.php b/lib/Cake/Error/ErrorHandler.php index 30873d7268..c260578deb 100644 --- a/lib/Cake/Error/ErrorHandler.php +++ b/lib/Cake/Error/ErrorHandler.php @@ -135,7 +135,7 @@ public static function handleException($exception) { ); static::$_bailExceptionRendering = true; - trigger_error($message, E_USER_ERROR); + static::handleFatalError(E_USER_ERROR, $message, $e->getFile(), $e->getLine()); } } diff --git a/lib/Cake/I18n/I18n.php b/lib/Cake/I18n/I18n.php index 56a92f52bd..dfa828bde4 100644 --- a/lib/Cake/I18n/I18n.php +++ b/lib/Cake/I18n/I18n.php @@ -196,6 +196,9 @@ public static function translate($singular, $plural = null, $domain = null, $cat $count = null, $language = null, $context = null ) { $_this = I18n::getInstance(); + if ($context === null) { + $context = ''; + } if (strpos($singular, "\r\n") !== false) { $singular = str_replace("\r\n", "\n", $singular); @@ -401,6 +404,9 @@ protected function _bindTextDomain($domain) { if (is_file($localeDef)) { $definitions = static::loadLocaleDefinition($localeDef); if ($definitions !== false) { + if (!isset($this->_domains[$domain][$this->_lang]) || !is_array($this->_domains[$domain][$this->_lang])) { + $this->_domains[$domain][$this->_lang] = array(); + } $this->_domains[$domain][$this->_lang][$this->category] = $definitions; $this->_noLocale = false; return $domain; @@ -419,6 +425,9 @@ protected function _bindTextDomain($domain) { } if ($translations !== false) { + if (!isset($this->_domains[$domain][$this->_lang]) || !is_array($this->_domains[$domain][$this->_lang])) { + $this->_domains[$domain][$this->_lang] = array(); + } $this->_domains[$domain][$this->_lang][$this->category] = $translations; $merge[$domain][$this->_lang][$this->category] = $this->_domains[$domain][$this->_lang][$this->category]; $this->_noLocale = false; @@ -477,8 +486,8 @@ protected function _bindTextDomain($domain) { } $this->_domains = Hash::mergeDiff($this->_domains, $merge); - if (isset($this->_domains[$domain][$this->_lang][$this->category][null])) { - unset($this->_domains[$domain][$this->_lang][$this->category][null]); + if (isset($this->_domains[$domain][$this->_lang][$this->category][''])) { + unset($this->_domains[$domain][$this->_lang][$this->category]['']); } } @@ -508,7 +517,7 @@ public static function loadMo($filename) { $r = unpack("L1len/L1offs", substr($data, $o_msg + $n * 8, 8)); $msgid = substr($data, $r["offs"], $r["len"]); unset($msgid_plural); - $context = null; + $context = ''; if (strpos($msgid, "\x04") !== false) { list($context, $msgid) = explode("\x04", $msgid); @@ -554,14 +563,14 @@ public static function loadPo($filename) { $type = 0; $translations = array(); $translationKey = ''; - $translationContext = null; + $translationContext = ''; $plural = 0; $header = ''; do { $line = trim(fgets($file)); if ($line === '' || $line[0] === '#') { - $translationContext = null; + $translationContext = ''; continue; } @@ -610,7 +619,7 @@ public static function loadPo($filename) { unset($translations[$translationKey][$translationContext]); $type = 0; $translationKey = ''; - $translationContext = null; + $translationContext = ''; $plural = 0; } } while (!feof($file)); diff --git a/lib/Cake/Log/Engine/ConsoleLog.php b/lib/Cake/Log/Engine/ConsoleLog.php index ced97e6398..6ba752c063 100644 --- a/lib/Cake/Log/Engine/ConsoleLog.php +++ b/lib/Cake/Log/Engine/ConsoleLog.php @@ -48,8 +48,10 @@ class ConsoleLog extends BaseLog { */ public function __construct($config = array()) { parent::__construct($config); + // The ConsoleOutput stream does not exist yet, so there is no handle to inspect with + // posix_isatty(). Keep the historical default: plain output wherever posix is available. if ((DS === '\\' && !(bool)env('ANSICON') && env('ConEmuANSI') !== 'ON') || - (function_exists('posix_isatty') && !posix_isatty($this->_output)) + function_exists('posix_isatty') ) { $outputAs = ConsoleOutput::PLAIN; } else { diff --git a/lib/Cake/Model/Behavior/TranslateBehavior.php b/lib/Cake/Model/Behavior/TranslateBehavior.php index e4bb53734b..b2fc10ba1c 100644 --- a/lib/Cake/Model/Behavior/TranslateBehavior.php +++ b/lib/Cake/Model/Behavior/TranslateBehavior.php @@ -86,11 +86,9 @@ class TranslateBehavior extends ModelBehavior { public function setup(Model $Model, $config = array()) { $db = ConnectionManager::getDataSource($Model->useDbConfig); if (!$db->connected) { - trigger_error( - __d('cake_dev', 'Datasource %s for TranslateBehavior of model %s is not connected', $Model->useDbConfig, $Model->alias), - E_USER_ERROR + throw new CakeException( + __d('cake_dev', 'Datasource %s for TranslateBehavior of model %s is not connected', $Model->useDbConfig, $Model->alias) ); - return false; } $this->settings[$Model->alias] = array(); @@ -689,11 +687,9 @@ public function bindTranslation(Model $Model, $fields, $reset = true) { foreach (array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany') as $type) { if (isset($Model->{$type}[$association]) || isset($Model->__backAssociation[$type][$association])) { - trigger_error( - __d('cake_dev', 'Association %s is already bound to model %s', $association, $Model->alias), - E_USER_ERROR + throw new CakeException( + __d('cake_dev', 'Association %s is already bound to model %s', $association, $Model->alias) ); - return false; } } $associations[$association] = array_merge($default, array('conditions' => array( diff --git a/lib/Cake/Model/BehaviorCollection.php b/lib/Cake/Model/BehaviorCollection.php index faae57064f..1320658950 100644 --- a/lib/Cake/Model/BehaviorCollection.php +++ b/lib/Cake/Model/BehaviorCollection.php @@ -120,7 +120,7 @@ public function load($behavior, $config = array()) { if (!class_exists($class)) { throw new MissingBehaviorException(array( 'class' => $class, - 'plugin' => substr($plugin, 0, -1) + 'plugin' => substr((string)$plugin, 0, -1) )); } diff --git a/lib/Cake/Model/CakeDatabaseConfig.php b/lib/Cake/Model/CakeDatabaseConfig.php new file mode 100644 index 0000000000..f4c12af192 --- /dev/null +++ b/lib/Cake/Model/CakeDatabaseConfig.php @@ -0,0 +1,27 @@ +plugin = $options['plugin']; } - if (strtolower($this->name) === 'cake') { + if (strtolower((string)$this->name) === 'cake') { $this->name = 'App'; } diff --git a/lib/Cake/Model/ConnectionManager.php b/lib/Cake/Model/ConnectionManager.php index 2a93624cbb..78422988fc 100644 --- a/lib/Cake/Model/ConnectionManager.php +++ b/lib/Cake/Model/ConnectionManager.php @@ -66,11 +66,32 @@ class ConnectionManager { protected static function _init() { include_once CONFIG . 'database.php'; if (class_exists('DATABASE_CONFIG')) { - static::$config = new DATABASE_CONFIG(); + static::$config = static::_createConfig(); } static::$_init = true; } +/** + * Instantiates the application's DATABASE_CONFIG class. + * + * ConnectionManager::create() stores runtime connections as properties of that object. + * Since PHP 8.2 creating such dynamic properties is deprecated unless the class allows them, + * so the object is created through CakeDatabaseConfig, a subclass that declares + * `#[\AllowDynamicProperties]`. The application class is used as-is when it cannot be extended. + * + * @return DATABASE_CONFIG + */ + protected static function _createConfig() { + $reflection = new ReflectionClass('DATABASE_CONFIG'); + if ($reflection->isFinal() || $reflection->getAttributes('AllowDynamicProperties')) { + return new DATABASE_CONFIG(); + } + if (!class_exists('CakeDatabaseConfig', false)) { + require CAKE . 'Model' . DS . 'CakeDatabaseConfig.php'; + } + return new CakeDatabaseConfig(); + } + /** * Gets a reference to a DataSource object * @@ -177,7 +198,7 @@ public static function loadDataSource($connName) { if (!class_exists($conn['classname'])) { throw new MissingDatasourceException(array( 'class' => $conn['classname'], - 'plugin' => substr($plugin, 0, -1) + 'plugin' => substr((string)$plugin, 0, -1) )); } return true; diff --git a/lib/Cake/Model/Datasource/CakeSession.php b/lib/Cake/Model/Datasource/CakeSession.php index 3cf7f24de5..7991c8ac04 100644 --- a/lib/Cake/Model/Datasource/CakeSession.php +++ b/lib/Cake/Model/Datasource/CakeSession.php @@ -202,8 +202,8 @@ protected static function _setPath($base = null) { */ protected static function _setHost($host) { static::$host = $host; - if (strpos(static::$host, ':') !== false) { - static::$host = substr(static::$host, 0, strpos(static::$host, ':')); + if (is_string($host) && strpos($host, ':') !== false) { + static::$host = substr($host, 0, strpos($host, ':')); } } @@ -250,6 +250,9 @@ public static function check($name) { if (!static::_hasSession() || !static::start()) { return false; } + if ($name === null) { + return false; + } if (isset($_SESSION[$name])) { return true; } diff --git a/lib/Cake/Model/Datasource/Database/Postgres.php b/lib/Cake/Model/Datasource/Database/Postgres.php index f81bee5b3b..e1ae7caeeb 100644 --- a/lib/Cake/Model/Datasource/Database/Postgres.php +++ b/lib/Cake/Model/Datasource/Database/Postgres.php @@ -681,9 +681,9 @@ protected function _alterIndexes($table, $indexes) { */ public function limit($limit, $offset = null) { if ($limit) { - $rt = sprintf(' LIMIT %u', $limit); + $rt = sprintf(' LIMIT %u', $this->_limitValue($limit)); if ($offset) { - $rt .= sprintf(' OFFSET %u', $offset); + $rt .= sprintf(' OFFSET %u', $this->_limitValue($offset)); } return $rt; } diff --git a/lib/Cake/Model/Datasource/Database/Sqlite.php b/lib/Cake/Model/Datasource/Database/Sqlite.php index 487868be4a..d53557bbef 100644 --- a/lib/Cake/Model/Datasource/Database/Sqlite.php +++ b/lib/Cake/Model/Datasource/Database/Sqlite.php @@ -179,7 +179,7 @@ public function describe($model) { ); foreach ($result as $column) { - $default = ($column['dflt_value'] === 'NULL') ? null : trim($column['dflt_value'], "'"); + $default = ($column['dflt_value'] === 'NULL') ? null : trim((string)$column['dflt_value'], "'"); $fields[$column['name']] = array( 'type' => $this->column($column['type']), @@ -187,7 +187,7 @@ public function describe($model) { 'default' => $default, 'length' => $this->length($column['type']) ); - if (in_array($fields[$column['name']]['type'], array('timestamp', 'datetime')) && strtoupper($fields[$column['name']]['default']) === 'CURRENT_TIMESTAMP') { + if (in_array($fields[$column['name']]['type'], array('timestamp', 'datetime')) && strtoupper((string)$fields[$column['name']]['default']) === 'CURRENT_TIMESTAMP') { $fields[$column['name']]['default'] = null; } if ($column['pk'] == 1) { @@ -393,9 +393,9 @@ public function fetchResult() { */ public function limit($limit, $offset = null) { if ($limit) { - $rt = sprintf(' LIMIT %u', $limit); + $rt = sprintf(' LIMIT %u', $this->_limitValue($limit)); if ($offset) { - $rt .= sprintf(' OFFSET %u', $offset); + $rt .= sprintf(' OFFSET %u', $this->_limitValue($offset)); } return $rt; } diff --git a/lib/Cake/Model/Datasource/Database/Sqlserver.php b/lib/Cake/Model/Datasource/Database/Sqlserver.php index c25934409b..0259da20de 100644 --- a/lib/Cake/Model/Datasource/Database/Sqlserver.php +++ b/lib/Cake/Model/Datasource/Database/Sqlserver.php @@ -414,9 +414,9 @@ public function limit($limit, $offset = null) { if (!strpos(strtolower($limit), 'top') || strpos(strtolower($limit), 'top') === 0) { $rt = ' TOP'; } - $rt .= sprintf(' %u', $limit); + $rt .= sprintf(' %u', $this->_limitValue($limit)); if ((is_int($offset) || ctype_digit($offset)) && $offset > 0) { - $rt = sprintf(' OFFSET %u ROWS FETCH FIRST %u ROWS ONLY', $offset, $limit); + $rt = sprintf(' OFFSET %u ROWS FETCH FIRST %u ROWS ONLY', $this->_limitValue($offset), $this->_limitValue($limit)); } return $rt; } diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index d356492df2..d320b61d04 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -3068,15 +3068,40 @@ public function limit($limit, $offset = null) { $rt = ' LIMIT'; if ($offset) { - $rt .= sprintf(' %u,', $offset); + $rt .= sprintf(' %u,', $this->_limitValue($offset)); } - $rt .= sprintf(' %u', $limit); + $rt .= sprintf(' %u', $this->_limitValue($limit)); return $rt; } return null; } +/** + * Normalizes a LIMIT/OFFSET value so that it can be formatted with sprintf('%u'). + * + * Floats that do not fit into an integer (for example a page number taken from the URL that is + * far too large) are clamped instead of being cast, which PHP 8.5 reports as a deprecation. + * + * @param mixed $value The limit or offset value. + * @return mixed Integer when the value was a float or a numeric string, the original value otherwise. + */ + protected function _limitValue($value) { + if (is_string($value) && is_numeric($value)) { + $value = $value + 0; + } + if (is_float($value)) { + if (!is_finite($value) || $value >= (float)PHP_INT_MAX) { + return PHP_INT_MAX; + } + if ($value <= (float)PHP_INT_MIN) { + return PHP_INT_MIN; + } + return (int)$value; + } + return $value; + } + /** * Returns an ORDER BY clause as a string. * diff --git a/lib/Cake/Model/Datasource/PDOExceptionWithQueryString.php b/lib/Cake/Model/Datasource/PDOExceptionWithQueryString.php index fdc2cf825d..7f0c55a252 100644 --- a/lib/Cake/Model/Datasource/PDOExceptionWithQueryString.php +++ b/lib/Cake/Model/Datasource/PDOExceptionWithQueryString.php @@ -4,6 +4,13 @@ class PDOExceptionWithQueryString extends PDOException { public string $queryString = ""; +/** + * Bound parameters of the failed query, displayed by the pdo_error view. + * + * @var array|null + */ + public $params = null; + /** * Wrapper for PDOException to avoid creating dynamic property. * diff --git a/lib/Cake/Model/Datasource/SessionHandlerAdapter.php b/lib/Cake/Model/Datasource/SessionHandlerAdapter.php index 4737011848..50b2517eab 100644 --- a/lib/Cake/Model/Datasource/SessionHandlerAdapter.php +++ b/lib/Cake/Model/Datasource/SessionHandlerAdapter.php @@ -10,32 +10,38 @@ public function __construct( ) { } + #[\ReturnTypeWillChange] public function close() { return $this->cakeSessionHandler->close(); } + #[\ReturnTypeWillChange] public function destroy(string $id) { return $this->cakeSessionHandler->destroy($id); } + #[\ReturnTypeWillChange] public function gc(int $max_lifetime) { return $this->cakeSessionHandler->gc($max_lifetime); } + #[\ReturnTypeWillChange] public function open(string $path, string $name) { //Cake interface ignores these parameters. return $this->cakeSessionHandler->open(); } + #[\ReturnTypeWillChange] public function read(string $id) { return $this->cakeSessionHandler->read($id); } + #[\ReturnTypeWillChange] public function write(string $id, string $data) { return $this->cakeSessionHandler->write($id, $data); diff --git a/lib/Cake/Network/CakeRequest.php b/lib/Cake/Network/CakeRequest.php index f8f5e176c8..c757173d9b 100644 --- a/lib/Cake/Network/CakeRequest.php +++ b/lib/Cake/Network/CakeRequest.php @@ -32,6 +32,7 @@ * @property array $pass Array of passed arguments parsed from the URL. * @package Cake.Network */ +#[\AllowDynamicProperties] class CakeRequest implements ArrayAccess { /** @@ -171,10 +172,10 @@ protected function _processPost() { if ($_POST) { $this->data = $_POST; } elseif (($this->is('put') || $this->is('delete')) && - strpos($this->contentType(), 'application/x-www-form-urlencoded') === 0 + strpos((string)$this->contentType(), 'application/x-www-form-urlencoded') === 0 ) { $data = $this->_readInput(); - parse_str($data, $this->data); + parse_str((string)$data, $this->data); } if (ini_get('magic_quotes_gpc') === '1') { $this->data = stripslashes_deep($this->data); @@ -1030,7 +1031,7 @@ public function input($callback = null) { $args = func_get_args(); if (!empty($args)) { $callback = array_shift($args); - array_unshift($args, $input); + array_unshift($args, (string)$input); return call_user_func_array($callback, $args); } return $input; diff --git a/lib/Cake/Network/CakeResponse.php b/lib/Cake/Network/CakeResponse.php index 6ad28da512..1da480e6ac 100644 --- a/lib/Cake/Network/CakeResponse.php +++ b/lib/Cake/Network/CakeResponse.php @@ -590,7 +590,7 @@ public function header($header = null, $value = null) { if ($value === null && strpos($header, ':') !== false) { list($header, $value) = explode(':', $header, 2); } - $this->_headers[$header] = is_array($value) ? array_map('trim', $value) : trim($value); + $this->_headers[$header] = is_array($value) ? array_map('trim', $value) : trim((string)$value); } return $this->_headers; } @@ -750,7 +750,7 @@ public function type($contentType = null) { * @return mixed string mapped mime type or false if $alias is not mapped */ public function getMimeType($alias) { - if (isset($this->_mimeTypes[$alias])) { + if ($alias !== null && isset($this->_mimeTypes[$alias])) { return $this->_mimeTypes[$alias]; } return false; @@ -1102,7 +1102,7 @@ public function compress() { * @return bool */ public function outputCompressed() { - return strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false + return strpos((string)env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false && (ini_get("zlib.output_compression") === '1' || in_array('ob_gzhandler', ob_list_handlers())); } @@ -1378,7 +1378,7 @@ public function file($path, $options = array()) { $fileSize = $file->size(); if ($download) { - $agent = env('HTTP_USER_AGENT'); + $agent = (string)env('HTTP_USER_AGENT'); if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent)) { $contentType = 'application/octet-stream'; diff --git a/lib/Cake/Network/Email/CakeEmail.php b/lib/Cake/Network/Email/CakeEmail.php index ee4da73d9b..fb1a545b30 100644 --- a/lib/Cake/Network/Email/CakeEmail.php +++ b/lib/Cake/Network/Email/CakeEmail.php @@ -353,7 +353,7 @@ public function __construct($config = null) { if ($this->_appCharset !== null) { $this->charset = $this->_appCharset; } - $this->_domain = preg_replace('/\:\d+$/', '', env('HTTP_HOST')); + $this->_domain = preg_replace('/\:\d+$/', '', (string)env('HTTP_HOST')); if (empty($this->_domain)) { $this->_domain = php_uname('n'); } @@ -1375,6 +1375,7 @@ protected function _encodeString($text, $charset) { * @return array Wrapped message */ protected function _wrap($message, $wrapLength = CakeEmail::LINE_LENGTH_MUST) { + $message = (string)$message; if (strlen($message) === 0) { return array(''); } diff --git a/lib/Cake/Network/Http/HttpResponse.php b/lib/Cake/Network/Http/HttpResponse.php index 61ffab0bba..00bf2737f4 100644 --- a/lib/Cake/Network/Http/HttpResponse.php +++ b/lib/Cake/Network/Http/HttpResponse.php @@ -18,10 +18,10 @@ App::uses('HttpSocketResponse', 'Network/Http'); if (class_exists('HttpResponse')) { - trigger_error(__d( + throw new CakeException(__d( 'cake_dev', "HttpResponse is deprecated due to naming conflicts. Use HttpSocketResponse instead." - ), E_USER_ERROR); + )); } /** diff --git a/lib/Cake/Network/Http/HttpSocket.php b/lib/Cake/Network/Http/HttpSocket.php index a02d71db58..061f7a79a5 100644 --- a/lib/Cake/Network/Http/HttpSocket.php +++ b/lib/Cake/Network/Http/HttpSocket.php @@ -843,7 +843,7 @@ protected function _parseQuery($query) { } $key = urldecode($key); - $value = urldecode($value); + $value = urldecode((string)$value); if (preg_match_all('/\[([^\[\]]*)\]/iUs', $key, $matches)) { $subKeys = $matches[1]; diff --git a/lib/Cake/Routing/Route/CakeRoute.php b/lib/Cake/Routing/Route/CakeRoute.php index 0ac38d2bfd..251d3d1208 100644 --- a/lib/Cake/Routing/Route/CakeRoute.php +++ b/lib/Cake/Routing/Route/CakeRoute.php @@ -275,7 +275,7 @@ protected function _parseArgs($args, $context) { $namedConfig = Router::namedConfig(); $greedy = $namedConfig['greedyNamed']; - $rules = $namedConfig['rules']; + $rules = is_array($namedConfig['rules']) ? $namedConfig['rules'] : array(); if (!empty($this->options['named'])) { $greedy = isset($this->options['greedyNamed']) && $this->options['greedyNamed'] === true; foreach ((array)$this->options['named'] as $key => $val) { @@ -510,7 +510,7 @@ protected function _writeUrl($params) { if (is_array($value)) { $flat = Hash::flatten($value, '%5D%5B'); foreach ($flat as $namedKey => $namedValue) { - $named[] = $key . "%5B{$namedKey}%5D" . $separator . rawurlencode($namedValue); + $named[] = $key . "%5B{$namedKey}%5D" . $separator . rawurlencode((string)$namedValue); } } else { $named[] = $key . $separator . rawurlencode($value); diff --git a/lib/Cake/Test/Case/Cache/CacheTest.php b/lib/Cake/Test/Case/Cache/CacheTest.php index 3bd850217d..334ced4679 100644 --- a/lib/Cake/Test/Case/Cache/CacheTest.php +++ b/lib/Cake/Test/Case/Cache/CacheTest.php @@ -24,6 +24,15 @@ * @package Cake.Test.Case.Cache */ class CacheTest extends CakeTestCase { +/** + * @var mixed + */ + protected $_cacheDisable = null; + +/** + * @var mixed + */ + protected $_defaultCacheConfig = null; protected $_count = 0; diff --git a/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php index 038fa6561b..4162940c69 100644 --- a/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php @@ -24,6 +24,10 @@ * @package Cake.Test.Case.Cache.Engine */ class ApcEngineTest extends CakeTestCase { +/** + * @var mixed + */ + protected $_cacheDisable = null; /** * APC extension to be used diff --git a/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php index 05a6dbf24c..86020406ac 100644 --- a/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php @@ -48,6 +48,10 @@ public function setMemcache($memcache) { * @package Cake.Test.Case.Cache.Engine */ class MemcacheEngineTest extends CakeTestCase { +/** + * @var mixed + */ + protected $_cacheDisable = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Cache/Engine/RedisEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/RedisEngineTest.php index 00c5ffb3ea..ba4f5c32c6 100644 --- a/lib/Cake/Test/Case/Cache/Engine/RedisEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/RedisEngineTest.php @@ -25,6 +25,10 @@ * @package Cake.Test.Case.Cache.Engine */ class RedisEngineTest extends CakeTestCase { +/** + * @var mixed + */ + protected $_cacheDisable = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Cache/Engine/WincacheEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/WincacheEngineTest.php index ba4b6da18c..1eb974ff7b 100644 --- a/lib/Cake/Test/Case/Cache/Engine/WincacheEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/WincacheEngineTest.php @@ -24,6 +24,10 @@ * @package Cake.Test.Case.Cache.Engine */ class WincacheEngineTest extends CakeTestCase { +/** + * @var mixed + */ + protected $_cacheDisable = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Cache/Engine/XcacheEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/XcacheEngineTest.php index 645a7fe024..27c895c8b5 100644 --- a/lib/Cake/Test/Case/Cache/Engine/XcacheEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/XcacheEngineTest.php @@ -24,6 +24,10 @@ * @package Cake.Test.Case.Cache.Engine */ class XcacheEngineTest extends CakeTestCase { +/** + * @var mixed + */ + protected $_cacheDisable = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Configure/IniReaderTest.php b/lib/Cake/Test/Case/Configure/IniReaderTest.php index 2644392337..ed812dd20b 100644 --- a/lib/Cake/Test/Case/Configure/IniReaderTest.php +++ b/lib/Cake/Test/Case/Configure/IniReaderTest.php @@ -24,6 +24,10 @@ * @package Cake.Test.Case.Configure */ class IniReaderTest extends CakeTestCase { +/** + * @var mixed + */ + public $path = null; /** * Test data to serialize and unserialize. diff --git a/lib/Cake/Test/Case/Configure/PhpReaderTest.php b/lib/Cake/Test/Case/Configure/PhpReaderTest.php index 4c5c65f140..03714c1695 100644 --- a/lib/Cake/Test/Case/Configure/PhpReaderTest.php +++ b/lib/Cake/Test/Case/Configure/PhpReaderTest.php @@ -24,6 +24,10 @@ * @package Cake.Test.Case.Configure */ class PhpReaderTest extends CakeTestCase { +/** + * @var mixed + */ + public $path = null; /** * Test data to serialize and unserialize. diff --git a/lib/Cake/Test/Case/Console/Command/AclShellTest.php b/lib/Cake/Test/Case/Console/Command/AclShellTest.php index 688d3acc3d..144d9a1696 100644 --- a/lib/Cake/Test/Case/Console/Command/AclShellTest.php +++ b/lib/Cake/Test/Case/Console/Command/AclShellTest.php @@ -29,6 +29,10 @@ * @package Cake.Test.Case.Console.Command */ class AclShellTest extends CakeTestCase { +/** + * @var mixed + */ + public $Task = null; /** * Fixtures diff --git a/lib/Cake/Test/Case/Console/Command/ApiShellTest.php b/lib/Cake/Test/Case/Console/Command/ApiShellTest.php index 0d67608147..f1cb89f796 100644 --- a/lib/Cake/Test/Case/Console/Command/ApiShellTest.php +++ b/lib/Cake/Test/Case/Console/Command/ApiShellTest.php @@ -28,6 +28,10 @@ * @package Cake.Test.Case.Console.Command */ class ApiShellTest extends CakeTestCase { +/** + * @var mixed + */ + public $Shell = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Console/Command/BakeShellTest.php b/lib/Cake/Test/Case/Console/Command/BakeShellTest.php index 060d6217e5..9f07a2db48 100644 --- a/lib/Cake/Test/Case/Console/Command/BakeShellTest.php +++ b/lib/Cake/Test/Case/Console/Command/BakeShellTest.php @@ -32,6 +32,10 @@ class UsersController extends Controller { } class BakeShellTest extends CakeTestCase { +/** + * @var mixed + */ + public $Shell = null; /** * fixtures diff --git a/lib/Cake/Test/Case/Console/Command/CommandListShellTest.php b/lib/Cake/Test/Case/Console/Command/CommandListShellTest.php index 2032af3f62..dba6871eea 100644 --- a/lib/Cake/Test/Case/Console/Command/CommandListShellTest.php +++ b/lib/Cake/Test/Case/Console/Command/CommandListShellTest.php @@ -43,6 +43,10 @@ protected function _write($message) { * @package Cake.Test.Case.Console.Command */ class CommandListShellTest extends CakeTestCase { +/** + * @var mixed + */ + public $Shell = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Console/Command/CompletionShellTest.php b/lib/Cake/Test/Case/Console/Command/CompletionShellTest.php index 427066a7a3..c8a4e06c82 100644 --- a/lib/Cake/Test/Case/Console/Command/CompletionShellTest.php +++ b/lib/Cake/Test/Case/Console/Command/CompletionShellTest.php @@ -45,6 +45,10 @@ protected function _write($message) { * @package Cake.Test.Case.Console.Command */ class CompletionShellTest extends CakeTestCase { +/** + * @var mixed + */ + public $Shell = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Console/Command/SchemaShellTest.php b/lib/Cake/Test/Case/Console/Command/SchemaShellTest.php index 2c3a07de23..c283fed3ca 100644 --- a/lib/Cake/Test/Case/Console/Command/SchemaShellTest.php +++ b/lib/Cake/Test/Case/Console/Command/SchemaShellTest.php @@ -86,6 +86,20 @@ class SchemaShellTestSchema extends CakeSchema { * @package Cake.Test.Case.Console.Command */ class SchemaShellTest extends CakeTestCase { +/** + * @var mixed + */ + public $Dispatcher = null; + +/** + * @var mixed + */ + public $Shell = null; + +/** + * @var mixed + */ + public $file = null; /** * Fixtures diff --git a/lib/Cake/Test/Case/Console/Command/Task/CommandTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/CommandTaskTest.php index 5dbc7a59de..1d1d1bfa68 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/CommandTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/CommandTaskTest.php @@ -22,6 +22,10 @@ * @package Cake.Test.Case.Console.Command.Task */ class CommandTaskTest extends CakeTestCase { +/** + * @var mixed + */ + public $CommandTask = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php index e9fbc57a67..e610453626 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php @@ -56,6 +56,10 @@ class BakeArticle extends Model { * @package Cake.Test.Case.Console.Command.Task */ class ControllerTaskTest extends CakeTestCase { +/** + * @var mixed + */ + public $Task = null; /** * fixtures diff --git a/lib/Cake/Test/Case/Console/Command/Task/DbConfigTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/DbConfigTaskTest.php index 2e1ebe8611..af956c4938 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/DbConfigTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/DbConfigTaskTest.php @@ -28,6 +28,10 @@ * @package Cake.Test.Case.Console.Command.Task */ class DbConfigTaskTest extends CakeTestCase { +/** + * @var mixed + */ + public $Task = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Console/Command/Task/ExtractTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/ExtractTaskTest.php index 53ba40ab60..fa7d1d5cd1 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/ExtractTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/ExtractTaskTest.php @@ -31,6 +31,25 @@ * @package Cake.Test.Case.Console.Command.Task */ class ExtractTaskTest extends CakeTestCase { +/** + * @var mixed + */ + public $Task = null; + +/** + * @var mixed + */ + public $in = null; + +/** + * @var mixed + */ + public $out = null; + +/** + * @var mixed + */ + public $path = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Console/Command/Task/FixtureTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/FixtureTaskTest.php index 9c5aba39e5..85e4dd7d43 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/FixtureTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/FixtureTaskTest.php @@ -31,6 +31,10 @@ * @package Cake.Test.Case.Console.Command.Task */ class FixtureTaskTest extends CakeTestCase { +/** + * @var mixed + */ + public $Task = null; /** * fixtures diff --git a/lib/Cake/Test/Case/Console/Command/Task/ModelTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/ModelTaskTest.php index 2df4980d05..1cb7533ab4 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/ModelTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/ModelTaskTest.php @@ -33,6 +33,10 @@ * @property ModelTask $Task */ class ModelTaskTest extends CakeTestCase { +/** + * @var mixed + */ + public $Task = null; /** * fixtures diff --git a/lib/Cake/Test/Case/Console/Command/Task/PluginTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/PluginTaskTest.php index c5e879d180..8ae3dfa36e 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/PluginTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/PluginTaskTest.php @@ -33,6 +33,30 @@ * @package Cake.Test.Case.Console.Command.Task */ class PluginTaskTest extends CakeTestCase { +/** + * @var mixed + */ + public $Task = null; + +/** + * @var mixed + */ + protected $_paths = null; + +/** + * @var mixed + */ + protected $_testPath = null; + +/** + * @var mixed + */ + public $in = null; + +/** + * @var mixed + */ + public $out = null; /** * setUp method @@ -68,7 +92,7 @@ public function setUp() : void { * @return void */ public function tearDown() : void { - if (file_exists($this->Task->bootstrap)) { + if ($this->Task->bootstrap !== null && file_exists($this->Task->bootstrap)) { unlink($this->Task->bootstrap); } parent::tearDown(); diff --git a/lib/Cake/Test/Case/Console/Command/Task/ProjectTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/ProjectTaskTest.php index 72c5563c1b..54dadcc223 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/ProjectTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/ProjectTaskTest.php @@ -32,6 +32,10 @@ * @package Cake.Test.Case.Console.Command.Task */ class ProjectTaskTest extends CakeTestCase { +/** + * @var mixed + */ + public $Task = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Console/Command/Task/TemplateTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/TemplateTaskTest.php index e90968f060..7a913e424b 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/TemplateTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/TemplateTaskTest.php @@ -30,6 +30,10 @@ * @package Cake.Test.Case.Console.Command.Task */ class TemplateTaskTest extends CakeTestCase { +/** + * @var mixed + */ + public $Task = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Console/Command/Task/TestTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/TestTaskTest.php index e53693da1c..31e4f51d5d 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/TestTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/TestTaskTest.php @@ -178,6 +178,25 @@ class TestTaskCommentsController extends Controller { * @package Cake.Test.Case.Console.Command.Task */ class TestTaskTest extends CakeTestCase { +/** + * @var mixed + */ + public $Example = null; + +/** + * @var mixed + */ + public $Task = null; + +/** + * @var mixed + */ + public $TestTaskArticle = null; + +/** + * @var mixed + */ + public $TestTaskComments = null; /** * Fixtures diff --git a/lib/Cake/Test/Case/Console/Command/Task/ViewTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/ViewTaskTest.php index 639e849212..d7c33aaae4 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/ViewTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/ViewTaskTest.php @@ -169,6 +169,10 @@ public function admin_delete() { * @package Cake.Test.Case.Console.Command.Task */ class ViewTaskTest extends CakeTestCase { +/** + * @var mixed + */ + public $Task = null; /** * Fixtures diff --git a/lib/Cake/Test/Case/Console/Command/TestShellTest.php b/lib/Cake/Test/Case/Console/Command/TestShellTest.php index 46a4d64f6b..291b32167d 100644 --- a/lib/Cake/Test/Case/Console/Command/TestShellTest.php +++ b/lib/Cake/Test/Case/Console/Command/TestShellTest.php @@ -42,6 +42,10 @@ public function mapFileToCategory($file) { * @package Cake.Test.Case.Console.Command */ class TestShellTest extends CakeTestCase { +/** + * @var mixed + */ + public $Shell = null; /** * setUp test case diff --git a/lib/Cake/Test/Case/Console/ConsoleErrorHandlerTest.php b/lib/Cake/Test/Case/Console/ConsoleErrorHandlerTest.php index dae3907e3f..e1c8d03743 100644 --- a/lib/Cake/Test/Case/Console/ConsoleErrorHandlerTest.php +++ b/lib/Cake/Test/Case/Console/ConsoleErrorHandlerTest.php @@ -24,6 +24,10 @@ * @package Cake.Test.Case.Console */ class ConsoleErrorHandlerTest extends CakeTestCase { +/** + * @var mixed + */ + public $Error = null; /** * setup, create mocks @@ -161,7 +165,9 @@ public function testNonIntegerExceptionCode() { $class = new ReflectionClass('Exception'); $property = $class->getProperty('code'); - $property->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $property->setAccessible(true); + } $property->setValue($exception, '42S22'); ConsoleErrorHandler::$stderr->expects($this->once())->method('write') diff --git a/lib/Cake/Test/Case/Console/ConsoleOutputTest.php b/lib/Cake/Test/Case/Console/ConsoleOutputTest.php index f6ac6fc319..055b4cc8d9 100644 --- a/lib/Cake/Test/Case/Console/ConsoleOutputTest.php +++ b/lib/Cake/Test/Case/Console/ConsoleOutputTest.php @@ -24,6 +24,10 @@ * @package Cake.Test.Case.Console */ class ConsoleOutputTest extends CakeTestCase { +/** + * @var mixed + */ + public $output = null; /** * setup diff --git a/lib/Cake/Test/Case/Console/Helper/ProgressShellHelperTest.php b/lib/Cake/Test/Case/Console/Helper/ProgressShellHelperTest.php index 03e9c6dc56..807a9c94c0 100644 --- a/lib/Cake/Test/Case/Console/Helper/ProgressShellHelperTest.php +++ b/lib/Cake/Test/Case/Console/Helper/ProgressShellHelperTest.php @@ -21,6 +21,15 @@ * @property ProgressShellHelper $helper */ class ProgressShellHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $consoleOutput = null; + +/** + * @var mixed + */ + public $helper = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Console/Helper/TableShellHelperTest.php b/lib/Cake/Test/Case/Console/Helper/TableShellHelperTest.php index 755fcb90ae..f757ce7f15 100644 --- a/lib/Cake/Test/Case/Console/Helper/TableShellHelperTest.php +++ b/lib/Cake/Test/Case/Console/Helper/TableShellHelperTest.php @@ -21,6 +21,15 @@ * @property TableShellHelper $helper */ class TableShellHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $consoleOutput = null; + +/** + * @var mixed + */ + public $helper = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Console/ShellTest.php b/lib/Cake/Test/Case/Console/ShellTest.php index 1886eac575..df0b7ed643 100644 --- a/lib/Cake/Test/Case/Console/ShellTest.php +++ b/lib/Cake/Test/Case/Console/ShellTest.php @@ -121,6 +121,10 @@ class TestBananaTask extends Shell { * @package Cake.Test.Case.Console.Command */ class ShellTest extends CakeTestCase { +/** + * @var mixed + */ + public $Shell = null; /** * Fixtures used in this test case diff --git a/lib/Cake/Test/Case/Console/TaskCollectionTest.php b/lib/Cake/Test/Case/Console/TaskCollectionTest.php index 8444427ce0..809a1fee3a 100644 --- a/lib/Cake/Test/Case/Console/TaskCollectionTest.php +++ b/lib/Cake/Test/Case/Console/TaskCollectionTest.php @@ -31,6 +31,10 @@ class DbConfigAliasedTask extends Shell { * @package Cake.Test.Case.Console */ class TaskCollectionTest extends CakeTestCase { +/** + * @var mixed + */ + public $Tasks = null; /** * setUp diff --git a/lib/Cake/Test/Case/Controller/ApplicationControllerTest.php b/lib/Cake/Test/Case/Controller/ApplicationControllerTest.php index 6c658b6d08..6c78dd06c5 100644 --- a/lib/Cake/Test/Case/Controller/ApplicationControllerTest.php +++ b/lib/Cake/Test/Case/Controller/ApplicationControllerTest.php @@ -51,12 +51,21 @@ public function next() { */ class ApplicationControllerTest extends ControllerTestCase { +/** + * Session configuration restored after each test + * + * @var array + */ + protected $_sessionConfig = array(); + /** * setupDown method * * @return void */ public function setUp() : void { + $this->_sessionConfig = Configure::read('Session'); + $this->skipIf(PHP_VERSION_ID >= 80500, 'Enabling session.use_trans_sid is deprecated since PHP 8.5.'); CakeSession::destroy(); parent::setUp(); } @@ -68,6 +77,9 @@ public function setUp() : void { */ public function tearDown() : void { CakeSession::destroy(); + if (!empty($this->_sessionConfig)) { + Configure::write('Session', $this->_sessionConfig); + } parent::tearDown(); } diff --git a/lib/Cake/Test/Case/Controller/Component/Acl/DbAclTest.php b/lib/Cake/Test/Case/Controller/Component/Acl/DbAclTest.php index 57d4854087..8ad0e19314 100644 --- a/lib/Cake/Test/Case/Controller/Component/Acl/DbAclTest.php +++ b/lib/Cake/Test/Case/Controller/Component/Acl/DbAclTest.php @@ -175,6 +175,10 @@ public function __construct() { * @package Cake.Test.Case.Controller.Component.Acl */ class DbAclTest extends CakeTestCase { +/** + * @var mixed + */ + public $Acl = null; /** * fixtures property diff --git a/lib/Cake/Test/Case/Controller/Component/Acl/PhpAclTest.php b/lib/Cake/Test/Case/Controller/Component/Acl/PhpAclTest.php index 07bfdc0370..fbc33b915f 100644 --- a/lib/Cake/Test/Case/Controller/Component/Acl/PhpAclTest.php +++ b/lib/Cake/Test/Case/Controller/Component/Acl/PhpAclTest.php @@ -26,6 +26,15 @@ class_exists('AclComponent'); * @package Cake.Test.Case.Controller.Component.Acl */ class PhpAclTest extends CakeTestCase { +/** + * @var mixed + */ + public $Acl = null; + +/** + * @var mixed + */ + public $PhpAcl = null; /** * Setup diff --git a/lib/Cake/Test/Case/Controller/Component/AclComponentTest.php b/lib/Cake/Test/Case/Controller/Component/AclComponentTest.php index f59ce29309..533b14c481 100644 --- a/lib/Cake/Test/Case/Controller/Component/AclComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/AclComponentTest.php @@ -25,6 +25,10 @@ class_exists('AclComponent'); * @package Cake.Test.Case.Controller.Component */ class AclComponentTest extends CakeTestCase { +/** + * @var mixed + */ + public $Acl = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Controller/Component/Auth/ActionsAuthorizeTest.php b/lib/Cake/Test/Case/Controller/Component/Auth/ActionsAuthorizeTest.php index a18a2b9db9..189ac7e62a 100644 --- a/lib/Cake/Test/Case/Controller/Component/Auth/ActionsAuthorizeTest.php +++ b/lib/Cake/Test/Case/Controller/Component/Auth/ActionsAuthorizeTest.php @@ -28,6 +28,25 @@ * @package Cake.Test.Case.Controller.Component.Auth */ class ActionsAuthorizeTest extends CakeTestCase { +/** + * @var mixed + */ + public $Acl = null; + +/** + * @var mixed + */ + public $Collection = null; + +/** + * @var mixed + */ + public $auth = null; + +/** + * @var mixed + */ + public $controller = null; /** * setUp diff --git a/lib/Cake/Test/Case/Controller/Component/Auth/BasicAuthenticateTest.php b/lib/Cake/Test/Case/Controller/Component/Auth/BasicAuthenticateTest.php index 7da3634fa9..784e2a429e 100644 --- a/lib/Cake/Test/Case/Controller/Component/Auth/BasicAuthenticateTest.php +++ b/lib/Cake/Test/Case/Controller/Component/Auth/BasicAuthenticateTest.php @@ -28,6 +28,20 @@ * @package Cake.Test.Case.Controller.Component.Auth */ class BasicAuthenticateTest extends CakeTestCase { +/** + * @var mixed + */ + public $Collection = null; + +/** + * @var mixed + */ + public $auth = null; + +/** + * @var mixed + */ + public $response = null; /** * Fixtures diff --git a/lib/Cake/Test/Case/Controller/Component/Auth/BlowfishAuthenticateTest.php b/lib/Cake/Test/Case/Controller/Component/Auth/BlowfishAuthenticateTest.php index d62bdee01e..69535a88e4 100644 --- a/lib/Cake/Test/Case/Controller/Component/Auth/BlowfishAuthenticateTest.php +++ b/lib/Cake/Test/Case/Controller/Component/Auth/BlowfishAuthenticateTest.php @@ -31,6 +31,20 @@ * @package Cake.Test.Case.Controller.Component.Auth */ class BlowfishAuthenticateTest extends CakeTestCase { +/** + * @var mixed + */ + public $Collection = null; + +/** + * @var mixed + */ + public $auth = null; + +/** + * @var mixed + */ + public $response = null; public $fixtures = array('core.user', 'core.auth_user'); diff --git a/lib/Cake/Test/Case/Controller/Component/Auth/ControllerAuthorizeTest.php b/lib/Cake/Test/Case/Controller/Component/Auth/ControllerAuthorizeTest.php index a06c40fb8e..69b822e21b 100644 --- a/lib/Cake/Test/Case/Controller/Component/Auth/ControllerAuthorizeTest.php +++ b/lib/Cake/Test/Case/Controller/Component/Auth/ControllerAuthorizeTest.php @@ -27,6 +27,20 @@ * @package Cake.Test.Case.Controller.Component.Auth */ class ControllerAuthorizeTest extends CakeTestCase { +/** + * @var mixed + */ + public $auth = null; + +/** + * @var mixed + */ + public $components = null; + +/** + * @var mixed + */ + public $controller = null; /** * setup diff --git a/lib/Cake/Test/Case/Controller/Component/Auth/CrudAuthorizeTest.php b/lib/Cake/Test/Case/Controller/Component/Auth/CrudAuthorizeTest.php index 784909e592..c2c6a9902e 100644 --- a/lib/Cake/Test/Case/Controller/Component/Auth/CrudAuthorizeTest.php +++ b/lib/Cake/Test/Case/Controller/Component/Auth/CrudAuthorizeTest.php @@ -28,6 +28,20 @@ * @package Cake.Test.Case.Controller.Component.Auth */ class CrudAuthorizeTest extends CakeTestCase { +/** + * @var mixed + */ + public $Acl = null; + +/** + * @var mixed + */ + public $Components = null; + +/** + * @var mixed + */ + public $auth = null; /** * setup diff --git a/lib/Cake/Test/Case/Controller/Component/Auth/DigestAuthenticateTest.php b/lib/Cake/Test/Case/Controller/Component/Auth/DigestAuthenticateTest.php index d7fcd63ac8..fd366e3946 100644 --- a/lib/Cake/Test/Case/Controller/Component/Auth/DigestAuthenticateTest.php +++ b/lib/Cake/Test/Case/Controller/Component/Auth/DigestAuthenticateTest.php @@ -29,6 +29,25 @@ * @package Cake.Test.Case.Controller.Component.Auth */ class DigestAuthenticateTest extends CakeTestCase { +/** + * @var mixed + */ + public $Collection = null; + +/** + * @var mixed + */ + public $auth = null; + +/** + * @var mixed + */ + public $response = null; + +/** + * @var mixed + */ + public $server = null; /** * Fixtures diff --git a/lib/Cake/Test/Case/Controller/Component/Auth/FormAuthenticateTest.php b/lib/Cake/Test/Case/Controller/Component/Auth/FormAuthenticateTest.php index 6480ed3d85..453e16502a 100644 --- a/lib/Cake/Test/Case/Controller/Component/Auth/FormAuthenticateTest.php +++ b/lib/Cake/Test/Case/Controller/Component/Auth/FormAuthenticateTest.php @@ -28,6 +28,20 @@ * @package Cake.Test.Case.Controller.Component.Auth */ class FormAuthenticateTest extends CakeTestCase { +/** + * @var mixed + */ + public $Collection = null; + +/** + * @var mixed + */ + public $auth = null; + +/** + * @var mixed + */ + public $response = null; /** * Fixtrues diff --git a/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php b/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php index 2d6af32cd2..63c8d3aaf4 100644 --- a/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php @@ -352,6 +352,20 @@ public function listenerFunction() { * @package Cake.Test.Case.Controller.Component */ class AuthComponentTest extends CakeTestCase { +/** + * @var mixed + */ + public $Auth = null; + +/** + * @var mixed + */ + public $AuthUser = null; + +/** + * @var mixed + */ + public $Controller = null; /** * name property diff --git a/lib/Cake/Test/Case/Controller/Component/CookieComponentTest.php b/lib/Cake/Test/Case/Controller/Component/CookieComponentTest.php index 99fc97b30f..bad180e500 100644 --- a/lib/Cake/Test/Case/Controller/Component/CookieComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/CookieComponentTest.php @@ -56,6 +56,10 @@ public function beforeFilter() { * @package Cake.Test.Case.Controller.Component */ class CookieComponentTest extends CakeTestCase { +/** + * @var mixed + */ + public $Cookie = null; /** * Controller property diff --git a/lib/Cake/Test/Case/Controller/Component/FlashComponentTest.php b/lib/Cake/Test/Case/Controller/Component/FlashComponentTest.php index d56d29fdfe..31e6ba86b8 100644 --- a/lib/Cake/Test/Case/Controller/Component/FlashComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/FlashComponentTest.php @@ -27,6 +27,15 @@ * @package Cake.Test.Case.Controller.Component */ class FlashComponentTest extends CakeTestCase { +/** + * @var mixed + */ + public $Components = null; + +/** + * @var mixed + */ + public $Flash = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Controller/Component/PaginatorComponentTest.php b/lib/Cake/Test/Case/Controller/Component/PaginatorComponentTest.php index b20bf6b46f..6afe862e41 100644 --- a/lib/Cake/Test/Case/Controller/Component/PaginatorComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/PaginatorComponentTest.php @@ -284,6 +284,20 @@ protected function _findTotalsOperation($state, $query, $results = array()) { } class PaginatorComponentTest extends CakeTestCase { +/** + * @var mixed + */ + public $Controller = null; + +/** + * @var mixed + */ + public $Paginator = null; + +/** + * @var mixed + */ + public $request = null; /** * fixtures property diff --git a/lib/Cake/Test/Case/Controller/Component/RequestHandlerComponentTest.php b/lib/Cake/Test/Case/Controller/Component/RequestHandlerComponentTest.php index c79e5bd1ee..5bcc9363f0 100644 --- a/lib/Cake/Test/Case/Controller/Component/RequestHandlerComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/RequestHandlerComponentTest.php @@ -86,6 +86,10 @@ class CustomJsonView extends JsonView { * @package Cake.Test.Case.Controller.Component */ class RequestHandlerComponentTest extends CakeTestCase { +/** + * @var mixed + */ + protected $_extensions = null; /** * Controller property @@ -442,7 +446,9 @@ public function testStartupCustomTypeProcess() { $this->Controller->request->expects($this->once()) ->method('_readInput') ->will($this->returnValue('"A","csv","string"')); - $this->RequestHandler->addInputType('csv', array('str_getcsv')); + $this->RequestHandler->addInputType('csv', array(function ($input) { + return str_getcsv($input, ',', '"', '\\'); + })); $this->RequestHandler->startup($this->Controller); $expected = array( 'A', 'csv', 'string' diff --git a/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php b/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php index e49e65c0a1..9e929953db 100644 --- a/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php @@ -140,6 +140,10 @@ protected function _fail() { * @package Cake.Test.Case.Controller.Component */ class SecurityComponentTest extends CakeTestCase { +/** + * @var mixed + */ + public $Security = null; /** * Controller property diff --git a/lib/Cake/Test/Case/Controller/Component/SessionComponentTest.php b/lib/Cake/Test/Case/Controller/Component/SessionComponentTest.php index ef587da278..d789151e7c 100644 --- a/lib/Cake/Test/Case/Controller/Component/SessionComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/SessionComponentTest.php @@ -75,6 +75,10 @@ public function sessionId() { * @package Cake.Test.Case.Controller.Component */ class SessionComponentTest extends CakeTestCase { +/** + * @var mixed + */ + public $ComponentCollection = null; protected static $_sessionBackup; diff --git a/lib/Cake/Test/Case/Controller/ComponentCollectionTest.php b/lib/Cake/Test/Case/Controller/ComponentCollectionTest.php index 8c66526cab..ac99b6ece2 100644 --- a/lib/Cake/Test/Case/Controller/ComponentCollectionTest.php +++ b/lib/Cake/Test/Case/Controller/ComponentCollectionTest.php @@ -28,6 +28,10 @@ class CookieAliasComponent extends CookieComponent { } class ComponentCollectionTest extends CakeTestCase { +/** + * @var mixed + */ + public $Components = null; /** * setUp diff --git a/lib/Cake/Test/Case/Controller/ComponentTest.php b/lib/Cake/Test/Case/Controller/ComponentTest.php index 0686ac335f..065ccf0c9d 100644 --- a/lib/Cake/Test/Case/Controller/ComponentTest.php +++ b/lib/Cake/Test/Case/Controller/ComponentTest.php @@ -200,6 +200,10 @@ class SomethingWithEmailComponent extends Component { * @package Cake.Test.Case.Controller */ class ComponentTest extends CakeTestCase { +/** + * @var mixed + */ + protected $_pluginPaths = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Controller/ScaffoldTest.php b/lib/Cake/Test/Case/Controller/ScaffoldTest.php index cbad1a32e8..8a4f5b90ba 100644 --- a/lib/Cake/Test/Case/Controller/ScaffoldTest.php +++ b/lib/Cake/Test/Case/Controller/ScaffoldTest.php @@ -113,6 +113,10 @@ public function beforeScaffold($method) { * @package Cake.Test.Case.Controller */ class TestScaffoldMock extends Scaffold { +/** + * @var mixed + */ + protected $_params = null; /** * Overload _scaffold diff --git a/lib/Cake/Test/Case/Core/CakeObjectTest.php b/lib/Cake/Test/Case/Core/CakeObjectTest.php index 5d829da079..527699c6c1 100644 --- a/lib/Cake/Test/Case/Core/CakeObjectTest.php +++ b/lib/Cake/Test/Case/Core/CakeObjectTest.php @@ -280,6 +280,10 @@ class ObjectTestModel extends CakeTestModel { * @package Cake.Test.Case.Core */ class ObjectTest extends CakeTestCase { +/** + * @var mixed + */ + public $object = null; /** * fixtures diff --git a/lib/Cake/Test/Case/Error/ExceptionRendererTest.php b/lib/Cake/Test/Case/Error/ExceptionRendererTest.php index 6309063d13..30f485e63a 100644 --- a/lib/Cake/Test/Case/Error/ExceptionRendererTest.php +++ b/lib/Cake/Test/Case/Error/ExceptionRendererTest.php @@ -863,6 +863,7 @@ public function testRenderWithNoRequest() { */ public function testPDOException() { $exception = new PDOException('There was an error in the SQL query'); + $exception = new PDOExceptionWithQueryString($exception); $exception->queryString = 'SELECT * from poo_query < 5 and :seven'; $exception->params = array('seven' => 7); $ExceptionRenderer = new ExceptionRenderer($exception); diff --git a/lib/Cake/Test/Case/Log/Engine/ConsoleLogTest.php b/lib/Cake/Test/Case/Log/Engine/ConsoleLogTest.php index 4de920e907..a432bc3a1f 100644 --- a/lib/Cake/Test/Case/Log/Engine/ConsoleLogTest.php +++ b/lib/Cake/Test/Case/Log/Engine/ConsoleLogTest.php @@ -35,7 +35,13 @@ class TestConsoleLog extends ConsoleLog { class TestCakeLog extends CakeLog { public static function replace($key, &$engine) { - static::$_Collection->{$key} = $engine; + $property = new ReflectionProperty('ObjectCollection', '_loaded'); + if (PHP_VERSION_ID < 80100) { + $property->setAccessible(true); + } + $loaded = $property->getValue(static::$_Collection); + $loaded[$key] = $engine; + $property->setValue(static::$_Collection, $loaded); } } @@ -140,7 +146,7 @@ public function testDefaultOutputAs() { 'engine' => 'TestConsole', )); if ((DS === '\\' && !(bool)env('ANSICON') && env('ConEmuANSI') !== 'ON') || - (function_exists('posix_isatty') && !posix_isatty(null)) + function_exists('posix_isatty') ) { $expected = ConsoleOutput::PLAIN; } else { diff --git a/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php b/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php index 9c5e639ee0..9eb0b85fed 100644 --- a/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/ContainableBehaviorTest.php @@ -27,6 +27,35 @@ * @package Cake.Test.Case.Model.Behavior */ class ContainableBehaviorTest extends CakeTestCase { +/** + * @var mixed + */ + public $Article = null; + +/** + * @var mixed + */ + public $JoinA = null; + +/** + * @var mixed + */ + public $JoinB = null; + +/** + * @var mixed + */ + public $JoinC = null; + +/** + * @var mixed + */ + public $Tag = null; + +/** + * @var mixed + */ + public $User = null; /** * Fixtures associated with this test case diff --git a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorAfterTest.php b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorAfterTest.php index e107de9a28..4ab2747e34 100644 --- a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorAfterTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorAfterTest.php @@ -27,6 +27,10 @@ * @package Cake.Test.Case.Model.Behavior */ class TreeBehaviorAfterTest extends CakeTestCase { +/** + * @var mixed + */ + public $Tree = null; /** * Whether backup global state for each test method or not diff --git a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorNumberTest.php b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorNumberTest.php index d7d8aadddc..bd2adf43fb 100644 --- a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorNumberTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorNumberTest.php @@ -29,6 +29,10 @@ * @package Cake.Test.Case.Model.Behavior */ class TreeBehaviorNumberTest extends CakeTestCase { +/** + * @var mixed + */ + public $Tree = null; /** * Whether backup global state for each test method or not diff --git a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorScopedTest.php b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorScopedTest.php index 22f3aeda9b..bb88aad176 100644 --- a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorScopedTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorScopedTest.php @@ -29,6 +29,20 @@ * @package Cake.Test.Case.Model.Behavior */ class TreeBehaviorScopedTest extends CakeTestCase { +/** + * @var mixed + */ + public $Ad = null; + +/** + * @var mixed + */ + public $Tree = null; + +/** + * @var mixed + */ + public $TreeTwo = null; /** * Whether backup global state for each test method or not diff --git a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorUuidTest.php b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorUuidTest.php index cfa2559af9..2a9fce72c4 100644 --- a/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorUuidTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/TreeBehaviorUuidTest.php @@ -30,6 +30,10 @@ * @package Cake.Test.Case.Model.Behavior */ class TreeBehaviorUuidTest extends CakeTestCase { +/** + * @var mixed + */ + public $Tree = null; /** * Whether backup global state for each test method or not diff --git a/lib/Cake/Test/Case/Model/CakeSchemaTest.php b/lib/Cake/Test/Case/Model/CakeSchemaTest.php index ac3e5490e7..b375efa7b9 100644 --- a/lib/Cake/Test/Case/Model/CakeSchemaTest.php +++ b/lib/Cake/Test/Case/Model/CakeSchemaTest.php @@ -434,6 +434,10 @@ class SchemaPrefixAuthUser extends CakeTestModel { * @package Cake.Test.Case.Model */ class CakeSchemaTest extends CakeTestCase { +/** + * @var mixed + */ + public $Schema = null; /** * fixtures property diff --git a/lib/Cake/Test/Case/Model/Datasource/CakeSessionTest.php b/lib/Cake/Test/Case/Model/Datasource/CakeSessionTest.php index 98e54a0912..0f316c5609 100644 --- a/lib/Cake/Test/Case/Model/Datasource/CakeSessionTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/CakeSessionTest.php @@ -142,13 +142,13 @@ public function testSessionConfigIniSetting() { 'checkAgent' => false, 'timeout' => 86400, 'ini' => array( - 'session.referer_check' => 'example.com', + 'session.cache_expire' => '123', 'session.use_trans_sid' => false ) )); TestCakeSession::start(); $this->assertEquals('', ini_get('session.use_trans_sid'), 'Ini value is incorrect'); - $this->assertEquals('example.com', ini_get('session.referer_check'), 'Ini value is incorrect'); + $this->assertEquals('123', ini_get('session.cache_expire'), 'Ini value is incorrect'); $this->assertEquals('test', ini_get('session.name'), 'Ini value is incorrect'); } diff --git a/lib/Cake/Test/Case/Model/Datasource/DataSourceTest.php b/lib/Cake/Test/Case/Model/Datasource/DataSourceTest.php index 846be3d87b..855aff5609 100644 --- a/lib/Cake/Test/Case/Model/Datasource/DataSourceTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/DataSourceTest.php @@ -94,6 +94,15 @@ public function calculate(Model $Model, $func, $params = array()) { * @package Cake.Test.Case.Model.Datasource */ class DataSourceTest extends CakeTestCase { +/** + * @var mixed + */ + public $Model = null; + +/** + * @var mixed + */ + public $Source = null; /** * Name of test source diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php index ed44627dae..8e8cb87b35 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php @@ -29,6 +29,30 @@ * @package Cake.Test.Case.Model.Datasource.Database */ class MysqlTest extends CakeTestCase { +/** + * @var mixed + */ + public $Featured2 = null; + +/** + * @var mixed + */ + public $Model = null; + +/** + * @var mixed + */ + protected $_debug = null; + +/** + * @var mixed + */ + public $model = null; + +/** + * @var mixed + */ + public $startTime = null; /** * autoFixtures property @@ -62,11 +86,11 @@ class MysqlTest extends CakeTestCase { */ public function setUp() : void { parent::setUp(); + $this->_debug = Configure::read('debug'); $this->Dbo = ConnectionManager::getDataSource('test'); if (!($this->Dbo instanceof Mysql)) { $this->markTestSkipped('The MySQL extension is not available.'); } - $this->_debug = Configure::read('debug'); Configure::write('debug', 1); $this->model = ClassRegistry::init('MysqlTestModel'); } diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/PostgresTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/PostgresTest.php index ed499e077d..02682e03a8 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/PostgresTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/PostgresTest.php @@ -176,6 +176,10 @@ public function schema($field = false) { * @package Cake.Test.Case.Model.Datasource.Database */ class PostgresTest extends CakeTestCase { +/** + * @var mixed + */ + public $model = null; /** * Do not automatically load fixtures for each test, they will be loaded manually diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php index 0ad014b3a2..f7d93bcb5b 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/SqlserverTest.php @@ -237,6 +237,15 @@ public function fetch() { * @package Cake.Test.Case.Model.Datasource.Database */ class SqlserverTest extends CakeTestCase { +/** + * @var mixed + */ + public $Dbo = null; + +/** + * @var mixed + */ + public $model = null; /** * The Dbo instance to be tested diff --git a/lib/Cake/Test/Case/Model/Datasource/DboSourceTest.php b/lib/Cake/Test/Case/Model/Datasource/DboSourceTest.php index 802cefc085..7561a8ad1a 100644 --- a/lib/Cake/Test/Case/Model/Datasource/DboSourceTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/DboSourceTest.php @@ -161,6 +161,15 @@ public function cacheMethodFilter($method, $key, $value) { * @package Cake.Test.Case.Model.Datasource */ class DboSourceTest extends CakeTestCase { +/** + * @var mixed + */ + public $Model = null; + +/** + * @var mixed + */ + public $testDb = null; /** * autoFixtures property @@ -2151,7 +2160,9 @@ public function testFlushQueryCache() { $this->db->query('SELECT 2'); $property = new ReflectionProperty($this->db, '_queryCache'); - $property->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $property->setAccessible(true); + } $this->assertCount(2, $property->getValue($this->db)); diff --git a/lib/Cake/Test/Case/Model/Datasource/Session/CacheSessionTest.php b/lib/Cake/Test/Case/Model/Datasource/Session/CacheSessionTest.php index d3e1c6738e..017bfc29a5 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Session/CacheSessionTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Session/CacheSessionTest.php @@ -26,6 +26,10 @@ class_exists('CakeSession'); * @package Cake.Test.Case.Model.Datasource.Session */ class CacheSessionTest extends CakeTestCase { +/** + * @var mixed + */ + public $storage = null; protected static $_sessionBackup; diff --git a/lib/Cake/Test/Case/Model/Datasource/Session/DatabaseSessionTest.php b/lib/Cake/Test/Case/Model/Datasource/Session/DatabaseSessionTest.php index 231fa3332f..9f20e2224b 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Session/DatabaseSessionTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Session/DatabaseSessionTest.php @@ -38,6 +38,10 @@ class SessionTestModel extends Model { * @package Cake.Test.Case.Model.Datasource.Session */ class DatabaseSessionTest extends CakeTestCase { +/** + * @var mixed + */ + public $storage = null; protected static $_sessionBackup; diff --git a/lib/Cake/Test/Case/Model/ModelTestBase.php b/lib/Cake/Test/Case/Model/ModelTestBase.php index 30f7efe3c2..77520be605 100644 --- a/lib/Cake/Test/Case/Model/ModelTestBase.php +++ b/lib/Cake/Test/Case/Model/ModelTestBase.php @@ -27,6 +27,10 @@ * @package Cake.Test.Case.Model */ abstract class BaseModelTest extends CakeTestCase { +/** + * @var mixed + */ + public $debug = null; /** * autoFixtures property diff --git a/lib/Cake/Test/Case/Network/CakeRequestTest.php b/lib/Cake/Test/Case/Network/CakeRequestTest.php index 6324d381f4..942637f292 100644 --- a/lib/Cake/Test/Case/Network/CakeRequestTest.php +++ b/lib/Cake/Test/Case/Network/CakeRequestTest.php @@ -58,6 +58,15 @@ public function reConstruct($url = 'some/path', $parseEnvironment = true) { * CakeRequestTest */ class CakeRequestTest extends CakeTestCase { +/** + * @var mixed + */ + protected $_app = null; + +/** + * @var mixed + */ + protected $_case = null; /** * Setup callback diff --git a/lib/Cake/Test/Case/Network/CakeSocketTest.php b/lib/Cake/Test/Case/Network/CakeSocketTest.php index 41651df9c5..166491e6bc 100644 --- a/lib/Cake/Test/Case/Network/CakeSocketTest.php +++ b/lib/Cake/Test/Case/Network/CakeSocketTest.php @@ -24,6 +24,10 @@ * @package Cake.Test.Case.Network */ class CakeSocketTest extends CakeTestCase { +/** + * @var mixed + */ + public $Socket = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php b/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php index 0bd0607fc1..c67cf32693 100644 --- a/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php +++ b/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php @@ -143,6 +143,15 @@ class ExtendTransport { * @package Cake.Test.Case.Network.Email */ class CakeEmailTest extends CakeTestCase { +/** + * @var mixed + */ + public $CakeEmail = null; + +/** + * @var mixed + */ + protected $_configFileExists = null; /** * setUp diff --git a/lib/Cake/Test/Case/Network/Email/DebugTransportTest.php b/lib/Cake/Test/Case/Network/Email/DebugTransportTest.php index 4f53868832..95d042c8d4 100644 --- a/lib/Cake/Test/Case/Network/Email/DebugTransportTest.php +++ b/lib/Cake/Test/Case/Network/Email/DebugTransportTest.php @@ -24,6 +24,10 @@ * Test case */ class DebugTransportTest extends CakeTestCase { +/** + * @var mixed + */ + public $DebugTransport = null; /** * Setup diff --git a/lib/Cake/Test/Case/Network/Email/MailTransportTest.php b/lib/Cake/Test/Case/Network/Email/MailTransportTest.php index 60f218c1f5..d8ace9969b 100644 --- a/lib/Cake/Test/Case/Network/Email/MailTransportTest.php +++ b/lib/Cake/Test/Case/Network/Email/MailTransportTest.php @@ -24,6 +24,10 @@ * Test case */ class MailTransportTest extends CakeTestCase { +/** + * @var mixed + */ + public $MailTransport = null; /** * Setup diff --git a/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php b/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php index 7377e88ec5..8f80234dee 100644 --- a/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php +++ b/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php @@ -61,6 +61,15 @@ public function __call($method, $args) { * Test case */ class SmtpTransportTest extends CakeTestCase { +/** + * @var mixed + */ + public $SmtpTransport = null; + +/** + * @var mixed + */ + public $socket = null; /** * Setup diff --git a/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php b/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php index 928fb0dbd8..36061e7061 100644 --- a/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php +++ b/lib/Cake/Test/Case/Network/Http/HttpResponseTest.php @@ -83,6 +83,10 @@ public function tokenEscapeChars($hex = true, $chars = null) { * @package Cake.Test.Case.Network.Http */ class HttpResponseTest extends CakeTestCase { +/** + * @var mixed + */ + public $HttpResponse = null; /** * This function sets up a HttpResponse diff --git a/lib/Cake/Test/Case/Routing/DispatcherTest.php b/lib/Cake/Test/Case/Routing/DispatcherTest.php index 08d45e46f8..ac4670dad3 100644 --- a/lib/Cake/Test/Case/Routing/DispatcherTest.php +++ b/lib/Cake/Test/Case/Routing/DispatcherTest.php @@ -45,6 +45,11 @@ protected function _sendHeader($name, $value = null) { */ class TestDispatcher extends Dispatcher { +/** + * @var mixed + */ + public $base = null; + /** * Controller instance, made publicly available for testing * @@ -537,6 +542,40 @@ public function afterDispatch(CakeEvent $event) { * @package Cake.Test.Case.Routing */ class DispatcherTest extends CakeTestCase { +/** + * @var mixed + */ + protected $_app = null; + +/** + * @var mixed + */ + protected $_cache = null; + +/** + * @var mixed + */ + protected $_debug = null; + +/** + * @var mixed + */ + protected $_files = null; + +/** + * @var mixed + */ + protected $_get = null; + +/** + * @var mixed + */ + protected $_post = null; + +/** + * @var mixed + */ + protected $_server = null; /** * setUp method diff --git a/lib/Cake/Test/Case/TestSuite/CakeTestFixtureTest.php b/lib/Cake/Test/Case/TestSuite/CakeTestFixtureTest.php index e501b50ac2..fa00a95376 100644 --- a/lib/Cake/Test/Case/TestSuite/CakeTestFixtureTest.php +++ b/lib/Cake/Test/Case/TestSuite/CakeTestFixtureTest.php @@ -222,6 +222,20 @@ class FixturePrefixTest extends Model { * @package Cake.Test.Case.TestSuite */ class CakeTestFixtureTest extends CakeTestCase { +/** + * @var mixed + */ + public $insertMulti = null; + +/** + * @var mixed + */ + protected $_backupConfig = null; + +/** + * @var mixed + */ + public $criticDb = null; /** * setUp method diff --git a/lib/Cake/Test/Case/TestSuite/ControllerTestCaseTest.php b/lib/Cake/Test/Case/TestSuite/ControllerTestCaseTest.php index a0a7f57e97..597c16487c 100644 --- a/lib/Cake/Test/Case/TestSuite/ControllerTestCaseTest.php +++ b/lib/Cake/Test/Case/TestSuite/ControllerTestCaseTest.php @@ -112,6 +112,10 @@ class ControllerTestCaseTestController extends AppController { * @package Cake.Test.Case.TestSuite */ class ControllerTestCaseTest extends CakeTestCase { +/** + * @var mixed + */ + public $Case = null; /** * fixtures property diff --git a/lib/Cake/Test/Case/TestSuite/Fixture/CakeFixtureManagerTest.php b/lib/Cake/Test/Case/TestSuite/Fixture/CakeFixtureManagerTest.php index 433065bab4..e908274899 100644 --- a/lib/Cake/Test/Case/TestSuite/Fixture/CakeFixtureManagerTest.php +++ b/lib/Cake/Test/Case/TestSuite/Fixture/CakeFixtureManagerTest.php @@ -63,7 +63,9 @@ public function testLoadTruncatesTable() { $fixtureManagerReflection = new ReflectionClass($fixtureManager); $loadedProperty = $fixtureManagerReflection->getProperty('_loaded'); - $loadedProperty->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $loadedProperty->setAccessible(true); + } $loadedProperty->setValue($fixtureManager, array('core.uuid' => $MockFixture)); $TestCase = $this->getMock('CakeTestCase'); @@ -90,7 +92,9 @@ public function testLoadSingleTruncatesTable() { $fixtureManagerReflection = new ReflectionClass($fixtureManager); $fixtureMapProperty = $fixtureManagerReflection->getProperty('_fixtureMap'); - $fixtureMapProperty->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $fixtureMapProperty->setAccessible(true); + } $fixtureMapProperty->setValue($fixtureManager, array('UuidFixture' => $MockFixture)); $dboMethods = array_diff(get_class_methods('DboSource'), array('enabled')); diff --git a/lib/Cake/Test/Case/TestSuite/HtmlCoverageReportTest.php b/lib/Cake/Test/Case/TestSuite/HtmlCoverageReportTest.php index d8299e0ed3..4be105e2bb 100644 --- a/lib/Cake/Test/Case/TestSuite/HtmlCoverageReportTest.php +++ b/lib/Cake/Test/Case/TestSuite/HtmlCoverageReportTest.php @@ -27,6 +27,10 @@ * @package Cake.Test.Case.TestSuite */ class HtmlCoverageReportTest extends CakeTestCase { +/** + * @var mixed + */ + public $Coverage = null; /** * setUp diff --git a/lib/Cake/Test/Case/TestSuite/Stub/ConsoleOutputStubTest.php b/lib/Cake/Test/Case/TestSuite/Stub/ConsoleOutputStubTest.php index 95e90f971c..10202cb7b1 100644 --- a/lib/Cake/Test/Case/TestSuite/Stub/ConsoleOutputStubTest.php +++ b/lib/Cake/Test/Case/TestSuite/Stub/ConsoleOutputStubTest.php @@ -18,6 +18,10 @@ * ConsoleOutputStub test */ class ConsoleOutputStubTest extends CakeTestCase { +/** + * @var mixed + */ + public $stub = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Utility/CakeNumberTest.php b/lib/Cake/Test/Case/Utility/CakeNumberTest.php index bf6576821e..29349a84fa 100644 --- a/lib/Cake/Test/Case/Utility/CakeNumberTest.php +++ b/lib/Cake/Test/Case/Utility/CakeNumberTest.php @@ -25,6 +25,10 @@ * @package Cake.Test.Case.Utility */ class CakeNumberTest extends CakeTestCase { +/** + * @var mixed + */ + public $Number = null; /** * setUp method diff --git a/lib/Cake/Test/Case/Utility/CakeTextTest.php b/lib/Cake/Test/Case/Utility/CakeTextTest.php index 185ade4e7a..e4a8aae1cd 100644 --- a/lib/Cake/Test/Case/Utility/CakeTextTest.php +++ b/lib/Cake/Test/Case/Utility/CakeTextTest.php @@ -25,6 +25,10 @@ * @coversDefaultClass CakeText */ class CakeTextTest extends CakeTestCase { +/** + * @var mixed + */ + public $Text = null; /** * Setup object under test diff --git a/lib/Cake/Test/Case/Utility/CakeTimeTest.php b/lib/Cake/Test/Case/Utility/CakeTimeTest.php index 3ff7fb1fdb..613546efe6 100644 --- a/lib/Cake/Test/Case/Utility/CakeTimeTest.php +++ b/lib/Cake/Test/Case/Utility/CakeTimeTest.php @@ -24,6 +24,10 @@ * @package Cake.Test.Case.View.Helper */ class CakeTimeTest extends CakeTestCase { +/** + * @var mixed + */ + public $Time = null; /** * Default system timezone identifier @@ -559,7 +563,7 @@ public function testToServer() { $expected = $date->format('Y-m-d H:i:s'); $this->assertEquals($expected, $result); - $date = new DateTime(null, new DateTimeZone('America/New_York')); + $date = new DateTime('now', new DateTimeZone('America/New_York')); $result = $this->Time->toServer($date, 'Pacific/Tahiti'); $date->setTimezone(new DateTimeZone(date_default_timezone_get())); $expected = $date->format('Y-m-d H:i:s'); diff --git a/lib/Cake/Test/Case/Utility/DebuggerTest.php b/lib/Cake/Test/Case/Utility/DebuggerTest.php index a14fd8110b..466fb80a0b 100644 --- a/lib/Cake/Test/Case/Utility/DebuggerTest.php +++ b/lib/Cake/Test/Case/Utility/DebuggerTest.php @@ -86,7 +86,8 @@ public function testExcerpt() { $this->assertTrue(is_array($result)); $this->assertEquals(4, count($result)); - $pattern = '/.*?.*?<\?php/'; + // PHP 8.3 changed highlight_string() output to `` and hexadecimal colors + $pattern = '/.*?.*?<\?php/'; $this->assertMatchesRegularExpression($pattern, $result[0]); $result = Debugger::excerpt(__FILE__, 11, 2); diff --git a/lib/Cake/Test/Case/Utility/ObjectCollectionTest.php b/lib/Cake/Test/Case/Utility/ObjectCollectionTest.php index dc0c9ec925..4a414d9762 100644 --- a/lib/Cake/Test/Case/Utility/ObjectCollectionTest.php +++ b/lib/Cake/Test/Case/Utility/ObjectCollectionTest.php @@ -23,6 +23,15 @@ * A generic object class */ class GenericObject { +/** + * @var mixed + */ + protected $_Collection = null; + +/** + * @var mixed + */ + public $settings = null; /** * Constructor @@ -128,6 +137,25 @@ public function setObject($name, $object, $settings = array()) { } class ObjectCollectionTest extends CakeTestCase { +/** + * @var mixed + */ + public $FirstGenericObject = null; + +/** + * @var mixed + */ + public $Objects = null; + +/** + * @var mixed + */ + public $SecondGenericObject = null; + +/** + * @var mixed + */ + public $ThirdGenericObject = null; /** * setUp diff --git a/lib/Cake/Test/Case/Utility/ValidationTest.php b/lib/Cake/Test/Case/Utility/ValidationTest.php index 37339eff82..e39504391e 100644 --- a/lib/Cake/Test/Case/Utility/ValidationTest.php +++ b/lib/Cake/Test/Case/Utility/ValidationTest.php @@ -113,6 +113,15 @@ protected static function _isUploadedFile($path) { * @package Cake.Test.Case.Utility */ class ValidationTest extends CakeTestCase { +/** + * @var mixed + */ + protected $_appEncoding = null; + +/** + * @var mixed + */ + protected $_appLocale = null; /** * setUp method @@ -175,7 +184,7 @@ public function testNotBlankISO88591AppEncoding() { $this->assertTrue(Validation::notBlank('fooo' . chr(243) . 'blabla')); $this->assertTrue(Validation::notBlank('abçďĕʑʘπй')); $this->assertTrue(Validation::notBlank('José')); - $this->assertTrue(Validation::notBlank(utf8_decode('José'))); + $this->assertTrue(Validation::notBlank(mb_convert_encoding('José', 'ISO-8859-1', 'UTF-8'))); $this->assertFalse(Validation::notBlank("\t ")); $this->assertFalse(Validation::notBlank("")); } @@ -1523,7 +1532,7 @@ public function testDecimalWithPlacesNull() { $this->assertTrue(Validation::decimal('-1234', null)); $this->assertTrue(Validation::decimal('+1234', null)); $this->assertTrue(Validation::decimal((float)1234, null)); - $this->assertTrue(Validation::decimal((double)1234, null)); + $this->assertTrue(Validation::decimal((float)1234, null)); $this->assertTrue(Validation::decimal((int)1234, null)); $this->assertFalse(Validation::decimal('', null)); @@ -1553,7 +1562,7 @@ public function testDecimalWithPlacesTrue() { $this->assertTrue(Validation::decimal(.01, true)); $this->assertTrue(Validation::decimal('.01', true)); $this->assertTrue(Validation::decimal((float)1234, true)); - $this->assertTrue(Validation::decimal((double)1234, true)); + $this->assertTrue(Validation::decimal((float)1234, true)); $this->assertFalse(Validation::decimal('', true)); $this->assertFalse(Validation::decimal('string', true)); @@ -1593,7 +1602,7 @@ public function testDecimalWithPlacesNumeric() { $this->assertFalse(Validation::decimal(.0, 1)); $this->assertFalse(Validation::decimal(.00, 2)); $this->assertFalse(Validation::decimal((float)1234, 1)); - $this->assertFalse(Validation::decimal((double)1234, 1)); + $this->assertFalse(Validation::decimal((float)1234, 1)); $this->assertFalse(Validation::decimal((int)1234, 1)); $this->assertFalse(Validation::decimal('1234.5678', '3')); $this->assertFalse(Validation::decimal(1234.5678, 3)); diff --git a/lib/Cake/Test/Case/Utility/XmlTest.php b/lib/Cake/Test/Case/Utility/XmlTest.php index 1aacb51fcf..3f04a5b0fd 100644 --- a/lib/Cake/Test/Case/Utility/XmlTest.php +++ b/lib/Cake/Test/Case/Utility/XmlTest.php @@ -78,6 +78,10 @@ class XmlUser extends CakeTestModel { * @package Cake.Test.Case.Utility */ class XmlTest extends CakeTestCase { +/** + * @var mixed + */ + protected $_appEncoding = null; /** * autoFixtures property diff --git a/lib/Cake/Test/Case/View/Helper/CacheHelperTest.php b/lib/Cake/Test/Case/View/Helper/CacheHelperTest.php index 1d276f8be1..41c647c785 100644 --- a/lib/Cake/Test/Case/View/Helper/CacheHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/CacheHelperTest.php @@ -58,6 +58,15 @@ public function cache_parsing() { * @package Cake.Test.Case.View.Helper */ class CacheHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $Cache = null; + +/** + * @var mixed + */ + public $Controller = null; /** * Checks if TMP/views is writable, and skips the case if it is not. diff --git a/lib/Cake/Test/Case/View/Helper/FlashHelperTest.php b/lib/Cake/Test/Case/View/Helper/FlashHelperTest.php index f01d69f760..7b32fed3b3 100644 --- a/lib/Cake/Test/Case/View/Helper/FlashHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/FlashHelperTest.php @@ -28,6 +28,15 @@ * @package Cake.Test.Case.View.Helper */ class FlashHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $Flash = null; + +/** + * @var mixed + */ + public $View = null; /** * setupBeforeClass method diff --git a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php index 6024e39fed..0ad11df506 100644 --- a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php @@ -505,6 +505,40 @@ class TestMail extends CakeTestModel { * @property FormHelper $Form */ class FormHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $Controller = null; + +/** + * @var mixed + */ + public $Form = null; + +/** + * @var mixed + */ + public $UserForm = null; + +/** + * @var mixed + */ + public $ValidateUser = null; + +/** + * @var mixed + */ + public $View = null; + +/** + * @var mixed + */ + public $dateRegex = null; + +/** + * @var mixed + */ + public $oldSalt = null; /** * Fixtures to be used @@ -8791,7 +8825,9 @@ public function testPostLinkSecurityHashInline() { $this->assertStringContainsString($hash, $result, 'Should contain the correct hash.'); $property = new ReflectionProperty($this->Form, '_lastAction'); - $property->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $property->setAccessible(true); + } $this->assertSame( '/basedir/posts/add', @@ -11217,7 +11253,9 @@ public function testLastActionWithNamedNumeric() { $this->Form->create('User'); $property = new ReflectionProperty($this->Form, '_lastAction'); - $property->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $property->setAccessible(true); + } $this->assertSame($here, $property->getValue($this->Form), "_lastAction shouldn't be empty."); } @@ -11236,7 +11274,9 @@ public function testLastActionWithNamedString() { $this->Form->create('User'); $property = new ReflectionProperty($this->Form, '_lastAction'); - $property->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $property->setAccessible(true); + } $this->assertSame($here, $property->getValue($this->Form), "_lastAction shouldn't be empty."); } diff --git a/lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php b/lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php index 755ad27d55..5f65508fb6 100644 --- a/lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php @@ -116,6 +116,10 @@ class Html5TestHelper extends TestHtmlHelper { * @package Cake.Test.Case.View.Helper */ class HtmlHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $View = null; /** * Regexp for CDATA start block diff --git a/lib/Cake/Test/Case/View/Helper/JqueryEngineHelperTest.php b/lib/Cake/Test/Case/View/Helper/JqueryEngineHelperTest.php index 671822e6da..baf2151b14 100644 --- a/lib/Cake/Test/Case/View/Helper/JqueryEngineHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/JqueryEngineHelperTest.php @@ -26,6 +26,15 @@ * @package Cake.Test.Case.View.Helper */ class JqueryEngineHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $Jquery = null; + +/** + * @var mixed + */ + public $View = null; /** * setUp diff --git a/lib/Cake/Test/Case/View/Helper/JsHelperTest.php b/lib/Cake/Test/Case/View/Helper/JsHelperTest.php index bd62b79912..a0f8c4ff3c 100644 --- a/lib/Cake/Test/Case/View/Helper/JsHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/JsHelperTest.php @@ -117,6 +117,15 @@ public function serializeForm($options = array()) { * @package Cake.Test.Case.View.Helper */ class JsHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $Js = null; + +/** + * @var mixed + */ + public $View = null; /** * Regexp for CDATA start block @@ -743,6 +752,15 @@ public function testSetVarsAtTopOfBufferedScripts() { * @package Cake.Test.Case.View.Helper */ class JsBaseEngineTest extends CakeTestCase { +/** + * @var mixed + */ + public $JsEngine = null; + +/** + * @var mixed + */ + public $View = null; /** * setUp method diff --git a/lib/Cake/Test/Case/View/Helper/MootoolsEngineHelperTest.php b/lib/Cake/Test/Case/View/Helper/MootoolsEngineHelperTest.php index 99cc49fd78..a6655bd9f2 100644 --- a/lib/Cake/Test/Case/View/Helper/MootoolsEngineHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/MootoolsEngineHelperTest.php @@ -26,6 +26,15 @@ * @package Cake.Test.Case.View.Helper */ class MootoolsEngineHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $Moo = null; + +/** + * @var mixed + */ + public $View = null; /** * setUp diff --git a/lib/Cake/Test/Case/View/Helper/NumberHelperTest.php b/lib/Cake/Test/Case/View/Helper/NumberHelperTest.php index f5074258e1..3066c0edd8 100644 --- a/lib/Cake/Test/Case/View/Helper/NumberHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/NumberHelperTest.php @@ -46,6 +46,10 @@ class CakeNumberMock { * @package Cake.Test.Case.View.Helper */ class NumberHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $View = null; /** * setUp method diff --git a/lib/Cake/Test/Case/View/Helper/PaginatorHelperTest.php b/lib/Cake/Test/Case/View/Helper/PaginatorHelperTest.php index ea5a02ca84..b0f98915ed 100644 --- a/lib/Cake/Test/Case/View/Helper/PaginatorHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/PaginatorHelperTest.php @@ -32,6 +32,15 @@ * @package Cake.Test.Case.View.Helper */ class PaginatorHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $Paginator = null; + +/** + * @var mixed + */ + public $View = null; /** * setUp method diff --git a/lib/Cake/Test/Case/View/Helper/PrototypeEngineHelperTest.php b/lib/Cake/Test/Case/View/Helper/PrototypeEngineHelperTest.php index 3808cfd414..b5194ba833 100644 --- a/lib/Cake/Test/Case/View/Helper/PrototypeEngineHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/PrototypeEngineHelperTest.php @@ -26,6 +26,15 @@ * @package Cake.Test.Case.View.Helper */ class PrototypeEngineHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $Proto = null; + +/** + * @var mixed + */ + public $View = null; /** * setUp diff --git a/lib/Cake/Test/Case/View/Helper/RssHelperTest.php b/lib/Cake/Test/Case/View/Helper/RssHelperTest.php index f8f1b6f75c..3a144f65d1 100644 --- a/lib/Cake/Test/Case/View/Helper/RssHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/RssHelperTest.php @@ -27,6 +27,15 @@ * @package Cake.Test.Case.View.Helper */ class RssHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $Rss = null; + +/** + * @var mixed + */ + public $View = null; /** * setUp method diff --git a/lib/Cake/Test/Case/View/Helper/SessionHelperTest.php b/lib/Cake/Test/Case/View/Helper/SessionHelperTest.php index c998e7c6ff..f06615b886 100644 --- a/lib/Cake/Test/Case/View/Helper/SessionHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/SessionHelperTest.php @@ -26,6 +26,15 @@ * @package Cake.Test.Case.View.Helper */ class SessionHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $Session = null; + +/** + * @var mixed + */ + public $View = null; /** * setUp method diff --git a/lib/Cake/Test/Case/View/Helper/TextHelperTest.php b/lib/Cake/Test/Case/View/Helper/TextHelperTest.php index b73a882e65..13f6292679 100644 --- a/lib/Cake/Test/Case/View/Helper/TextHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/TextHelperTest.php @@ -50,6 +50,15 @@ class CakeTextMock { * @package Cake.Test.Case.View.Helper */ class TextHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $Text = null; + +/** + * @var mixed + */ + public $View = null; /** * setUp method diff --git a/lib/Cake/Test/Case/View/Helper/TimeHelperTest.php b/lib/Cake/Test/Case/View/Helper/TimeHelperTest.php index 109789d2f4..81f0ae657b 100644 --- a/lib/Cake/Test/Case/View/Helper/TimeHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/TimeHelperTest.php @@ -51,6 +51,10 @@ class CakeTimeMock { * @package Cake.Test.Case.View.Helper */ class TimeHelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $View = null; public $Time = null; diff --git a/lib/Cake/Test/Case/View/HelperCollectionTest.php b/lib/Cake/Test/Case/View/HelperCollectionTest.php index bdcaf4c781..f7cc528933 100644 --- a/lib/Cake/Test/Case/View/HelperCollectionTest.php +++ b/lib/Cake/Test/Case/View/HelperCollectionTest.php @@ -32,6 +32,15 @@ class HtmlAliasHelper extends HtmlHelper { * @package Cake.Test.Case.View */ class HelperCollectionTest extends CakeTestCase { +/** + * @var mixed + */ + public $Helpers = null; + +/** + * @var mixed + */ + public $View = null; /** * setUp diff --git a/lib/Cake/Test/Case/View/HelperTest.php b/lib/Cake/Test/Case/View/HelperTest.php index d3f4b1b77f..371a361cb8 100644 --- a/lib/Cake/Test/Case/View/HelperTest.php +++ b/lib/Cake/Test/Case/View/HelperTest.php @@ -195,6 +195,15 @@ public function parseAttributes($options, $exclude = null, $insertBefore = ' ', * @package Cake.Test.Case.View */ class HelperTest extends CakeTestCase { +/** + * @var mixed + */ + public $Helper = null; + +/** + * @var mixed + */ + public $View = null; /** * setUp method diff --git a/lib/Cake/Test/Case/View/MediaViewTest.php b/lib/Cake/Test/Case/View/MediaViewTest.php index 1ba2af21e5..93669a20e9 100644 --- a/lib/Cake/Test/Case/View/MediaViewTest.php +++ b/lib/Cake/Test/Case/View/MediaViewTest.php @@ -26,6 +26,10 @@ * @package Cake.Test.Case.View */ class MediaViewTest extends CakeTestCase { +/** + * @var mixed + */ + public $MediaView = null; /** * setUp method diff --git a/lib/Cake/Test/Case/View/ScaffoldViewTest.php b/lib/Cake/Test/Case/View/ScaffoldViewTest.php index 9b118c9c12..13aa805110 100644 --- a/lib/Cake/Test/Case/View/ScaffoldViewTest.php +++ b/lib/Cake/Test/Case/View/ScaffoldViewTest.php @@ -70,6 +70,15 @@ class ScaffoldViewMockController extends Controller { * @package Cake.Test.Case.Controller */ class ScaffoldViewTest extends CakeTestCase { +/** + * @var mixed + */ + public $Controller = null; + +/** + * @var mixed + */ + public $request = null; /** * fixtures property diff --git a/lib/Cake/Test/Case/View/ThemeViewTest.php b/lib/Cake/Test/Case/View/ThemeViewTest.php index bfa7ddae13..381e36deac 100644 --- a/lib/Cake/Test/Case/View/ThemeViewTest.php +++ b/lib/Cake/Test/Case/View/ThemeViewTest.php @@ -97,6 +97,20 @@ public function getLayoutFileName($name = null) { * @package Cake.Test.Case.View */ class ThemeViewTest extends CakeTestCase { +/** + * @var mixed + */ + public $Controller = null; + +/** + * @var mixed + */ + public $PostsController = null; + +/** + * @var mixed + */ + public $ThemeView = null; /** * setUp method diff --git a/lib/Cake/Test/Case/View/ViewTest.php b/lib/Cake/Test/Case/View/ViewTest.php index 2b0f808e5a..a5369f592a 100644 --- a/lib/Cake/Test/Case/View/ViewTest.php +++ b/lib/Cake/Test/Case/View/ViewTest.php @@ -301,6 +301,35 @@ public function afterRender($event) { * @package Cake.Test.Case.View */ class ViewTest extends CakeTestCase { +/** + * @var mixed + */ + public $Controller = null; + +/** + * @var mixed + */ + public $PostsController = null; + +/** + * @var mixed + */ + public $ThemeController = null; + +/** + * @var mixed + */ + public $ThemePostsController = null; + +/** + * @var mixed + */ + public $ThemeView = null; + +/** + * @var mixed + */ + public $View = null; /** * Fixtures used in this test. diff --git a/lib/Cake/TestSuite/CakeTestCase.php b/lib/Cake/TestSuite/CakeTestCase.php index 3d8acaca37..45ff451a64 100644 --- a/lib/Cake/TestSuite/CakeTestCase.php +++ b/lib/Cake/TestSuite/CakeTestCase.php @@ -33,6 +33,13 @@ abstract class CakeTestCase extends \PHPUnit\Framework\TestCase { */ public $fixtureManager = null; +/** + * The test datasource, assigned by CakeFixtureManager::fixturize() + * + * @var DataSource|null + */ + public $db = null; + /** * By default, all fixtures attached to this class will be truncated and reloaded after each test. * Set this to false to handle manually diff --git a/lib/Cake/TestSuite/ControllerTestCase.php b/lib/Cake/TestSuite/ControllerTestCase.php index 8d8f7bd3a3..562cd0669e 100644 --- a/lib/Cake/TestSuite/ControllerTestCase.php +++ b/lib/Cake/TestSuite/ControllerTestCase.php @@ -38,6 +38,13 @@ class ControllerTestDispatcher extends Dispatcher { */ public $testController = null; +/** + * The response object used by the dispatched request + * + * @var CakeResponse + */ + public $response = null; + /** * Use custom routes during tests * diff --git a/lib/Cake/TestSuite/Fixture/CakeFixtureManager.php b/lib/Cake/TestSuite/Fixture/CakeFixtureManager.php index f671713b1e..41cb25210f 100644 --- a/lib/Cake/TestSuite/Fixture/CakeFixtureManager.php +++ b/lib/Cake/TestSuite/Fixture/CakeFixtureManager.php @@ -54,6 +54,13 @@ class CakeFixtureManager { */ protected $_fixtureMap = array(); +/** + * Test classes whose fixtures have already been loaded, indexed by class name + * + * @var array + */ + protected $_processed = array(); + /** * Inspects the test to look for unloaded fixtures and loads them * diff --git a/lib/Cake/TestSuite/Fixture/CakeTestFixture.php b/lib/Cake/TestSuite/Fixture/CakeTestFixture.php index 0bf9d1e8d8..920250c4a0 100644 --- a/lib/Cake/TestSuite/Fixture/CakeTestFixture.php +++ b/lib/Cake/TestSuite/Fixture/CakeTestFixture.php @@ -90,6 +90,21 @@ class CakeTestFixture { */ public $canUseMemory = true; +/** + * Import configuration (table or model to import the schema and/or records from). + * Defined by fixtures that import their definition instead of declaring $fields. + * + * @var array|null + */ + public $import = null; + +/** + * Schema object used to create/drop the fixture table. + * + * @var CakeSchema + */ + public $Schema = null; + /** * Instantiate the fixture. * diff --git a/lib/Cake/Utility/CakeNumber.php b/lib/Cake/Utility/CakeNumber.php index 7aa05d5216..ff8e45e595 100644 --- a/lib/Cake/Utility/CakeNumber.php +++ b/lib/Cake/Utility/CakeNumber.php @@ -269,7 +269,7 @@ protected static function _numberFormat($value, $places = 0, $decimals = '.', $t static::$_numberFormatSupport = version_compare(PHP_VERSION, '5.4.0', '>='); } if (static::$_numberFormatSupport) { - return number_format($value, $places, $decimals, $thousands); + return number_format($value, (int)$places, (string)$decimals, (string)$thousands); } $value = number_format($value, $places, '.', ''); $after = ''; diff --git a/lib/Cake/Utility/CakeText.php b/lib/Cake/Utility/CakeText.php index 66f7d87746..7519af4214 100644 --- a/lib/Cake/Utility/CakeText.php +++ b/lib/Cake/Utility/CakeText.php @@ -233,9 +233,9 @@ public static function cleanInsert($str, $options) { ), $clean); $kleenex = sprintf( '/[\s]*[a-z]+=(")(%s%s%s[\s]*)+\\1/i', - preg_quote($options['before'], '/'), + preg_quote((string)$options['before'], '/'), $clean['word'], - preg_quote($options['after'], '/') + preg_quote((string)$options['after'], '/') ); $str = preg_replace($kleenex, $clean['replacement'], $str); if ($clean['andText']) { @@ -252,14 +252,14 @@ public static function cleanInsert($str, $options) { $kleenex = sprintf( '/(%s%s%s%s|%s%s%s%s)/', - preg_quote($options['before'], '/'), + preg_quote((string)$options['before'], '/'), $clean['word'], - preg_quote($options['after'], '/'), + preg_quote((string)$options['after'], '/'), $clean['gap'], $clean['gap'], - preg_quote($options['before'], '/'), + preg_quote((string)$options['before'], '/'), $clean['word'], - preg_quote($options['after'], '/') + preg_quote((string)$options['after'], '/') ); $str = preg_replace($kleenex, $clean['replacement'], $str); break; @@ -657,7 +657,7 @@ public static function toList($list, $and = null, $separator = ', ') { $and = __d('cake', 'and'); } if (count($list) > 1) { - return implode($separator, array_slice($list, null, -1)) . ' ' . $and . ' ' . array_pop($list); + return implode($separator, array_slice($list, 0, -1)) . ' ' . $and . ' ' . array_pop($list); } return array_pop($list); diff --git a/lib/Cake/Utility/Debugger.php b/lib/Cake/Utility/Debugger.php index e71e0ea5ed..42785fc451 100644 --- a/lib/Cake/Utility/Debugger.php +++ b/lib/Cake/Utility/Debugger.php @@ -592,7 +592,9 @@ protected static function _object($var, $depth, $indent) { foreach ($filters as $filter => $visibility) { $reflectionProperties = $ref->getProperties($filter); foreach ($reflectionProperties as $reflectionProperty) { - $reflectionProperty->setAccessible(true); + if (PHP_VERSION_ID < 80100) { + $reflectionProperty->setAccessible(true); + } if (!$reflectionProperty->isInitialized($var)) { continue; diff --git a/lib/Cake/Utility/File.php b/lib/Cake/Utility/File.php index 6c4bed1070..5de5846a99 100644 --- a/lib/Cake/Utility/File.php +++ b/lib/Cake/Utility/File.php @@ -394,7 +394,7 @@ public function md5($maxsize = 5) { public function pwd() { if ($this->path === null) { $dir = $this->Folder->pwd(); - if (is_dir($dir)) { + if ($dir !== null && is_dir($dir)) { $this->path = $this->Folder->slashTerm($dir) . $this->name; } } @@ -409,7 +409,7 @@ public function pwd() { */ public function exists() { $this->clearStatCache(); - return (file_exists($this->path) && is_file($this->path)); + return ($this->path !== null && file_exists($this->path) && is_file($this->path)); } /** @@ -580,7 +580,7 @@ public function mime() { * @return void */ public function clearStatCache($all = false) { - if ($all === false && version_compare(PHP_VERSION, '5.3.0') >= 0) { + if ($all === false && $this->path !== null) { return clearstatcache(true, $this->path); } diff --git a/lib/Cake/Utility/Hash.php b/lib/Cake/Utility/Hash.php index b2396f5d5d..cc8d53b78f 100644 --- a/lib/Cake/Utility/Hash.php +++ b/lib/Cake/Utility/Hash.php @@ -230,7 +230,7 @@ protected static function _matches(array $data, $selector) { // Pattern matches and other operators. if ($op === '=' && $val && $val[0] === '/') { - if (!preg_match($val, $prop)) { + if (!preg_match($val, (string)$prop)) { return false; } } elseif (($op === '=' && $prop != $val) || diff --git a/lib/Cake/Utility/Inflector.php b/lib/Cake/Utility/Inflector.php index a14fbb1e89..cfff6cec43 100644 --- a/lib/Cake/Utility/Inflector.php +++ b/lib/Cake/Utility/Inflector.php @@ -576,7 +576,7 @@ public static function slug($string, $replacement = '_') { ); $map = static::$_transliteration + $merge; - return preg_replace(array_keys($map), array_values($map), $string); + return preg_replace(array_keys($map), array_values($map), (string)$string); } } diff --git a/lib/Cake/Utility/Security.php b/lib/Cake/Utility/Security.php index 322b29d9e0..18118629d4 100644 --- a/lib/Cake/Utility/Security.php +++ b/lib/Cake/Utility/Security.php @@ -109,7 +109,7 @@ public static function hash($string, $type = null, $salt = false) { if (empty($type)) { $type = static::$hashType; } - $type = strtolower($type); + $type = strtolower((string)$type); if ($type === 'blowfish') { return static::_crypt($string, $salt); @@ -128,14 +128,7 @@ public static function hash($string, $type = null, $salt = false) { $type = 'sha256'; } - if ($type === 'sha256' && function_exists('mhash')) { - return bin2hex(mhash(MHASH_SHA256, $string)); - } - - if (function_exists('hash')) { - return hash($type, $string); - } - return md5($string); + return hash($type, $string); } /** @@ -202,6 +195,36 @@ public static function randomBytes($length) { return substr($bytes, 0, $length); } +/** + * Converts the configured cipher seed into an integer. + * + * `Security.cipherSeed` is traditionally a numeric string that is larger than PHP_INT_MAX. + * Before PHP 8.5 `(int)(float)$seed` silently wrapped the value around modulo 2^64; PHP 8.5 + * deprecates that cast. This reproduces the same wrap-around so that existing seeds keep + * producing the same cipher output. + * + * @param mixed $seed The configured seed. + * @return int Seed usable with srand(). + */ + protected static function _cipherSeed($seed) { + $seed = (float)$seed; + if (!is_finite($seed)) { + return 0; + } + if ($seed >= (float)PHP_INT_MIN && $seed < (float)PHP_INT_MAX) { + return (int)$seed; + } + $twoPow64 = 18446744073709551616.0; + $seed = fmod($seed, $twoPow64); + if ($seed < 0) { + $seed += $twoPow64; + } + if ($seed >= 9223372036854775808.0) { + $seed -= $twoPow64; + } + return (int)$seed; + } + /** * Runs $text through a XOR cipher. * @@ -223,8 +246,9 @@ public static function cipher($text, $key) { return ''; } - srand((int)(float)Configure::read('Security.cipherSeed')); + srand(static::_cipherSeed(Configure::read('Security.cipherSeed'))); $out = ''; + $text = (string)$text; $keyLength = strlen($key); for ($i = 0, $textLength = strlen($text); $i < $textLength; $i++) { $j = ord(substr($key, $i % $keyLength, 1)); @@ -352,7 +376,7 @@ public static function encrypt($plain, $key, $hmacSalt = null) { // Generate the encryption and hmac key. $key = substr(hash('sha256', $key . $hmacSalt), 0, 32); - if (Configure::read('Security.useOpenSsl')) { + if (Configure::read('Security.useOpenSsl') || !function_exists('mcrypt_encrypt')) { $method = 'AES-256-CBC'; $ivSize = openssl_cipher_iv_length($method); $iv = openssl_random_pseudo_bytes($ivSize); @@ -419,7 +443,7 @@ public static function decrypt($cipher, $key, $hmacSalt = null) { return false; } - if (Configure::read('Security.useOpenSsl')) { + if (Configure::read('Security.useOpenSsl') || !function_exists('mcrypt_decrypt')) { $method = 'AES-256-CBC'; $ivSize = openssl_cipher_iv_length($method); $iv = substr($cipher, 0, $ivSize); diff --git a/lib/Cake/Utility/Set.php b/lib/Cake/Utility/Set.php index ac811da75a..53f8683b92 100644 --- a/lib/Cake/Utility/Set.php +++ b/lib/Cake/Utility/Set.php @@ -390,7 +390,7 @@ public static function extract($path, $data = null, $options = array()) { if (!is_numeric($key)) { $ctext[] = $token; $tok = array_shift($tokens); - if (isset($items[$tok])) { + if ($tok !== null && isset($items[$tok])) { $ctext[] = $tok; $item = $items[$tok]; $matches[] = array( @@ -501,7 +501,7 @@ public static function matches($conditions, $data = array(), $i = null, $length $val = $data[$key]; if ($op === '=' && $expected && $expected[0] === '/') { - return preg_match($expected, $val); + return preg_match($expected, (string)$val); } if ($op === '=' && $val != $expected) { return false; diff --git a/lib/Cake/Utility/Validation.php b/lib/Cake/Utility/Validation.php index 95a960ce0a..ceaadf4d05 100644 --- a/lib/Cake/Utility/Validation.php +++ b/lib/Cake/Utility/Validation.php @@ -229,7 +229,7 @@ public static function comparison($check1, $operator = null, $check2 = null) { if ((float)$check1 != $check1) { return false; } - $operator = str_replace(array(' ', "\t", "\n", "\r", "\0", "\x0B"), '', strtolower($operator)); + $operator = str_replace(array(' ', "\t", "\n", "\r", "\0", "\x0B"), '', strtolower((string)$operator)); switch ($operator) { case 'isgreater': diff --git a/lib/Cake/Utility/Xml.php b/lib/Cake/Utility/Xml.php index 33f93f944b..2808f13a28 100644 --- a/lib/Cake/Utility/Xml.php +++ b/lib/Cake/Utility/Xml.php @@ -76,7 +76,8 @@ class Xml { * * - `return` Can be 'simplexml' to return object of SimpleXMLElement or 'domdocument' to return DOMDocument. * - `loadEntities` Defaults to false. Set to true to enable loading of ` array('redirect' => 10))); $response = $socket->get($input); @@ -133,11 +134,7 @@ public static function build($input, $options = array()) { * @throws XmlException */ protected static function _loadXml($input, $options) { - $hasDisable = function_exists('libxml_disable_entity_loader'); $internalErrors = libxml_use_internal_errors(true); - if ($hasDisable && !$options['loadEntities']) { - libxml_disable_entity_loader(true); - } $flags = LIBXML_NOCDATA; if (!empty($options['parseHuge'])) { $flags |= LIBXML_PARSEHUGE; @@ -152,9 +149,6 @@ protected static function _loadXml($input, $options) { } catch (Exception $e) { $xml = null; } - if ($hasDisable && !$options['loadEntities']) { - libxml_disable_entity_loader(false); - } libxml_use_internal_errors($internalErrors); if ($xml === null) { throw new XmlException(__d('cake_dev', 'Xml cannot be read.')); @@ -221,7 +215,7 @@ public static function fromArray($input, $options = array()) { ); $options += $defaults; - $dom = new DOMDocument($options['version'], $options['encoding']); + $dom = new DOMDocument($options['version'], (string)$options['encoding']); if ($options['pretty']) { $dom->formatOutput = true; } diff --git a/lib/Cake/View/Helper.php b/lib/Cake/View/Helper.php index 7512e5c899..d3fce20da3 100644 --- a/lib/Cake/View/Helper.php +++ b/lib/Cake/View/Helper.php @@ -605,6 +605,7 @@ public function setEntity($entity, $setScope = false) { $this->_association = null; $isHabtm = ( + $this->_modelScope !== null && isset($this->fieldset[$this->_modelScope]['fields'][$parts[0]]['type']) && $this->fieldset[$this->_modelScope]['fields'][$parts[0]]['type'] === 'multiple' ); diff --git a/lib/Cake/View/Helper/FormHelper.php b/lib/Cake/View/Helper/FormHelper.php index 032771bfb3..b8100393f6 100644 --- a/lib/Cake/View/Helper/FormHelper.php +++ b/lib/Cake/View/Helper/FormHelper.php @@ -213,8 +213,9 @@ protected function _introspectModel($model, $key, $field = null) { if ($key === 'fields') { if (!isset($this->fieldset[$model]['fields'])) { $this->fieldset[$model]['fields'] = $object->schema(); + $objectAlias = $object->alias === null ? '' : $object->alias; foreach ($object->hasAndBelongsToMany as $alias => $assocData) { - $this->fieldset[$object->alias]['fields'][$alias] = array('type' => 'multiple'); + $this->fieldset[$objectAlias]['fields'][$alias] = array('type' => 'multiple'); } } if ($field === null || $field === false) { @@ -1684,7 +1685,7 @@ public function radio($fieldName, $options = array(), $attributes = array()) { )); } } - $out = $hidden . implode($separator, $out); + $out = $hidden . implode((string)$separator, $out); if (is_array($between)) { $between = ''; @@ -2300,7 +2301,7 @@ public function day($fieldName = null, $attributes = array()) { $attributes += array('empty' => true, 'value' => null); $attributes = $this->_dateTimeSelected('day', $fieldName, $attributes); - if (strlen($attributes['value']) > 2) { + if (strlen((string)$attributes['value']) > 2) { $date = date_create($attributes['value']); $attributes['value'] = null; if ($date) { @@ -2356,7 +2357,7 @@ public function year($fieldName, $minYear = null, $maxYear = null, $attributes = } } - if (strlen($attributes['value']) > 4 || $attributes['value'] === 'now') { + if (strlen((string)$attributes['value']) > 4 || $attributes['value'] === 'now') { $date = date_create($attributes['value']); $attributes['value'] = null; if ($date) { @@ -2396,7 +2397,7 @@ public function month($fieldName, $attributes = array()) { $attributes += array('empty' => true, 'value' => null); $attributes = $this->_dateTimeSelected('month', $fieldName, $attributes); - if (strlen($attributes['value']) > 2) { + if (strlen((string)$attributes['value']) > 2) { $date = date_create($attributes['value']); $attributes['value'] = null; if ($date) { @@ -2441,7 +2442,7 @@ public function hour($fieldName, $format24Hours = false, $attributes = array()) $attributes += array('empty' => true, 'value' => null); $attributes = $this->_dateTimeSelected('hour', $fieldName, $attributes); - if (strlen($attributes['value']) > 2) { + if (strlen((string)$attributes['value']) > 2) { try { $date = new DateTime($attributes['value']); if ($format24Hours) { @@ -2488,7 +2489,7 @@ public function minute($fieldName, $attributes = array()) { $attributes += array('empty' => true, 'value' => null); $attributes = $this->_dateTimeSelected('min', $fieldName, $attributes); - if (strlen($attributes['value']) > 2) { + if (strlen((string)$attributes['value']) > 2) { $date = date_create($attributes['value']); $attributes['value'] = null; if ($date) { diff --git a/lib/Cake/View/Helper/HtmlHelper.php b/lib/Cake/View/Helper/HtmlHelper.php index bc7163e869..267ea26385 100644 --- a/lib/Cake/View/Helper/HtmlHelper.php +++ b/lib/Cake/View/Helper/HtmlHelper.php @@ -318,7 +318,7 @@ public function meta($type, $url = null, $options = array()) { */ public function charset($charset = null) { if (empty($charset)) { - $charset = strtolower(Configure::read('App.encoding')); + $charset = strtolower((string)Configure::read('App.encoding')); } return sprintf($this->_tags['charset'], (!empty($charset) ? $charset : 'utf-8')); } diff --git a/lib/Cake/View/Helper/PrototypeEngineHelper.php b/lib/Cake/View/Helper/PrototypeEngineHelper.php index 332ea69496..920cd9b425 100644 --- a/lib/Cake/View/Helper/PrototypeEngineHelper.php +++ b/lib/Cake/View/Helper/PrototypeEngineHelper.php @@ -219,7 +219,7 @@ public function effect($name, $options = array()) { case 'fadeIn': case 'fadeOut': $name = ($name === 'fadeIn') ? 'appear' : 'fade'; - $effect = $this->selection . '.' . $name . '(' . substr($optionString, 2) . ');'; + $effect = $this->selection . '.' . $name . '(' . substr((string)$optionString, 2) . ');'; break; } return $effect; diff --git a/lib/Cake/View/HelperCollection.php b/lib/Cake/View/HelperCollection.php index 2f67b23669..69e248e74d 100644 --- a/lib/Cake/View/HelperCollection.php +++ b/lib/Cake/View/HelperCollection.php @@ -130,7 +130,7 @@ public function load($helper, $settings = array()) { if (!class_exists($helperClass)) { throw new MissingHelperException(array( 'class' => $helperClass, - 'plugin' => substr($plugin, 0, -1) + 'plugin' => substr((string)$plugin, 0, -1) )); } $this->_loaded[$alias] = new $helperClass($this->_View, $settings); diff --git a/lib/Cake/basics.php b/lib/Cake/basics.php index 3b9417ed81..e78aeeaf75 100644 --- a/lib/Cake/basics.php +++ b/lib/Cake/basics.php @@ -250,7 +250,7 @@ function h($text, $double = true, $charset = null) { * @link https://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#pluginSplit */ function pluginSplit($name, $dotAppend = false, $plugin = null) { - if (strpos($name, '.') !== false) { + if (strpos((string)$name, '.') !== false) { $parts = explode('.', $name, 2); if ($dotAppend) { $parts[0] .= '.'; @@ -359,12 +359,12 @@ function env($key) { $name = env('SCRIPT_NAME'); $filename = env('SCRIPT_FILENAME'); $offset = 0; - if (!strpos($name, '.php')) { + if (!strpos((string)$name, '.php')) { $offset = 4; } - return substr($filename, 0, -(strlen($name) + $offset)); + return substr((string)$filename, 0, -(strlen((string)$name) + $offset)); case 'PHP_SELF': - return str_replace(env('DOCUMENT_ROOT'), '', env('SCRIPT_FILENAME')); + return str_replace((string)env('DOCUMENT_ROOT'), '', (string)env('SCRIPT_FILENAME')); case 'CGI_MODE': return (PHP_SAPI === 'cgi'); case 'HTTP_BASE': @@ -490,7 +490,7 @@ function cache($path, $data = null, $expires = '+1 day', $target = 'cache') { */ function clearCache($params = null, $type = 'views', $ext = '.php') { if (is_string($params) || $params === null) { - $params = preg_replace('/\/\//', '/', $params); + $params = preg_replace('/\/\//', '/', (string)$params); $cache = CACHE . $type . DS . $params; if (is_file($cache . $ext)) { From dc18015956d585e7e428cac19433360772521b60 Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Fri, 16 Oct 2026 17:14:57 -0300 Subject: [PATCH 16/30] Fix PHP 8.4+ CI errors in Redis and Memcache cache engine tests - 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 --- lib/Cake/Cache/Engine/RedisEngine.php | 2 +- lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Cache/Engine/RedisEngine.php b/lib/Cake/Cache/Engine/RedisEngine.php index 405729227f..c95619bf3c 100644 --- a/lib/Cake/Cache/Engine/RedisEngine.php +++ b/lib/Cake/Cache/Engine/RedisEngine.php @@ -173,7 +173,7 @@ public function decrement($key, $offset = 1) { * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed */ public function delete($key) { - return $this->_Redis->delete($key) > 0; + return $this->_Redis->del($key) > 0; } /** diff --git a/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php index 86020406ac..d4e5a23898 100644 --- a/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php @@ -417,7 +417,9 @@ public function testLongDurationEqualToZero() { $memcache = new TestMemcacheEngine(); $memcache->settings['compress'] = false; - $mock = $this->getMock('Memcache'); + // Only mock set(): generating stubs for every Memcache method reproduces the extension's + // implicitly nullable parameters, which PHP 8.4 reports as deprecated. + $mock = $this->getMock('Memcache', array('set')); $memcache->setMemcache($mock); $mock->expects($this->once()) ->method('set') From 33238a305fd97c21d5dafceb14962ed4129aeaee Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Fri, 16 Oct 2026 17:28:10 -0300 Subject: [PATCH 17/30] Bump version to 2.11.0 for the PHP 8.5 branch Co-Authored-By: Claude Fable 5.1 --- README.md | 3 ++- lib/Cake/VERSION.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 698138b171..9c3d268bd4 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Point `composer.json` at this repository and at the branch: ``` { "require": { - "cakephp/cakephp": "dev-cakephp2-php85 as 2.10.24" + "cakephp/cakephp": "dev-cakephp2-php85 as 2.11.0" }, "repositories": [ { @@ -112,6 +112,7 @@ It means that composer will look at `master` branch of repository configured und ### 2026-10-16 (Zumba, branch `cakephp2-php85`) +- Version bumped to **2.11.0** (`lib/Cake/VERSION.txt`): new runtime requirement (`ext-intl`) and behaviour changes (exceptions instead of `E_USER_ERROR`, OpenSSL fallback) warrant a minor bump; upstream stays at 2.10.24, so the number also identifies this branch. Alias it as `dev-cakephp2-php85 as 2.11.0` in applications. - Synced all upstream fixes up to `kamilwylegala/cakephp2-php8` #87 (includes the CSRF fix #76 and the PHP 8.4/8.5 fixes #75, #83, #86). - CI matrix now runs PHP 8.0, 8.1, 8.4 and 8.5; the test `app/Config/core.php` reports `E_ALL` so any deprecation fails the suite. - PHP 8.5: `Security::cipher()` no longer casts the out-of-range `Security.cipherSeed` float to int (same seed value, no deprecation); `limit()`/`offset()` clamp out-of-range values instead of casting; `ConsoleLog` no longer calls `posix_isatty(null)`; `ReflectionProperty::setAccessible()` only on PHP < 8.1; null array offsets in `I18n`, `Helper`, `CakeResponse`, `ConsoleOptionParser`, `RequestHandlerComponent`, `Set`, `Configure`, `CakeSession` replaced by explicit checks; `libxml_disable_entity_loader()` calls removed. diff --git a/lib/Cake/VERSION.txt b/lib/Cake/VERSION.txt index cef2c4856c..81c948a6e9 100644 --- a/lib/Cake/VERSION.txt +++ b/lib/Cake/VERSION.txt @@ -17,4 +17,4 @@ // @license https://opensource.org/licenses/mit-license.php MIT License // +--------------------------------------------------------------------------------------------+ // //////////////////////////////////////////////////////////////////////////////////////////////////// -2.10.24 +2.11.0 From e4543fb5eab34792c49043c3852a33d65e2b9206 Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Fri, 16 Oct 2026 17:36:42 -0300 Subject: [PATCH 18/30] MemcacheEngineTest: use a recording fake instead of mocking Memcache 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 --- .../Case/Cache/Engine/MemcacheEngineTest.php | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php index d4e5a23898..9fa4bdb545 100644 --- a/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/MemcacheEngineTest.php @@ -24,6 +24,33 @@ * * @package Cake.Test.Case.Cache.Engine */ +/** + * Records the arguments passed to set() instead of talking to a memcache server. + * + * A PHPUnit mock of the Memcache class cannot be used here: the generated mock repeats the + * extension's implicitly nullable parameters, which PHP 8.4+ reports as deprecated. + */ +class RecordingMemcache { + +/** + * Arguments of every set() call + * + * @var array + */ + public $calls = array(); + +/** + * Record a set() call. + * + * @return bool + */ + public function set() { + $this->calls[] = func_get_args(); + return true; + } + +} + class TestMemcacheEngine extends MemcacheEngine { /** @@ -417,16 +444,13 @@ public function testLongDurationEqualToZero() { $memcache = new TestMemcacheEngine(); $memcache->settings['compress'] = false; - // Only mock set(): generating stubs for every Memcache method reproduces the extension's - // implicitly nullable parameters, which PHP 8.4 reports as deprecated. - $mock = $this->getMock('Memcache', array('set')); - $memcache->setMemcache($mock); - $mock->expects($this->once()) - ->method('set') - ->with('key', 'value', false, 0); + $recorder = new RecordingMemcache(); + $memcache->setMemcache($recorder); $value = 'value'; $memcache->write('key', $value, 50 * DAY); + + $this->assertEquals(array(array('key', 'value', false, 0)), $recorder->calls); } /** From cf9909a3b70631c2ef86cf6bafa5b3c6a3ee4d4d Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Fri, 16 Oct 2026 18:21:24 -0300 Subject: [PATCH 19/30] Fix PHP 8.1+ deprecations hit only by the MySQL and Postgres test jobs - 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 --- lib/Cake/Model/Datasource/Database/Postgres.php | 2 +- lib/Cake/TestSuite/Fixture/CakeTestFixture.php | 2 +- {{pkgetc}}/cert.pem | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 120000 {{pkgetc}}/cert.pem diff --git a/lib/Cake/Model/Datasource/Database/Postgres.php b/lib/Cake/Model/Datasource/Database/Postgres.php index e1ae7caeeb..0043a3ca8f 100644 --- a/lib/Cake/Model/Datasource/Database/Postgres.php +++ b/lib/Cake/Model/Datasource/Database/Postgres.php @@ -251,7 +251,7 @@ public function describe($model) { 'default' => preg_replace( "/^'(.*)'$/", "$1", - preg_replace('/::[\w\s]+/', '', $c->default) + preg_replace('/::[\w\s]+/', '', (string)$c->default) ), 'length' => $length, ); diff --git a/lib/Cake/TestSuite/Fixture/CakeTestFixture.php b/lib/Cake/TestSuite/Fixture/CakeTestFixture.php index 920250c4a0..245d46e5d0 100644 --- a/lib/Cake/TestSuite/Fixture/CakeTestFixture.php +++ b/lib/Cake/TestSuite/Fixture/CakeTestFixture.php @@ -179,7 +179,7 @@ public function init() { ClassRegistry::flush(); } - if (!empty($db->config['prefix']) && strpos($this->table, $db->config['prefix']) === 0) { + if (!empty($db->config['prefix']) && strpos((string)$this->table, $db->config['prefix']) === 0) { $this->table = str_replace($db->config['prefix'], '', $this->table); } diff --git a/{{pkgetc}}/cert.pem b/{{pkgetc}}/cert.pem new file mode 120000 index 0000000000..5c12e2c8d4 --- /dev/null +++ b/{{pkgetc}}/cert.pem @@ -0,0 +1 @@ +/Users/alenarmona/Developer/Zumba/cakephp2-php8/{{etc}}/ca-certificates/cert.pem \ No newline at end of file From 4e44e9cd9bd3693e12fbad91ba7822d7741164a4 Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Fri, 16 Oct 2026 18:31:02 -0300 Subject: [PATCH 20/30] Remove stray certificate bundle added by a local Homebrew run Co-Authored-By: Claude Fable 5.1 --- {{pkgetc}}/cert.pem | 1 - 1 file changed, 1 deletion(-) delete mode 120000 {{pkgetc}}/cert.pem diff --git a/{{pkgetc}}/cert.pem b/{{pkgetc}}/cert.pem deleted file mode 120000 index 5c12e2c8d4..0000000000 --- a/{{pkgetc}}/cert.pem +++ /dev/null @@ -1 +0,0 @@ -/Users/alenarmona/Developer/Zumba/cakephp2-php8/{{etc}}/ca-certificates/cert.pem \ No newline at end of file From c05d404d48c570c7bf0fc1c155579aab319935b8 Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Sat, 17 Oct 2026 19:01:20 -0300 Subject: [PATCH 21/30] Skip the use_trans_sid redirect test from PHP 8.4, where enabling it 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 --- app/tmp/cache/views/empty | 0 lib/Cake/Test/Case/Controller/ApplicationControllerTest.php | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 app/tmp/cache/views/empty diff --git a/app/tmp/cache/views/empty b/app/tmp/cache/views/empty deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lib/Cake/Test/Case/Controller/ApplicationControllerTest.php b/lib/Cake/Test/Case/Controller/ApplicationControllerTest.php index 6c78dd06c5..26a5c7f189 100644 --- a/lib/Cake/Test/Case/Controller/ApplicationControllerTest.php +++ b/lib/Cake/Test/Case/Controller/ApplicationControllerTest.php @@ -65,7 +65,7 @@ class ApplicationControllerTest extends ControllerTestCase { */ public function setUp() : void { $this->_sessionConfig = Configure::read('Session'); - $this->skipIf(PHP_VERSION_ID >= 80500, 'Enabling session.use_trans_sid is deprecated since PHP 8.5.'); + $this->skipIf(PHP_VERSION_ID >= 80400, 'Enabling session.use_trans_sid is deprecated since PHP 8.4.'); CakeSession::destroy(); parent::setUp(); } From 774f5e032a1b4aff617b836b4332697c3eb487db Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Mon, 19 Oct 2026 09:33:14 -0300 Subject: [PATCH 22/30] CakeSocketTest: skip the context tests when smtp.gmail.com is unreachable 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 --- lib/Cake/Test/Case/Network/CakeSocketTest.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Test/Case/Network/CakeSocketTest.php b/lib/Cake/Test/Case/Network/CakeSocketTest.php index 166491e6bc..75a2ce4af0 100644 --- a/lib/Cake/Test/Case/Network/CakeSocketTest.php +++ b/lib/Cake/Test/Case/Network/CakeSocketTest.php @@ -397,7 +397,11 @@ public function testGetContext() { ) ); $this->Socket = new CakeSocket($config); - $this->Socket->connect(); + try { + $this->Socket->connect(); + } catch (SocketException $e) { + $this->markTestSkipped('Cannot test network, skipping.'); + } $result = $this->Socket->context(); $this->assertSame($config['context']['ssl']['capture_peer'], $result['ssl']['capture_peer']); } @@ -420,7 +424,11 @@ public function testConfigContext() { ); $this->Socket = new CakeSocket($config); - $this->Socket->connect(); + try { + $this->Socket->connect(); + } catch (SocketException $e) { + $this->markTestSkipped('Cannot test network, skipping.'); + } $result = $this->Socket->context(); $this->assertTrue($result['ssl']['verify_peer']); From 509d6766a7655844a540b3b81ca149922abcf59a Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Mon, 19 Oct 2026 09:50:10 -0300 Subject: [PATCH 23/30] CI: publish PHPUnit failures as GitHub annotations Co-Authored-By: Claude Fable 5.1 --- .github/workflows/annotate-junit.py | 47 +++++++++++++++++++++++++++++ .github/workflows/tests.yml | 7 ++++- 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/annotate-junit.py diff --git a/.github/workflows/annotate-junit.py b/.github/workflows/annotate-junit.py new file mode 100644 index 0000000000..f17ab80d9f --- /dev/null +++ b/.github/workflows/annotate-junit.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""Turn PHPUnit JUnit results into GitHub Actions annotations. + +GitHub shows at most 10 annotations per step, so the first failures and errors are +annotated individually and the rest are summarised in the step summary. +""" +import html +import os +import sys +import xml.etree.ElementTree as ET + +path = sys.argv[1] if len(sys.argv) > 1 else 'junit.xml' +if not os.path.exists(path): + print('::error::PHPUnit did not write %s (the run probably aborted before the summary)' % path) + sys.exit(0) + +root = ET.parse(path).getroot() +problems = [] +for case in root.iter('testcase'): + for kind in ('error', 'failure'): + node = case.find(kind) + if node is None: + continue + text = (node.text or '').strip() + message = text.splitlines()[0] if text else node.get('message', kind) + location = next((line for line in text.splitlines() if line.startswith('/')), '') + problems.append((kind, case.get('class') or '', case.get('name') or '', message, location, text)) + +summary = ['## PHPUnit: %d problem(s)' % len(problems), ''] +for i, (kind, cls, name, message, location, text) in enumerate(problems): + file_hint = '' + if ':' in location: + file_part, _, line_part = location.rpartition(':') + if line_part.isdigit(): + file_hint = 'file=%s,line=%s,' % (file_part.split('/cakephp2-php8/', 1)[-1], line_part) + if i < 10: + print('::%s %stitle=%s::%s' % (kind, file_hint, '%s::%s' % (cls, name), message.replace('\n', ' ')[:900])) + summary.append('### %s %s::%s' % (kind, cls, name)) + summary.append('```') + summary.append(text[:1500]) + summary.append('```') + +step_summary = os.environ.get('GITHUB_STEP_SUMMARY') +if step_summary: + with open(step_summary, 'a') as fh: + fh.write('\n'.join(summary) + '\n') +print('%d problem(s) annotated' % len(problems)) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9e492210e9..02ef0d3d5b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -127,4 +127,9 @@ jobs: run: cp ./.github/workflows/configs/database.php ./app/Config/ - name: Run Tests - run: ./vendors/bin/phpunit --stderr --verbose lib/Cake/Test/Case/AllTestsTest.php + id: tests + run: ./vendors/bin/phpunit --stderr --verbose --log-junit junit.xml lib/Cake/Test/Case/AllTestsTest.php + + - name: Annotate failures + if: failure() && steps.tests.conclusion == 'failure' + run: python3 .github/workflows/annotate-junit.py junit.xml From e2cea0b7103340e01e7e931ed75b5f234b7fa71e Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Mon, 19 Oct 2026 09:59:45 -0300 Subject: [PATCH 24/30] CI annotations: skip risky tests, show the failure message Co-Authored-By: Claude Fable 5.1 --- .github/workflows/annotate-junit.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/annotate-junit.py b/.github/workflows/annotate-junit.py index f17ab80d9f..960fe504fd 100644 --- a/.github/workflows/annotate-junit.py +++ b/.github/workflows/annotate-junit.py @@ -21,9 +21,13 @@ node = case.find(kind) if node is None: continue + if 'Risky' in (node.get('type') or '') or 'Warning' in (node.get('type') or ''): + continue # risky tests and PHPUnit warnings do not fail the build text = (node.text or '').strip() - message = text.splitlines()[0] if text else node.get('message', kind) - location = next((line for line in text.splitlines() if line.startswith('/')), '') + lines = [line for line in text.splitlines() if line.strip()] + # PHPUnit writes "Class::test" on the first line, then the message + message = node.get('message') or (lines[1] if len(lines) > 1 else (lines[0] if lines else kind)) + location = next((line for line in lines if line.startswith('/')), '') problems.append((kind, case.get('class') or '', case.get('name') or '', message, location, text)) summary = ['## PHPUnit: %d problem(s)' % len(problems), ''] From 1dac86390197937e73fcd5ae02512007f346c84c Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Mon, 19 Oct 2026 10:21:30 -0300 Subject: [PATCH 25/30] CI: keep the upstream matrix plus PHP 8.5 only Co-Authored-By: Claude Fable 5.1 --- .github/workflows/tests.yml | 2 -- README.md | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 02ef0d3d5b..8569f27c09 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,8 +29,6 @@ jobs: db-type: sqlite - php-version: '8.1' db-type: mysql - - php-version: '8.4' - db-type: mysql - php-version: '8.5' db-type: mysql - php-version: '8.5' diff --git a/README.md b/README.md index 9c3d268bd4..cde2fbcbb5 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This repository (`zumba/cakephp2-php8`) is Zumba's fork of [kamilwylegala/cakeph The `cakephp2-php85` branch is the version our applications use on PHP 8.5 containers. It contains: - Every upstream fix up to and including the CSRF hardening (upstream #76), the PHP 8.4 fixes (#75, #83) and the PHP 8.5 fixes (#86). -- A CI matrix that runs the framework test suite on PHP 8.0, 8.1, 8.4 and 8.5. The test bootstrap converts every deprecation into a failure, so a green build means no deprecation notices on those versions. +- A CI matrix that runs the framework test suite on PHP 8.0, 8.1 and 8.5. The test bootstrap converts every deprecation into a failure, so a green build means no deprecation notices on those versions. - A local `docker/web` image based on `php:8.5-apache`. The code stays compatible with PHP 8.0+; no PHP 8.5-only syntax is used. @@ -114,7 +114,7 @@ It means that composer will look at `master` branch of repository configured und - Version bumped to **2.11.0** (`lib/Cake/VERSION.txt`): new runtime requirement (`ext-intl`) and behaviour changes (exceptions instead of `E_USER_ERROR`, OpenSSL fallback) warrant a minor bump; upstream stays at 2.10.24, so the number also identifies this branch. Alias it as `dev-cakephp2-php85 as 2.11.0` in applications. - Synced all upstream fixes up to `kamilwylegala/cakephp2-php8` #87 (includes the CSRF fix #76 and the PHP 8.4/8.5 fixes #75, #83, #86). -- CI matrix now runs PHP 8.0, 8.1, 8.4 and 8.5; the test `app/Config/core.php` reports `E_ALL` so any deprecation fails the suite. +- CI matrix now runs PHP 8.0, 8.1 and 8.5; the test `app/Config/core.php` reports `E_ALL` so any deprecation fails the suite. - PHP 8.5: `Security::cipher()` no longer casts the out-of-range `Security.cipherSeed` float to int (same seed value, no deprecation); `limit()`/`offset()` clamp out-of-range values instead of casting; `ConsoleLog` no longer calls `posix_isatty(null)`; `ReflectionProperty::setAccessible()` only on PHP < 8.1; null array offsets in `I18n`, `Helper`, `CakeResponse`, `ConsoleOptionParser`, `RequestHandlerComponent`, `Set`, `Configure`, `CakeSession` replaced by explicit checks; `libxml_disable_entity_loader()` calls removed. - PHP 8.4: `trigger_error(..., E_USER_ERROR)` replaced by exceptions (`ConfigureException`, `CakeException`, `RuntimeException` in the webroot/console bootstrap scripts) or `ErrorHandler::handleFatalError()`. - PHP 8.2: undeclared (dynamic) properties declared on `Scaffold`, `ConsoleOptionParser`, `HelpFormatter`, `MemcachedEngine`, `CakeTestCase`, `CakeTestFixture`, `CakeFixtureManager`, `ControllerTestDispatcher` and ~100 test classes; `ConnectionManager::create()` stores runtime connections on a `#[\AllowDynamicProperties]` subclass of `DATABASE_CONFIG`; `CakeRequest` allows dynamic properties. From c47255eb80caaf42d1854343a3991d11be91b075 Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Mon, 19 Oct 2026 10:28:28 -0300 Subject: [PATCH 26/30] CI annotations: only skip risky tests; PHP warnings are real errors Co-Authored-By: Claude Fable 5.1 --- .github/workflows/annotate-junit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/annotate-junit.py b/.github/workflows/annotate-junit.py index 960fe504fd..940ca2b2b9 100644 --- a/.github/workflows/annotate-junit.py +++ b/.github/workflows/annotate-junit.py @@ -21,8 +21,8 @@ node = case.find(kind) if node is None: continue - if 'Risky' in (node.get('type') or '') or 'Warning' in (node.get('type') or ''): - continue # risky tests and PHPUnit warnings do not fail the build + if (node.get('type') or '').endswith('RiskyTestError'): + continue # risky tests do not fail the build text = (node.text or '').strip() lines = [line for line in text.splitlines() if line.strip()] # PHPUnit writes "Class::test" on the first line, then the message From f4f9d2a91711c63a504fbac935fb9fef5ae343b1 Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Mon, 19 Oct 2026 11:10:16 -0300 Subject: [PATCH 27/30] CI annotations: publish PHPUnit's console summary as well Co-Authored-By: Claude Fable 5.1 --- .github/workflows/annotate-junit.py | 27 +++++++++++++++++++++++++++ .github/workflows/tests.yml | 7 +++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/.github/workflows/annotate-junit.py b/.github/workflows/annotate-junit.py index 940ca2b2b9..83e495ff85 100644 --- a/.github/workflows/annotate-junit.py +++ b/.github/workflows/annotate-junit.py @@ -44,6 +44,33 @@ summary.append(text[:1500]) summary.append('```') +# Also publish PHPUnit's own console summary (errors and failures, without the warnings +# section), so the reason is visible even when the JUnit log has nothing to say. +output_path = sys.argv[2] if len(sys.argv) > 2 else None +if output_path and os.path.exists(output_path): + lines = open(output_path, errors='replace').read().splitlines() + starts = [i for i, line in enumerate(lines) if line.startswith('There w')] + if starts: + kept = [] + skipping = False + for line in lines[starts[0]:]: + if line.startswith('There w'): + skipping = 'warning' in line or 'risky' in line + if not skipping: + kept.append(line) + tail = kept + else: + tail = lines[-40:] + text = '\n'.join(tail).strip() + chunk = 900 + pieces = [text[i:i + chunk] for i in range(0, len(text), chunk)][:8] + for n, piece in enumerate(pieces, 1): + print('::error title=PHPUnit output %d/%d::%s' % (n, len(pieces), piece.replace('\n', '%0A'))) + summary.append('## PHPUnit console summary') + summary.append('```') + summary.append(text[:6000]) + summary.append('```') + step_summary = os.environ.get('GITHUB_STEP_SUMMARY') if step_summary: with open(step_summary, 'a') as fh: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8569f27c09..af783b2485 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -126,8 +126,11 @@ jobs: - name: Run Tests id: tests - run: ./vendors/bin/phpunit --stderr --verbose --log-junit junit.xml lib/Cake/Test/Case/AllTestsTest.php + shell: bash + run: | + set -o pipefail + ./vendors/bin/phpunit --stderr --verbose --log-junit junit.xml lib/Cake/Test/Case/AllTestsTest.php 2>&1 | tee phpunit-output.txt - name: Annotate failures if: failure() && steps.tests.conclusion == 'failure' - run: python3 .github/workflows/annotate-junit.py junit.xml + run: python3 .github/workflows/annotate-junit.py junit.xml phpunit-output.txt From 9c2d2da6796442890455b5bcad6d9bb6329cb06f Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Mon, 19 Oct 2026 11:15:42 -0300 Subject: [PATCH 28/30] CI annotations: still publish the console summary when JUnit output is missing Co-Authored-By: Claude Fable 5.1 --- .github/workflows/annotate-junit.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/annotate-junit.py b/.github/workflows/annotate-junit.py index 83e495ff85..31ec199a31 100644 --- a/.github/workflows/annotate-junit.py +++ b/.github/workflows/annotate-junit.py @@ -10,13 +10,13 @@ import xml.etree.ElementTree as ET path = sys.argv[1] if len(sys.argv) > 1 else 'junit.xml' -if not os.path.exists(path): - print('::error::PHPUnit did not write %s (the run probably aborted before the summary)' % path) - sys.exit(0) - -root = ET.parse(path).getroot() problems = [] -for case in root.iter('testcase'): +cases = [] +if not os.path.exists(path) or os.path.getsize(path) == 0: + print('::error::PHPUnit did not write %s (the run probably aborted before the summary)' % path) +else: + cases = list(ET.parse(path).getroot().iter('testcase')) +for case in cases: for kind in ('error', 'failure'): node = case.find(kind) if node is None: From 50cc46df67766fba361b97ce06678020581e7fa5 Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Mon, 19 Oct 2026 11:21:50 -0300 Subject: [PATCH 29/30] Restore app/tmp/cache/views placeholder 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 --- app/tmp/cache/views/empty | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/tmp/cache/views/empty diff --git a/app/tmp/cache/views/empty b/app/tmp/cache/views/empty new file mode 100644 index 0000000000..e69de29bb2 From 4b361f0f930886aaa82ad6781160f8d91fca4dfb Mon Sep 17 00:00:00 2001 From: Alejandro Narmona Date: Mon, 19 Oct 2026 12:06:39 -0300 Subject: [PATCH 30/30] CI: drop the annotation step Co-Authored-By: Claude Fable 5.1 --- .github/workflows/annotate-junit.py | 78 ----------------------------- .github/workflows/tests.yml | 10 +--- 2 files changed, 1 insertion(+), 87 deletions(-) delete mode 100644 .github/workflows/annotate-junit.py diff --git a/.github/workflows/annotate-junit.py b/.github/workflows/annotate-junit.py deleted file mode 100644 index 31ec199a31..0000000000 --- a/.github/workflows/annotate-junit.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python3 -"""Turn PHPUnit JUnit results into GitHub Actions annotations. - -GitHub shows at most 10 annotations per step, so the first failures and errors are -annotated individually and the rest are summarised in the step summary. -""" -import html -import os -import sys -import xml.etree.ElementTree as ET - -path = sys.argv[1] if len(sys.argv) > 1 else 'junit.xml' -problems = [] -cases = [] -if not os.path.exists(path) or os.path.getsize(path) == 0: - print('::error::PHPUnit did not write %s (the run probably aborted before the summary)' % path) -else: - cases = list(ET.parse(path).getroot().iter('testcase')) -for case in cases: - for kind in ('error', 'failure'): - node = case.find(kind) - if node is None: - continue - if (node.get('type') or '').endswith('RiskyTestError'): - continue # risky tests do not fail the build - text = (node.text or '').strip() - lines = [line for line in text.splitlines() if line.strip()] - # PHPUnit writes "Class::test" on the first line, then the message - message = node.get('message') or (lines[1] if len(lines) > 1 else (lines[0] if lines else kind)) - location = next((line for line in lines if line.startswith('/')), '') - problems.append((kind, case.get('class') or '', case.get('name') or '', message, location, text)) - -summary = ['## PHPUnit: %d problem(s)' % len(problems), ''] -for i, (kind, cls, name, message, location, text) in enumerate(problems): - file_hint = '' - if ':' in location: - file_part, _, line_part = location.rpartition(':') - if line_part.isdigit(): - file_hint = 'file=%s,line=%s,' % (file_part.split('/cakephp2-php8/', 1)[-1], line_part) - if i < 10: - print('::%s %stitle=%s::%s' % (kind, file_hint, '%s::%s' % (cls, name), message.replace('\n', ' ')[:900])) - summary.append('### %s %s::%s' % (kind, cls, name)) - summary.append('```') - summary.append(text[:1500]) - summary.append('```') - -# Also publish PHPUnit's own console summary (errors and failures, without the warnings -# section), so the reason is visible even when the JUnit log has nothing to say. -output_path = sys.argv[2] if len(sys.argv) > 2 else None -if output_path and os.path.exists(output_path): - lines = open(output_path, errors='replace').read().splitlines() - starts = [i for i, line in enumerate(lines) if line.startswith('There w')] - if starts: - kept = [] - skipping = False - for line in lines[starts[0]:]: - if line.startswith('There w'): - skipping = 'warning' in line or 'risky' in line - if not skipping: - kept.append(line) - tail = kept - else: - tail = lines[-40:] - text = '\n'.join(tail).strip() - chunk = 900 - pieces = [text[i:i + chunk] for i in range(0, len(text), chunk)][:8] - for n, piece in enumerate(pieces, 1): - print('::error title=PHPUnit output %d/%d::%s' % (n, len(pieces), piece.replace('\n', '%0A'))) - summary.append('## PHPUnit console summary') - summary.append('```') - summary.append(text[:6000]) - summary.append('```') - -step_summary = os.environ.get('GITHUB_STEP_SUMMARY') -if step_summary: - with open(step_summary, 'a') as fh: - fh.write('\n'.join(summary) + '\n') -print('%d problem(s) annotated' % len(problems)) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index af783b2485..1d30e338a5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -125,12 +125,4 @@ jobs: run: cp ./.github/workflows/configs/database.php ./app/Config/ - name: Run Tests - id: tests - shell: bash - run: | - set -o pipefail - ./vendors/bin/phpunit --stderr --verbose --log-junit junit.xml lib/Cake/Test/Case/AllTestsTest.php 2>&1 | tee phpunit-output.txt - - - name: Annotate failures - if: failure() && steps.tests.conclusion == 'failure' - run: python3 .github/workflows/annotate-junit.py junit.xml phpunit-output.txt + run: ./vendors/bin/phpunit --stderr --verbose lib/Cake/Test/Case/AllTestsTest.php