‪TYPO3CMS  ‪main
ErrorHandlerTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use PHPUnit\Framework\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
22 use PHPUnit\Framework\Attributes\WithoutErrorHandler;
23 use Psr\Log\LoggerInterface;
24 use Psr\Log\LoggerTrait;
25 use Psr\Log\LogLevel;
31 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
32 
36 final class ‪ErrorHandlerTest extends UnitTestCase
37 {
39 
40  protected LoggerInterface ‪$unusedLogger;
41  protected LoggerInterface ‪$trackingLogger;
42 
43  // These are borrowed from DefaultConfiguration.php.
44  protected const ‪DEFAULT_ERROR_HANDLER_LEVELS = E_ALL & ~(E_STRICT | E_NOTICE | E_COMPILE_WARNING | E_COMPILE_ERROR | E_CORE_WARNING | E_CORE_ERROR | E_PARSE | E_ERROR);
45  protected const ‪DEFAULT_EXCEPTIONAL_ERROR_LEVELS = E_ALL & ~(E_STRICT | E_NOTICE | E_COMPILE_WARNING | E_COMPILE_ERROR | E_CORE_WARNING | E_CORE_ERROR | E_PARSE | E_ERROR | E_DEPRECATED | E_USER_DEPRECATED | E_WARNING | E_USER_ERROR | E_USER_NOTICE | E_USER_WARNING);
46 
47  protected bool ‪$resetSingletonInstances = true;
48 
52  protected function ‪setUp(): void
53  {
54  parent::setUp();
55 
56  $this->trackingLogger = new class () implements LoggerInterface {
57  use LoggerTrait;
58  public array $records = [];
59  public function log($level, string|\Stringable $message, array $context = []): void
60  {
61  $this->records[] = [
62  'level' => $level,
63  'message' => $message,
64  'context' => $context,
65  ];
66  }
67  };
68  }
69 
70  #[WithoutErrorHandler]
71  #[Test]
72  #[DataProvider('errorTests')]
74  int $levelsToHandle,
75  int $levelsToThrow,
76  int $levelToTrigger,
77  string $message,
78  string $file,
79  int $line,
80  ?bool $expectedReturn,
81  ?string $deprecationsLogMessage,
82  ?string $deprecationsLogLevel,
83  ?string $errorsLogMessage,
84  ?string $errorsLogLevel,
85  ?string $exceptionMessage
86  ): void {
87  $logManager = new class () extends ‪LogManager implements ‪LogManagerInterface {
88  protected array $loggers = [];
89  public function getLogger(string $name = ''): LoggerInterface
90  {
91  return $this->loggers[$name];
92  }
93  public function setLogger(string $name, LoggerInterface $logger): self
94  {
95  $this->loggers[$name] = $logger;
96  return $this;
97  }
98  };
99 
100  ‪$subject = new ‪ErrorHandler($levelsToHandle);
101  ‪$subject->‪setExceptionalErrors($levelsToThrow);
102  $logManager->setLogger('TYPO3.CMS.deprecations', clone $this->trackingLogger);
103  // disabled until the new logger is in place
104  // $logManager->setLogger('TYPO3.CMS.php_errors', clone $this->trackingLogger);
105  GeneralUtility::setSingletonInstance(LogManager::class, $logManager);
106 
107  try {
108  $return = ‪$subject->‪handleError($levelToTrigger, $message, $file, $line);
109  } catch (\‪Exception $e) {
110  if ($exceptionMessage) {
111  self::assertEquals($exceptionMessage, $e->getMessage());
112  return;
113  }
114  // An exception happened when it shouldn't; let PHPUnit deal with it.
115  throw $e;
116  }
117  self::assertEquals($expectedReturn, $return);
118  if ($deprecationsLogMessage) {
119  self::assertEquals($deprecationsLogMessage, $logManager->getLogger('TYPO3.CMS.deprecations')->records[0]['message']);
120  self::assertEquals($deprecationsLogLevel, $logManager->getLogger('TYPO3.CMS.deprecations')->records[0]['level']);
121  }
129  }
130 
131  public static function ‪errorTests(): iterable
132  {
133  // @todo Clean up the code base so the defaults can change to report notices.
134  yield 'defaults ignore a notice' => [
135  'levelsToHandle' => ‪self::DEFAULT_ERROR_HANDLER_LEVELS,
137  'levelToTrigger' => E_NOTICE,
138  'message' => 'A slightly bad thing happened',
139  'file' => 'foo.php',
140  'line' => 42,
141  'expectedReturn' => ‪ErrorHandlerInterface::ERROR_HANDLED,
142  'deprecationsLogMessage' => null,
143  'deprecationsLogLevel' => null,
144  'errorsLogMessage' => null,
145  'errorsLogLevel' => null,
146  'exceptionMessage' => null,
147  ];
148  yield 'defaults log a warning' => [
149  'levelsToHandle' => ‪self::DEFAULT_ERROR_HANDLER_LEVELS,
151  'levelToTrigger' => E_WARNING,
152  'message' => 'A bad thing happened',
153  'file' => 'foo.php',
154  'line' => 42,
155  'expectedReturn' => ‪ErrorHandlerInterface::ERROR_HANDLED,
156  'deprecationsLogMessage' => null,
157  'deprecationsLogLevel' => LogLevel::NOTICE,
158  'errorsLogMessage' => 'Core: Error handler (BE): PHP Warning: A bad thing happened in foo.php line 42',
159  'errorsLogLevel' => LogLevel::WARNING,
160  'exceptionMessage' => null,
161  ];
162  // @todo Currently Errors are supressed by default. This seems unwise, but changing it is a separate task.
163  /*
164  yield 'defaults log an error' => [
165  'levelsToHandle' => self::DEFAULT_ERROR_HANDLER_LEVELS,
166  'levelsToThrow' => self::DEFAULT_EXCEPTIONAL_ERROR_LEVELS,
167  'levelToTrigger' => E_ERROR,
168  'message' => 'A very bad thing happened',
169  'file' => 'foo.php',
170  'line' => 42,
171  'expectedReturn' => ErrorHandlerInterface::ERROR_HANDLED,
172  'deprecationsLogMessage' => null,
173  'deprecationsLogLevel' => null,
174  'errorsLogMessage' => 'Core: Error handler (BE): PHP Error: A very bad thing happened in foo.php line 42',
175  'errorsLogLevel' => LogLevel::ERROR,
176  'exceptionMessage' => null,
177  ];
178  */
179  yield 'user deprecations are logged' => [
180  'levelsToHandle' => ‪self::DEFAULT_ERROR_HANDLER_LEVELS,
182  'levelToTrigger' => E_USER_DEPRECATED,
183  'message' => 'Stop doing that',
184  'file' => 'foo.php',
185  'line' => 42,
186  'expectedReturn' => ‪ErrorHandlerInterface::ERROR_HANDLED,
187  'deprecationsLogMessage' => 'Core: Error handler (BE): TYPO3 Deprecation Notice: Stop doing that in foo.php line 42',
188  'deprecationsLogLevel' => LogLevel::NOTICE,
189  'errorsLogMessage' => null,
190  'errorsLogLevel' => null,
191  'exceptionMessage' => null,
192  ];
193  // @todo These ought to get logged to the deprecations channel.
194  yield 'system deprecations are logged' => [
195  'levelsToHandle' => ‪self::DEFAULT_ERROR_HANDLER_LEVELS,
197  'levelToTrigger' => E_DEPRECATED,
198  'message' => 'Stop doing that',
199  'file' => 'foo.php',
200  'line' => 42,
201  'expectedReturn' => ‪ErrorHandlerInterface::ERROR_HANDLED,
202  'deprecationsLogMessage' => 'Core: Error handler (BE): PHP Runtime Deprecation Notice: Stop doing that in foo.php line 42',
203  'deprecationsLogLevel' => LogLevel::NOTICE,
204  'errorsLogMessage' => '',
205  'errorsLogLevel' => null,
206  'exceptionMessage' => null,
207  ];
208  yield 'user errors are logged but continue to PHP' => [
209  'levelsToHandle' => ‪self::DEFAULT_ERROR_HANDLER_LEVELS,
211  'levelToTrigger' => E_USER_ERROR,
212  'message' => 'A horrible thing happened',
213  'file' => 'foo.php',
214  'line' => 42,
215  'expectedReturn' => ‪ErrorHandlerInterface::PROPAGATE_ERROR,
216  'deprecationsLogMessage' => null,
217  'deprecationsLogLevel' => null,
218  'errorsLogMessage' => 'Core: Error handler (BE): PHP User Error: A horrible thing happened in foo.php line 42',
219  'errorsLogLevel' => LogLevel::ERROR,
220  'exceptionMessage' => null,
221  ];
222  yield 'can force errors to exceptions' => [
223  'levelsToHandle' => self::DEFAULT_ERROR_HANDLER_LEVELS | E_WARNING,
224  'levelsToThrow' => self::DEFAULT_EXCEPTIONAL_ERROR_LEVELS | E_WARNING,
225  'levelToTrigger' => E_WARNING,
226  'message' => 'A throwable thing happened',
227  'file' => 'foo.php',
228  'line' => 42,
229  'expectedReturn' => null,
230  'deprecationsLogMessage' => null,
231  'deprecationsLogLevel' => null,
232  'errorsLogMessage' => null,
233  'errorsLogLevel' => LogLevel::ERROR,
234  'exceptionMessage' => 'PHP Warning: A throwable thing happened in foo.php line 42',
235  ];
236  }
237 }
‪TYPO3\CMS\Core\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\Log\LogManagerInterface
Definition: LogManagerInterface.php:24
‪TYPO3\CMS\Core\Error\ErrorHandlerInterface\handleError
‪bool handleError($errorLevel, $errorMessage, $errorFile, $errorLine)
‪TYPO3\CMS\Core\Error\ErrorHandlerInterface\setExceptionalErrors
‪setExceptionalErrors($exceptionalErrors)
‪TYPO3\CMS\Core\Tests\Unit\Error\ErrorHandlerTest\DEFAULT_ERROR_HANDLER_LEVELS
‪const DEFAULT_ERROR_HANDLER_LEVELS
Definition: ErrorHandlerTest.php:44
‪TYPO3\CMS\Core\Tests\Unit\Error\ErrorHandlerTest\$trackingLogger
‪LoggerInterface $trackingLogger
Definition: ErrorHandlerTest.php:41
‪TYPO3\CMS\Core\Tests\Unit\Error\ErrorHandlerTest
Definition: ErrorHandlerTest.php:37
‪TYPO3\CMS\Core\Tests\Unit\Error\ErrorHandlerTest\setUp
‪setUp()
Definition: ErrorHandlerTest.php:52
‪TYPO3\CMS\Core\Error\ErrorHandlerInterface\PROPAGATE_ERROR
‪const PROPAGATE_ERROR
Definition: ErrorHandlerInterface.php:27
‪TYPO3\CMS\Core\Tests\Unit\Error\ErrorHandlerTest\errorTests
‪static errorTests()
Definition: ErrorHandlerTest.php:131
‪TYPO3\CMS\Core\Tests\Unit\Error
Definition: DebugExceptionHandlerTest.php:18
‪TYPO3\CMS\Core\Tests\Unit\Error\ErrorHandlerTest\errorHandlerLogsCorrectly
‪errorHandlerLogsCorrectly(int $levelsToHandle, int $levelsToThrow, int $levelToTrigger, string $message, string $file, int $line, ?bool $expectedReturn, ?string $deprecationsLogMessage, ?string $deprecationsLogLevel, ?string $errorsLogMessage, ?string $errorsLogLevel, ?string $exceptionMessage)
Definition: ErrorHandlerTest.php:73
‪TYPO3\CMS\Core\Error\ErrorHandlerInterface
Definition: ErrorHandlerInterface.php:24
‪TYPO3\CMS\Core\Tests\Unit\Error\ErrorHandlerTest\DEFAULT_EXCEPTIONAL_ERROR_LEVELS
‪const DEFAULT_EXCEPTIONAL_ERROR_LEVELS
Definition: ErrorHandlerTest.php:45
‪TYPO3\CMS\Core\Tests\Unit\Error\ErrorHandlerTest\$unusedLogger
‪LoggerInterface $unusedLogger
Definition: ErrorHandlerTest.php:40
‪TYPO3\CMS\Core\Error\ErrorHandlerInterface\ERROR_HANDLED
‪const ERROR_HANDLED
Definition: ErrorHandlerInterface.php:26
‪TYPO3\CMS\Core\Tests\Unit\Error\ErrorHandlerTest\$resetSingletonInstances
‪bool $resetSingletonInstances
Definition: ErrorHandlerTest.php:47
‪TYPO3\CMS\Core\Log\LogManager
Definition: LogManager.php:33
‪TYPO3\CMS\Core\Tests\Unit\Error\ErrorHandlerTest\$subject
‪ErrorHandlerInterface $subject
Definition: ErrorHandlerTest.php:38
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Error\ErrorHandler
Definition: ErrorHandler.php:41