Run test with PHP 8.5-rc (#9970)

* Allow to inject composer arguments into testing scripts

* Run unit tests with PHP v8.5, too

* Run browser tests with PHP 8.5, too

* Depend on php-cs-fixer v3.8, which supports PHP v8.4

* Run code style checks in CI on PHP v8.4

* Check for vars being set and not null before using them as array keys

* Use generic tag name in container image build script

The script is meant for locally building images (the CI workflow runs
other code), so we now use localhost/ as namespace.

* Check that variable is usable before using it as array key

This includes proper type declarations for the method arguments and its return value.

* Ensure that the input to chr() is between 0 and 255.

* Require guzzle v7.10.0, which supports PHP 8.5

* Update phpunit a little to decide when to fail on deprecations

PHPUnit 10.5.47 and later know the flag `--do-not-fail-on-deprecation`, which allows us to make it not exit with code 1
in case of deprecations on the second run of the script. That second run uses the lowest valid dependencies, which might
contain deprecations when used with newer versions of PHP, but still are acceptable versions, and should not make our
tests fail.

* Run message rendering tests with PHP v8.4 and v8.5, too

* Check explicitly for null-ness

0 would be a valid value here.

* Replace chr() by mb_chr() and remove the workaround
This commit is contained in:
Pablo Zmdl 2025-09-14 11:33:38 +02:00 committed by GitHub
parent 6189a81815
commit a03221041e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 76 additions and 35 deletions

View File

@ -23,14 +23,14 @@ if ! test -f config/config-test.inc.php; then
fi
# Install dependencies for to remote control the browser.
composer require -n "nesbot/carbon:^2.62.1" --no-update
composer require -n "laravel/dusk:^7.9" --no-update
composer require $COMPOSER_ARGS -n "nesbot/carbon:^2.62.1" --no-update
composer require $COMPOSER_ARGS -n "laravel/dusk:^7.9" --no-update
if $(echo $PHP_VERSION | grep -q '^8.3'); then
# Downgrade dependencies (for PHP 8.3 only)
composer update --prefer-dist --prefer-stable --prefer-lowest --no-interaction --no-progress --optimize-autoloader
composer update $COMPOSER_ARGS --prefer-dist --prefer-stable --prefer-lowest --no-interaction --no-progress --optimize-autoloader
else
composer update --prefer-dist --no-interaction --no-progress
composer update $COMPOSER_ARGS --prefer-dist --no-interaction --no-progress
fi
# Install development tools.

View File

@ -5,7 +5,7 @@ if ! test -f config/config-test.inc.php; then
fi
# Install dependencies, prefer highest.
composer update --prefer-dist --no-interaction --no-progress
composer update $COMPOSER_ARGS --prefer-dist --no-interaction --no-progress
# Execute tests.
vendor/bin/phpunit -c ./tests/MessageRendering/phpunit.xml --fail-on-warning --fail-on-risky

View File

@ -4,16 +4,17 @@ if ! test -f config/config-test.inc.php; then
cp -v .ci/config-test.inc.php config/config-test.inc.php
fi
composer require "kolab/net_ldap3:~1.1.4" --no-update
composer require $COMPOSER_ARGS "kolab/net_ldap3:~1.1.4" --no-update
# Install dependencies, prefer highest.
composer update --prefer-dist --no-interaction --no-progress
composer update $COMPOSER_ARGS --prefer-dist --no-interaction --no-progress
# Execute tests.
vendor/bin/phpunit -c tests/phpunit.xml --fail-on-warning --fail-on-risky
vendor/bin/phpunit -c tests/phpunit.xml --fail-on-warning --fail-on-risky --display-deprecations
# Downgrade dependencies to the lowest versions.
composer update --prefer-dist --prefer-stable --prefer-lowest --no-interaction --no-progress --optimize-autoloader
composer update $COMPOSER_ARGS --prefer-dist --prefer-stable --prefer-lowest --no-interaction --no-progress --optimize-autoloader
# Execute tests again.
vendor/bin/phpunit -c tests/phpunit.xml --fail-on-warning --fail-on-risky
# Execute tests again. We do not want this run to fail due to deprecations though, because those can only occur from
# (older) dependencies, which we migh still consider good enough.
vendor/bin/phpunit -c tests/phpunit.xml --fail-on-warning --fail-on-risky --display-deprecations --do-not-fail-on-deprecation

View File

@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php: ["8.1", "8.3"]
php: ["8.1", "8.3", "8.5"]
name: Linux / PHP ${{ matrix.php }}
@ -67,8 +67,18 @@ jobs:
run: sudo apt-get -y install aspell aspell-en aspell-de
- name: Execute tests
if: ${{ matrix.php != '8.5' }}
run: .ci/run_browser_tests.sh
# Pass in a composer arg to make composer not complain about PHP version constrains with v8.5-rc until all
# dependency packages have released a version that supports v8.5.
- name: Execute tests
if: ${{ matrix.php == '8.5' }}
env:
COMPOSER_ARGS: "--ignore-platform-reqs"
run: .ci/run_browser_tests.sh
- name: Upload screenshots as artifacts
# Upload screenshot if the test suite failed.
if: failure()
@ -76,4 +86,3 @@ jobs:
with:
name: screenshots
path: tests/Browser/screenshots

View File

@ -21,7 +21,7 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0
with:
php-version: "8.3"
php-version: "8.4"
extensions: mbstring
tools: composer:v2
coverage: none

View File

@ -14,8 +14,10 @@ jobs:
strategy:
fail-fast: false
matrix:
php: ["8.3", "8.4", "8.5"]
name: Linux / PHP 8.3
name: Linux / PHP ${{ matrix.php }}
steps:
- name: Checkout code
@ -26,6 +28,14 @@ jobs:
# services are started before the repo is cloned). And instead of
# re-building what our compose-file contains we can just use it.
- name: Run tests via docker compose
if: ${{ matrix.php != '8.5' }}
run: docker compose -f .ci/compose.yaml run test_message_rendering
- name: Run tests via docker compose
if: ${{ matrix.php == '8.5' }}
env:
ROUNDCUBEMAIL_IMAGE_MESSAGE_RENDERING: ghcr.io/roundcube/roundcubemail:php8.5-rc
COMPOSE_ARGS: '--ignore-platform-reqs'
run: docker compose -f .ci/compose.yaml run test_message_rendering
- name: Upload artifacts
@ -34,4 +44,3 @@ jobs:
with:
name: Logs
path: logs/errors.log

View File

@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php: ["8.1", "8.2", "8.3", "8.4"]
php: ["8.1", "8.2", "8.3", "8.4", "8.5"]
name: Linux / PHP ${{ matrix.php }}
@ -38,6 +38,15 @@ jobs:
run: sudo apt-get -y install aspell aspell-en aspell-de hunspell-en-us
- name: Execute tests
if: ${{ matrix.php != '8.5' }}
run: .ci/run_tests.sh
# Pass in a composer arg to make composer not complain about PHP version constrains with v8.5-rc until all
# dependency packages have released a version that supports v8.5.
- name: Execute tests
if: ${{ matrix.php == '8.5' }}
env:
COMPOSER_ARGS: "--ignore-platform-reqs"
run: .ci/run_tests.sh
- name: Upload artifacts
@ -54,7 +63,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php: ["8.1", "8.2", "8.3", "8.4"]
php: ["8.1", "8.2", "8.3", "8.4", "8.5"]
name: Windows / PHP ${{ matrix.php }}
@ -71,6 +80,15 @@ jobs:
coverage: none
- name: Execute tests
if: ${{ matrix.php != '8.5' }}
run: bash -ex .ci/run_tests.sh
# Pass in a composer arg to make composer not complain about PHP version constrains with v8.5-rc until all
# dependency packages have released a version that supports v8.5.
- name: Execute tests
if: ${{ matrix.php == '8.5' }}
env:
COMPOSER_ARGS: "--ignore-platform-reqs"
run: bash -ex .ci/run_tests.sh
- name: Upload artifacts

View File

@ -5,7 +5,7 @@
"require": {
"php": ">=8.1 <8.5",
"bacon/bacon-qr-code": "^3.0.0",
"guzzlehttp/guzzle": "^7.9.2",
"guzzlehttp/guzzle": "^7.10.0",
"guzzlehttp/promises": "^2.0",
"league/commonmark": "^2.7",
"masterminds/html5": "~2.9.0",
@ -21,12 +21,12 @@
"require-dev": {
"ergebnis/composer-normalize": "^2.13",
"ergebnis/phpunit-slow-test-detector": "^2.11",
"friendsofphp/php-cs-fixer": "^3.0",
"friendsofphp/php-cs-fixer": "^3.8",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^2.0",
"phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^10.5.45 || ^11.0",
"phpunit/phpunit": "^10.5.47 || ^11.0",
"roundcube/acl": "*",
"roundcube/additional_message_headers": "*",
"roundcube/archive": "*",

View File

@ -300,15 +300,15 @@ class rcmail extends rcube
/**
* Return instance of the internal address book class
*
* @param string $id Address book identifier. It accepts also special values:
* - rcube_addressbook::TYPE_CONTACT (or 'sql') for the SQL addressbook
* - rcube_addressbook::TYPE_DEFAULT for the default addressbook
* @param bool $writeable True if the address book needs to be writeable
* @param bool $fallback Fallback to the first existing source, if the configured default wasn't found
* @param int|string $id Address book identifier. It accepts also special values:
* - rcube_addressbook::TYPE_CONTACT (or 'sql') for the SQL addressbook
* - rcube_addressbook::TYPE_DEFAULT for the default addressbook
* @param bool $writeable True if the address book needs to be writeable
* @param bool $fallback Fallback to the first existing source, if the configured default wasn't found
*
* @return rcube_addressbook|null Address book object
*/
public function get_address_book($id, $writeable = false, $fallback = true)
public function get_address_book(int|string $id, bool $writeable = false, bool $fallback = true): ?rcube_addressbook
{
$contacts = null;
$ldap_config = (array) $this->config->get('ldap_public');
@ -325,7 +325,7 @@ class rcmail extends rcube
}
// use existing instance
if (isset($this->address_books[$id]) && ($this->address_books[$id] instanceof rcube_addressbook)) {
if ($id !== null && isset($this->address_books[$id]) && ($this->address_books[$id] instanceof rcube_addressbook)) {
$contacts = $this->address_books[$id];
} elseif ($id && !empty($ldap_config[$id])) {
$domain = $this->config->mail_domain($_SESSION['storage_host']);

View File

@ -811,11 +811,11 @@ class rcube
/**
* Check the given string and return a valid language code
*
* @param string $lang Language code
* @param string|null $lang Language code
*
* @return string Valid language code
*/
protected function language_prop($lang)
protected function language_prop(?string $lang): string
{
static $rcube_languages, $rcube_language_aliases;
@ -834,7 +834,7 @@ class rcube
}
// check if we have an alias for that language
if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) {
if (isset($lang) && !isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) {
$lang = $rcube_language_aliases[$lang];
}
// try the first two chars
@ -851,7 +851,7 @@ class rcube
}
}
if (!isset($rcube_languages[$lang]) || !is_dir(RCUBE_LOCALIZATION_DIR . $lang)) {
if (!isset($lang) || !isset($rcube_languages[$lang]) || !is_dir(RCUBE_LOCALIZATION_DIR . $lang)) {
$lang = 'en_US';
}

View File

@ -907,7 +907,11 @@ class rcube_db
'integer' => \PDO::PARAM_INT,
];
$type = $map[$type] ?? \PDO::PARAM_STR;
if (isset($type) && isset($map[$type])) {
$type = $map[$type];
} else {
$type = \PDO::PARAM_STR;
}
return strtr($this->dbh->quote($input, $type),
// escape ? and `

View File

@ -721,10 +721,10 @@ class rcube_utils
*
* @return string Decoded string
*/
public static function xss_entity_decode($content)
public static function xss_entity_decode(string $content): string
{
$callback = static function ($matches) {
return chr(hexdec($matches[1]));
return strval(mb_chr(hexdec((string) $matches[1])));
};
$out = html_entity_decode(html_entity_decode($content));