From 65de8515aca5130d6777044052019a251031dc71 Mon Sep 17 00:00:00 2001 From: Dan Hudson Date: Thu, 9 Oct 2025 15:35:00 +0100 Subject: [PATCH 1/5] New: add filters to widen action schedulers cleanup --- .../classes/ThirdParty/ActionScheduler.php | 58 +++++++++++++++++++ orbit.php | 1 + 2 files changed, 59 insertions(+) create mode 100644 includes/classes/ThirdParty/ActionScheduler.php diff --git a/includes/classes/ThirdParty/ActionScheduler.php b/includes/classes/ThirdParty/ActionScheduler.php new file mode 100644 index 0000000..075f7df --- /dev/null +++ b/includes/classes/ThirdParty/ActionScheduler.php @@ -0,0 +1,58 @@ + $statuses List of statuses to be cleaned up. + * @return array Modified list of statuses. + */ + public function set_action_scheduler_default_cleaner_statuses( array $statuses ): array { + return apply_filters( 'orbit_action_scheduler_default_cleaner_statuses', [ 'complete', 'canceled', 'failed' ] ); + } + + /** + * Set the cleanup batch size for Action Scheduler. + * + * @param int $size Number of actions to process per cleanup batch. + * @return int Modified batch size. + */ + public function set_action_scheduler_cleanup_batch_size( int $size ): int { + return apply_filters( 'orbit_action_scheduler_cleanup_batch_size', 1000 ); + } +} diff --git a/orbit.php b/orbit.php index 5860f42..2acb1e7 100644 --- a/orbit.php +++ b/orbit.php @@ -31,6 +31,7 @@ Performance\Fast404::instance()->setup(); ThirdParty\WooCommerce::instance()->setup(); ThirdParty\Altcha::instance()->setup(); +ThirdParty\ActionScheduler::instance()->setup(); add_action( 'init', From 44cbd55c4f9be51c27d1a9a5754ecaf15c2ce4e1 Mon Sep 17 00:00:00 2001 From: Ed Jeavons Date: Wed, 29 Oct 2025 20:54:12 +0000 Subject: [PATCH 2/5] Set action scheduler retention period to two weeks --- includes/classes/ThirdParty/ActionScheduler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/classes/ThirdParty/ActionScheduler.php b/includes/classes/ThirdParty/ActionScheduler.php index 075f7df..f34dcdb 100644 --- a/includes/classes/ThirdParty/ActionScheduler.php +++ b/includes/classes/ThirdParty/ActionScheduler.php @@ -33,7 +33,7 @@ public function setup() { * @return int Modified retention period in seconds. */ public function set_action_scheduler_retention_period( int $period ): int { - return apply_filters( 'orbit_action_scheduler_retention_period', WEEK_IN_SECONDS * 4 ); + return apply_filters( 'orbit_action_scheduler_retention_period', WEEK_IN_SECONDS * 2 ); } /** From 731a348d7a2616979e566b5d37dab58c88fb12b1 Mon Sep 17 00:00:00 2001 From: Ed Jeavons Date: Wed, 29 Oct 2025 21:06:56 +0000 Subject: [PATCH 3/5] Composer update --- composer.lock | 318 +++++++++++++----- .../generated/Exceptions/ApacheException.php | 11 + .../generated/Exceptions/ApcuException.php | 11 + .../generated/Exceptions/ArrayException.php | 11 + .../generated/Exceptions/Bzip2Exception.php | 11 + .../Exceptions/CalendarException.php | 11 + .../Exceptions/ClassobjException.php | 11 + .../generated/Exceptions/ComException.php | 11 + .../generated/Exceptions/CubridException.php | 11 + .../Exceptions/DatetimeException.php | 11 + .../generated/Exceptions/DirException.php | 11 + .../generated/Exceptions/EioException.php | 11 + .../Exceptions/ErrorfuncException.php | 11 + .../generated/Exceptions/ExecException.php | 11 + .../Exceptions/FileinfoException.php | 11 + .../Exceptions/FilesystemException.php | 11 + .../generated/Exceptions/FilterException.php | 11 + .../generated/Exceptions/FpmException.php | 11 + .../generated/Exceptions/FtpException.php | 11 + .../Exceptions/FunchandException.php | 11 + .../generated/Exceptions/GettextException.php | 11 + .../generated/Exceptions/GmpException.php | 11 + .../generated/Exceptions/GnupgException.php | 11 + .../generated/Exceptions/HashException.php | 11 + .../generated/Exceptions/IbaseException.php | 11 + .../generated/Exceptions/IbmDb2Exception.php | 11 + .../generated/Exceptions/IconvException.php | 11 + .../generated/Exceptions/ImageException.php | 11 + .../generated/Exceptions/ImapException.php | 11 + .../generated/Exceptions/InfoException.php | 11 + .../generated/Exceptions/InotifyException.php | 11 + .../generated/Exceptions/LdapException.php | 11 + .../generated/Exceptions/LibxmlException.php | 11 + .../generated/Exceptions/LzfException.php | 11 + .../Exceptions/MailparseException.php | 11 + .../Exceptions/MbstringException.php | 11 + .../generated/Exceptions/MiscException.php | 11 + .../generated/Exceptions/MysqlException.php | 11 + .../generated/Exceptions/MysqliException.php | 11 + .../generated/Exceptions/NetworkException.php | 11 + .../generated/Exceptions/Oci8Exception.php | 11 + .../generated/Exceptions/OpcacheException.php | 11 + .../Exceptions/OutcontrolException.php | 11 + .../generated/Exceptions/PcntlException.php | 11 + .../generated/Exceptions/PgsqlException.php | 11 + .../generated/Exceptions/PosixException.php | 11 + .../safe/generated/Exceptions/PsException.php | 11 + .../generated/Exceptions/PspellException.php | 11 + .../Exceptions/ReadlineException.php | 11 + .../generated/Exceptions/RnpException.php | 11 + .../generated/Exceptions/RpminfoException.php | 11 + .../generated/Exceptions/RrdException.php | 11 + .../generated/Exceptions/SemException.php | 11 + .../generated/Exceptions/SessionException.php | 11 + .../generated/Exceptions/ShmopException.php | 11 + .../generated/Exceptions/SocketsException.php | 11 + .../generated/Exceptions/SodiumException.php | 11 + .../generated/Exceptions/SolrException.php | 11 + .../generated/Exceptions/SplException.php | 11 + .../generated/Exceptions/SqlsrvException.php | 11 + .../generated/Exceptions/SsdeepException.php | 11 + .../generated/Exceptions/Ssh2Exception.php | 11 + .../generated/Exceptions/StreamException.php | 11 + .../generated/Exceptions/StringsException.php | 11 + .../generated/Exceptions/SwooleException.php | 11 + .../generated/Exceptions/UodbcException.php | 11 + .../generated/Exceptions/UopzException.php | 11 + .../generated/Exceptions/UrlException.php | 11 + .../generated/Exceptions/VarException.php | 11 + .../generated/Exceptions/XdiffException.php | 11 + .../generated/Exceptions/XmlException.php | 11 + .../generated/Exceptions/XmlrpcException.php | 11 + .../generated/Exceptions/YamlException.php | 11 + .../generated/Exceptions/YazException.php | 11 + .../generated/Exceptions/ZipException.php | 11 + .../generated/Exceptions/ZlibException.php | 11 + .../safe/lib/Exceptions/CurlException.php | 14 + .../safe/lib/Exceptions/JsonException.php | 11 + .../safe/lib/Exceptions/OpensslException.php | 11 + .../safe/lib/Exceptions/PcreException.php | 20 ++ .../lib/Exceptions/SafeExceptionInterface.php | 7 + .../lib/Exceptions/SimplexmlException.php | 12 + .../Emogrifier/Caching/SimpleStringCache.php | 13 +- .../lib/Pelago/Emogrifier/Css/CssDocument.php | 54 ++- .../lib/Pelago/Emogrifier/Css/StyleRule.php | 10 +- includes/lib/Pelago/Emogrifier/CssInliner.php | 206 +++++------- .../HtmlProcessor/AbstractHtmlProcessor.php | 88 ++--- .../HtmlProcessor/CssToAttributeConverter.php | 55 ++- .../HtmlProcessor/CssVariableEvaluator.php | 7 +- .../Emogrifier/HtmlProcessor/HtmlPruner.php | 11 +- .../Emogrifier/Utilities/CssConcatenator.php | 93 +++-- .../Utilities/DeclarationBlockParser.php | 44 +-- .../lib/Sabberworm/CSS/CSSList/CSSList.php | 4 +- .../Sabberworm/CSS/Parsing/ParserState.php | 25 +- .../CSS/Property/KeyframeSelector.php | 44 ++- .../lib/Sabberworm/CSS/Property/Selector.php | 33 +- 96 files changed, 1458 insertions(+), 447 deletions(-) create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ApacheException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ApcuException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ArrayException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/Bzip2Exception.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/CalendarException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ClassobjException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ComException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/CubridException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/DatetimeException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/DirException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/EioException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ErrorfuncException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ExecException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/FileinfoException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/FilesystemException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/FilterException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/FpmException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/FtpException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/FunchandException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/GettextException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/GmpException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/GnupgException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/HashException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/IbaseException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/IbmDb2Exception.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/IconvException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ImageException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ImapException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/InfoException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/InotifyException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/LdapException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/LibxmlException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/LzfException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/MailparseException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/MbstringException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/MiscException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/MysqlException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/MysqliException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/NetworkException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/Oci8Exception.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/OpcacheException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/OutcontrolException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/PcntlException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/PgsqlException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/PosixException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/PsException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/PspellException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ReadlineException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/RnpException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/RpminfoException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/RrdException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/SemException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/SessionException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ShmopException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/SocketsException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/SodiumException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/SolrException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/SplException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/SqlsrvException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/SsdeepException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/Ssh2Exception.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/StreamException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/StringsException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/SwooleException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/UodbcException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/UopzException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/UrlException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/VarException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/XdiffException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/XmlException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/XmlrpcException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/YamlException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/YazException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ZipException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ZlibException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/lib/Exceptions/CurlException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/lib/Exceptions/JsonException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/lib/Exceptions/OpensslException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/lib/Exceptions/PcreException.php create mode 100644 includes/classes/lib/thecodingmachine/safe/lib/Exceptions/SafeExceptionInterface.php create mode 100644 includes/classes/lib/thecodingmachine/safe/lib/Exceptions/SimplexmlException.php diff --git a/composer.lock b/composer.lock index 11af9b6..c8d9260 100644 --- a/composer.lock +++ b/composer.lock @@ -4,45 +4,46 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "90df7d8ce1d1a015ce236bcebe9e4049", + "content-hash": "2b1bb23d6ff9a6ca0352b5e29a40818a", "packages": [ { "name": "pelago/emogrifier", - "version": "v8.0.0", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/MyIntervals/emogrifier.git", - "reference": "7fdf4ee46fe57bd017b92640bca1ec42d124440a" + "reference": "e59cdcc21139c5a630edf7896aad02f01140fa01" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/7fdf4ee46fe57bd017b92640bca1ec42d124440a", - "reference": "7fdf4ee46fe57bd017b92640bca1ec42d124440a", + "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/e59cdcc21139c5a630edf7896aad02f01140fa01", + "reference": "e59cdcc21139c5a630edf7896aad02f01140fa01", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", - "php": "~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", - "sabberworm/php-css-parser": "^9.0.0", - "symfony/css-selector": "^5.4.35 || ~6.3.12 || ^6.4.3 || ^7.0.3" + "php": "~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "sabberworm/php-css-parser": "^9.1.0", + "symfony/css-selector": "^5.4.35 || ~6.3.12 || ^6.4.3 || ^7.0.3", + "thecodingmachine/safe": "^1.3 || ^2.5 || ^3.3" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "1.4.0", "phpmd/phpmd": "2.15.0", "phpstan/extension-installer": "1.4.3", - "phpstan/phpstan": "1.12.28 || 2.1.20", + "phpstan/phpstan": "1.12.28 || 2.1.25", "phpstan/phpstan-phpunit": "1.4.2 || 2.0.7", "phpstan/phpstan-strict-rules": "1.6.2 || 2.0.6", - "phpunit/phpunit": "9.6.23", + "phpunit/phpunit": "9.6.27", "rawr/phpunit-data-provider": "3.3.1", - "rector/rector": "1.2.10 || 2.1.2", + "rector/rector": "1.2.10 || 2.1.7", "rector/type-perfect": "1.0.0 || 2.1.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "8.1.x-dev" + "dev-main": "8.2.x-dev" } }, "autoload": { @@ -89,35 +90,36 @@ "issues": "https://github.com/MyIntervals/emogrifier/issues", "source": "https://github.com/MyIntervals/emogrifier" }, - "time": "2025-07-27T17:45:42+00:00" + "time": "2025-09-15T12:53:23+00:00" }, { "name": "sabberworm/php-css-parser", - "version": "v9.0.0", + "version": "v9.1.0", "source": { "type": "git", "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", - "reference": "54574e3de2f8cdc91175ffd2337e5c6804a7d729" + "reference": "1b363fdbdc6dd0ca0f4bf98d3a4d7f388133f1fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/54574e3de2f8cdc91175ffd2337e5c6804a7d729", - "reference": "54574e3de2f8cdc91175ffd2337e5c6804a7d729", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/1b363fdbdc6dd0ca0f4bf98d3a4d7f388133f1fb", + "reference": "1b363fdbdc6dd0ca0f4bf98d3a4d7f388133f1fb", "shasum": "" }, "require": { "ext-iconv": "*", - "php": "^7.2.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "^7.2.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "thecodingmachine/safe": "^1.3 || ^2.5 || ^3.3" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "1.4.0", "phpstan/extension-installer": "1.4.3", - "phpstan/phpstan": "1.12.28 || 2.1.19", + "phpstan/phpstan": "1.12.28 || 2.1.25", "phpstan/phpstan-phpunit": "1.4.2 || 2.0.7", "phpstan/phpstan-strict-rules": "1.6.2 || 2.0.6", - "phpunit/phpunit": "8.5.42", + "phpunit/phpunit": "8.5.46", "rawr/phpunit-data-provider": "3.3.1", - "rector/rector": "1.2.10 || 2.1.2", + "rector/rector": "1.2.10 || 2.1.7", "rector/type-perfect": "1.0.0 || 2.1.0" }, "suggest": { @@ -126,7 +128,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "9.1.x-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { @@ -160,9 +162,9 @@ ], "support": { "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", - "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.0.0" + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.1.0" }, - "time": "2025-07-27T07:24:01+00:00" + "time": "2025-09-14T07:37:21+00:00" }, { "name": "symfony/css-selector", @@ -228,6 +230,145 @@ } ], "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "thecodingmachine/safe", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/thecodingmachine/safe.git", + "reference": "2cdd579eeaa2e78e51c7509b50cc9fb89a956236" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/2cdd579eeaa2e78e51c7509b50cc9fb89a956236", + "reference": "2cdd579eeaa2e78e51c7509b50cc9fb89a956236", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^10", + "squizlabs/php_codesniffer": "^3.2" + }, + "type": "library", + "autoload": { + "files": [ + "lib/special_cases.php", + "generated/apache.php", + "generated/apcu.php", + "generated/array.php", + "generated/bzip2.php", + "generated/calendar.php", + "generated/classobj.php", + "generated/com.php", + "generated/cubrid.php", + "generated/curl.php", + "generated/datetime.php", + "generated/dir.php", + "generated/eio.php", + "generated/errorfunc.php", + "generated/exec.php", + "generated/fileinfo.php", + "generated/filesystem.php", + "generated/filter.php", + "generated/fpm.php", + "generated/ftp.php", + "generated/funchand.php", + "generated/gettext.php", + "generated/gmp.php", + "generated/gnupg.php", + "generated/hash.php", + "generated/ibase.php", + "generated/ibmDb2.php", + "generated/iconv.php", + "generated/image.php", + "generated/imap.php", + "generated/info.php", + "generated/inotify.php", + "generated/json.php", + "generated/ldap.php", + "generated/libxml.php", + "generated/lzf.php", + "generated/mailparse.php", + "generated/mbstring.php", + "generated/misc.php", + "generated/mysql.php", + "generated/mysqli.php", + "generated/network.php", + "generated/oci8.php", + "generated/opcache.php", + "generated/openssl.php", + "generated/outcontrol.php", + "generated/pcntl.php", + "generated/pcre.php", + "generated/pgsql.php", + "generated/posix.php", + "generated/ps.php", + "generated/pspell.php", + "generated/readline.php", + "generated/rnp.php", + "generated/rpminfo.php", + "generated/rrd.php", + "generated/sem.php", + "generated/session.php", + "generated/shmop.php", + "generated/sockets.php", + "generated/sodium.php", + "generated/solr.php", + "generated/spl.php", + "generated/sqlsrv.php", + "generated/ssdeep.php", + "generated/ssh2.php", + "generated/stream.php", + "generated/strings.php", + "generated/swoole.php", + "generated/uodbc.php", + "generated/uopz.php", + "generated/url.php", + "generated/var.php", + "generated/xdiff.php", + "generated/xml.php", + "generated/xmlrpc.php", + "generated/yaml.php", + "generated/yaz.php", + "generated/zip.php", + "generated/zlib.php" + ], + "classmap": [ + "lib/DateTime.php", + "lib/DateTimeImmutable.php", + "lib/Exceptions/", + "generated/Exceptions/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP core functions that throw exceptions instead of returning FALSE on error", + "support": { + "issues": "https://github.com/thecodingmachine/safe/issues", + "source": "https://github.com/thecodingmachine/safe/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://github.com/OskarStark", + "type": "github" + }, + { + "url": "https://github.com/shish", + "type": "github" + }, + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2025-05-14T06:15:44+00:00" } ], "packages-dev": [ @@ -620,16 +761,16 @@ }, { "name": "phpcompatibility/phpcompatibility-paragonie", - "version": "1.3.3", + "version": "1.3.4", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", - "reference": "293975b465e0e709b571cbf0c957c6c0a7b9a2ac" + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/293975b465e0e709b571cbf0c957c6c0a7b9a2ac", - "reference": "293975b465e0e709b571cbf0c957c6c0a7b9a2ac", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", "shasum": "" }, "require": { @@ -686,22 +827,26 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" } ], - "time": "2024-04-24T21:30:46+00:00" + "time": "2025-09-19T17:43:28+00:00" }, { "name": "phpcompatibility/phpcompatibility-wp", - "version": "2.1.7", + "version": "2.1.8", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", - "reference": "5bfbbfbabb3df2b9a83e601de9153e4a7111962c" + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/5bfbbfbabb3df2b9a83e601de9153e4a7111962c", - "reference": "5bfbbfbabb3df2b9a83e601de9153e4a7111962c", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/7c8d18b4d90dac9e86b0869a608fa09158e168fa", + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa", "shasum": "" }, "require": { @@ -763,26 +908,26 @@ "type": "thanks_dev" } ], - "time": "2025-05-12T16:38:37+00:00" + "time": "2025-10-18T00:05:59+00:00" }, { "name": "phpcsstandards/phpcsextra", - "version": "1.4.0", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", - "reference": "fa4b8d051e278072928e32d817456a7fdb57b6ca" + "reference": "8e89a01c7b8fed84a12a2a7f5a23a44cdbe4f62e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/fa4b8d051e278072928e32d817456a7fdb57b6ca", - "reference": "fa4b8d051e278072928e32d817456a7fdb57b6ca", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/8e89a01c7b8fed84a12a2a7f5a23a44cdbe4f62e", + "reference": "8e89a01c7b8fed84a12a2a7f5a23a44cdbe4f62e", "shasum": "" }, "require": { "php": ">=5.4", - "phpcsstandards/phpcsutils": "^1.1.0", - "squizlabs/php_codesniffer": "^3.13.0 || ^4.0" + "phpcsstandards/phpcsutils": "^1.1.2", + "squizlabs/php_codesniffer": "^3.13.4 || ^4.0" }, "require-dev": { "php-parallel-lint/php-console-highlighter": "^1.0", @@ -845,26 +990,26 @@ "type": "thanks_dev" } ], - "time": "2025-06-14T07:40:39+00:00" + "time": "2025-10-28T17:00:02+00:00" }, { "name": "phpcsstandards/phpcsutils", - "version": "1.1.1", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", - "reference": "f7eb16f2fa4237d5db9e8fed8050239bee17a9bd" + "reference": "8b8e17615d04f2fc2cd46fc1d2fd888fa21b3cf9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/f7eb16f2fa4237d5db9e8fed8050239bee17a9bd", - "reference": "f7eb16f2fa4237d5db9e8fed8050239bee17a9bd", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/8b8e17615d04f2fc2cd46fc1d2fd888fa21b3cf9", + "reference": "8b8e17615d04f2fc2cd46fc1d2fd888fa21b3cf9", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.13.0 || ^4.0" + "squizlabs/php_codesniffer": "^3.13.3 || ^4.0" }, "require-dev": { "ext-filter": "*", @@ -938,7 +1083,7 @@ "type": "thanks_dev" } ], - "time": "2025-08-10T01:04:45+00:00" + "time": "2025-10-16T16:39:32+00:00" }, { "name": "psr/container", @@ -995,16 +1140,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.13.2", + "version": "3.13.4", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "5b5e3821314f947dd040c70f7992a64eac89025c" + "reference": "ad545ea9c1b7d270ce0fc9cbfb884161cd706119" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5b5e3821314f947dd040c70f7992a64eac89025c", - "reference": "5b5e3821314f947dd040c70f7992a64eac89025c", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/ad545ea9c1b7d270ce0fc9cbfb884161cd706119", + "reference": "ad545ea9c1b7d270ce0fc9cbfb884161cd706119", "shasum": "" }, "require": { @@ -1075,7 +1220,7 @@ "type": "thanks_dev" } ], - "time": "2025-06-17T22:17:01+00:00" + "time": "2025-09-05T05:47:09+00:00" }, { "name": "symfony/console", @@ -1308,7 +1453,7 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -1367,7 +1512,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -1378,6 +1523,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -1387,16 +1536,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -1445,7 +1594,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -1456,16 +1605,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -1526,7 +1679,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -1537,6 +1690,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -1546,7 +1703,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -1607,7 +1764,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -1618,6 +1775,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -1627,7 +1788,7 @@ }, { "name": "symfony/polyfill-php73", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", @@ -1683,7 +1844,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.33.0" }, "funding": [ { @@ -1694,6 +1855,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -1703,7 +1868,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -1763,7 +1928,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -1774,6 +1939,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -1866,16 +2035,16 @@ }, { "name": "symfony/string", - "version": "v6.4.24", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f0ce0bd36a3accb4a225435be077b4b4875587f4" + "reference": "5621f039a71a11c87c106c1c598bdcd04a19aeea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f0ce0bd36a3accb4a225435be077b4b4875587f4", - "reference": "f0ce0bd36a3accb4a225435be077b4b4875587f4", + "url": "https://api.github.com/repos/symfony/string/zipball/5621f039a71a11c87c106c1c598bdcd04a19aeea", + "reference": "5621f039a71a11c87c106c1c598bdcd04a19aeea", "shasum": "" }, "require": { @@ -1889,7 +2058,6 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0|^7.0", "symfony/http-client": "^5.4|^6.0|^7.0", "symfony/intl": "^6.2|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -1932,7 +2100,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.24" + "source": "https://github.com/symfony/string/tree/v6.4.26" }, "funding": [ { @@ -1952,7 +2120,7 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:14:14+00:00" + "time": "2025-09-11T14:32:46+00:00" }, { "name": "wp-coding-standards/wpcs", diff --git a/includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ApacheException.php b/includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ApacheException.php new file mode 100644 index 0000000..ab2149d --- /dev/null +++ b/includes/classes/lib/thecodingmachine/safe/generated/Exceptions/ApacheException.php @@ -0,0 +1,11 @@ + 'PREG_INTERNAL_ERROR: Internal error', + PREG_BACKTRACK_LIMIT_ERROR => 'PREG_BACKTRACK_LIMIT_ERROR: Backtrack limit reached', + PREG_RECURSION_LIMIT_ERROR => 'PREG_RECURSION_LIMIT_ERROR: Recursion limit reached', + PREG_BAD_UTF8_ERROR => 'PREG_BAD_UTF8_ERROR: Invalid UTF8 character', + PREG_BAD_UTF8_OFFSET_ERROR => 'PREG_BAD_UTF8_OFFSET_ERROR', + PREG_JIT_STACKLIMIT_ERROR => 'PREG_JIT_STACKLIMIT_ERROR', + ]; + $errMsg = $errorMap[preg_last_error()] ?? 'Unknown PCRE error: ' . preg_last_error(); + return new self($errMsg, \preg_last_error()); + } +} diff --git a/includes/classes/lib/thecodingmachine/safe/lib/Exceptions/SafeExceptionInterface.php b/includes/classes/lib/thecodingmachine/safe/lib/Exceptions/SafeExceptionInterface.php new file mode 100644 index 0000000..cc7f71f --- /dev/null +++ b/includes/classes/lib/thecodingmachine/safe/lib/Exceptions/SafeExceptionInterface.php @@ -0,0 +1,7 @@ + + * @var array */ private $values = []; /** * Checks whether there is an entry stored for the given key. * - * @param string $key the key to check; must not be empty + * @param non-empty-string $key * * @throws \InvalidArgumentException */ @@ -45,9 +45,7 @@ public function has(string $key): bool * Returns the entry stored for the given key, and throws an exception if the value does not exist * (which helps keep the return type simple). * - * @param string $key the key to of the item to retrieve; must not be empty - * - * @return string the retrieved value; may be empty + * @param non-empty-string $key * * @throws \BadMethodCallException */ @@ -63,10 +61,9 @@ public function get(string $key): string /** * Sets or overwrites an entry. * - * @param string $key the key to of the item to set; must not be empty - * @param string $value the value to set; can be empty + * @param non-empty-string $key * - * @throws \BadMethodCallException + * @throws \InvalidArgumentException */ public function set(string $key, string $value): void { diff --git a/includes/lib/Pelago/Emogrifier/Css/CssDocument.php b/includes/lib/Pelago/Emogrifier/Css/CssDocument.php index 27d8d1d..4693b2c 100644 --- a/includes/lib/Pelago/Emogrifier/Css/CssDocument.php +++ b/includes/lib/Pelago/Emogrifier/Css/CssDocument.php @@ -38,7 +38,6 @@ final class CssDocument private $isImportRuleAllowed = true; /** - * @param string $css * @param bool $debug * If this is `true`, an exception will be thrown if invalid CSS is encountered. * Otherwise the parser will try to do the best it can. @@ -69,7 +68,7 @@ private function hasNestedAtRule(string $css): bool /** * Collates the media query, selectors and declarations for individual rules from the parsed CSS, in order. * - * @param array $allowedMediaTypes + * @param list $allowedMediaTypes * * @return list */ @@ -98,8 +97,6 @@ public function getStyleRulesData(array $allowedMediaTypes): array * Renders at-rules from the parsed CSS that are valid and not conditional group rules (i.e. not rules such as * `@media` which contain style rules whose data is returned by {@see getStyleRulesData}). Also does not render * `@charset` rules; these are discarded (only UTF-8 is supported). - * - * @return string */ public function renderNonConditionalAtRules(): string { @@ -118,40 +115,35 @@ public function renderNonConditionalAtRules(): string } /** - * @param CssAtRuleBlockList $rule - * @param array $allowedMediaTypes + * @param list $allowedMediaTypes * - * @return ?string + * @return string|null * If the nested at-rule is supported, it's opening declaration (e.g. "@media (max-width: 768px)") is * returned; otherwise the return value is null. */ private function getFilteredAtIdentifierAndRule(CssAtRuleBlockList $rule, array $allowedMediaTypes): ?string { - $result = null; - - if ($rule->atRuleName() === 'media') { - $mediaQueryList = $rule->atRuleArgs(); - [$mediaType] = \explode('(', $mediaQueryList, 2); - if (\trim($mediaType) !== '') { - $escapedAllowedMediaTypes = \array_map( - static function (string $allowedMediaType): string { - return \preg_quote($allowedMediaType, '/'); - }, - $allowedMediaTypes - ); - $mediaTypesMatcher = \implode('|', $escapedAllowedMediaTypes); - $isAllowed - = (new Preg())->match('/^\\s*+(?:only\\s++)?+(?:' . $mediaTypesMatcher . ')/i', $mediaType) !== 0; - } else { - $isAllowed = true; - } + if ($rule->atRuleName() !== 'media') { + return null; + } - if ($isAllowed) { - $result = '@media ' . $mediaQueryList; - } + $mediaQueryList = $rule->atRuleArgs(); + [$mediaType] = \explode('(', $mediaQueryList, 2); + if (\trim($mediaType) !== '') { + $escapedAllowedMediaTypes = \array_map( + static function (string $allowedMediaType): string { + return \preg_quote($allowedMediaType, '/'); + }, + $allowedMediaTypes + ); + $mediaTypesMatcher = \implode('|', $escapedAllowedMediaTypes); + $isAllowed + = (new Preg())->match('/^\\s*+(?:only\\s++)?+(?:' . $mediaTypesMatcher . ')/i', $mediaType) !== 0; + } else { + $isAllowed = true; } - return $result; + return $isAllowed ? '@media ' . $mediaQueryList : null; } /** @@ -163,10 +155,6 @@ static function (string $allowedMediaType): string { * - `@media` rules are processed separately to see if their nested rules apply - `false` is returned; * - `@font-face` rules are checked for validity - they must contain both a `src` and `font-family` property; * - other at-rules are assumed to be valid and treated as a black box - `true` is returned. - * - * @param CssRenderable $rule - * - * @return bool */ private function isValidAtRuleToRender(CssRenderable $rule): bool { diff --git a/includes/lib/Pelago/Emogrifier/Css/StyleRule.php b/includes/lib/Pelago/Emogrifier/Css/StyleRule.php index 09a2d87..c579d60 100644 --- a/includes/lib/Pelago/Emogrifier/Css/StyleRule.php +++ b/includes/lib/Pelago/Emogrifier/Css/StyleRule.php @@ -26,7 +26,6 @@ final class StyleRule private $containingAtRule; /** - * @param DeclarationBlock $declarationBlock * @param string $containingAtRule e.g. `@media screen and (max-width: 480px)` */ public function __construct(DeclarationBlock $declarationBlock, string $containingAtRule = '') @@ -36,15 +35,16 @@ public function __construct(DeclarationBlock $declarationBlock, string $containi } /** - * @return array the selectors, e.g. `["h1", "p"]` + * @return array the selectors, e.g. `["h1", "p"]` */ public function getSelectors(): array { - /** @var array $selectors */ $selectors = $this->declarationBlock->getSelectors(); return \array_map( static function (Selector $selector): string { - return $selector->getSelector(); + $selectorAsString = $selector->getSelector(); + \assert($selectorAsString !== ''); + return $selectorAsString; }, $selectors ); @@ -74,7 +74,7 @@ public function hasAtLeastOneDeclaration(): bool } /** - * @returns string e.g. `@media screen and (max-width: 480px)`, or an empty string + * @return string e.g. `@media screen and (max-width: 480px)`, or an empty string */ public function getContainingAtRule(): string { diff --git a/includes/lib/Pelago/Emogrifier/CssInliner.php b/includes/lib/Pelago/Emogrifier/CssInliner.php index 4a9623c..a946b7a 100644 --- a/includes/lib/Pelago/Emogrifier/CssInliner.php +++ b/includes/lib/Pelago/Emogrifier/CssInliner.php @@ -18,12 +18,12 @@ final class CssInliner extends AbstractHtmlProcessor { /** - * @var int + * @var int<0, 1> */ private const CACHE_KEY_SELECTOR = 0; /** - * @var int + * @var int<0, 1> */ private const CACHE_KEY_COMBINED_STYLES = 1; @@ -33,52 +33,52 @@ final class CssInliner extends AbstractHtmlProcessor * (Contains alternation without a group and is intended to be placed within a capturing, non-capturing or lookahead * group, as appropriate for the usage context.) * - * @var string + * @var non-empty-string */ private const PSEUDO_CLASS_MATCHER = 'empty|(?:first|last|nth(?:-last)?+|only)-(?:child|of-type)|not\\([[:ascii:]]*\\)|root'; /** - * This regular expression componenet matches an `...of-type` pseudo class name, without the preceding ":". These + * This regular expression component matches an `...of-type` pseudo class name, without the preceding ":". These * pseudo-classes can currently online be inlined if they have an associated type in the selector expression. * - * @var string + * @var non-empty-string */ private const OF_TYPE_PSEUDO_CLASS_MATCHER = '(?:first|last|nth(?:-last)?+|only)-of-type'; /** * regular expression component to match a selector combinator * - * @var string + * @var non-empty-string */ private const COMBINATOR_MATCHER = '(?:\\s++|\\s*+[>+~]\\s*+)(?=[[:alpha:]_\\-.#*:\\[])'; /** * options array key for `querySelectorAll` * - * @var string + * @var non-empty-string */ private const QSA_ALWAYS_THROW_PARSE_EXCEPTION = 'alwaysThrowParseException'; /** - * @var array + * @var array */ private $excludedSelectors = []; /** - * @var array + * @var array */ private $excludedCssSelectors = []; /** - * @var array + * @var array */ private $allowedMediaTypes = ['all' => true, 'screen' => true, 'print' => true]; /** * @var array{ - * 0: array, - * 1: array + * 0: array>, + * 1: array * } */ private $caches = [ @@ -87,14 +87,14 @@ final class CssInliner extends AbstractHtmlProcessor ]; /** - * @var ?CssSelectorConverter + * @var CssSelectorConverter|null */ private $cssSelectorConverter = null; /** * the visited nodes with the XPath paths as array keys * - * @var array + * @var array */ private $visitedNodes = []; @@ -102,7 +102,7 @@ final class CssInliner extends AbstractHtmlProcessor * the styles to apply to the nodes with the XPath paths as array keys for the outer array * and the attribute names/values as key/value pairs for the inner array * - * @var array> + * @var array> */ private $styleAttributesForNodes = []; @@ -131,7 +131,7 @@ final class CssInliner extends AbstractHtmlProcessor * Keys are a regular expression part to match before a CSS name. * Values are a multiplier factor per match to weight specificity. * - * @var array + * @var array> */ private $selectorPrecedenceMatchers = [ // IDs: worth 10000 @@ -148,10 +148,10 @@ final class CssInliner extends AbstractHtmlProcessor * * @var array * }>|null */ private $matchingUninlinableCssRules = null; @@ -242,7 +242,7 @@ public function disableStyleBlocksParsing(): self /** * Marks a media query type to keep. * - * @param string $mediaName the media type name, e.g., "braille" + * @param non-empty-string $mediaName the media type name, e.g., "braille" * * @return $this */ @@ -256,7 +256,7 @@ public function addAllowedMediaType(string $mediaName): self /** * Drops a media query type from the allowed list. * - * @param string $mediaName the tag name, e.g., "braille" + * @param non-empty-string $mediaName the tag name, e.g., "braille" * * @return $this */ @@ -274,7 +274,7 @@ public function removeAllowedMediaType(string $mediaName): self * * Any nodes that match the selector will not have their style altered. * - * @param string $selector the selector to exclude, e.g., ".editor" + * @param non-empty-string $selector the selector to exclude, e.g., ".editor" * * @return $this */ @@ -288,7 +288,7 @@ public function addExcludedSelector(string $selector): self /** * No longer excludes the nodes matching this selector from emogrification. * - * @param string $selector the selector to no longer exclude, e.g., ".editor" + * @param non-empty-string $selector the selector to no longer exclude, e.g., ".editor" * * @return $this */ @@ -363,10 +363,10 @@ public function getMatchingUninlinableSelectors(): array /** * @return array * }> * * @throws \BadMethodCallException if `inlineCss` has not been called first @@ -408,7 +408,6 @@ private function purgeVisitedNodes(): void */ private function normalizeStyleAttributesOfAllNodes(): void { - /** @var \DOMElement $node */ foreach ($this->getAllNodesWithStyleAttribute() as $node) { if ($this->isInlineStyleAttributesParsingEnabled) { $this->normalizeStyleAttributes($node); @@ -424,7 +423,7 @@ private function normalizeStyleAttributesOfAllNodes(): void /** * Returns a list with all DOM nodes that have a style attribute. * - * @return \DOMNodeList + * @return \DOMNodeList<\DOMElement> * * @throws \RuntimeException */ @@ -435,31 +434,29 @@ private function getAllNodesWithStyleAttribute(): \DOMNodeList if (!$matches instanceof \DOMNodeList) { throw new \RuntimeException('XPatch query failed: ' . $query, 1618577797); } + /** @var \DOMNodeList<\DOMElement> $matches */ return $matches; } /** * Normalizes the value of the "style" attribute and saves it. - * - * @param \DOMElement $node */ private function normalizeStyleAttributes(\DOMElement $node): void { $declarationBlockParser = new DeclarationBlockParser(); - $normalizedOriginalStyle = (new Preg())->throwExceptions($this->debug)->replaceCallback( - '/-{0,2}+[_a-zA-Z][\\w\\-]*+(?=:)/S', - /** @param array $propertyNameMatches */ - static function (array $propertyNameMatches) use ($declarationBlockParser): string { - return $declarationBlockParser->normalizePropertyName($propertyNameMatches[0]); - }, - $node->getAttribute('style') - ); + $pattern = '/-{0,2}+[_a-zA-Z][\\w\\-]*+(?=:)/S'; + /** @param array $propertyNameMatches */ + $callback = static function (array $propertyNameMatches) use ($declarationBlockParser): string { + return $declarationBlockParser->normalizePropertyName($propertyNameMatches[0]); + }; + $normalizedOriginalStyle = (new Preg())->throwExceptions($this->debug) + ->replaceCallback($pattern, $callback, $node->getAttribute('style')); // In order to not overwrite existing style attributes in the HTML, we have to save the original HTML styles. $nodePath = $node->getNodePath(); - if (\is_string($nodePath) && !isset($this->styleAttributesForNodes[$nodePath])) { + if (\is_string($nodePath) && ($nodePath !== '') && !isset($this->styleAttributesForNodes[$nodePath])) { $this->styleAttributesForNodes[$nodePath] = $declarationBlockParser->parse($normalizedOriginalStyle); $this->visitedNodes[$nodePath] = $node; } @@ -469,8 +466,6 @@ static function (array $propertyNameMatches) use ($declarationBlockParser): stri /** * Returns CSS content. - * - * @return string */ private function getCssFromAllStyleNodes(): string { @@ -515,11 +510,11 @@ private function getNodesToExclude(): array } /** - * @param array{}|array{alwaysThrowParseException: bool} $options + * @param array{alwaysThrowParseException?: bool} $options * This is an array of option values to control behaviour: * - `QSA_ALWAYS_THROW_PARSE_EXCEPTION` - `bool` - throw any `ParseException` regardless of debug setting. * - * @return \DOMNodeList<\DOMNode> the HTML elements that match the provided CSS `$selectors` + * @return \DOMNodeList<\DOMElement> the HTML elements that match the provided CSS `$selectors` * * @throws ParseException * in debug mode (or with `QSA_ALWAYS_THROW_PARSE_EXCEPTION` option), if an invalid selector is encountered @@ -533,6 +528,7 @@ private function querySelectorAll(string $selectors, array $options = []): \DOMN if ($result === false) { throw new \RuntimeException('query failed with selector \'' . $selectors . '\'', 1726533051); } + /** @var \DOMNodeList<\DOMElement> $result */ return $result; } catch (ParseException $exception) { @@ -540,7 +536,9 @@ private function querySelectorAll(string $selectors, array $options = []): \DOMN if ($this->debug || $alwaysThrowParseException) { throw $exception; } - return new \DOMNodeList(); + $list = new \DOMNodeList(); + /** @var \DOMNodeList<\DOMElement> $list */ + return $list; } catch (\RuntimeException $exception) { if ( $this->debug @@ -549,7 +547,9 @@ private function querySelectorAll(string $selectors, array $options = []): \DOMN } // `RuntimeException` indicates a bug in CssSelector so pass the message to the error handler. \trigger_error($exception->getMessage()); - return new \DOMNodeList(); + $list = new \DOMNodeList(); + /** @var \DOMNodeList<\DOMElement> $list */ + return $list; } } @@ -558,7 +558,7 @@ private function querySelectorAll(string $selectors, array $options = []): \DOMN */ private function ensureNodeIsElement(\DOMNode $node): \DOMElement { - if (!$node instanceof \DOMElement) { + if (!($node instanceof \DOMElement)) { $path = $node->getNodePath() ?? '$node'; throw new \UnexpectedValueException($path . ' is not a DOMElement.', 1617975914); } @@ -566,9 +566,6 @@ private function ensureNodeIsElement(\DOMNode $node): \DOMElement return $node; } - /** - * @return CssSelectorConverter - */ private function getCssSelectorConverter(): CssSelectorConverter { if (!$this->cssSelectorConverter instanceof CssSelectorConverter) { @@ -581,14 +578,12 @@ private function getCssSelectorConverter(): CssSelectorConverter /** * Collates the individual rules from a `CssDocument` object. * - * @param CssDocument $parsedCss - * * @return array * }>> * This 2-entry array has the key "inlinable" containing rules which can be inlined as `style` attributes * and the key "uninlinable" containing rules which cannot. Each value is an array of sub-arrays with the @@ -626,7 +621,7 @@ private function collateCssRules(CssDocument $parsedCss): array $selectorsNormalized = \array_map(static function (string $selector) use ($preg): string { return $preg->replace('@\\s++@u', ' ', $selector); }, $selectors); - + /** @var array $selectors */ $selectors = \array_filter($selectorsNormalized, function (string $selector): bool { return !isset($this->excludedCssSelectors[$selector]); }); @@ -654,8 +649,8 @@ private function collateCssRules(CssDocument $parsedCss): array \usort( $cssRules['inlinable'], /** - * @param array{selector: string, line: int, ...} $first - * @param array{selector: string, line: int, ...} $second + * @param array{selector: non-empty-string, line: int<0, max>} $first + * @param array{selector: non-empty-string, line: int<0, max>} $second */ function (array $first, array $second): int { return $this->sortBySelectorPrecedence($first, $second); @@ -671,10 +666,6 @@ function (array $first, array $second): int { * * Any pseudo class that does not match {@see PSEUDO_CLASS_MATCHER} cannot be converted. Additionally, `...of-type` * pseudo-classes cannot be converted if they are not associated with a type selector. - * - * @param string $selector - * - * @return bool */ private function hasUnsupportedPseudoClass(string $selector): bool { @@ -717,10 +708,8 @@ private function selectorPartHasUnsupportedOfTypePseudoClass(string $selectorPar } /** - * @param array{selector: string, line: int, ...} $first - * @param array{selector: string, line: int, ...} $second - * - * @return int + * @param array{selector: non-empty-string, line: int<0, max>} $first + * @param array{selector: non-empty-string, line: int<0, max>} $second */ private function sortBySelectorPrecedence(array $first, array $second): int { @@ -735,9 +724,9 @@ private function sortBySelectorPrecedence(array $first, array $second): int } /** - * @param string $selector + * @param non-empty-string $selector * - * @return int + * @return int<0, max> */ private function getCssSelectorPrecedence(string $selector): int { @@ -755,6 +744,7 @@ private function getCssSelectorPrecedence(string $selector): int $count = 0; $selector = $preg->replace('/' . $matcher . '\\w+/', '', $selector, -1, $count); $precedence += ($value * $count); + \assert($precedence >= 0); } $this->caches[self::CACHE_KEY_SELECTOR][$selectorKey] = $precedence; @@ -762,17 +752,16 @@ private function getCssSelectorPrecedence(string $selector): int } /** - * Copies $cssRule into the style attribute of $node. + * Copies `$cssRule` into the style attribute of `$node`. * * Note: This method does not check whether $cssRule matches $node. * - * @param \DOMElement $node * @param array{ * media: string, - * selector: string, + * selector: non-empty-string, * hasUnmatchablePseudo: bool, * declarationsBlock: string, - * line: int + * line: int<0, max> * } $cssRule */ private function copyInlinableCssToStyleAttribute(\DOMElement $node, array $cssRule): void @@ -806,13 +795,12 @@ private function copyInlinableCssToStyleAttribute(\DOMElement $node, array $cssR * @param array $oldStyles * @param array $newStyles * - * @return string - * * @throws \UnexpectedValueException if an empty property name is encountered (which should not happen) */ private function generateStyleStringFromDeclarationsArrays(array $oldStyles, array $newStyles): string { $cacheKey = \serialize([$oldStyles, $newStyles]); + \assert($cacheKey !== ''); if (isset($this->caches[self::CACHE_KEY_COMBINED_STYLES][$cacheKey])) { return $this->caches[self::CACHE_KEY_COMBINED_STYLES][$cacheKey]; } @@ -855,11 +843,7 @@ private function generateStyleStringFromDeclarationsArrays(array $oldStyles, arr } /** - * Checks whether $attributeValue is marked as !important. - * - * @param string $attributeValue - * - * @return bool + * Checks whether `$attributeValue` is marked as `!important`. */ private function attributeValueIsImportant(string $attributeValue): bool { @@ -893,7 +877,6 @@ private function fillStyleAttributesWithMergedStyles(): void */ private function removeImportantAnnotationFromAllInlineStyles(): void { - /** @var \DOMElement $node */ foreach ($this->getAllNodesWithStyleAttribute() as $node) { $this->removeImportantAnnotationFromNodeInlineStyle($node); } @@ -907,8 +890,6 @@ private function removeImportantAnnotationFromAllInlineStyles(): void * For example "font: 12px serif !important; font-size: 13px;" must be reordered * to "font-size: 13px; font: 12px serif;" in order to remain correct. * - * @param \DOMElement $node - * * @throws \RuntimeException */ private function removeImportantAnnotationFromNodeInlineStyle(\DOMElement $node): void @@ -938,8 +919,6 @@ private function removeImportantAnnotationFromNodeInlineStyle(\DOMElement $node) * Generates a CSS style string suitable to be used inline from the $styleDeclarations property => value array. * * @param array $styleDeclarations - * - * @return string */ private function generateStyleStringFromSingleDeclarationsArray(array $styleDeclarations): string { @@ -952,10 +931,10 @@ private function generateStyleStringFromSingleDeclarationsArray(array $styleDecl * * @param array * }> $cssRules * the "uninlinable" array of CSS rules returned by `collateCssRules` */ @@ -978,14 +957,12 @@ function (array $cssRule): bool { * * @param array{ * media: string, - * selector: string, + * selector: non-empty-string, * hasUnmatchablePseudo: bool, * declarationsBlock: string, - * line: int + * line: int<0, max> * } $cssRule * - * @return bool - * * @throws ParseException */ private function existsMatchForSelectorInCssRule(array $cssRule): bool @@ -1002,10 +979,6 @@ private function existsMatchForSelectorInCssRule(array $cssRule): bool * When not in debug mode, it returns true also for invalid selectors (because they may be valid, * just not implemented/recognized yet by Emogrifier). * - * @param string $cssSelector - * - * @return bool - * * @throws ParseException in debug mode, if an invalid selector is encountered * @throws \RuntimeException in debug mode, if `CssSelectorConverter::toXPath` returns an invalid XPath expression */ @@ -1028,8 +1001,6 @@ private function existsMatchForCssSelector(string $cssSelector): bool * Removes pseudo-elements and dynamic pseudo-classes from a CSS selector, replacing them with "*" if necessary. * If such a pseudo-component is within the argument of `:not`, the entire `:not` component is removed or replaced. * - * @param string $selector - * * @return string * selector which will match the relevant DOM elements if the pseudo-classes are assumed to apply, or in the * case of pseudo-elements will match their originating element @@ -1040,39 +1011,39 @@ private function removeUnmatchablePseudoComponents(string $selector): string // The regex allows nested brackets via `(?2)`. // A space is temporarily prepended because the callback can't determine if the match was at the very start. - $selectorWithoutNots = \ltrim((new Preg())->throwExceptions($this->debug)->replaceCallback( - '/([\\s>+~]?+):not(\\([^()]*+(?:(?2)[^()]*+)*+\\))/i', - /** @param array $matches */ - function (array $matches): string { - return $this->replaceUnmatchableNotComponent($matches); - }, - ' ' . $selector - )); + $pattern = '/([\\s>+~]?+):not(\\([^()]*+(?:(?2)[^()]*+)*+\\))/i'; + /** @param array $matches */ + $callback = function (array $matches): string { + return $this->replaceUnmatchableNotComponent($matches); + }; + $untrimmedSelectorWithoutNots = (new Preg())->throwExceptions($this->debug) + ->replaceCallback($pattern, $callback, ' ' . $selector); + $selectorWithoutNots = \ltrim($untrimmedSelectorWithoutNots); $selectorWithoutUnmatchablePseudoComponents = $this->removeSelectorComponents( ':(?!' . self::PSEUDO_CLASS_MATCHER . '):?+[\\w\\-]++(?:\\([^\\)]*+\\))?+', $selectorWithoutNots ); - if ( - $preg->match( - '/:(?:' . self::OF_TYPE_PSEUDO_CLASS_MATCHER . ')/i', - $selectorWithoutUnmatchablePseudoComponents - ) - === 0 - ) { + if ($preg->match( + '/:(?:' . self::OF_TYPE_PSEUDO_CLASS_MATCHER . ')/i', + $selectorWithoutUnmatchablePseudoComponents + ) === 0) { return $selectorWithoutUnmatchablePseudoComponents; } + + $selectorParts = $preg->split( + '/(' . self::COMBINATOR_MATCHER . ')/', + $selectorWithoutUnmatchablePseudoComponents, + -1, + PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY + ); + return \implode('', \array_map( function (string $selectorPart): string { return $this->removeUnsupportedOfTypePseudoClasses($selectorPart); }, - $preg->split( - '/(' . self::COMBINATOR_MATCHER . ')/', - $selectorWithoutUnmatchablePseudoComponents, - -1, - PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY - ) + $selectorParts )); } @@ -1100,7 +1071,6 @@ private function replaceUnmatchableNotComponent(array $matches): string * Removes components from a CSS selector, replacing them with "*" if necessary. * * @param string $matcher regular expression part to match the components to remove - * @param string $selector * * @return string * selector which will match the relevant DOM elements if the removed components are assumed to apply (or in @@ -1170,13 +1140,11 @@ private function copyUninlinableCssToStyleNode(CssDocument $parsedCss): void } /** - * Adds a style element with $css to $this->domDocument. + * Adds a style element with `$css` to `$this->domDocument`. * * This method is protected to allow overriding. * * @see https://github.com/MyIntervals/emogrifier/issues/103 - * - * @param string $css */ protected function addStyleElementToDocument(string $css): void { @@ -1191,12 +1159,10 @@ protected function addStyleElementToDocument(string $css): void } /** - * Returns the HEAD element. + * Returns the `HEAD` element. * * This method assumes that there always is a HEAD element. * - * @return \DOMElement - * * @throws \UnexpectedValueException */ private function getHeadElement(): \DOMElement diff --git a/includes/lib/Pelago/Emogrifier/HtmlProcessor/AbstractHtmlProcessor.php b/includes/lib/Pelago/Emogrifier/HtmlProcessor/AbstractHtmlProcessor.php index 683310a..eb8cb8f 100644 --- a/includes/lib/Pelago/Emogrifier/HtmlProcessor/AbstractHtmlProcessor.php +++ b/includes/lib/Pelago/Emogrifier/HtmlProcessor/AbstractHtmlProcessor.php @@ -14,19 +14,19 @@ abstract class AbstractHtmlProcessor { /** - * @var string + * @var non-empty-string */ protected const DEFAULT_DOCUMENT_TYPE = ''; /** - * @var string + * @var non-empty-string */ protected const CONTENT_TYPE_META_TAG = ''; /** - * @var string Regular expression part to match tag names that PHP's DOMDocument implementation is not aware are - * self-closing. These are mostly HTML5 elements, but for completeness (obsolete) and - * (deprecated) are also included. + * @var non-empty-string Regular expression part to match tag names that PHP's DOMDocument implementation is not + * aware are self-closing. These are mostly HTML5 elements, but for completeness `` (obsolete) and + * `` (deprecated) are also included. * * @see https://bugs.php.net/bug.php?id=73175 */ @@ -36,7 +36,7 @@ abstract class AbstractHtmlProcessor * Regular expression part to match tag names that may appear before the start of the `` element. A start tag * for any other element would implicitly start the `` element due to tag omission rules. * - * @var string + * @var non-empty-string */ protected const TAGNAME_ALLOWED_BEFORE_BODY_MATCHER = '(?:html|head|base|command|link|meta|noscript|script|style|template|title)'; @@ -44,25 +44,25 @@ abstract class AbstractHtmlProcessor /** * regular expression pattern to match an HTML comment, including delimiters and modifiers * - * @var string + * @var non-empty-string */ protected const HTML_COMMENT_PATTERN = '/|$)/'; /** * regular expression pattern to match an HTML `|$)%i'; /** - * @var ?\DOMDocument + * @var \DOMDocument|null */ protected $domDocument = null; /** - * @var ?\DOMXPath + * @var \DOMXPath|null */ private $xPath = null; @@ -76,7 +76,7 @@ private function __construct() {} /** * Builds a new instance from the given HTML. * - * @param string $unprocessedHtml raw HTML, must be UTF-encoded, must not be empty + * @param non-empty-string $unprocessedHtml raw HTML, must be UTF-encoded * * @return static * @@ -84,6 +84,7 @@ private function __construct() {} */ public static function fromHtml(string $unprocessedHtml): self { + // @phpstan-ignore-next-line argument.type We're checking for a contract violation here. if ($unprocessedHtml === '') { throw new \InvalidArgumentException('The provided HTML must not be empty.', 1515763647); } @@ -122,8 +123,6 @@ private function setHtml(string $html): void /** * Provides access to the internal DOMDocument representation of the HTML in its current state. * - * @return \DOMDocument - * * @throws \UnexpectedValueException */ public function getDomDocument(): \DOMDocument @@ -136,9 +135,6 @@ public function getDomDocument(): \DOMDocument return $this->domDocument; } - /** - * @param \DOMDocument $domDocument - */ private function setDomDocument(\DOMDocument $domDocument): void { $this->domDocument = $domDocument; @@ -146,8 +142,6 @@ private function setDomDocument(\DOMDocument $domDocument): void } /** - * @return \DOMXPath - * * @throws \UnexpectedValueException */ protected function getXPath(): \DOMXPath @@ -162,8 +156,6 @@ protected function getXPath(): \DOMXPath /** * Renders the normalized and processed HTML. - * - * @return string */ public function render(): string { @@ -174,8 +166,6 @@ public function render(): string /** * Renders the content of the BODY element of the normalized and processed HTML. - * - * @return string */ public function renderBodyContent(): string { @@ -187,10 +177,6 @@ public function renderBodyContent(): string /** * Eliminates any invalid closing tags for void elements from the given HTML. - * - * @param string $html - * - * @return string */ private function removeSelfClosingTagsClosingTags(string $html): string { @@ -219,8 +205,6 @@ protected function getHtmlElement(): \DOMElement * * This method assumes that there always is a BODY element. * - * @return \DOMElement - * * @throws \RuntimeException */ private function getBodyElement(): \DOMElement @@ -237,8 +221,6 @@ private function getBodyElement(): \DOMElement * Creates a DOM document from the given HTML and stores it in $this->domDocument. * * The DOM document will always have a BODY element and a document type. - * - * @param string $html */ private function createUnifiedDomDocument(string $html): void { @@ -248,8 +230,6 @@ private function createUnifiedDomDocument(string $html): void /** * Creates a DOMDocument instance from the given HTML and stores it in $this->domDocument. - * - * @param string $html */ private function createRawDomDocument(string $html): void { @@ -267,10 +247,6 @@ private function createRawDomDocument(string $html): void /** * Returns the HTML with added document type, Content-Type meta tag, and self-closing slashes, if needed, * ensuring that the HTML will be good for creating a DOM document from it. - * - * @param string $html - * - * @return string the unified HTML */ private function prepareHtmlForDomConversion(string $html): string { @@ -283,9 +259,7 @@ private function prepareHtmlForDomConversion(string $html): string /** * Makes sure that the passed HTML has a document type, with lowercase "html". * - * @param string $html - * - * @return string HTML with document type + * @return non-empty-string HTML with document type */ private function ensureDocumentType(string $html): string { @@ -298,21 +272,24 @@ private function ensureDocumentType(string $html): string } /** - * Makes sure the document type in the passed HTML has lowercase "html". + * Makes sure the document type in the passed HTML has lowercase `html`. * - * @param string $html + * @param non-empty-string $html * - * @return string HTML with normalized document type + * @return non-empty-string HTML with normalized document type */ private function normalizeDocumentType(string $html): string { // Limit to replacing the first occurrence: as an optimization; and in case an example exists as unescaped text. - return (new Preg())->replace( + $result = (new Preg())->replace( '/])/i', '` element. Due to tag * omission rules, HTML parsers are expected to end the `` element and start the `` element upon * encountering a start tag for any element which is permitted only within the ``. - * - * @param string $html - * - * @return bool */ private function hasContentTypeMetaTagInHead(string $html): bool { @@ -391,10 +365,6 @@ private function hasContentTypeMetaTagInHead(string $html): bool * expected to end the `` element and start the `` element upon encountering a start tag for any element * which is permitted only within the ``. * - * @param string $html - * - * @return bool - * * @throws \RuntimeException */ private function hasEndOfHeadElement(string $html): bool @@ -419,10 +389,6 @@ private function hasEndOfHeadElement(string $html): bool * Removes comments from the given HTML, including any which are unterminated, for which the remainder of the string * is removed. * - * @param string $html - * - * @return string - * * @throws \RuntimeException */ private function removeHtmlComments(string $html): string @@ -434,10 +400,6 @@ private function removeHtmlComments(string $html): string * Removes `