Skip to content

PHP 8.4 compatibility #17786

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 26, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ jobs:
matrix:
include:
- {php-version: "8.2"} # Lint on lower PHP version to detected too early usage of new syntaxes
- {php-version: "8.3"} # Lint on higher PHP version to detected deprecated elements usage
- {php-version: "8.4"} # Lint on higher PHP version to detected deprecated elements usage
env:
COMPOSE_FILE: ".github/actions/docker-compose-app.yml"
APPLICATION_ROOT: "${{ github.workspace }}"
@@ -116,20 +116,20 @@ jobs:
MATRIX='
{
"include": [
{"php-version": "8.3", "db-image": "mariadb:11.4"},
{"php-version": "8.3", "db-image": "mysql:8.4"},
{"php-version": "8.4", "db-image": "mariadb:11.4"},
{"php-version": "8.4", "db-image": "mysql:8.4"},
{"php-version": "8.2", "db-image": "mariadb:11.4"},
{"php-version": "8.3", "db-image": "mariadb:10.5"},
{"php-version": "8.3", "db-image": "percona:8.0"}
{"php-version": "8.4", "db-image": "mariadb:10.5"},
{"php-version": "8.4", "db-image": "percona:8.0"}
]
}
'
else
MATRIX='
{
"include": [
{"php-version": "8.3", "db-image": "mariadb:11.4"},
{"php-version": "8.3", "db-image": "mysql:8.4"}
{"php-version": "8.4", "db-image": "mariadb:11.4"},
{"php-version": "8.4", "db-image": "mysql:8.4"}
]
}
'
18 changes: 10 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
@@ -104,8 +104,8 @@
},
"require-dev": {
"ext-xml": "*",
"atoum/atoum": "^4.2",
"atoum/stubs": "^2.6",
"atoum/atoum": "dev-main#e43a6a84809635cf5dcdf80bf3c773e7cf1d5a04",
"atoum/stubs": "dev-master#fb8890f347a7ecf1e3cf2b5a139a8b038359a6ab",
"friendsoftwig/twigcs": "^6.4",
"glpi-project/tools": "^0.7",
"mikey179/vfsstream": "^1.6",
@@ -188,19 +188,21 @@
"post-install-cmd": [
"@php -r \"file_put_contents('.composer.hash', sha1_file('composer.lock'));\"",
"@php -f vendor/bin/build_hw_jsons",
"patch -f -p1 -d vendor/laminas/laminas-mail/ < tools/patches/laminas-mail-invalid-header-ignore.patch || true",
"patch -f -p1 -d vendor/laminas/laminas-mail/ < tools/patches/laminas-mail-address-no-length-check.patch || true",
"patch -f -p1 -d vendor/guzzlehttp/guzzle/ < tools/patches/guzzle-http-client-restrict-http-methods.patch || true"
"@patch"
],
"post-update-cmd": [
"@php -r \"file_put_contents('.composer.hash', sha1_file('composer.lock'));\"",
"@php -f vendor/bin/build_hw_jsons",
"patch -f -p1 -d vendor/laminas/laminas-mail/ < tools/patches/laminas-mail-invalid-header-ignore.patch || true",
"patch -f -p1 -d vendor/laminas/laminas-mail/ < tools/patches/laminas-mail-address-no-length-check.patch || true",
"patch -f -p1 -d vendor/guzzlehttp/guzzle/ < tools/patches/guzzle-http-client-restrict-http-methods.patch || true"
"@patch"
],
"build": [
"bin/console dependencies install"
],
"patch": [
"patch -f -p1 -d vendor/laminas/laminas-mail/ < tools/patches/laminas-mail-invalid-header-ignore.patch || true",
"patch -f -p1 -d vendor/laminas/laminas-mail/ < tools/patches/laminas-mail-address-no-length-check.patch || true",
"patch -f -p1 -d vendor/guzzlehttp/guzzle/ < tools/patches/guzzle-http-client-restrict-http-methods.patch || true",
"patch -f -p1 -d vendor/apereo/phpcas/ < tools/patches/apereo-phpcas-php84.patch || true"
]
}
}
31 changes: 18 additions & 13 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions phpunit/functional/TicketTest.php
Original file line number Diff line number Diff line change
@@ -7579,7 +7579,10 @@ public function testDynamicProperties(): void
{
$ticket = new \Ticket();

$reporting_level = \error_reporting(E_ALL); // be sure to report deprecations
$ticket->plugin_xxx_data = 'test';
\error_reporting($reporting_level); // restore previous level

$this->hasPhpLogRecordThatContains(
'Creation of dynamic property Ticket::$plugin_xxx_data is deprecated',
LogLevel::INFO
12 changes: 12 additions & 0 deletions phpunit/functional/ToolboxTest.php
Original file line number Diff line number Diff line change
@@ -1080,7 +1080,10 @@ public function testIsValidWebUrl(string $url, bool $result)

public function testDeprecated()
{
$reporting_level = \error_reporting(E_ALL); // be sure to report deprecations
\Toolbox::deprecated('Calling this function is deprecated');
\error_reporting($reporting_level); // restore previous level

$this->hasPhpLogRecordThatContains(
'Calling this function is deprecated',
LogLevel::INFO
@@ -1090,7 +1093,10 @@ public function testDeprecated()
public function testDeprecatedPast()
{
// Test planned deprecation in the past
$reporting_level = \error_reporting(E_ALL); // be sure to report deprecations
\Toolbox::deprecated('Calling this function is deprecated', true, '10.0');
\error_reporting($reporting_level); // restore previous level

$this->hasPhpLogRecordThatContains(
'Calling this function is deprecated',
LogLevel::INFO
@@ -1100,7 +1106,10 @@ public function testDeprecatedPast()
public function testDeprecatedCurrent()
{
// Test planned deprecation in current version
$reporting_level = \error_reporting(E_ALL); // be sure to report deprecations
\Toolbox::deprecated('Calling this function is deprecated', true, GLPI_VERSION);
\error_reporting($reporting_level); // restore previous level

$this->hasPhpLogRecordThatContains(
'Calling this function is deprecated',
LogLevel::INFO
@@ -1110,7 +1119,10 @@ public function testDeprecatedCurrent()
public function testFutureDeprecated()
{
// Test planned deprecation in the future does NOT throw an error
$reporting_level = \error_reporting(E_ALL); // be sure to report deprecations
\Toolbox::deprecated('Calling this function is deprecated', true, '99.0');
\error_reporting($reporting_level); // restore previous level

$this->assertTrue(true); //non empty test
}

4 changes: 2 additions & 2 deletions public/index.php
Original file line number Diff line number Diff line change
@@ -37,8 +37,8 @@

// Check PHP version not to have trouble
// Need to be the very fist step before any include
if (version_compare(PHP_VERSION, '8.2.0', '<') || version_compare(PHP_VERSION, '8.3.999', '>')) {
exit('PHP version must be between 8.2 and 8.3.');
if (version_compare(PHP_VERSION, '8.2.0', '<') || version_compare(PHP_VERSION, '8.4.999', '>')) {
exit('PHP version must be between 8.2 and 8.4.');
}

// Check the resources state before trying to instanciate the Kernel.
2 changes: 1 addition & 1 deletion src/Consumable.php
Original file line number Diff line number Diff line change
@@ -197,7 +197,7 @@ public static function getMassiveActionsForItemtype(
array &$actions,
$itemtype,
$is_deleted = 0,
CommonDBTM $checkitem = null
?CommonDBTM $checkitem = null
) {
// Special actions only for self
if ($itemtype !== static::class) {
44 changes: 30 additions & 14 deletions src/Glpi/Application/ErrorHandler.php
Original file line number Diff line number Diff line change
@@ -63,7 +63,7 @@ class ErrorHandler
E_USER_ERROR => LogLevel::ERROR,
E_USER_WARNING => LogLevel::WARNING,
E_USER_NOTICE => LogLevel::NOTICE,
E_STRICT => LogLevel::NOTICE,
2048 => LogLevel::NOTICE, // 2048 = deprecated E_STRICT
E_RECOVERABLE_ERROR => LogLevel::ERROR,
E_DEPRECATED => LogLevel::INFO,
E_USER_DEPRECATED => LogLevel::INFO,
@@ -227,9 +227,34 @@ public function register(): void
set_exception_handler([$this, 'handleException']);
}

// Force reporting of all errors, to ensure that all log levels that are supposed to be
// pushed in logs according to `GLPI_LOG_LVL`/`GLPI_ENVIRONMENT_TYPE` are actually reported.
error_reporting(E_ALL);
// Adjust reporting level to the environment, to ensure that all the errors supposed to be logged are
// actually reported, and to prevent reporting other errors.
$reporting_level = E_ALL;
foreach (self::ERROR_LEVEL_MAP as $value => $log_level) {
if (
$this->env !== GLPI::ENV_DEVELOPMENT
&& in_array($log_level, [LogLevel::DEBUG, LogLevel::INFO])
) {
// Do not report debug and info messages unless in development env.
// Suppressing the INFO level will prevent deprecations to be pushed in other environments logs.
//
// Suppressing the deprecations in the testing environment is mandatory to prevent deprecations
// triggered in vendor code to make our test suite fail.
// We may review this part once we will have migrate all our test suite on PHPUnit.
// For now, we rely on PHPStan to detect usages of deprecated code.
$reporting_level = $reporting_level & ~$value;
}

if (
!in_array($this->env, [GLPI::ENV_DEVELOPMENT, GLPI::ENV_TESTING], true)
&& $log_level === LogLevel::NOTICE
) {
// Do not report notice messages unless in development/testing env.
// Notices are errors with no functional impact, so we do not want people to report them as issues.
$reporting_level = $reporting_level & ~$value;
}
}
error_reporting($reporting_level);

// Disable native error displaying as it will be handled by `self::outputDebugMessage()`.
ini_set('display_errors', 'Off');
@@ -435,15 +460,6 @@ private function outputDebugMessage(string $error_type, string $message, string
return;
}

if (
in_array($this->env, [GLPI::ENV_STAGING, GLPI::ENV_PRODUCTION])
&& in_array($log_level, [LogLevel::DEBUG, LogLevel::INFO])
) {
// On staging/production environment, do not output debug/info messages.
// These messages are not supposed to be intended for end users, as they have no functional impact.
return;
}

$is_dev_env = $this->env === GLPI::ENV_DEVELOPMENT;
$is_debug_mode = isset($_SESSION['glpi_use_mode']) && $_SESSION['glpi_use_mode'] == \Session::DEBUG_MODE;
$is_console_context = $this->output_handler instanceof OutputInterface;
@@ -513,7 +529,7 @@ private function codeToString(int $error_code): string
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Runtime Notice',
2048 => 'Runtime Notice', // 2048 = deprecated E_STRICT
E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
E_DEPRECATED => 'Deprecated function',
E_USER_DEPRECATED => 'User deprecated function',
2 changes: 1 addition & 1 deletion src/Glpi/Asset/Asset_PeripheralAsset.php
Original file line number Diff line number Diff line change
@@ -258,7 +258,7 @@ public static function getMassiveActionsForItemtype(
array &$actions,
$itemtype,
$is_deleted = false,
CommonDBTM $checkitem = null
?CommonDBTM $checkitem = null
) {
$action_prefix = __CLASS__ . MassiveAction::CLASS_ACTION_SEPARATOR;
$specificities = self::getRelationMassiveActionsSpecificities();
6 changes: 3 additions & 3 deletions src/Glpi/DBAL/QueryFunction.php
Original file line number Diff line number Diff line change
@@ -175,7 +175,7 @@ public static function ifnull(string|QueryExpression $expression, string|QueryEx
* @param string|null $alias Function result alias (will be automatically quoted)
* @return QueryExpression
*/
public static function groupConcat(string|QueryExpression $expression, ?string $separator = null, bool $distinct = false, array|string $order_by = null, ?string $alias = null): QueryExpression
public static function groupConcat(string|QueryExpression $expression, ?string $separator = null, bool $distinct = false, array|string|null $order_by = null, ?string $alias = null): QueryExpression
{
/** @var \DBmysql $DB */
global $DB;
@@ -309,7 +309,7 @@ public static function replace(string|QueryExpression $expression, string|QueryE
* @param string|null $alias Function result alias (will be automatically quoted)
* @return QueryExpression
*/
public static function fromUnixtime(string|QueryExpression $expression, string|QueryExpression $format = null, ?string $alias = null): QueryExpression
public static function fromUnixtime(string|QueryExpression $expression, string|QueryExpression|null $format = null, ?string $alias = null): QueryExpression
{
$params = [$expression];
if ($format !== null) {
@@ -433,7 +433,7 @@ public static function timediff(string|QueryExpression $expression1, string|Quer
* @param string|null $alias Function result alias (will be automatically quoted)
* @return QueryExpression
*/
public static function unixTimestamp(string|QueryExpression $expression = null, ?string $alias = null): QueryExpression
public static function unixTimestamp(string|QueryExpression|null $expression = null, ?string $alias = null): QueryExpression
{
$params = [];
if ($expression !== null) {
6 changes: 3 additions & 3 deletions src/Glpi/Dashboard/FakeProvider.php
Original file line number Diff line number Diff line change
@@ -125,7 +125,7 @@ private static function getItemCount(?string $itemtype = null)
return $values[$itemtype] ?? null;
}

public static function bigNumberItem(CommonDBTM $item = null, array $params = []): array
public static function bigNumberItem(?CommonDBTM $item = null, array $params = []): array
{
return [
'number' => self::getItemCount($item::class) ?? 1500,
@@ -278,7 +278,7 @@ public static function nbTicketsByAgreementStatusAndTechnicianGroup(array $param
];
}

public static function nbItemByFk(CommonDBTM $item = null, CommonDBTM $fk_item = null, array $params = []): array
public static function nbItemByFk(?CommonDBTM $item = null, ?CommonDBTM $fk_item = null, array $params = []): array
{
$item_counts = self::getItemCount();
$number_fk = $item_counts[$fk_item::class] ?? 20;
@@ -306,7 +306,7 @@ public static function nbItemByFk(CommonDBTM $item = null, CommonDBTM $fk_item =
];
}

public static function articleListItem(CommonDBTM $item = null, array $params = []): array
public static function articleListItem(?CommonDBTM $item = null, array $params = []): array
{
$data = [];
for ($i = 0; $i < 5; $i++) {
5 changes: 2 additions & 3 deletions src/Glpi/Features/State.php
Original file line number Diff line number Diff line change
@@ -50,12 +50,11 @@ private function checkSetup(): void
global $CFG_GLPI;

if (!in_array(static::class, $CFG_GLPI['state_types'])) {
trigger_error(
throw new \LogicException(
sprintf(
'Class %s must be present in $CFG_GLPI[\'state_types\']',
static::class
),
E_USER_ERROR
)
);
}
}
Loading