Skip to content

collect autoloading deprecations before running testCase #6165

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
30 changes: 30 additions & 0 deletions src/Runner/ErrorHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ final class ErrorHandler
private ?int $originalErrorReportingLevel = null;
private readonly Source $source;

/**
* @var list<array{int, string, string, int}>
*/
private array $globalDeprecations = [];

/**
* @var ?array{functions: list<non-empty-string>, methods: list<array{className: class-string, methodName: non-empty-string}>}
*/
Expand Down Expand Up @@ -197,6 +202,23 @@ public function __invoke(int $errorNumber, string $errorString, string $errorFil
return false;
}

public function deprecationHandler(int $errorNumber, string $errorString, string $errorFile, int $errorLine): bool
{
$this->globalDeprecations[] = [$errorNumber, $errorString, $errorFile, $errorLine];

return true;
}

public function registerDeprecationHandler(): void
{
set_error_handler([self::$instance, 'deprecationHandler'], E_USER_DEPRECATED);
}

public function restoreDeprecationHandler(): void
{
restore_error_handler();
}

public function enable(): void
{
if ($this->enabled) {
Expand All @@ -213,6 +235,7 @@ public function enable(): void

$this->enabled = true;
$this->originalErrorReportingLevel = error_reporting();
$this->triggerGlobalDeprecations();

error_reporting($this->originalErrorReportingLevel & self::UNHANDLEABLE_LEVELS);
}
Expand Down Expand Up @@ -422,4 +445,11 @@ private function stackTrace(): string

return $buffer;
}

private function triggerGlobalDeprecations(): void
{
foreach ($this->globalDeprecations ?? [] as $d) {
$this->__invoke(...$d);
}
}
}
4 changes: 4 additions & 0 deletions src/TextUI/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,12 @@ public function run(array $argv): int

EventFacade::instance()->seal();

ErrorHandler::instance()->registerDeprecationHandler();

$testSuite = $this->buildTestSuite($configuration);

ErrorHandler::instance()->restoreDeprecationHandler();

$this->executeCommandsThatRequireTheTestSuite($configuration, $cliConfiguration, $testSuite);

if ($testSuite->isEmpty() && !$configuration->hasCliArguments() && $configuration->testSuite()->isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../phpunit.xsd"
bootstrap="vendor/autoload.php"
>
<testsuites>
<testsuite name="default">
<directory>tests</directory>
</testsuite>
</testsuites>

<source>
<include>
<directory>src</directory>
</include>
</source>
</phpunit>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\TestFixture\SelfDirectIndirect;

final class FirstPartyClass
{
public function method(): true
{
return ThirdPartyClass::A;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\TestFixture\SelfDirectIndirect;

use PHPUnit\Framework\TestCase;

final class FirstPartyClassTest extends TestCase
{
public function testOne(): void
{
$this->assertTrue((new FirstPartyClass)->method());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php declare(strict_types=1);
namespace PHPUnit\TestFixture\SelfDirectIndirect;

@trigger_error('This class is deprecated', E_USER_DEPRECATED);

final class ThirdPartyClass
{
const A = true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php declare(strict_types=1);

spl_autoload_register(function ($class) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reflects what happens when loading classes usually, if we do the same as in #5844 we'll trigger the deprecation during the loadBootstrapScript.

$parts = explode('\\', $class);
$file = end($parts) . '.php';

match ($file) {
'FirstPartyClass.php' => require __DIR__ . '/../src/' . $file,
'ThirdPartyClass.php' => require __DIR__ . '/' . $file,
default => throw new LogicException
};
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
The right events are emitted in the right order for a test that loads a class that triggers E_USER_DEPRECATED
--FILE--
<?php declare(strict_types=1);
$_SERVER['argv'][] = '--do-not-cache-result';
$_SERVER['argv'][] = '--debug';
$_SERVER['argv'][] = '--configuration';
$_SERVER['argv'][] = __DIR__ . '/_files/deprecation-trigger-class';

require __DIR__ . '/../../bootstrap.php';

(new PHPUnit\TextUI\Application)->run($_SERVER['argv']);
--EXPECTF--
PHPUnit Started (PHPUnit %s using %s)
Test Runner Configured
Bootstrap Finished (%sautoload.php)
Event Facade Sealed
Test Suite Loaded (1 test)
Test Runner Started
Test Suite Sorted
Test Runner Execution Started (1 test)
Test Suite Started (%sphpunit.xml, 1 test)
Test Suite Started (default, 1 test)
Test Suite Started (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest, 1 test)
Test Preparation Started (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne)
Test Prepared (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne)
Test Triggered Deprecation (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne, issue triggered by third-party code, suppressed using operator) in %s:%d
This class is deprecated
Test Passed (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne)
Test Finished (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest::testOne)
Test Suite Finished (PHPUnit\TestFixture\SelfDirectIndirect\FirstPartyClassTest, 1 test)
Test Suite Finished (default, 1 test)
Test Suite Finished (%sphpunit.xml, 1 test)
Test Runner Execution Finished
Test Runner Finished
PHPUnit Finished (Shell Exit Code: 0)
35 changes: 35 additions & 0 deletions tests/unit/Runner/ErrorHandlerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Runner;

use const E_USER_DEPRECATED;
use function trigger_error;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Small;
use PHPUnit\Framework\TestCase;
use ReflectionClass;

#[CoversClass(ErrorHandler::class)]
#[Small]
final class ErrorHandlerTest extends TestCase
{
public function testThrowsExceptionWhenUsingInvalidOrderOption(): void
{
$errorHandler = ErrorHandler::instance();
$errorHandler->registerDeprecationHandler();
trigger_error('deprecation', E_USER_DEPRECATED);
$errorHandler->restoreDeprecationHandler();
$refl = new ReflectionClass($errorHandler);
$globalDeprecations = $refl->getProperty('globalDeprecations');
$registeredDeprecations = $globalDeprecations->getValue($errorHandler);
$this->assertCount(1, $registeredDeprecations);
$this->assertSame('deprecation', $registeredDeprecations[0][1]);
}
}