Compare commits

...

44 Commits

Author SHA1 Message Date
Alexander M. Turek
9109e4414e [Debug] Skip a test that was meant for HHVM. 2020-09-09 00:19:14 +02:00
Alexander M. Turek
5422b88cf9 [Debug] Parse "x not found" errors correctly on php 8. 2020-09-04 00:12:04 +02:00
Nicolas Grekas
093e4e512f Enable "native_constant_invocation" CS rule 2020-09-02 18:06:40 +02:00
Alexander M. Turek
0893a0b07c Modernized deprecated PHPUnit assertion calls 2020-08-09 10:13:48 +02:00
Thomas Calvet
7ce874f443 [SCA] Minor fixes on tests 2020-07-16 11:41:49 +02:00
Nicolas Grekas
f34d2bf035 [ErrorHandler] fix throwing from __toString() 2020-06-30 19:28:29 +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
ce9f3b5e8e [Debug] fix for PHP 7.3.16+/7.4.4+ 2020-03-23 11:22:40 +01:00
Fabien Potencier
ed3231ef38 Add missing dots at the end of exception messages 2020-03-15 10:38:08 +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
70dd18e93b [Debug] fix ClassNotFoundFatalErrorHandler 2020-01-08 17:36:15 +01:00
Shaharia Azam
9a81b6360b Update links to documentation 2020-01-04 13:05:51 +01:00
Jan Rosier
cbe127b719 Update year in license files 2020-01-01 12:03:25 +01:00
Artem Henvald
9d32af2d83 Use ::class constants instead of __NAMESPACE__ when possible 2019-12-16 11:25:54 +01:00
Thomas Calvet
f72e33fdb1 Remove unused local variables in tests 2019-10-24 17:33:53 +02:00
Nicolas Grekas
b3e7ce815d Re-enable previously failing PHP 7.4 test cases 2019-09-19 17:32:51 +02:00
Alexander M. Turek
0b60030091 Fix inconsistent return points. 2019-08-20 15:31:17 +02:00
Nicolas Grekas
2ab93347e8 Fix return statements 2019-08-13 08:33:05 +02:00
Nicolas Grekas
4645282605 minor #32800 Improve some URLs (Arman-Hosseini)
This PR was squashed before being merged into the 3.4 branch (closes #32800).

Discussion
----------

Improve some URLs

| Q             | A
| ------------- | ---
| Branch?       | 3.4 <!-- see below -->
| Bug fix?      | no
| New feature?  | no <!-- please update src/**/CHANGELOG.md files -->
| BC breaks?    | no     <!-- see https://symfony.com/bc -->
| Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tests pass?   | yes    <!-- please add some, will be required by reviewers -->
| Fixed tickets | N/A   <!-- #-prefixed issue number(s), if any -->
| License       | MIT
| Doc PR        | N/A <!-- required for new features -->

<!--
Replace this notice by a short README for your feature/bugfix. This will help people
understand your PR and can be used as a start for the documentation.

Additionally (see https://symfony.com/roadmap):
 - Bug fixes must be submitted against the lowest maintained branch where they apply
   (lowest branches are regularly merged to upper ones so they get the fixes too).
 - Features and deprecations must be submitted against branch 4.4.
 - Legacy code removals go to the master branch.
-->

Commits
-------

fab17a4487 Improve some URLs
2019-08-08 17:01:55 +02:00
Arman Hosseini
93fd0e9f20 Improve some URLs 2019-08-08 17:01:12 +02:00
Jérémy Derussé
c5366e87da Fix remaining tests 2019-08-06 15:24:37 +02:00
Jérémy Derussé
a78bf4dc3a Use assertStringContainsString when needed 2019-08-06 08:15:37 +02:00
Jérémy Derussé
4dc880d082 Remove use of ForwardCompatTrait 2019-08-03 23:15:25 +02:00
Nicolas Grekas
63f26f1891 Adopt @PHPUnit55Migration:risky rule of php-cs-fixer 2019-08-03 15:46:27 +02:00
Jérémy Derussé
bef2cd01a1 Fix deprecated phpunit annotation 2019-08-02 00:48:42 +02:00
Nicolas Grekas
22fea487fa Make tests support phpunit 8 2019-07-31 23:55:24 +02:00
Tobias Weichart
89429ed0eb minor fix for wrong case 2019-07-31 08:13:25 +01:00
Nicolas Grekas
bc977cb268 minor #32619 [Debug][ExceptionHandler] Add tests for custom handlers (fancyweb)
This PR was merged into the 3.4 branch.

Discussion
----------

[Debug][ExceptionHandler] Add tests for custom handlers

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

In https://github.com/symfony/symfony/pull/31694 I mixed many things but the whole PR was closed. I wrote some tests for custom handlers + the handle tests don't use mock anymore

I think they are useful even if the `ExceptionHandler` will disappear in the new component because it will still exists in 4.4 for the next 3 years.

Commits
-------

c53e25332a [Debug][ExceptionHandler] Add tests for custom handlers
2019-07-23 10:39:19 +02:00
Nicolas Grekas
dabd21d13b minor #32623 Remove dead tests fixtures (fancyweb)
This PR was merged into the 3.4 branch.

Discussion
----------

Remove dead tests fixtures

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

Once this is merged up to 4.2, I will check 4.2.

Commits
-------

f7e24c2c80 Remove dead tests fixtures
2019-07-19 14:05:10 +02:00
Thomas Calvet
b6f28caaf2 Remove dead tests fixtures 2019-07-19 13:52:08 +02:00
Thomas Calvet
5bf4824968 [Debug][ExceptionHandler] Add tests for custom handlers 2019-07-19 10:43:44 +02:00
Nicolas Grekas
dff676526c minor #32612 [Debug] Fix 3.4 tests (yceruto)
This PR was merged into the 3.4 branch.

Discussion
----------

[Debug] Fix 3.4 tests

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT

Commits
-------

77fa283091 Fix Debug component tests
2019-07-19 10:32:24 +02:00
Yonel Ceruto
b49ea98332 Fix Debug component tests 2019-07-18 18:35:50 -04:00
Grégoire Paris
a808f15333 Use mocks before replacing the error handler
We want the bridge to mute the deprecations triggered when building mocks.
2019-07-18 08:26:12 +02:00
Thomas Calvet
32d260af46 [Debug][DebugClassLoader] Don't check class if the included file don't exist 2019-07-12 10:40:08 +02:00
Thomas Calvet
740602e8b0 [Debug][DebugClassLoader] Include found files instead of requiring them 2019-07-11 20:09:53 +02:00
Christian Flothmann
d58c0d65d7 fix Debug component dependencies 2019-06-28 11:18:39 +02:00
Nicolas Grekas
1172dc1abe [Debug] workaround BC break in PHP 7.3 2019-06-18 23:26:03 +02:00
Fabien Potencier
7e1a4ec082 fixed CS 2019-06-13 12:34:15 +02:00
Alexander M. Turek
e79bbe15d8 Use willReturn() instead of will(returnValue()). 2019-05-30 17:47:52 +02:00
Grégoire Pineau
671fc55bd1 [Debug] Wrap call to require_once in a try/catch
If the included file contains an error, it hides the real error. This
makes debugging harder.

How to reproduce:

```
composer create-project symfony/skeleton symfony-3.4 3.4
cd symfony-3.4
composer req monolog
```

Add to `monolog.yaml`:
```yaml
        elasticsearch:
            type: "elasticsearch"
            elasticsearch:
                host: 'elasticsearch'
                port: '9200'
            index: 'ep_php_logs_dev'
            level: 'debug'
            tags: 'monolog.logger'
            channels: ['!event']
```

This will fail because the the \Elastica\Client class does not exist.
But this error will be hidden by the `ClassNotFoundFatalErrorHandler`
because it will try to load the `Symfony\Component\Kernel\Client` and
this class extends `Symfony\Component\BrowserKit\Client`. The last one
is a soft dependency...

---

Before
```
Fatal error: Uncaught Error: Class 'Symfony\Component\BrowserKit\Client' not found in /tmp/symfony-3.4/vendor/symfony/http-kernel/Client.php:31
```

After:
```
Fatal error: Uncaught Symfony\Component\Debug\Exception\ClassNotFoundException: Attempted to load class "Client" from namespace "Elastica".
Did you forget a "use" statement for another namespace? in /tmp/symfony-es/var/cache/dev/ContainerWXN4mS9/srcApp_KernelDevDebugContainer.php:303
```
2019-05-18 15:32:47 +02:00
19 changed files with 441 additions and 345 deletions

View File

@@ -28,7 +28,7 @@ class Debug
* @param int $errorReportingLevel The level of error reporting you want
* @param bool $displayErrors Whether to display errors (for development) or just log them (for production)
*/
public static function enable($errorReportingLevel = E_ALL, $displayErrors = true)
public static function enable($errorReportingLevel = \E_ALL, $displayErrors = true)
{
if (static::$enabled) {
return;
@@ -39,13 +39,13 @@ class Debug
if (null !== $errorReportingLevel) {
error_reporting($errorReportingLevel);
} else {
error_reporting(E_ALL);
error_reporting(\E_ALL);
}
if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
ini_set('display_errors', 0);
ExceptionHandler::register();
} elseif ($displayErrors && (!filter_var(ini_get('log_errors'), FILTER_VALIDATE_BOOLEAN) || ini_get('error_log'))) {
} elseif ($displayErrors && (!filter_var(ini_get('log_errors'), \FILTER_VALIDATE_BOOLEAN) || ini_get('error_log'))) {
// CLI - display errors only if they're not already logged to STDERR
ini_set('display_errors', 1);
}

View File

@@ -56,7 +56,7 @@ class DebugClassLoader
} elseif (substr($test, -\strlen($file)) === $file) {
// filesystem is case insensitive and realpath() normalizes the case of characters
self::$caseCheck = 1;
} elseif (false !== stripos(PHP_OS, 'darwin')) {
} elseif (false !== stripos(\PHP_OS, 'darwin')) {
// on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters
self::$caseCheck = 2;
} else {
@@ -141,7 +141,7 @@ class DebugClassLoader
*/
public function loadClass($class)
{
$e = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
$e = error_reporting(error_reporting() | \E_PARSE | \E_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR);
try {
if ($this->isFinder && !isset($this->loaded[$class])) {
@@ -149,11 +149,11 @@ class DebugClassLoader
if (!$file = $this->classLoader[0]->findFile($class) ?: false) {
// no-op
} elseif (\function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file)) {
require $file;
include $file;
return;
} else {
require $file;
} elseif (false === include $file) {
return;
}
} else {
\call_user_func($this->classLoader, $class);
@@ -168,7 +168,7 @@ class DebugClassLoader
private function checkClass($class, $file = null)
{
$exists = null === $file || \class_exists($class, false) || \interface_exists($class, false) || \trait_exists($class, false);
$exists = null === $file || class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false);
if (null !== $file && $class && '\\' === $class[0]) {
$class = substr($class, 1);
@@ -186,18 +186,18 @@ class DebugClassLoader
}
$name = $refl->getName();
if ($name !== $class && 0 === \strcasecmp($name, $class)) {
if ($name !== $class && 0 === strcasecmp($name, $class)) {
throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name));
}
$deprecations = $this->checkAnnotations($refl, $name);
if (isset(self::$php7Reserved[\strtolower($refl->getShortName())])) {
if (isset(self::$php7Reserved[strtolower($refl->getShortName())])) {
$deprecations[] = sprintf('The "%s" class uses the reserved name "%s", it will break on PHP 7 and higher', $name, $refl->getShortName());
}
foreach ($deprecations as $message) {
@trigger_error($message, E_USER_DEPRECATED);
@trigger_error($message, \E_USER_DEPRECATED);
}
}
@@ -223,23 +223,23 @@ class DebugClassLoader
$deprecations = [];
// Don't trigger deprecations for classes in the same vendor
if (2 > $len = 1 + (\strpos($class, '\\') ?: \strpos($class, '_'))) {
if (2 > $len = 1 + (strpos($class, '\\') ?: strpos($class, '_'))) {
$len = 0;
$ns = '';
} else {
$ns = \str_replace('_', '\\', \substr($class, 0, $len));
$ns = str_replace('_', '\\', substr($class, 0, $len));
}
// Detect annotations on the class
if (false !== $doc = $refl->getDocComment()) {
foreach (['final', 'deprecated', 'internal'] as $annotation) {
if (false !== \strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s', $doc, $notice)) {
if (false !== strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s', $doc, $notice)) {
self::${$annotation}[$class] = isset($notice[1]) ? preg_replace('#\.?\r?\n( \*)? *(?= |\r?\n|$)#', '', $notice[1]) : '';
}
}
}
$parent = \get_parent_class($class);
$parent = get_parent_class($class);
$parentAndOwnInterfaces = $this->getOwnInterfaces($class, $parent);
if ($parent) {
$parentAndOwnInterfaces[$parent] = $parent;
@@ -254,22 +254,22 @@ class DebugClassLoader
}
// Detect if the parent is annotated
foreach ($parentAndOwnInterfaces + \class_uses($class, false) as $use) {
foreach ($parentAndOwnInterfaces + class_uses($class, false) as $use) {
if (!isset(self::$checkedClasses[$use])) {
$this->checkClass($use);
}
if (isset(self::$deprecated[$use]) && \strncmp($ns, \str_replace('_', '\\', $use), $len) && !isset(self::$deprecated[$class])) {
if (isset(self::$deprecated[$use]) && strncmp($ns, str_replace('_', '\\', $use), $len) && !isset(self::$deprecated[$class])) {
$type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait');
$verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses');
$deprecations[] = sprintf('The "%s" %s %s "%s" that is deprecated%s.', $class, $type, $verb, $use, self::$deprecated[$use]);
}
if (isset(self::$internal[$use]) && \strncmp($ns, \str_replace('_', '\\', $use), $len)) {
if (isset(self::$internal[$use]) && strncmp($ns, str_replace('_', '\\', $use), $len)) {
$deprecations[] = sprintf('The "%s" %s is considered internal%s. It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $class);
}
}
if (\trait_exists($class)) {
if (trait_exists($class)) {
return $deprecations;
}
@@ -296,7 +296,7 @@ class DebugClassLoader
if (isset(self::$internalMethods[$class][$method->name])) {
list($declaringClass, $message) = self::$internalMethods[$class][$method->name];
if (\strncmp($ns, $declaringClass, $len)) {
if (strncmp($ns, $declaringClass, $len)) {
$deprecations[] = sprintf('The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $class);
}
}
@@ -307,7 +307,7 @@ class DebugClassLoader
}
foreach (['final', 'internal'] as $annotation) {
if (false !== \strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s', $doc, $notice)) {
if (false !== strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s', $doc, $notice)) {
$message = isset($notice[1]) ? preg_replace('#\.?\r?\n( \*)? *(?= |\r?\n|$)#', '', $notice[1]) : '';
self::${$annotation.'Methods'}[$class][$method->name] = [$class, $message];
}
@@ -317,6 +317,12 @@ class DebugClassLoader
return $deprecations;
}
/**
* @param string $file
* @param string $class
*
* @return array|null
*/
public function checkCase(\ReflectionClass $refl, $file, $class)
{
$real = explode('\\', $class.strrchr($file, '.'));
@@ -333,7 +339,7 @@ class DebugClassLoader
array_splice($tail, 0, $i + 1);
if (!$tail) {
return;
return null;
}
$tail = \DIRECTORY_SEPARATOR.implode(\DIRECTORY_SEPARATOR, $tail);
@@ -349,6 +355,8 @@ class DebugClassLoader
) {
return [substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)];
}
return null;
}
/**
@@ -396,7 +404,7 @@ class DebugClassLoader
}
if (isset($dirFiles[$file])) {
return $real .= $dirFiles[$file];
return $real.$dirFiles[$file];
}
$kFile = strtolower($file);
@@ -415,7 +423,7 @@ class DebugClassLoader
self::$darwinCache[$kDir][1] = $dirFiles;
}
return $real .= $dirFiles[$kFile];
return $real.$dirFiles[$kFile];
}
/**

View File

@@ -49,39 +49,39 @@ use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
class ErrorHandler
{
private $levels = [
E_DEPRECATED => 'Deprecated',
E_USER_DEPRECATED => 'User Deprecated',
E_NOTICE => 'Notice',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Runtime Notice',
E_WARNING => 'Warning',
E_USER_WARNING => 'User Warning',
E_COMPILE_WARNING => 'Compile Warning',
E_CORE_WARNING => 'Core Warning',
E_USER_ERROR => 'User Error',
E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
E_COMPILE_ERROR => 'Compile Error',
E_PARSE => 'Parse Error',
E_ERROR => 'Error',
E_CORE_ERROR => 'Core Error',
\E_DEPRECATED => 'Deprecated',
\E_USER_DEPRECATED => 'User Deprecated',
\E_NOTICE => 'Notice',
\E_USER_NOTICE => 'User Notice',
\E_STRICT => 'Runtime Notice',
\E_WARNING => 'Warning',
\E_USER_WARNING => 'User Warning',
\E_COMPILE_WARNING => 'Compile Warning',
\E_CORE_WARNING => 'Core Warning',
\E_USER_ERROR => 'User Error',
\E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
\E_COMPILE_ERROR => 'Compile Error',
\E_PARSE => 'Parse Error',
\E_ERROR => 'Error',
\E_CORE_ERROR => 'Core Error',
];
private $loggers = [
E_DEPRECATED => [null, LogLevel::INFO],
E_USER_DEPRECATED => [null, LogLevel::INFO],
E_NOTICE => [null, LogLevel::WARNING],
E_USER_NOTICE => [null, LogLevel::WARNING],
E_STRICT => [null, LogLevel::WARNING],
E_WARNING => [null, LogLevel::WARNING],
E_USER_WARNING => [null, LogLevel::WARNING],
E_COMPILE_WARNING => [null, LogLevel::WARNING],
E_CORE_WARNING => [null, LogLevel::WARNING],
E_USER_ERROR => [null, LogLevel::CRITICAL],
E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL],
E_COMPILE_ERROR => [null, LogLevel::CRITICAL],
E_PARSE => [null, LogLevel::CRITICAL],
E_ERROR => [null, LogLevel::CRITICAL],
E_CORE_ERROR => [null, LogLevel::CRITICAL],
\E_DEPRECATED => [null, LogLevel::INFO],
\E_USER_DEPRECATED => [null, LogLevel::INFO],
\E_NOTICE => [null, LogLevel::WARNING],
\E_USER_NOTICE => [null, LogLevel::WARNING],
\E_STRICT => [null, LogLevel::WARNING],
\E_WARNING => [null, LogLevel::WARNING],
\E_USER_WARNING => [null, LogLevel::WARNING],
\E_COMPILE_WARNING => [null, LogLevel::WARNING],
\E_CORE_WARNING => [null, LogLevel::WARNING],
\E_USER_ERROR => [null, LogLevel::CRITICAL],
\E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL],
\E_COMPILE_ERROR => [null, LogLevel::CRITICAL],
\E_PARSE => [null, LogLevel::CRITICAL],
\E_ERROR => [null, LogLevel::CRITICAL],
\E_CORE_ERROR => [null, LogLevel::CRITICAL],
];
private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
@@ -154,7 +154,7 @@ class ErrorHandler
$handler->setExceptionHandler($prev);
}
$handler->throwAt(E_ALL & $handler->thrownErrors, true);
$handler->throwAt(\E_ALL & $handler->thrownErrors, true);
return $handler;
}
@@ -176,7 +176,7 @@ class ErrorHandler
* @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants
* @param bool $replace Whether to replace or not any existing logger
*/
public function setDefaultLogger(LoggerInterface $logger, $levels = E_ALL, $replace = false)
public function setDefaultLogger(LoggerInterface $logger, $levels = \E_ALL, $replace = false)
{
$loggers = [];
@@ -188,7 +188,7 @@ class ErrorHandler
}
} else {
if (null === $levels) {
$levels = E_ALL;
$levels = \E_ALL;
}
foreach ($this->loggers as $type => $log) {
if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) {
@@ -223,14 +223,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];
@@ -242,7 +242,7 @@ class ErrorHandler
if ($flush) {
foreach ($this->bootstrappingLogger->cleanLogs() as $log) {
$type = $log[2]['exception'] instanceof \ErrorException ? $log[2]['exception']->getSeverity() : E_ERROR;
$type = $log[2]['exception'] instanceof \ErrorException ? $log[2]['exception']->getSeverity() : \E_ERROR;
if (!isset($flush[$type])) {
$this->bootstrappingLogger->log($log[0], $log[1], $log[2]);
} elseif ($this->loggers[$type][0]) {
@@ -280,7 +280,7 @@ class ErrorHandler
public function throwAt($levels, $replace = false)
{
$prev = $this->thrownErrors;
$this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED;
$this->thrownErrors = ($levels | \E_RECOVERABLE_ERROR | \E_USER_ERROR) & ~\E_USER_DEPRECATED & ~\E_DEPRECATED;
if (!$replace) {
$this->thrownErrors |= $prev;
}
@@ -382,11 +382,15 @@ class ErrorHandler
*/
public function handleError($type, $message, $file, $line)
{
if (\PHP_VERSION_ID >= 70300 && \E_WARNING === $type && '"' === $message[0] && false !== strpos($message, '" targeting switch is equivalent to "break')) {
$type = \E_DEPRECATED;
}
// Level is the current error reporting level to manage silent error.
$level = error_reporting();
$silenced = 0 === ($level & $type);
// Strong errors are not authorized to be silenced.
$level |= E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
$level |= \E_RECOVERABLE_ERROR | \E_USER_ERROR | \E_DEPRECATED | \E_USER_DEPRECATED;
$log = $this->loggedErrors & $type;
$throw = $this->thrownErrors & $type & $level;
$type &= $level | $this->screamedErrors;
@@ -397,7 +401,7 @@ class ErrorHandler
$scope = $this->scopedErrors & $type;
if (4 < $numArgs = \func_num_args()) {
$context = $scope ? (func_get_arg(4) ?: []) : [];
$context = func_get_arg(4) ?: [];
$backtrace = 5 < $numArgs ? func_get_arg(5) : null; // defined on HHVM
} else {
$context = [];
@@ -410,7 +414,7 @@ class ErrorHandler
$context = $e;
}
if (null !== $backtrace && $type & E_ERROR) {
if (null !== $backtrace && $type & \E_ERROR) {
// E_ERROR fatal errors are triggered on HHVM when
// hhvm.error_handling.call_user_handler_on_fatals=1
// which is the way to get their backtrace.
@@ -426,7 +430,7 @@ class ErrorHandler
self::$toStringException = null;
} elseif (!$throw && !($type & $level)) {
if (!isset(self::$silencedErrorCache[$id = $file.':'.$line])) {
$lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3), $type, $file, $line, false) : [];
$lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 3), $type, $file, $line, false) : [];
$errorAsException = new SilencedErrorContext($type, $file, $line, $lightTrace);
} elseif (isset(self::$silencedErrorCache[$id][$message])) {
$lightTrace = null;
@@ -445,7 +449,7 @@ class ErrorHandler
self::$silencedErrorCache[$id][$message] = $errorAsException;
}
if (null === $lightTrace) {
return;
return true;
}
} else {
if ($scope) {
@@ -465,7 +469,7 @@ class ErrorHandler
}
if ($throw) {
if (E_USER_ERROR & $type) {
if (\PHP_VERSION_ID < 70400 && \E_USER_ERROR & $type) {
for ($i = 1; isset($backtrace[$i]); ++$i) {
if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function'])
&& '__toString' === $backtrace[$i]['function']
@@ -517,7 +521,7 @@ class ErrorHandler
$errorAsException ? ['exception' => $errorAsException] : [],
];
} 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();
}
@@ -529,7 +533,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);
}
}
@@ -554,7 +558,7 @@ class ErrorHandler
if (!$exception instanceof \Exception) {
$exception = new FatalThrowableError($exception);
}
$type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR;
$type = $exception instanceof FatalErrorException ? $exception->getSeverity() : \E_ERROR;
$handlerException = null;
if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) {
@@ -594,7 +598,9 @@ class ErrorHandler
$this->exceptionHandler = null;
try {
if (null !== $exceptionHandler) {
return \call_user_func($exceptionHandler, $exception);
$exceptionHandler($exception);
return;
}
$handlerException = $handlerException ?: $exception;
} catch (\Exception $handlerException) {
@@ -668,7 +674,7 @@ class ErrorHandler
// Handled below
}
if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) {
if ($error && $error['type'] &= \E_PARSE | \E_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR) {
// Let's not throw anymore but keep logging
$handler->throwAt(0, true);
$trace = isset($error['backtrace']) ? $error['backtrace'] : null;
@@ -710,9 +716,9 @@ class ErrorHandler
*/
public static function stackErrors()
{
@trigger_error('Support for stacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED);
@trigger_error('Support for stacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', \E_USER_DEPRECATED);
self::$stackedErrorLevels[] = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
self::$stackedErrorLevels[] = error_reporting(error_reporting() | \E_PARSE | \E_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR);
}
/**
@@ -722,13 +728,13 @@ class ErrorHandler
*/
public static function unstackErrors()
{
@trigger_error('Support for unstacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED);
@trigger_error('Support for unstacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', \E_USER_DEPRECATED);
$level = array_pop(self::$stackedErrorLevels);
if (null !== $level) {
$errorReportingLevel = error_reporting($level);
if ($errorReportingLevel !== ($level | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) {
if ($errorReportingLevel !== ($level | \E_PARSE | \E_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR)) {
// If the user changed the error level, do not overwrite it
error_reporting($errorReportingLevel);
}

View File

@@ -33,7 +33,7 @@ class ContextErrorException extends \ErrorException
*/
public function getContext()
{
@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED);
@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0.', __CLASS__), \E_USER_DEPRECATED);
return $this->context;
}

View File

@@ -22,13 +22,13 @@ class FatalThrowableError extends FatalErrorException
{
if ($e instanceof \ParseError) {
$message = 'Parse error: '.$e->getMessage();
$severity = E_PARSE;
$severity = \E_PARSE;
} elseif ($e instanceof \TypeError) {
$message = 'Type error: '.$e->getMessage();
$severity = E_RECOVERABLE_ERROR;
$severity = \E_RECOVERABLE_ERROR;
} else {
$message = $e->getMessage();
$severity = E_ERROR;
$severity = \E_ERROR;
}
\ErrorException::__construct(

View File

@@ -54,7 +54,7 @@ class SilencedErrorContext implements \JsonSerializable
return $this->trace;
}
public function JsonSerialize()
public function jsonSerialize()
{
return [
'severity' => $this->severity,

View File

@@ -377,7 +377,7 @@ EOF;
if (\is_string($fmt)) {
$i = strpos($f = $fmt, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: \strlen($f);
$fmt = [substr($f, 0, $i)] + preg_split('/&([^>]++)>/', substr($f, $i), -1, PREG_SPLIT_DELIM_CAPTURE);
$fmt = [substr($f, 0, $i)] + preg_split('/&([^>]++)>/', substr($f, $i), -1, \PREG_SPLIT_DELIM_CAPTURE);
for ($i = 1; isset($fmt[$i]); ++$i) {
if (0 === strpos($path, $k = $fmt[$i++])) {
@@ -434,7 +434,7 @@ EOF;
*/
private function escapeHtml($str)
{
return htmlspecialchars($str, ENT_COMPAT | ENT_SUBSTITUTE, $this->charset);
return htmlspecialchars($str, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset);
}
private function getSymfonyGhostAsSvg()

View File

@@ -29,48 +29,34 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
*/
public function handleError(array $error, FatalErrorException $exception)
{
$messageLen = \strlen($error['message']);
$notFoundSuffix = '\' not found';
$notFoundSuffixLen = \strlen($notFoundSuffix);
if ($notFoundSuffixLen > $messageLen) {
return;
if (!preg_match('/^(Class|Interface|Trait) [\'"]([^\'"]+)[\'"] not found$/', $error['message'], $matches)) {
return null;
}
$typeName = strtolower($matches[1]);
$fullyQualifiedClassName = $matches[2];
if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) {
$className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1);
$namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex);
$message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix);
$tail = ' for another namespace?';
} else {
$className = $fullyQualifiedClassName;
$message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className);
$tail = '?';
}
if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) {
return;
}
foreach (['class', 'interface', 'trait'] as $typeName) {
$prefix = ucfirst($typeName).' \'';
$prefixLen = \strlen($prefix);
if (0 !== strpos($error['message'], $prefix)) {
continue;
}
$fullyQualifiedClassName = substr($error['message'], $prefixLen, -$notFoundSuffixLen);
if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) {
$className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1);
$namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex);
$message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix);
$tail = ' for another namespace?';
if ($candidates = $this->getClassCandidates($className)) {
$tail = array_pop($candidates).'"?';
if ($candidates) {
$tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail;
} else {
$className = $fullyQualifiedClassName;
$message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className);
$tail = '?';
$tail = ' for "'.$tail;
}
if ($candidates = $this->getClassCandidates($className)) {
$tail = array_pop($candidates).'"?';
if ($candidates) {
$tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail;
} else {
$tail = ' for "'.$tail;
}
}
$message .= "\nDid you forget a \"use\" statement".$tail;
return new ClassNotFoundException($message, $exception);
}
$message .= "\nDid you forget a \"use\" statement".$tail;
return new ClassNotFoundException($message, $exception);
}
/**
@@ -185,13 +171,19 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
}
}
require_once $file;
try {
require_once $file;
} catch (\Throwable $e) {
return null;
}
foreach ($candidates as $candidate) {
if ($this->classExists($candidate)) {
return $candidate;
}
}
return null;
}
/**

View File

@@ -30,17 +30,17 @@ class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface
$notFoundSuffix = '()';
$notFoundSuffixLen = \strlen($notFoundSuffix);
if ($notFoundSuffixLen > $messageLen) {
return;
return null;
}
if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) {
return;
return null;
}
$prefix = 'Call to undefined function ';
$prefixLen = \strlen($prefix);
if (0 !== strpos($error['message'], $prefix)) {
return;
return null;
}
$fullyQualifiedFunctionName = substr($error['message'], $prefixLen, -$notFoundSuffixLen);

View File

@@ -28,7 +28,7 @@ class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
{
preg_match('/^Call to undefined method (.*)::(.*)\(\)$/', $error['message'], $matches);
if (!$matches) {
return;
return null;
}
$className = $matches[1];

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2019 Fabien Potencier
Copyright (c) 2004-2020 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -3,10 +3,22 @@ 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
---------
* [Documentation](https://symfony.com/doc/current/components/debug/index.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)

View File

@@ -59,21 +59,19 @@ class DebugClassLoaderTest extends TestCase
$this->fail('DebugClassLoader did not register');
}
/**
* @expectedException \Exception
* @expectedExceptionMessage boo
*/
public function testThrowingClass()
{
$this->expectException('Exception');
$this->expectExceptionMessage('boo');
try {
class_exists(__NAMESPACE__.'\Fixtures\Throwing');
class_exists(Fixtures\Throwing::class);
$this->fail('Exception expected');
} catch (\Exception $e) {
$this->assertSame('boo', $e->getMessage());
}
// the second call also should throw
class_exists(__NAMESPACE__.'\Fixtures\Throwing');
class_exists(Fixtures\Throwing::class);
}
public function testUnsilencing()
@@ -92,13 +90,16 @@ class DebugClassLoaderTest extends TestCase
// See below: this will fail with parse error
// but this should not be @-silenced.
@class_exists(__NAMESPACE__.'\TestingUnsilencing', true);
@class_exists(TestingUnsilencing::class, true);
$output = ob_get_clean();
$this->assertStringMatchesFormat('%aParse error%a', $output);
}
/**
* @requires PHP < 8.0
*/
public function testStacking()
{
// the ContextErrorException must not be loaded to test the workaround
@@ -127,10 +128,10 @@ class DebugClassLoaderTest extends TestCase
// if an exception is thrown, the test passed
$this->assertStringStartsWith(__FILE__, $exception->getFile());
if (\PHP_VERSION_ID < 70000) {
$this->assertRegExp('/^Runtime Notice: Declaration/', $exception->getMessage());
$this->assertMatchesRegularExpression('/^Runtime Notice: Declaration/', $exception->getMessage());
$this->assertEquals(E_STRICT, $exception->getSeverity());
} else {
$this->assertRegExp('/^Warning: Declaration/', $exception->getMessage());
$this->assertMatchesRegularExpression('/^Warning: Declaration/', $exception->getMessage());
$this->assertEquals(E_WARNING, $exception->getSeverity());
}
} finally {
@@ -139,34 +140,28 @@ class DebugClassLoaderTest extends TestCase
}
}
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Case mismatch between loaded and declared class names
*/
public function testNameCaseMismatch()
{
class_exists(__NAMESPACE__.'\TestingCaseMismatch', true);
$this->expectException('RuntimeException');
$this->expectExceptionMessage('Case mismatch between loaded and declared class names');
class_exists(TestingCaseMismatch::class, true);
}
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Case mismatch between class and real file names
*/
public function testFileCaseMismatch()
{
$this->expectException('RuntimeException');
$this->expectExceptionMessage('Case mismatch between class and real file names');
if (!file_exists(__DIR__.'/Fixtures/CaseMismatch.php')) {
$this->markTestSkipped('Can only be run on case insensitive filesystems');
}
class_exists(__NAMESPACE__.'\Fixtures\CaseMismatch', true);
class_exists(Fixtures\CaseMismatch::class, true);
}
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Case mismatch between loaded and declared class names
*/
public function testPsr4CaseMismatch()
{
$this->expectException('RuntimeException');
$this->expectExceptionMessage('Case mismatch between loaded and declared class names');
class_exists(__NAMESPACE__.'\Fixtures\Psr4CaseMismatch', true);
}
@@ -182,7 +177,7 @@ class DebugClassLoaderTest extends TestCase
public function testClassAlias()
{
$this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\ClassAlias', true));
$this->assertTrue(class_exists(Fixtures\ClassAlias::class, true));
}
/**
@@ -224,7 +219,7 @@ class DebugClassLoaderTest extends TestCase
$e = error_reporting(0);
trigger_error('', E_USER_NOTICE);
class_exists('Test\\'.__NAMESPACE__.'\\NonDeprecatedInterfaceClass', true);
class_exists('Test\\'.NonDeprecatedInterfaceClass::class, true);
error_reporting($e);
restore_error_handler();
@@ -272,7 +267,7 @@ class DebugClassLoaderTest extends TestCase
$e = error_reporting(0);
trigger_error('', E_USER_NOTICE);
class_exists('Test\\'.__NAMESPACE__.'\\Float', true);
class_exists('Test\\'.Float::class, true);
error_reporting($e);
restore_error_handler();
@@ -297,7 +292,7 @@ class DebugClassLoaderTest extends TestCase
require __DIR__.'/Fixtures/FinalClasses.php';
$i = 1;
while (class_exists($finalClass = __NAMESPACE__.'\\Fixtures\\FinalClass'.$i++, false)) {
while (class_exists($finalClass = Fixtures\FinalClass::class.$i++, false)) {
spl_autoload_call($finalClass);
class_exists('Test\\'.__NAMESPACE__.'\\Extends'.substr($finalClass, strrpos($finalClass, '\\') + 1), true);
}
@@ -323,7 +318,7 @@ class DebugClassLoaderTest extends TestCase
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
$e = error_reporting(E_USER_DEPRECATED);
class_exists(__NAMESPACE__.'\\Fixtures\\ExtendedFinalMethod', true);
class_exists(Fixtures\ExtendedFinalMethod::class, true);
error_reporting($e);
restore_error_handler();
@@ -342,7 +337,7 @@ class DebugClassLoaderTest extends TestCase
$e = error_reporting(0);
trigger_error('', E_USER_NOTICE);
class_exists('Test\\'.__NAMESPACE__.'\\ExtendsAnnotatedClass', true);
class_exists('Test\\'.ExtendsAnnotatedClass::class, true);
error_reporting($e);
restore_error_handler();
@@ -359,7 +354,7 @@ class DebugClassLoaderTest extends TestCase
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
$e = error_reporting(E_USER_DEPRECATED);
class_exists('Test\\'.__NAMESPACE__.'\\ExtendsInternals', true);
class_exists('Test\\'.ExtendsInternals::class, true);
error_reporting($e);
restore_error_handler();
@@ -378,7 +373,7 @@ class DebugClassLoaderTest extends TestCase
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
$e = error_reporting(E_USER_DEPRECATED);
class_exists('Test\\'.__NAMESPACE__.'\\UseTraitWithInternalMethod', true);
class_exists('Test\\'.UseTraitWithInternalMethod::class, true);
error_reporting($e);
restore_error_handler();
@@ -388,7 +383,7 @@ class DebugClassLoaderTest extends TestCase
public function testEvaluatedCode()
{
$this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\DefinitionInEvaluatedCode', true));
$this->assertTrue(class_exists(Fixtures\DefinitionInEvaluatedCode::class, true));
}
}
@@ -407,11 +402,11 @@ class ClassLoader
{
$fixtureDir = __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR;
if (__NAMESPACE__.'\TestingUnsilencing' === $class) {
if (TestingUnsilencing::class === $class) {
eval('-- parse error --');
} elseif (__NAMESPACE__.'\TestingStacking' === $class) {
} elseif (TestingStacking::class === $class) {
eval('namespace '.__NAMESPACE__.'; class TestingStacking { function foo() {} }');
} elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) {
} elseif (TestingCaseMismatch::class === $class) {
eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}');
} elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) {
return $fixtureDir.'psr4'.\DIRECTORY_SEPARATOR.'Psr4CaseMismatch.php';
@@ -421,31 +416,33 @@ class ClassLoader
return $fixtureDir.'notPsr0Bis.php';
} elseif ('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent' === $class) {
eval('namespace Symfony\Bridge\Debug\Tests\Fixtures; class ExtendsDeprecatedParent extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}');
} elseif ('Test\\'.__NAMESPACE__.'\DeprecatedParentClass' === $class) {
} elseif ('Test\\'.DeprecatedParentClass::class === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedParentClass extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}');
} elseif ('Test\\'.__NAMESPACE__.'\DeprecatedInterfaceClass' === $class) {
} elseif ('Test\\'.DeprecatedInterfaceClass::class === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\DeprecatedInterface {}');
} elseif ('Test\\'.__NAMESPACE__.'\NonDeprecatedInterfaceClass' === $class) {
} elseif ('Test\\'.NonDeprecatedInterfaceClass::class === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class NonDeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\NonDeprecatedInterface {}');
} elseif ('Test\\'.__NAMESPACE__.'\Float' === $class) {
} elseif ('Test\\'.Float::class === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class Float {}');
} elseif (0 === strpos($class, 'Test\\'.__NAMESPACE__.'\ExtendsFinalClass')) {
} elseif (0 === strpos($class, 'Test\\'.ExtendsFinalClass::class)) {
$classShortName = substr($class, strrpos($class, '\\') + 1);
eval('namespace Test\\'.__NAMESPACE__.'; class '.$classShortName.' extends \\'.__NAMESPACE__.'\Fixtures\\'.substr($classShortName, 7).' {}');
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsAnnotatedClass' === $class) {
} elseif ('Test\\'.ExtendsAnnotatedClass::class === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsAnnotatedClass extends \\'.__NAMESPACE__.'\Fixtures\AnnotatedClass {
public function deprecatedMethod() { }
}');
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternals' === $class) {
} elseif ('Test\\'.ExtendsInternals::class === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternals extends ExtendsInternalsParent {
use \\'.__NAMESPACE__.'\Fixtures\InternalTrait;
public function internalMethod() { }
}');
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternalsParent' === $class) {
} elseif ('Test\\'.ExtendsInternalsParent::class === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternalsParent extends \\'.__NAMESPACE__.'\Fixtures\InternalClass implements \\'.__NAMESPACE__.'\Fixtures\InternalInterface { }');
} elseif ('Test\\'.__NAMESPACE__.'\UseTraitWithInternalMethod' === $class) {
} elseif ('Test\\'.UseTraitWithInternalMethod::class === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class UseTraitWithInternalMethod { use \\'.__NAMESPACE__.'\Fixtures\TraitWithInternalMethod; }');
}
return null;
}
}

View File

@@ -70,15 +70,15 @@ class ErrorHandlerTest extends TestCase
public function testErrorGetLast()
{
$handler = ErrorHandler::register();
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$handler = ErrorHandler::register();
$handler->setDefaultLogger($logger);
$handler->screamAt(E_ALL);
$handler->screamAt(\E_ALL);
try {
@trigger_error('Hello', E_USER_WARNING);
@trigger_error('Hello', \E_USER_WARNING);
$expected = [
'type' => E_USER_WARNING,
'type' => \E_USER_WARNING,
'message' => 'Hello',
'file' => __FILE__,
'line' => __LINE__ - 5,
@@ -101,9 +101,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->assertMatchesRegularExpression('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
} else {
$this->assertEquals(\E_WARNING, $exception->getSeverity());
$this->assertMatchesRegularExpression('/^Warning: Undefined variable \$(foo|bar)/', $exception->getMessage());
}
$this->assertEquals(__FILE__, $exception->getFile());
$this->assertRegExp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
$trace = $exception->getTrace();
@@ -133,7 +138,7 @@ class ErrorHandlerTest extends TestCase
try {
$handler = ErrorHandler::register();
$handler->throwAt(3, true);
$this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, $handler->throwAt(0));
$this->assertEquals(3 | \E_RECOVERABLE_ERROR | \E_USER_ERROR, $handler->throwAt(0));
} finally {
restore_error_handler();
restore_exception_handler();
@@ -143,29 +148,28 @@ class ErrorHandlerTest extends TestCase
public function testDefaultLogger()
{
try {
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$handler = ErrorHandler::register();
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$handler->setDefaultLogger($logger, E_NOTICE);
$handler->setDefaultLogger($logger, [E_USER_NOTICE => LogLevel::CRITICAL]);
$handler->setDefaultLogger($logger, \E_NOTICE);
$handler->setDefaultLogger($logger, [\E_USER_NOTICE => LogLevel::CRITICAL]);
$loggers = [
E_DEPRECATED => [null, LogLevel::INFO],
E_USER_DEPRECATED => [null, LogLevel::INFO],
E_NOTICE => [$logger, LogLevel::WARNING],
E_USER_NOTICE => [$logger, LogLevel::CRITICAL],
E_STRICT => [null, LogLevel::WARNING],
E_WARNING => [null, LogLevel::WARNING],
E_USER_WARNING => [null, LogLevel::WARNING],
E_COMPILE_WARNING => [null, LogLevel::WARNING],
E_CORE_WARNING => [null, LogLevel::WARNING],
E_USER_ERROR => [null, LogLevel::CRITICAL],
E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL],
E_COMPILE_ERROR => [null, LogLevel::CRITICAL],
E_PARSE => [null, LogLevel::CRITICAL],
E_ERROR => [null, LogLevel::CRITICAL],
E_CORE_ERROR => [null, LogLevel::CRITICAL],
\E_DEPRECATED => [null, LogLevel::INFO],
\E_USER_DEPRECATED => [null, LogLevel::INFO],
\E_NOTICE => [$logger, LogLevel::WARNING],
\E_USER_NOTICE => [$logger, LogLevel::CRITICAL],
\E_STRICT => [null, LogLevel::WARNING],
\E_WARNING => [null, LogLevel::WARNING],
\E_USER_WARNING => [null, LogLevel::WARNING],
\E_COMPILE_WARNING => [null, LogLevel::WARNING],
\E_CORE_WARNING => [null, LogLevel::WARNING],
\E_USER_ERROR => [null, LogLevel::CRITICAL],
\E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL],
\E_COMPILE_ERROR => [null, LogLevel::CRITICAL],
\E_PARSE => [null, LogLevel::CRITICAL],
\E_ERROR => [null, LogLevel::CRITICAL],
\E_CORE_ERROR => [null, LogLevel::CRITICAL],
];
$this->assertSame($loggers, $handler->setLoggers([]));
} finally {
@@ -206,15 +210,15 @@ class ErrorHandlerTest extends TestCase
restore_exception_handler();
$handler = ErrorHandler::register();
$handler->throwAt(E_USER_DEPRECATED, true);
$this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, []));
$handler->throwAt(\E_USER_DEPRECATED, true);
$this->assertFalse($handler->handleError(\E_USER_DEPRECATED, 'foo', 'foo.php', 12, []));
restore_error_handler();
restore_exception_handler();
$handler = ErrorHandler::register();
$handler->throwAt(E_DEPRECATED, true);
$this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, []));
$handler->throwAt(\E_DEPRECATED, true);
$this->assertFalse($handler->handleError(\E_DEPRECATED, 'foo', 'foo.php', 12, []));
restore_error_handler();
restore_exception_handler();
@@ -228,18 +232,18 @@ class ErrorHandlerTest extends TestCase
$exception = $context['exception'];
$this->assertInstanceOf(\ErrorException::class, $exception);
$this->assertSame('User Deprecated: foo', $exception->getMessage());
$this->assertSame(E_USER_DEPRECATED, $exception->getSeverity());
$this->assertSame(\E_USER_DEPRECATED, $exception->getSeverity());
};
$logger
->expects($this->once())
->method('log')
->will($this->returnCallback($warnArgCheck))
->willReturnCallback($warnArgCheck)
;
$handler = ErrorHandler::register();
$handler->setDefaultLogger($logger, E_USER_DEPRECATED);
$this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, []));
$handler->setDefaultLogger($logger, \E_USER_DEPRECATED);
$this->assertTrue($handler->handleError(\E_USER_DEPRECATED, 'foo', 'foo.php', 12, []));
restore_error_handler();
restore_exception_handler();
@@ -248,11 +252,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());
@@ -262,12 +272,17 @@ class ErrorHandlerTest extends TestCase
$logger
->expects($this->once())
->method('log')
->will($this->returnCallback($logArgCheck))
->willReturnCallback($logArgCheck)
;
$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++;
@@ -284,6 +299,10 @@ class ErrorHandlerTest extends TestCase
public function testHandleUserError()
{
if (\PHP_VERSION_ID >= 70400) {
$this->markTestSkipped('PHP 7.4 allows __toString to throw exceptions');
}
try {
$handler = ErrorHandler::register();
$handler->throwAt(0, true);
@@ -318,14 +337,12 @@ class ErrorHandlerTest extends TestCase
$logger
->expects($this->once())
->method('log')
->will($this->returnCallback($logArgCheck))
->willReturnCallback($logArgCheck)
;
$handler = new ErrorHandler();
$handler->setDefaultLogger($logger);
@$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, []);
restore_error_handler();
@$handler->handleError(\E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, []);
}
/**
@@ -334,12 +351,11 @@ class ErrorHandlerTest extends TestCase
public function testHandleException()
{
try {
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$handler = ErrorHandler::register();
$exception = new \Exception('foo');
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logArgCheck = function ($level, $message, $context) {
$this->assertSame('Uncaught Exception: foo', $message);
$this->assertArrayHasKey('exception', $context);
@@ -349,10 +365,10 @@ class ErrorHandlerTest extends TestCase
$logger
->expects($this->exactly(2))
->method('log')
->will($this->returnCallback($logArgCheck))
->willReturnCallback($logArgCheck)
;
$handler->setDefaultLogger($logger, E_ERROR);
$handler->setDefaultLogger($logger, \E_ERROR);
try {
$handler->handleException($exception);
@@ -379,7 +395,7 @@ class ErrorHandlerTest extends TestCase
{
try {
$handler = ErrorHandler::register();
$handler->screamAt(E_USER_WARNING);
$handler->screamAt(\E_USER_WARNING);
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
@@ -392,10 +408,10 @@ class ErrorHandlerTest extends TestCase
)
;
$handler->setDefaultLogger($logger, [E_USER_WARNING => LogLevel::WARNING]);
$handler->setDefaultLogger($logger, [\E_USER_WARNING => LogLevel::WARNING]);
ErrorHandler::stackErrors();
@trigger_error('Silenced warning', E_USER_WARNING);
@trigger_error('Silenced warning', \E_USER_WARNING);
$logger->log(LogLevel::WARNING, 'Dummy log');
ErrorHandler::unstackErrors();
} finally {
@@ -410,26 +426,26 @@ class ErrorHandlerTest extends TestCase
$handler = new ErrorHandler($bootLogger);
$loggers = [
E_DEPRECATED => [$bootLogger, LogLevel::INFO],
E_USER_DEPRECATED => [$bootLogger, LogLevel::INFO],
E_NOTICE => [$bootLogger, LogLevel::WARNING],
E_USER_NOTICE => [$bootLogger, LogLevel::WARNING],
E_STRICT => [$bootLogger, LogLevel::WARNING],
E_WARNING => [$bootLogger, LogLevel::WARNING],
E_USER_WARNING => [$bootLogger, LogLevel::WARNING],
E_COMPILE_WARNING => [$bootLogger, LogLevel::WARNING],
E_CORE_WARNING => [$bootLogger, LogLevel::WARNING],
E_USER_ERROR => [$bootLogger, LogLevel::CRITICAL],
E_RECOVERABLE_ERROR => [$bootLogger, LogLevel::CRITICAL],
E_COMPILE_ERROR => [$bootLogger, LogLevel::CRITICAL],
E_PARSE => [$bootLogger, LogLevel::CRITICAL],
E_ERROR => [$bootLogger, LogLevel::CRITICAL],
E_CORE_ERROR => [$bootLogger, LogLevel::CRITICAL],
\E_DEPRECATED => [$bootLogger, LogLevel::INFO],
\E_USER_DEPRECATED => [$bootLogger, LogLevel::INFO],
\E_NOTICE => [$bootLogger, LogLevel::WARNING],
\E_USER_NOTICE => [$bootLogger, LogLevel::WARNING],
\E_STRICT => [$bootLogger, LogLevel::WARNING],
\E_WARNING => [$bootLogger, LogLevel::WARNING],
\E_USER_WARNING => [$bootLogger, LogLevel::WARNING],
\E_COMPILE_WARNING => [$bootLogger, LogLevel::WARNING],
\E_CORE_WARNING => [$bootLogger, LogLevel::WARNING],
\E_USER_ERROR => [$bootLogger, LogLevel::CRITICAL],
\E_RECOVERABLE_ERROR => [$bootLogger, LogLevel::CRITICAL],
\E_COMPILE_ERROR => [$bootLogger, LogLevel::CRITICAL],
\E_PARSE => [$bootLogger, LogLevel::CRITICAL],
\E_ERROR => [$bootLogger, LogLevel::CRITICAL],
\E_CORE_ERROR => [$bootLogger, LogLevel::CRITICAL],
];
$this->assertSame($loggers, $handler->setLoggers([]));
$handler->handleError(E_DEPRECATED, 'Foo message', __FILE__, 123, []);
$handler->handleError(\E_DEPRECATED, 'Foo message', __FILE__, 123, []);
$logs = $bootLogger->cleanLogs();
@@ -443,7 +459,7 @@ class ErrorHandlerTest extends TestCase
$this->assertSame('Deprecated: Foo message', $exception->getMessage());
$this->assertSame(__FILE__, $exception->getFile());
$this->assertSame(123, $exception->getLine());
$this->assertSame(E_DEPRECATED, $exception->getSeverity());
$this->assertSame(\E_DEPRECATED, $exception->getSeverity());
$bootLogger->log(LogLevel::WARNING, 'Foo message', ['exception' => $exception]);
@@ -452,7 +468,7 @@ class ErrorHandlerTest extends TestCase
->method('log')
->with(LogLevel::WARNING, 'Foo message', ['exception' => $exception]);
$handler->setLoggers([E_DEPRECATED => [$mockLogger, LogLevel::WARNING]]);
$handler->setLoggers([\E_DEPRECATED => [$mockLogger, LogLevel::WARNING]]);
}
/**
@@ -483,17 +499,16 @@ class ErrorHandlerTest extends TestCase
public function testHandleFatalError()
{
try {
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$handler = ErrorHandler::register();
$error = [
'type' => E_PARSE,
'type' => \E_PARSE,
'message' => 'foo',
'file' => 'bar',
'line' => 123,
];
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logArgCheck = function ($level, $message, $context) {
$this->assertEquals('Fatal Parse Error: foo', $message);
$this->assertArrayHasKey('exception', $context);
@@ -503,10 +518,10 @@ class ErrorHandlerTest extends TestCase
$logger
->expects($this->once())
->method('log')
->will($this->returnCallback($logArgCheck))
->willReturnCallback($logArgCheck)
;
$handler->setDefaultLogger($logger, E_PARSE);
$handler->setDefaultLogger($logger, \E_PARSE);
$handler->handleFatalError($error);
@@ -543,6 +558,10 @@ class ErrorHandlerTest extends TestCase
*/
public function testHandleFatalErrorOnHHVM()
{
if (!\defined('HHVM_VERSION')) {
$this->markTestSkipped('This test requires HHVM.');
}
try {
$handler = ErrorHandler::register();
@@ -556,10 +575,10 @@ class ErrorHandlerTest extends TestCase
)
;
$handler->setDefaultLogger($logger, E_ERROR);
$handler->setDefaultLogger($logger, \E_ERROR);
$error = [
'type' => E_ERROR + 0x1000000, // This error level is used by HHVM for fatal errors
'type' => \E_ERROR + 0x1000000, // This error level is used by HHVM for fatal errors
'message' => 'foo',
'file' => 'bar',
'line' => 123,
@@ -576,11 +595,11 @@ class ErrorHandlerTest extends TestCase
}
/**
* @expectedException \Exception
* @group no-hhvm
*/
public function testCustomExceptionHandler()
{
$this->expectException('Exception');
$handler = new ErrorHandler();
$handler->setExceptionHandler(function ($e) use ($handler) {
$handler->handleException($e);
@@ -608,8 +627,8 @@ class ErrorHandlerTest extends TestCase
$handler = ErrorHandlerThatUsesThePreviousOne::register();
}
@trigger_error('foo', E_USER_DEPRECATED);
@trigger_error('bar', E_USER_DEPRECATED);
@trigger_error('foo', \E_USER_DEPRECATED);
@trigger_error('bar', \E_USER_DEPRECATED);
$this->assertSame([$handler, 'handleError'], set_error_handler('var_dump'));

View File

@@ -147,9 +147,9 @@ class FlattenExceptionTest extends TestCase
$flattened = FlattenException::create($exception)->getPrevious();
$this->assertEquals($flattened->getMessage(), 'Parse error: 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(), 'Symfony\Component\Debug\Exception\FatalThrowableError', 'The class is set to the class of the original exception');
$this->assertEquals('Parse error: 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('Symfony\Component\Debug\Exception\FatalThrowableError', $flattened->getClass(), 'The class is set to the class of the original exception');
}
/**
@@ -199,6 +199,10 @@ class FlattenExceptionTest extends TestCase
public function testArguments()
{
if (\PHP_VERSION_ID >= 70400) {
$this->markTestSkipped('PHP 7.4 removes arguments from exception traces.');
}
$dh = opendir(__DIR__);
$fh = tmpfile();
@@ -220,8 +224,8 @@ class FlattenExceptionTest extends TestCase
0.0,
'0',
'',
INF,
NAN,
\INF,
\NAN,
]);
$flattened = FlattenException::create($exception);
@@ -252,26 +256,34 @@ class FlattenExceptionTest extends TestCase
$this->assertSame(['float', 0.0], $array[$i++]);
$this->assertSame(['string', '0'], $array[$i++]);
$this->assertSame(['string', ''], $array[$i++]);
$this->assertSame(['float', INF], $array[$i++]);
$this->assertSame(['float', \INF], $array[$i++]);
// assertEquals() does not like NAN values.
$this->assertEquals($array[$i][0], 'float');
$this->assertTrue(is_nan($array[$i++][1]));
$this->assertEquals('float', $array[$i][0]);
$this->assertNan($array[$i][1]);
}
public function testRecursionInArguments()
{
if (\PHP_VERSION_ID >= 70400) {
$this->markTestSkipped('PHP 7.4 removes arguments from exception traces.');
}
$a = null;
$a = ['foo', [2, &$a]];
$exception = $this->createException($a);
$flattened = FlattenException::create($exception);
$trace = $flattened->getTrace();
$this->assertContains('*DEEP NESTED ARRAY*', serialize($trace));
$this->assertStringContainsString('*DEEP NESTED ARRAY*', serialize($trace));
}
public function testTooBigArray()
{
if (\PHP_VERSION_ID >= 70400) {
$this->markTestSkipped('PHP 7.4 removes arguments from exception traces.');
}
$a = [];
for ($i = 0; $i < 20; ++$i) {
for ($j = 0; $j < 50; ++$j) {
@@ -291,8 +303,8 @@ class FlattenExceptionTest extends TestCase
$serializeTrace = serialize($trace);
$this->assertContains('*SKIPPED over 10000 entries*', $serializeTrace);
$this->assertNotContains('*value1*', $serializeTrace);
$this->assertStringContainsString('*SKIPPED over 10000 entries*', $serializeTrace);
$this->assertStringNotContainsString('*value1*', $serializeTrace);
}
private function createException($foo)

View File

@@ -39,8 +39,8 @@ class ExceptionHandlerTest extends TestCase
$handler->sendPhpResponse(new \RuntimeException('Foo'));
$response = ob_get_clean();
$this->assertContains('Whoops, looks like something went wrong.', $response);
$this->assertNotContains('<div class="trace trace-as-html">', $response);
$this->assertStringContainsString('Whoops, looks like something went wrong.', $response);
$this->assertStringNotContainsString('<div class="trace trace-as-html">', $response);
$handler = new ExceptionHandler(true);
@@ -48,8 +48,8 @@ class ExceptionHandlerTest extends TestCase
$handler->sendPhpResponse(new \RuntimeException('Foo'));
$response = ob_get_clean();
$this->assertContains('Whoops, looks like something went wrong.', $response);
$this->assertContains('<div class="trace trace-as-html">', $response);
$this->assertStringContainsString('Whoops, looks like something went wrong.', $response);
$this->assertStringContainsString('<div class="trace trace-as-html">', $response);
}
public function testStatusCode()
@@ -60,7 +60,7 @@ class ExceptionHandlerTest extends TestCase
$handler->sendPhpResponse(new NotFoundHttpException('Foo'));
$response = ob_get_clean();
$this->assertContains('Sorry, the page you are looking for could not be found.', $response);
$this->assertStringContainsString('Sorry, the page you are looking for could not be found.', $response);
$expectedHeaders = [
['HTTP/1.0 404', true, null],
@@ -76,7 +76,7 @@ class ExceptionHandlerTest extends TestCase
ob_start();
$handler->sendPhpResponse(new MethodNotAllowedHttpException(['POST']));
$response = ob_get_clean();
ob_get_clean();
$expectedHeaders = [
['HTTP/1.0 405', true, null],
@@ -99,35 +99,65 @@ class ExceptionHandlerTest extends TestCase
public function testHandle()
{
$exception = new \Exception('foo');
$handler = new ExceptionHandler(true);
ob_start();
$handler = $this->getMockBuilder('Symfony\Component\Debug\ExceptionHandler')->setMethods(['sendPhpResponse'])->getMock();
$handler
->expects($this->exactly(2))
->method('sendPhpResponse');
$handler->handle(new \Exception('foo'));
$handler->handle($exception);
$this->assertThatTheExceptionWasOutput(ob_get_clean(), \Exception::class, 'Exception', 'foo');
}
$handler->setHandler(function ($e) use ($exception) {
$this->assertSame($exception, $e);
public function testHandleWithACustomHandlerThatOutputsSomething()
{
$handler = new ExceptionHandler(true);
ob_start();
$handler->setHandler(function () {
echo 'ccc';
});
$handler->handle($exception);
$handler->handle(new \Exception());
ob_end_flush(); // Necessary because of this PHP bug : https://bugs.php.net/76563
$this->assertSame('ccc', ob_get_clean());
}
public function testHandleWithACustomHandlerThatOutputsNothing()
{
$handler = new ExceptionHandler(true);
$handler->setHandler(function () {});
$handler->handle(new \Exception('ccc'));
$this->assertThatTheExceptionWasOutput(ob_get_clean(), \Exception::class, 'Exception', 'ccc');
}
public function testHandleWithACustomHandlerThatFails()
{
$handler = new ExceptionHandler(true);
$handler->setHandler(function () {
throw new \RuntimeException();
});
$handler->handle(new \Exception('ccc'));
$this->assertThatTheExceptionWasOutput(ob_get_clean(), \Exception::class, 'Exception', 'ccc');
}
public function testHandleOutOfMemoryException()
{
$exception = new OutOfMemoryException('foo', 0, E_ERROR, __FILE__, __LINE__);
$handler = $this->getMockBuilder('Symfony\Component\Debug\ExceptionHandler')->setMethods(['sendPhpResponse'])->getMock();
$handler
->expects($this->once())
->method('sendPhpResponse');
$handler->setHandler(function ($e) {
$handler = new ExceptionHandler(true);
ob_start();
$handler->setHandler(function () {
$this->fail('OutOfMemoryException should bypass the handler');
});
$handler->handle($exception);
$handler->handle(new OutOfMemoryException('foo', 0, \E_ERROR, __FILE__, __LINE__));
$this->assertThatTheExceptionWasOutput(ob_get_clean(), OutOfMemoryException::class, 'OutOfMemoryException', 'foo');
}
private function assertThatTheExceptionWasOutput($content, $expectedClass, $expectedTitle, $expectedMessage)
{
$this->assertStringContainsString(sprintf('<span class="exception_title"><abbr title="%s">%s</abbr></span>', $expectedClass, $expectedTitle), $content);
$this->assertStringContainsString(sprintf('<p class="break-long-words trace-message">%s</p>', $expectedMessage), $content);
}
}

View File

@@ -29,6 +29,10 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
// get class loaders wrapped by DebugClassLoader
if ($function[0] instanceof DebugClassLoader) {
$function = $function[0]->getClassLoader();
if (!\is_array($function)) {
continue;
}
}
if ($function[0] instanceof ComposerClassLoader) {
@@ -61,7 +65,7 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
}
$this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception);
$this->assertSame($translatedMessage, $exception->getMessage());
$this->assertMatchesRegularExpression($translatedMessage, $exception->getMessage());
$this->assertSame($error['type'], $exception->getSeverity());
$this->assertSame($error['file'], $exception->getFile());
$this->assertSame($error['line'], $exception->getLine());
@@ -71,10 +75,20 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
{
$autoloader = new ComposerClassLoader();
$autoloader->add('Symfony\Component\Debug\Exception\\', realpath(__DIR__.'/../../Exception'));
$autoloader->add('Symfony_Component_Debug_Tests_Fixtures', realpath(__DIR__.'/../../Tests/Fixtures'));
$debugClassLoader = new DebugClassLoader([$autoloader, 'loadClass']);
return [
[
[
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class "WhizBangFactory" not found',
],
"/^Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement\?$/",
],
[
[
'type' => 1,
@@ -82,7 +96,7 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
'file' => 'foo.php',
'message' => 'Class \'WhizBangFactory\' not found',
],
"Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement?",
"/^Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement\?$/",
],
[
[
@@ -91,7 +105,34 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\WhizBangFactory\' not found',
],
"Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
"/^Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\\\Bar\".\nDid you forget a \"use\" statement for another namespace\?$/",
],
[
[
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class "Foo\\Bar\\WhizBangFactory" not found',
],
"/^Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\\\Bar\".\nDid you forget a \"use\" statement for another namespace\?$/",
],
[
[
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Interface "Foo\\Bar\\WhizBangInterface" not found',
],
"/^Attempted to load interface \"WhizBangInterface\" from namespace \"Foo\\\\Bar\".\nDid you forget a \"use\" statement for another namespace\?$/",
],
[
[
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Trait "Foo\\Bar\\WhizBangTrait" not found',
],
"/^Attempted to load trait \"WhizBangTrait\" from namespace \"Foo\\\\Bar\".\nDid you forget a \"use\" statement for another namespace\?$/",
],
[
[
@@ -100,7 +141,8 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
'file' => 'foo.php',
'message' => 'Class \'UndefinedFunctionException\' not found',
],
"Attempted to load class \"UndefinedFunctionException\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
"/^Attempted to load class \"UndefinedFunctionException\" from the global namespace.\nDid you forget a \"use\" statement for .*\"Symfony\\\\Component\\\\Debug\\\\Exception\\\\UndefinedFunctionException\"\?$/",
[$debugClassLoader, 'loadClass'],
],
[
[
@@ -109,35 +151,7 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
'file' => 'foo.php',
'message' => 'Class \'PEARClass\' not found',
],
"Attempted to load class \"PEARClass\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony_Component_Debug_Tests_Fixtures_PEARClass\"?",
],
[
[
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
],
"Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
],
[
[
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
],
"Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
[$autoloader, 'loadClass'],
],
[
[
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
],
"Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
"/^Attempted to load class \"PEARClass\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony_Component_Debug_Tests_Fixtures_PEARClass\"\?$/",
[$debugClassLoader, 'loadClass'],
],
[
@@ -147,7 +161,37 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
],
"Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
"/^Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\\\\Bar\".\nDid you forget a \"use\" statement for .*\"Symfony\\\\Component\\\\Debug\\\\Exception\\\\UndefinedFunctionException\"\?$/",
[$debugClassLoader, 'loadClass'],
],
[
[
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
],
"/^Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\\\\Bar\".\nDid you forget a \"use\" statement for \"Symfony\\\\Component\\\\Debug\\\\Exception\\\\UndefinedFunctionException\"\?$/",
[$autoloader, 'loadClass'],
],
[
[
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
],
"/^Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\\\\Bar\".\nDid you forget a \"use\" statement for \"Symfony\\\\Component\\\\Debug\\\\Exception\\\\UndefinedFunctionException\"\?/",
[$debugClassLoader, 'loadClass'],
],
[
[
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
],
"/^Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\\\\Bar\".\nDid you forget a \"use\" statement for another namespace\?$/",
function ($className) { /* do nothing here */ },
],
];

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

@@ -1,24 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Debug\Tests;
use Symfony\Component\Debug\ExceptionHandler;
class MockExceptionHandler extends ExceptionHandler
{
public $e;
public function handle(\Exception $e)
{
$this->e = $e;
}
}