Compare commits

..

22 Commits

Author SHA1 Message Date
Nicolas Grekas
47aa9064d7 Merge branch '3.4' into 4.4
* 3.4:
  [Cache] fix catching auth errors
  Fix CS
  [FrameworkBundle] set default session.handler alias if handler_id is not provided
  Fix CS
  Readability update
  Fix checks for phpunit releases on Composer 2 (resolves #37601)
  [SCA] Minor fixes on tests
2020-07-23 10:31:43 +02:00
Thomas Calvet
7ce874f443 [SCA] Minor fixes on tests 2020-07-16 11:41:49 +02:00
Nicolas Grekas
bd613a9662 Merge branch '3.4' into 4.4
* 3.4:
  [ErrorHandler] fix throwing from __toString()
  Removed comments and requirements relative to php <5.5 (not supported anymore)
  fix validating lazy properties that evaluate to null
2020-06-30 19:40:09 +02:00
Nicolas Grekas
f34d2bf035 [ErrorHandler] fix throwing from __toString() 2020-06-30 19:28:29 +02:00
Alexander M. Turek
28f92d08bb Parse and render anonymous classes correctly on php 8 2020-05-24 10:33:35 +02:00
Nicolas Grekas
8f904cac22 Merge branch '3.4' into 4.4
* 3.4:
  Skip Doctrine DBAL on php 8 until we have a compatible version.
  [DomCrawler] Catch expected ValueError.
  [Validator] Catch expected ValueError.
  [VarDumper] ReflectionFunction::isDisabled() is deprecated.
  [PropertyAccess] Parse php 8 TypeErrors correctly.
  [Intl] Fix call to ReflectionProperty::getValue() for static properties.
  [HttpKernel] Prevent calling method_exists() with non-string values.
  [Debug] php 8 does not pass $context to error handlers.
  [Config] Removed implicit cast of ReflectionProperty to string.
  [Debug] Undefined variables raise a warning in php 8.
  [Debug] Skip test that would trigger a fatal error on php 8.
  Address deprecation of ReflectionType::getClass().
  Properties $originalName and $mimeType are never null in UploadedFile
2020-05-23 11:11:46 +02:00
Nicolas Grekas
518c6a00d0 minor #36898 [Debug] php 8 does not pass $context to error handlers (derrabus)
This PR was merged into the 3.4 branch.

Discussion
----------

[Debug] php 8 does not pass $context to error handlers

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | #36872
| License       | MIT
| Doc PR        | N/A

php 8 will call error handlers without the optional `$context` parameter. Thus, error handlers that make that parameter mandatory will break.

Commits
-------

593897c9e1 [Debug] php 8 does not pass $context to error handlers.
2020-05-22 20:25:20 +02:00
Alexander M. Turek
2560754c36 [Debug] php 8 does not pass $context to error handlers. 2020-05-22 13:12:29 +02:00
Alexander M. Turek
44897cd605 [Debug] Undefined variables raise a warning in php 8. 2020-05-22 12:23:23 +02:00
Alexander M. Turek
7e9cd457b4 [Debug] Skip test that would trigger a fatal error on php 8. 2020-05-21 16:02:48 +02:00
Nicolas Grekas
6aa62a36bc Use ">=" for the "php" requirement 2020-05-20 10:37:50 +02:00
Nicolas Grekas
4431584a89 [Debug][ErrorHandler] cleanup phpunit.xml.dist files 2020-05-01 18:55:10 +02:00
Tobias Schultze
346636d2ca add missing gitattributes for phpunit-bridge 2020-03-27 17:54:36 +01:00
Nicolas Grekas
f0ae2b4150 Merge branch '3.4' into 4.4
* 3.4:
  [Http Foundation] Fix clear cookie samesite
  [Security] Check if firewall is stateless before checking for session/previous session
  [Form] Support customized intl php.ini settings
  [Security] Remember me: allow to set the samesite cookie flag
  [Debug] fix for PHP 7.3.16+/7.4.4+
  [Validator] Backport translations
  Prevent warning in proc_open()
2020-03-23 13:37:11 +01:00
Nicolas Grekas
ce9f3b5e8e [Debug] fix for PHP 7.3.16+/7.4.4+ 2020-03-23 11:22:40 +01:00
Fabien Potencier
f9385709e7 Merge branch '3.4' into 4.4
* 3.4:
  Add missing dots at the end of exception messages
2020-03-15 11:05:03 +01:00
Fabien Potencier
ed3231ef38 Add missing dots at the end of exception messages 2020-03-15 10:38:08 +01:00
Jules Pietri
a980d87a65 [Debug][ErrorHandler] improved deprecation notices for methods new args and return type 2020-02-23 15:41:43 +01:00
Fabien Potencier
ef8d82f96d Merge branch '3.4' into 4.4
* 3.4:
  [Translation] Add missing use statement
  [Translation][Debug] Add installation and minimal example to README
  [Validator] try to call __get method if property is uninitialized
  Fix handling of empty_data's \Closure value in Date/Time form types
2020-02-04 08:40:16 +01:00
Wouter de Jong
a99278d50a [Translation][Debug] Add installation and minimal example to README 2020-02-03 16:10:40 +01:00
Nicolas Grekas
2023647105 Merge branch '4.3' into 4.4
* 4.3:
  Fix displaying anonymous classes on PHP >= 7.4.2
2020-01-25 13:44:29 +01:00
Nicolas Grekas
8e36299635 Fix displaying anonymous classes on PHP >= 7.4.2 2020-01-25 13:32:28 +01:00
12 changed files with 92 additions and 51 deletions

1
.gitattributes vendored
View File

@@ -1,3 +1,4 @@
/Tests export-ignore
/phpunit.xml.dist export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore

View File

@@ -391,7 +391,7 @@ class DebugClassLoader
foreach ($matches as list(, $parameterType, $parameterName)) {
if (!isset($definedParameters[$parameterName])) {
$parameterType = trim($parameterType);
self::$annotatedParameters[$class][$method->name][$parameterName] = sprintf('The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its parent class "%s", not defining it is deprecated.', $method->name, $parameterType ? $parameterType.' ' : '', $parameterName, $method->class);
self::$annotatedParameters[$class][$method->name][$parameterName] = sprintf('The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its %s "%s", not defining it is deprecated.', $method->name, $parameterType ? $parameterType.' ' : '', $parameterName, interface_exists($class) ? 'interface' : 'parent class', $method->class);
}
}
}

View File

@@ -226,14 +226,14 @@ class ErrorHandler
if (!\is_array($log)) {
$log = [$log];
} elseif (!\array_key_exists(0, $log)) {
throw new \InvalidArgumentException('No logger provided');
throw new \InvalidArgumentException('No logger provided.');
}
if (null === $log[0]) {
$this->loggedErrors &= ~$type;
} elseif ($log[0] instanceof LoggerInterface) {
$this->loggedErrors |= $type;
} else {
throw new \InvalidArgumentException('Invalid logger provided');
throw new \InvalidArgumentException('Invalid logger provided.');
}
$this->loggers[$type] = $log + $prev[$type];
@@ -403,19 +403,7 @@ class ErrorHandler
}
$scope = $this->scopedErrors & $type;
if (4 < $numArgs = \func_num_args()) {
$context = $scope ? (func_get_arg(4) ?: []) : [];
} else {
$context = [];
}
if (isset($context['GLOBALS']) && $scope) {
$e = $context; // Whatever the signature of the method,
unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
$context = $e;
}
if (false !== strpos($message, "class@anonymous\0")) {
if (false !== strpos($message, "@anonymous\0")) {
$logMessage = $this->levels[$type].': '.(new FlattenException())->setMessage($message)->getMessage();
} else {
$logMessage = $this->levels[$type].': '.$message;
@@ -476,6 +464,8 @@ class ErrorHandler
// `return trigger_error($e, E_USER_ERROR);` allows this error handler
// to make $e get through the __toString() barrier.
$context = 4 < \func_num_args() ? (func_get_arg(4) ?: []) : [];
foreach ($context as $e) {
if ($e instanceof \Throwable && $e->__toString() === $message) {
self::$toStringException = $e;
@@ -499,7 +489,7 @@ class ErrorHandler
if ($this->isRecursive) {
$log = 0;
} else {
if (!\defined('HHVM_VERSION')) {
if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404) && !\defined('HHVM_VERSION')) {
$currentErrorHandler = set_error_handler('var_dump');
restore_error_handler();
}
@@ -511,7 +501,7 @@ class ErrorHandler
} finally {
$this->isRecursive = false;
if (!\defined('HHVM_VERSION')) {
if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404) && !\defined('HHVM_VERSION')) {
set_error_handler($currentErrorHandler);
}
}
@@ -540,7 +530,7 @@ class ErrorHandler
$handlerException = null;
if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) {
if (false !== strpos($message = $exception->getMessage(), "class@anonymous\0")) {
if (false !== strpos($message = $exception->getMessage(), "@anonymous\0")) {
$message = (new FlattenException())->setMessage($message)->getMessage();
}
if ($exception instanceof FatalErrorException) {

View File

@@ -26,7 +26,7 @@ class FatalThrowableError extends FatalErrorException
public function __construct(\Throwable $e)
{
$this->originalClassName = \get_class($e);
$this->originalClassName = get_debug_type($e);
if ($e instanceof \ParseError) {
$severity = E_PARSE;

View File

@@ -67,7 +67,7 @@ class FlattenException
$e->setStatusCode($statusCode);
$e->setHeaders($headers);
$e->setTraceFromThrowable($exception);
$e->setClass($exception instanceof FatalThrowableError ? $exception->getOriginalClassName() : \get_class($exception));
$e->setClass($exception instanceof FatalThrowableError ? $exception->getOriginalClassName() : get_debug_type($exception));
$e->setFile($exception->getFile());
$e->setLine($exception->getLine());
@@ -134,7 +134,7 @@ class FlattenException
*/
public function setClass($class)
{
$this->class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
$this->class = false !== strpos($class, "@anonymous\0") ? (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous' : $class;
return $this;
}
@@ -179,9 +179,9 @@ class FlattenException
*/
public function setMessage($message)
{
if (false !== strpos($message, "class@anonymous\0")) {
$message = preg_replace_callback('/class@anonymous\x00.*?\.php(?:0x?|:)[0-9a-fA-F]++/', function ($m) {
return class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0];
if (false !== strpos($message, "@anonymous\0")) {
$message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) {
return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0];
}, $message);
}

View File

@@ -8,6 +8,19 @@ Debug Component
The Debug component provides tools to ease debugging PHP code.
Getting Started
---------------
```
$ composer install symfony/debug
```
```php
use Symfony\Component\Debug\Debug;
Debug::enable();
```
Resources
---------

View File

@@ -292,12 +292,12 @@ class DebugClassLoaderTest extends TestCase
$this->assertSame([
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::quzMethod()" method will require a new "Quz $quz" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\ClassWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::whereAmI()" method will require a new "bool $matrix" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "$noType" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable(\Throwable|null $reason, mixed $value) $callback" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "string $param" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable ($a, $b) $anotherOne" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "Type$WithDollarIsStillAType $ccc" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::whereAmI()" method will require a new "bool $matrix" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "$noType" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable(\Throwable|null $reason, mixed $value) $callback" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "string $param" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable ($a, $b) $anotherOne" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "Type$WithDollarIsStillAType $ccc" argument in the next major version of its interface "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::isSymfony()" method will require a new "true $yes" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\ClassWithAnnotatedParameters", not defining it is deprecated.',
], $deprecations);
}

View File

@@ -103,9 +103,14 @@ class ErrorHandlerTest extends TestCase
$this->fail('ErrorException expected');
} catch (\ErrorException $exception) {
// if an exception is thrown, the test passed
$this->assertEquals(E_NOTICE, $exception->getSeverity());
if (\PHP_VERSION_ID < 80000) {
$this->assertEquals(E_NOTICE, $exception->getSeverity());
$this->assertRegExp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
} else {
$this->assertEquals(E_WARNING, $exception->getSeverity());
$this->assertRegExp('/^Warning: Undefined variable \$(foo|bar)/', $exception->getMessage());
}
$this->assertEquals(__FILE__, $exception->getFile());
$this->assertRegExp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
$trace = $exception->getTrace();
@@ -249,11 +254,17 @@ class ErrorHandlerTest extends TestCase
$line = null;
$logArgCheck = function ($level, $message, $context) use (&$line) {
$this->assertEquals('Notice: Undefined variable: undefVar', $message);
$this->assertArrayHasKey('exception', $context);
$exception = $context['exception'];
if (\PHP_VERSION_ID < 80000) {
$this->assertEquals('Notice: Undefined variable: undefVar', $message);
$this->assertSame(E_NOTICE, $exception->getSeverity());
} else {
$this->assertEquals('Warning: Undefined variable $undefVar', $message);
$this->assertSame(E_WARNING, $exception->getSeverity());
}
$this->assertInstanceOf(SilencedErrorContext::class, $exception);
$this->assertSame(E_NOTICE, $exception->getSeverity());
$this->assertSame(__FILE__, $exception->getFile());
$this->assertSame($line, $exception->getLine());
$this->assertNotEmpty($exception->getTrace());
@@ -267,8 +278,13 @@ class ErrorHandlerTest extends TestCase
;
$handler = ErrorHandler::register();
$handler->setDefaultLogger($logger, E_NOTICE);
$handler->screamAt(E_NOTICE);
if (\PHP_VERSION_ID < 80000) {
$handler->setDefaultLogger($logger, E_NOTICE);
$handler->screamAt(E_NOTICE);
} else {
$handler->setDefaultLogger($logger, E_WARNING);
$handler->screamAt(E_WARNING);
}
unset($undefVar);
$line = __LINE__ + 1;
@$undefVar++;
@@ -309,6 +325,26 @@ class ErrorHandlerTest extends TestCase
}
}
public function testHandleErrorWithAnonymousClass()
{
$handler = ErrorHandler::register();
$handler->throwAt(E_USER_WARNING, true);
try {
$handler->handleError(E_USER_WARNING, 'foo '.\get_class(new class() extends \stdClass {
}).' bar', 'foo.php', 12);
$this->fail('Exception expected.');
} catch (\ErrorException $e) {
} finally {
restore_error_handler();
restore_exception_handler();
}
$this->assertSame('User Warning: foo stdClass@anonymous bar', $e->getMessage());
$this->assertSame(E_USER_WARNING, $e->getSeverity());
$this->assertSame('foo.php', $e->getFile());
$this->assertSame(12, $e->getLine());
}
public function testHandleDeprecation()
{
$logArgCheck = function ($level, $message, $context) {
@@ -329,8 +365,6 @@ class ErrorHandlerTest extends TestCase
$handler = new ErrorHandler();
$handler->setDefaultLogger($logger);
@$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, []);
restore_error_handler();
}
public function testHandleException()

View File

@@ -174,9 +174,9 @@ class FlattenExceptionTest extends TestCase
$flattened = FlattenException::create($exception)->getPrevious();
$this->assertEquals($flattened->getMessage(), 'Oh noes!', 'The message is copied from the original exception.');
$this->assertEquals($flattened->getCode(), 42, 'The code is copied from the original exception.');
$this->assertEquals($flattened->getClass(), 'ParseError', 'The class is set to the class of the original exception');
$this->assertEquals('Oh noes!', $flattened->getMessage(), 'The message is copied from the original exception.');
$this->assertEquals(42, $flattened->getCode(), 'The code is copied from the original exception.');
$this->assertEquals('ParseError', $flattened->getClass(), 'The class is set to the class of the original exception');
}
/**
@@ -300,7 +300,7 @@ class FlattenExceptionTest extends TestCase
$this->assertSame(['float', INF], $array[$i++]);
// assertEquals() does not like NAN values.
$this->assertEquals($array[$i][0], 'float');
$this->assertEquals('float', $array[$i][0]);
$this->assertNan($array[$i][1]);
}
@@ -355,6 +355,11 @@ class FlattenExceptionTest extends TestCase
$this->assertSame('RuntimeException@anonymous', $flattened->getClass());
$flattened->setClass(\get_class(new class('Oops') extends NotFoundHttpException {
}));
$this->assertSame('Symfony\Component\HttpKernel\Exception\NotFoundHttpException@anonymous', $flattened->getClass());
$flattened = FlattenException::create(new \Exception(sprintf('Class "%s" blah.', \get_class(new class() extends \RuntimeException {
}))));

View File

@@ -15,8 +15,8 @@ class ErrorHandlerThatUsesThePreviousOne
return $handler;
}
public function handleError($type, $message, $file, $line, $context)
public function handleError()
{
return \call_user_func(self::$previous, $type, $message, $file, $line, $context);
return \call_user_func_array(self::$previous, \func_get_args());
}
}

View File

@@ -16,8 +16,9 @@
}
],
"require": {
"php": "^7.1.3",
"psr/log": "~1.0"
"php": ">=7.1.3",
"psr/log": "~1.0",
"symfony/polyfill-php80": "^1.15"
},
"conflict": {
"symfony/http-kernel": "<3.4"

View File

@@ -14,10 +14,7 @@
<testsuites>
<testsuite name="Symfony Debug Component Test Suite">
<directory>./Tests/</directory>
</testsuite>
<testsuite name="Symfony Debug Extension Test Suite">
<directory suffix=".phpt">./Resources/ext/tests/</directory>
<directory suffix=".phpt">./Tests/</directory>
</testsuite>
</testsuites>