N°7662 Fix the handling of PHP native deprecations (e.g. call strftime on PHP 8.1)

This commit is contained in:
Romain Quetiez
2024-08-23 16:35:52 +02:00
parent ffb61503dc
commit 37cd12fb21
2 changed files with 323 additions and 208 deletions

View File

@@ -13,143 +13,256 @@ use DeprecatedCallsLog;
class DeprecatedCallsLogTest extends ItopTestCase
{
/**
* We are testing for a undefined offset error. This was throwing a Notice, but starting with PHP 8.0 it was converted to a Warning ! Also the message was changed :(
*
* @link https://www.php.net/manual/en/migration80.incompatible.php check "A number of notices have been converted into warnings:"
* @dataProvider StripCallStackProvider
*/
private function SetUndefinedOffsetExceptionToExpect(): void
public function testStripCallStack($sInputStack, $sExpectedStack)
{
/** @noinspection ConstantCanBeUsedInspection Preferring the function call as it is easier to read and won't cost that much in this PHPUnit context */
if (version_compare(PHP_VERSION, '8.0', '>=')) {
$this->expectWarning();
$sUndefinedOffsetExceptionMessage = 'Undefined array key "tutu"';
} else {
$this->expectNotice();
$sUndefinedOffsetExceptionMessage = 'Undefined index: tutu';
}
$this->expectExceptionMessage($sUndefinedOffsetExceptionMessage);
$this->assertEquals(
$sExpectedStack,
$this->InvokeNonPublicStaticMethod(DeprecatedCallsLog::class, 'StripCallStack', [$sInputStack]),
'The top item of the call stack should be the first item meaningful to track deprecated calls, not the intermediate layers of the PHP engine'
);
}
public function testPhpNoticeWithoutDeprecatedCallsLog(): void
{
$this->SetUndefinedOffsetExceptionToExpect();
$aArray = [];
if ('toto' === $aArray['tutu']) {
//Do nothing, just raising a undefined offset warning
}
}
/**
* @runInSeparateProcess Necessary, due to the DeprecatedCallsLog being enabled (no mean to reset)
*
* The error handler set by DeprecatedCallsLog during startup was causing PHPUnit to miss PHP notices like "undefined offset"
*
* The error handler is now disabled when running PHPUnit
*
* @since 3.0.4 N°6274
* @covers DeprecatedCallsLog::DeprecatedNoticesErrorHandler
*/
public function testPhpNoticeWithDeprecatedCallsLog(): void
{
$this->RequireOnceItopFile('core/log.class.inc.php');
DeprecatedCallsLog::Enable(); // will set error handler
$this->SetUndefinedOffsetExceptionToExpect();
$aArray = [];
if ('toto' === $aArray['tutu']) {
//Do nothing, just raising a undefined offset warning
}
}
/**
* @dataProvider GetMessageFromStackProvider
*/
public function testGetMessageFromStack($aDebugBacktrace, $sExpectedMessage): void
{
$sActualMessage = $this->InvokeNonPublicStaticMethod(DeprecatedCallsLog::class, 'GetMessageFromStack', [$aDebugBacktrace]);
$this->assertEquals($sExpectedMessage, $sActualMessage);
}
public function GetMessageFromStackProvider()
public function StripCallStackProvider()
{
return [
'Call in a file outside of a function or class' => [
[
/* A deprecated PHP method is invoked from scratch.php, at line 25 (note: the name of the method is not present in the callstack) */
'Should preserve the handler when the notice is fired by PHP itself' => [
'in' => [
[
'file' => 'whateverfolder/scratch.php',
'line' => 25,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
],
'out' => [
[
'file' => 'whateverfolder/scratch.php',
'line' => 25,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
],
],
'Should skip the handler when the notice is fired by a call to trigger_error' => [
'in' => [
[
'file' => 'C:\Dev\wamp64\www\itop-32\sources\Application\WebPage\WebPage.php',
'line' => '866',
'function' => 'NotifyDeprecatedPhpMethod',
'class' => 'DeprecatedCallsLog',
'type' => '::',
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'C:\Dev\wamp64\www\itop-32\extensions\itop-object-copier\copy.php',
'line' => '130',
'function' => 'add_linked_script',
'class' => 'Combodo\iTop\Application\WebPage\WebPage',
'type' => '->',
'file' => 'whateverfolder/scratch.php',
'line' => 25,
'function' => 'trigger_error',
],
],
'out' => [
[
'file' => 'whateverfolder/scratch.php',
'line' => 25,
'function' => 'trigger_error',
],
],
],
'Should skip two levels when the notice is fired by trigger_deprecation (Symfony helper)' => [
'in' => [
[
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'C:\Dev\wamp64\www\itop-32\pages\exec.php',
'line' => '102',
'args' => ['C:\Dev\wamp64\www\itop-32\extensions\itop-object-copier\copy.php'],
'file' => 'symfony/deprecation.php',
'line' => 12,
'function' => 'trigger_error',
],
[
'file' => 'symfony/service.php',
'line' => 25,
'function' => 'trigger_deprecation',
],
],
'out' => [
[
'file' => 'symfony/service.php',
'line' => 25,
'function' => 'trigger_deprecation',
],
],
],
];
}
/**
* @dataProvider SummarizeCallStackProvider
*/
public function testSummarizeCallStack($sInputStackStripped, $sExpectedSummary)
{
$this->assertEquals(
$sExpectedSummary,
$this->InvokeNonPublicStaticMethod(DeprecatedCallsLog::class, 'SummarizeCallStack', [$sInputStackStripped])
);
}
public function SummarizeCallStackProvider()
{
// All tests are based on a call stack issued from a deprecated PHP function
// Other cases are similar: what counts on the top level item is the file and line number of the deprecated call
return [
'From the main page (deprecated PHP function)' => [
'in:stripped call stack' => [
[
'file' => 'whateverfolder/scratch.php',
'line' => 25,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
],
'out' => 'whateverfolder/scratch.php#25',
],
'From an iTop method (deprecated PHP function)' => [
'in:stripped call stack' => [
[
'file' => 'whateverfolder/someclass.php',
'line' => 18,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'whateverfolder/index.php',
'line' => 25,
'function' => 'SomeMethod',
'class' => 'SomeClass',
'type' => '->',
],
],
'out' => 'SomeClass->SomeMethod (whateverfolder/someclass.php#18), itself called from whateverfolder/index.php#25',
],
'From an iTop static method (deprecated PHP function)' => [
'in:stripped call stack' => [
[
'file' => 'whateverfolder/someclass.php',
'line' => 18,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'whateverfolder/index.php',
'line' => 25,
'function' => 'SomeMethod',
'class' => 'SomeClass',
'type' => '::',
],
],
'out' => 'SomeClass::SomeMethod (whateverfolder/someclass.php#18), itself called from whateverfolder/index.php#25',
],
'From an iTop function (deprecated PHP function)' => [
'in:stripped call stack' => [
[
'file' => 'whateverfolder/someclass.php',
'line' => 18,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'whateverfolder/index.php',
'line' => 25,
'function' => 'SomeFunction',
],
],
'out' => 'SomeFunction (whateverfolder/someclass.php#18), itself called from whateverfolder/index.php#25',
],
'From a code snippet (deprecated PHP function)' => [
'in:stripped call stack' => [
[
'file' => 'itop-root/env-production/core/main.php',
'line' => 1290,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'itop-root/core/metamodel.class.php',
'line' => 6698,
'function' => 'require_once',
],
[
'file' => 'itop-root/env-production/autoload.php',
'line' => 6,
'function' => 'IncludeModule',
'class' => 'MetaModel',
'type' => '::',
],
[
'file' => 'itop-root/core/metamodel.class.php',
'line' => 6487,
'function' => 'require_once',
],
],
'Call to Combodo\iTop\Application\WebPage\WebPage::add_linked_script in C:\Dev\wamp64\www\itop-32\extensions\itop-object-copier\copy.php#L130 (from C:\Dev\wamp64\www\itop-32\pages\exec.php#L102)',
'out' => 'itop-root/env-production/core/main.php#1290'
],
'Call in a file function, outside of a class' => [
[
[
'file' => 'C:\\Dev\\wamp64\\www\\itop-32\\sources\\Application\\WebPage\\WebPage.php',
'line' => 866,
'function' => 'NotifyDeprecatedPhpMethod',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'C:\\Dev\\wamp64\\www\\itop-32\\extensions\\itop-object-copier\\copy.php',
'line' => 81,
'function' => 'add_linked_script',
'class' => 'Combodo\\iTop\\Application\\WebPage\\WebPage',
'type' => '->',
],
[
'file' => 'C:\\Dev\\wamp64\\www\\itop-32\\extensions\\itop-object-copier\\copy.php',
'line' => 123,
'function' => 'myFunction',
],
'From a persistent object method (deprecated PHP function)' => [
'in:stripped call stack' => [
[
'file' => 'itop-root/env-production/itop-tickets/model.itop-tickets.php',
'line' => 165,
'function' => 'DeprecatedNoticesErrorHandler',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'itop-root/core/dbobject.class.php',
'line' => 6575,
'function' => 'OnBeforeWriteTicket',
'class' => 'Ticket',
'type' => '->',
],
[
'file' => 'itop-root/application/cmdbabstract.class.inc.php',
'line' => 5933,
'function' => 'FireEvent',
'class' => 'DBObject',
'type' => '->',
],
[
'file' => 'itop-root/core/dbobject.class.php',
'line' => 3643,
'function' => 'FireEventBeforeWrite',
'class' => 'cmdbAbstractObject',
'type' => '->',
],
[
'file' => 'itop-root/application/cmdbabstract.class.inc.php',
'line' => 4593,
'function' => 'DBUpdate',
'class' => 'DBObject',
'type' => '->',
],
[
'file' => 'itop-root/sources/Controller/Base/Layout/ObjectController.php',
'line' => 649,
'function' => 'DBUpdate',
'class' => 'cmdbAbstractObject',
'type' => '->',
],
[
'file' => 'itop-root/pages/UI.php',
'line' => 720,
'function' => 'OperationApplyModify',
'class' => 'Combodo\\iTop\\Controller\\Base\\Layout\\ObjectController',
'type' => '->',
],
],
'Call to Combodo\iTop\Application\WebPage\WebPage::add_linked_script in C:\Dev\wamp64\www\itop-32\extensions\itop-object-copier\copy.php#L81 (from C:\Dev\wamp64\www\itop-32\extensions\itop-object-copier\copy.php#L123)',
],
'Call from a class method' => [
[
[
'file' => 'C:\\Dev\\wamp64\\www\\itop-32\\sources\\Application\\WebPage\\WebPage.php',
'line' => 866,
'function' => 'NotifyDeprecatedPhpMethod',
'class' => 'DeprecatedCallsLog',
'type' => '::',
],
[
'file' => 'C:\\Dev\\wamp64\\www\\itop-32\\extensions\\itop-object-copier\\copy.php',
'line' => 82,
'function' => 'add_linked_script',
'class' => 'Combodo\\iTop\\Application\\WebPage\\WebPage',
'type' => '->',
],
[
'file' => 'C:\\Dev\\wamp64\\www\\itop-32\\extensions\\itop-object-copier\\copy.php',
'line' => 125,
'function' => 'MyMethod',
'class' => 'MyClass',
'type' => '::',
],
],
'Call to Combodo\iTop\Application\WebPage\WebPage::add_linked_script in C:\Dev\wamp64\www\itop-32\extensions\itop-object-copier\copy.php#L82 (from MyClass::MyMethod in C:\Dev\wamp64\www\itop-32\extensions\itop-object-copier\copy.php#L125)',
'out' => 'Ticket->OnBeforeWriteTicket (itop-root/env-production/itop-tickets/model.itop-tickets.php#165), itself called from DBObject->FireEvent (itop-root/core/dbobject.class.php#6575)'
],
];
}