diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index a0e2bd033aa..9e8e898b3d7 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -65,8 +65,13 @@ jobs:
uses: niden/actions-memcached@v7
- name: Install dependencies.
+ if: matrix.php != '8.4'
run: composer update $DEFAULT_COMPOSER_FLAGS
+ - name: Install dependencies with PHP 8.4.
+ if: matrix.php == '8.4'
+ run: composer update $DEFAULT_COMPOSER_FLAGS --ignore-platform-reqs
+
- name: Run tests with PHPUnit and generate coverage.
if: matrix.php == '7.4'
run: vendor/bin/phpunit --verbose --exclude-group $PHPUNIT_EXCLUDE_GROUP --coverage-clover=coverage.xml --colors=always
diff --git a/.github/workflows/ci-mssql.yml b/.github/workflows/ci-mssql.yml
index 698cf613040..5ed0abcb763 100644
--- a/.github/workflows/ci-mssql.yml
+++ b/.github/workflows/ci-mssql.yml
@@ -66,8 +66,13 @@ jobs:
run: composer self-update
- name: Install dependencies with composer
+ if: matrix.php != '8.4'
run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
+ - name: Install dependencies with PHP 8.4.
+ if: matrix.php == '8.4'
+ run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ignore-platform-reqs --ansi
+
- name: Run MSSQL tests with PHPUnit and generate coverage.
run: vendor/bin/phpunit --group mssql --coverage-clover=coverage.xml --colors=always
diff --git a/.github/workflows/ci-mysql.yml b/.github/workflows/ci-mysql.yml
index 7bec2352248..5753d916958 100644
--- a/.github/workflows/ci-mysql.yml
+++ b/.github/workflows/ci-mysql.yml
@@ -47,9 +47,14 @@ jobs:
php-version: ${{ matrix.php }}
tools: composer:v2, pecl
- - name: Install dependencies with composer.
+ - name: Install dependencies with composer
+ if: matrix.php != '8.4'
run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
+ - name: Install dependencies with PHP 8.4.
+ if: matrix.php == '8.4'
+ run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ignore-platform-reqs --ansi
+
- name: Run MySQL tests with PHPUnit and generate coverage.
run: vendor/bin/phpunit --group mysql --coverage-clover=coverage.xml --colors=always
diff --git a/.github/workflows/ci-pgsql.yml b/.github/workflows/ci-pgsql.yml
index 8dd8b3d6da5..4b926645b82 100644
--- a/.github/workflows/ci-pgsql.yml
+++ b/.github/workflows/ci-pgsql.yml
@@ -51,9 +51,14 @@ jobs:
- name: Update composer.
run: composer self-update
- - name: Install dependencies with composer.
+ - name: Install dependencies with composer
+ if: matrix.php != '8.4'
run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
+ - name: Install dependencies with PHP 8.4.
+ if: matrix.php == '8.4'
+ run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ignore-platform-reqs --ansi
+
- name: Run Pgsql tests with PHPUnit and generate coverage.
run: vendor/bin/phpunit --group pgsql --coverage-clover=coverage.xml --colors=always
diff --git a/.github/workflows/ci-sqlite.yml b/.github/workflows/ci-sqlite.yml
index bf53dc35975..fe7405ea4e2 100644
--- a/.github/workflows/ci-sqlite.yml
+++ b/.github/workflows/ci-sqlite.yml
@@ -40,9 +40,14 @@ jobs:
- name: Update composer.
run: composer self-update
- - name: Install dependencies with composer.
+ - name: Install dependencies with composer
+ if: matrix.php != '8.4'
run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
+ - name: Install dependencies with PHP 8.4.
+ if: matrix.php == '8.4'
+ run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ignore-platform-reqs --ansi
+
- name: Run SQLite tests with PHPUnit and generate coverage.
run: vendor/bin/phpunit --group sqlite --coverage-clover=coverage.xml --colors=always
diff --git a/README.md b/README.md
index b2aa8475b7a..4bc05115c8d 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,20 @@ and a [Definitive Guide Mirror](http://stuff.cebe.cc/yii2docs/) which is updated
- For Yii 1.1 users, there is [Upgrading from Yii 1.1](https://www.yiiframework.com/doc/guide/2.0/en/intro-upgrade-from-v1)
to get an idea of what has changed in 2.0.
+Versions & PHP compatibility
+----------------------------
+
+| Yii2 Version | PHP version | Development status | EOL ¹ |
+|--------------|----------------|-----------------------------------|----------------------------------------------------------------|
+| <= 2.0.49.* | >= 5.4, <= 8.3 | security fixes only | 23 Nov 2026 ³ |
+| >= 2.0.50 | >= 7.3, <= 8.4 | bug fixes and security fixes only | bugfixes till 23 Nov 2026 ³, security fixes till 21 Nov 2027 ⁴ |
+| >= 2.2.0 ² | >= 8.1 | active development | |
+
+¹ All mentioned dates may be subject to change and no rights can be derived from them.
+² Note: Yii 2.1 was [skipped](https://github.com/yiisoft/yii2/discussions/19831#discussioncomment-5858046), [Yii 2.2](https://github.com/yiisoft/yii2/tree/2.2) has not yet been released.
+³ [PHP 8.3 EOL date](https://www.php.net/supported-versions.php).
+⁴ [Expected PHP 8.4 EOL date](https://wiki.php.net/todo/php84).
+
Community
---------
diff --git a/composer.json b/composer.json
index 90dc2720717..af172dbb762 100644
--- a/composer.json
+++ b/composer.json
@@ -73,7 +73,7 @@
"ext-ctype": "*",
"lib-pcre": "*",
"yiisoft/yii2-composer": "~2.0.4",
- "ezyang/htmlpurifier": "^4.6",
+ "ezyang/htmlpurifier": "^4.17",
"cebe/markdown": "~1.0.0 | ~1.1.0 | ~1.2.0",
"bower-asset/jquery": "3.7.*@stable | 3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable",
"bower-asset/inputmask": "^5.0.8 ",
diff --git a/docs/internals/git-workflow.md b/docs/internals/git-workflow.md
index 11e8a499a6c..b3ede5011a0 100644
--- a/docs/internals/git-workflow.md
+++ b/docs/internals/git-workflow.md
@@ -111,7 +111,7 @@ review your suggestion, and provide appropriate feedback along the way.
### 2. Pull the latest code from the main Yii branch
```
-git pull upstream
+git pull upstream master
```
You should start at this point for every new contribution to make sure you are working on the latest code.
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index e0164c0136f..8d01a3f505f 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -20,10 +20,12 @@ Yii Framework 2 Change Log
- Enh #20042: Add empty array check to `ActiveQueryTrait::findWith()` (renkas)
- Enh #20032: Added `yii\helpers\BaseStringHelper::mask()` method for string masking with multibyte support (salehhashemi1992)
- Enh #20034: Added `yii\helpers\BaseStringHelper::findBetween()` to retrieve a substring that lies between two strings (salehhashemi1992)
+- Bug #20083: Fix deprecated warning implicit conversion from float (skepticspriggan)
+- Enh #20087: Add custom attributes to script tags (skepticspriggan)
- Enh #20121: Added `yiisoft/yii2-coding-standards` to composer `require-dev` and lint code to comply with PSR12 (razvanphp)
- New: Added `yii\caching\CallbackDependency` to allow using a callback to determine if a cache dependency is still valid (laxity7)
- Enh #20134: Raise minimum `PHP` version to `7.3` (@terabytesoftw)
-
+- Bug #20141: Update `ezyang/htmlpurifier` dependency to version `4.17` (@terabytesoftw)
2.0.49.2 October 12, 2023
-------------------------
diff --git a/framework/composer.json b/framework/composer.json
index 37c438afb17..d24703fb442 100644
--- a/framework/composer.json
+++ b/framework/composer.json
@@ -68,7 +68,7 @@
"ext-ctype": "*",
"lib-pcre": "*",
"yiisoft/yii2-composer": "~2.0.4",
- "ezyang/htmlpurifier": "^4.6",
+ "ezyang/htmlpurifier": "^4.17",
"cebe/markdown": "~1.0.0 | ~1.1.0 | ~1.2.0",
"bower-asset/jquery": "3.7.*@stable | 3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable",
"bower-asset/inputmask": "^5.0.8 ",
diff --git a/framework/db/BaseActiveRecord.php b/framework/db/BaseActiveRecord.php
index 41732caab1d..8931c84f3ed 100644
--- a/framework/db/BaseActiveRecord.php
+++ b/framework/db/BaseActiveRecord.php
@@ -681,6 +681,7 @@ public function getDirtyAttributes($names = null)
* @param array|null $attributeNames list of attribute names that need to be saved. Defaults to null,
* meaning all attributes that are loaded from DB will be saved.
* @return bool whether the saving succeeded (i.e. no validation errors occurred).
+ * @throws Exception in case update or insert failed.
*/
public function save($runValidation = true, $attributeNames = null)
{
diff --git a/framework/helpers/BaseHtml.php b/framework/helpers/BaseHtml.php
index e610af6ca1f..0eab768e6e9 100644
--- a/framework/helpers/BaseHtml.php
+++ b/framework/helpers/BaseHtml.php
@@ -220,6 +220,11 @@ public static function style($content, $options = [])
*/
public static function script($content, $options = [])
{
+ $view = Yii::$app->getView();
+ if ($view instanceof \yii\web\View && !empty($view->scriptOptions)) {
+ $options = array_merge($view->scriptOptions, $options);
+ }
+
return static::tag('script', $content, $options);
}
diff --git a/framework/i18n/Formatter.php b/framework/i18n/Formatter.php
index b72385eaa59..c1b9d9106eb 100644
--- a/framework/i18n/Formatter.php
+++ b/framework/i18n/Formatter.php
@@ -1059,7 +1059,7 @@ public function asDuration($value, $implodeString = ', ', $negativeSign = '-')
} elseif (is_numeric($value)) {
$isNegative = $value < 0;
$zeroDateTime = (new DateTime())->setTimestamp(0);
- $valueDateTime = (new DateTime())->setTimestamp(abs($value));
+ $valueDateTime = (new DateTime())->setTimestamp(abs((int) $value));
$interval = $valueDateTime->diff($zeroDateTime);
} elseif (strncmp($value, 'P-', 2) === 0) {
$interval = new DateInterval('P' . substr($value, 2));
diff --git a/framework/web/View.php b/framework/web/View.php
index 21970711a59..a23b1228ca1 100644
--- a/framework/web/View.php
+++ b/framework/web/View.php
@@ -131,6 +131,11 @@ class View extends \yii\base\View
* @see registerJsFile()
*/
public $jsFiles = [];
+ /**
+ * @since 2.0.50
+ * @var array the script tag options.
+ */
+ public $scriptOptions = [];
private $_assetManager;
diff --git a/tests/framework/helpers/HtmlTest.php b/tests/framework/helpers/HtmlTest.php
index c8d36466813..24c8186181b 100644
--- a/tests/framework/helpers/HtmlTest.php
+++ b/tests/framework/helpers/HtmlTest.php
@@ -90,6 +90,21 @@ public function testScript()
$this->assertEquals("", Html::script($content, ['type' => 'text/js']));
}
+ public function testScriptCustomAttribute()
+ {
+ $nonce = Yii::$app->security->generateRandomString();
+ $this->mockApplication([
+ 'components' => [
+ 'view' => [
+ 'class' => 'yii\web\View',
+ 'scriptOptions' => ['nonce' => $nonce],
+ ],
+ ],
+ ]);
+ $content = 'a <>';
+ $this->assertEquals("", Html::script($content));
+ }
+
public function testCssFile()
{
$this->assertEquals('', Html::cssFile('http://example.com'));
diff --git a/tests/framework/i18n/FormatterDateTest.php b/tests/framework/i18n/FormatterDateTest.php
index c62ff491b11..1af75fc49e0 100644
--- a/tests/framework/i18n/FormatterDateTest.php
+++ b/tests/framework/i18n/FormatterDateTest.php
@@ -536,6 +536,7 @@ public function testAsDuration()
// other options
$this->assertSame('minus 244 seconds', $this->formatter->asDuration($interval_244_seconds, ' and ', 'minus '));
$this->assertSame('minus 4 minutes and 4 seconds', $this->formatter->asDuration(-244, ' and ', 'minus '));
+ $this->assertSame('1 second', $this->formatter->asDuration(1.5));
// Pass a inverted DateInterval string
$this->assertSame('-1 year, 2 months, 10 days, 2 hours, 30 minutes', $this->formatter->asDuration('2008-05-11T15:30:00Z/2007-03-01T13:00:00Z'));