From b918df80082ae8a546da9b47af0d15a68b183daf Mon Sep 17 00:00:00 2001 From: Kamil Wylegala Date: Wed, 5 Jun 2024 22:21:39 +0200 Subject: [PATCH] Intl recommendation + `PHP81_BC\strftime` + Symfony ICU Polyfill (#69) * Intl extension requirement. * CakeTime migrated to Intl. * FormHelper migration strftime to Intl. * Readme update. --- 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 517cd6059..4e1257b4b 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 8fbb39241..8557694a0 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 f9ce1158c..3ff7fb1fd 100644 --- a/lib/Cake/Test/Case/Utility/CakeTimeTest.php +++ b/lib/Cake/Test/Case/Utility/CakeTimeTest.php @@ -279,9 +279,7 @@ class CakeTimeTest extends CakeTestCase { $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 @@ class CakeTimeTest extends CakeTestCase { 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 @@ class CakeTimeTest extends CakeTestCase { * @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 @@ class CakeTimeTest extends CakeTestCase { * @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 @@ class CakeTimeTest extends CakeTestCase { $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 @@ class CakeTimeTest extends CakeTestCase { $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 @@ class CakeTimeTest extends CakeTestCase { $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 75fae995e..fb9f1cad0 100644 --- a/lib/Cake/Utility/CakeTime.php +++ b/lib/Cake/Utility/CakeTime.php @@ -1167,9 +1167,8 @@ class CakeTime { * @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 89b7f1127..032771bfb 100644 --- a/lib/Cake/View/Helper/FormHelper.php +++ b/lib/Cake/View/Helper/FormHelper.php @@ -2767,9 +2767,7 @@ class FormHelper extends AppHelper { } 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 @@ class FormHelper extends AppHelper { } 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 } }