ClassNotFoundErrorEnhancerTest.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\ErrorHandler\Tests\ErrorEnhancer;
  11. use Composer\Autoload\ClassLoader as ComposerClassLoader;
  12. use PHPUnit\Framework\TestCase;
  13. use Symfony\Component\ErrorHandler\DebugClassLoader;
  14. use Symfony\Component\ErrorHandler\Error\ClassNotFoundError;
  15. use Symfony\Component\ErrorHandler\Error\FatalError;
  16. use Symfony\Component\ErrorHandler\ErrorEnhancer\ClassNotFoundErrorEnhancer;
  17. class ClassNotFoundErrorEnhancerTest extends TestCase
  18. {
  19. public static function setUpBeforeClass(): void
  20. {
  21. foreach (spl_autoload_functions() as $function) {
  22. if (!\is_array($function)) {
  23. continue;
  24. }
  25. // get class loaders wrapped by DebugClassLoader
  26. if ($function[0] instanceof DebugClassLoader) {
  27. $function = $function[0]->getClassLoader();
  28. if (!\is_array($function)) {
  29. continue;
  30. }
  31. }
  32. if ($function[0] instanceof ComposerClassLoader) {
  33. $function[0]->add('Symfony_Component_ErrorHandler_Tests_Fixtures', \dirname(__DIR__, 5));
  34. break;
  35. }
  36. }
  37. }
  38. /**
  39. * @dataProvider provideClassNotFoundData
  40. */
  41. public function testEnhance(string $originalMessage, string $enhancedMessage, $autoloader = null)
  42. {
  43. try {
  44. if ($autoloader) {
  45. // Unregister all autoloaders to ensure the custom provided
  46. // autoloader is the only one to be used during the test run.
  47. $autoloaders = spl_autoload_functions();
  48. array_map('spl_autoload_unregister', $autoloaders);
  49. spl_autoload_register($autoloader);
  50. }
  51. $expectedLine = __LINE__ + 1;
  52. $error = (new ClassNotFoundErrorEnhancer())->enhance(new \Error($originalMessage));
  53. } finally {
  54. if ($autoloader) {
  55. spl_autoload_unregister($autoloader);
  56. array_map('spl_autoload_register', $autoloaders);
  57. }
  58. }
  59. $this->assertInstanceOf(ClassNotFoundError::class, $error);
  60. $this->assertSame($enhancedMessage, $error->getMessage());
  61. $this->assertSame(realpath(__FILE__), $error->getFile());
  62. $this->assertSame($expectedLine, $error->getLine());
  63. }
  64. public static function provideClassNotFoundData()
  65. {
  66. $autoloader = new ComposerClassLoader();
  67. $autoloader->add('Symfony\Component\ErrorHandler\Error\\', realpath(__DIR__.'/../../Error'));
  68. $autoloader->add('Symfony_Component_ErrorHandler_Tests_Fixtures', realpath(__DIR__.'/../../Tests/Fixtures'));
  69. $debugClassLoader = new DebugClassLoader([$autoloader, 'loadClass']);
  70. return [
  71. [
  72. 'Class "WhizBangFactory" not found',
  73. "Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement?",
  74. ],
  75. [
  76. 'Class \'WhizBangFactory\' not found',
  77. "Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement?",
  78. ],
  79. [
  80. 'Class "Foo\\Bar\\WhizBangFactory" not found',
  81. "Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
  82. ],
  83. [
  84. 'Class \'Foo\\Bar\\WhizBangFactory\' not found',
  85. "Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
  86. ],
  87. [
  88. 'Interface "Foo\\Bar\\WhizBangInterface" not found',
  89. "Attempted to load interface \"WhizBangInterface\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
  90. ],
  91. [
  92. 'Trait "Foo\\Bar\\WhizBangTrait" not found',
  93. "Attempted to load trait \"WhizBangTrait\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
  94. ],
  95. [
  96. 'Class \'UndefinedFunctionError\' not found',
  97. "Attempted to load class \"UndefinedFunctionError\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony\Component\ErrorHandler\Error\UndefinedFunctionError\"?",
  98. [$debugClassLoader, 'loadClass'],
  99. ],
  100. [
  101. 'Class \'PEARClass\' not found',
  102. "Attempted to load class \"PEARClass\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony_Component_ErrorHandler_Tests_Fixtures_PEARClass\"?",
  103. [$debugClassLoader, 'loadClass'],
  104. ],
  105. [
  106. 'Class \'Foo\\Bar\\UndefinedFunctionError\' not found',
  107. "Attempted to load class \"UndefinedFunctionError\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\ErrorHandler\Error\UndefinedFunctionError\"?",
  108. [$debugClassLoader, 'loadClass'],
  109. ],
  110. [
  111. 'Class \'Foo\\Bar\\UndefinedFunctionError\' not found',
  112. "Attempted to load class \"UndefinedFunctionError\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\ErrorHandler\Error\UndefinedFunctionError\"?",
  113. [$autoloader, 'loadClass'],
  114. ],
  115. [
  116. 'Class \'Foo\\Bar\\UndefinedFunctionError\' not found',
  117. "Attempted to load class \"UndefinedFunctionError\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\ErrorHandler\Error\UndefinedFunctionError\"?",
  118. [$debugClassLoader, 'loadClass'],
  119. ],
  120. [
  121. 'Class \'Foo\\Bar\\UndefinedFunctionError\' not found',
  122. "Attempted to load class \"UndefinedFunctionError\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
  123. function ($className) { /* do nothing here */ },
  124. ],
  125. ];
  126. }
  127. public function testEnhanceWithFatalError()
  128. {
  129. $error = (new ClassNotFoundErrorEnhancer())->enhance(new FatalError('foo', 0, [
  130. 'type' => \E_ERROR,
  131. 'message' => "Class 'FooBarCcc' not found",
  132. 'file' => $expectedFile = realpath(__FILE__),
  133. 'line' => $expectedLine = __LINE__,
  134. ]));
  135. $this->assertInstanceOf(ClassNotFoundError::class, $error);
  136. $this->assertSame("Attempted to load class \"FooBarCcc\" from the global namespace.\nDid you forget a \"use\" statement?", $error->getMessage());
  137. $this->assertSame($expectedFile, $error->getFile());
  138. $this->assertSame($expectedLine, $error->getLine());
  139. }
  140. public function testCannotRedeclareClass()
  141. {
  142. if (!file_exists(__DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP')) {
  143. $this->markTestSkipped('Can only be run on case insensitive filesystems');
  144. }
  145. require_once __DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP';
  146. $enhancer = new ClassNotFoundErrorEnhancer();
  147. $error = $enhancer->enhance(new \Error("Class 'Foo\\Bar\\RequiredTwice' not found"));
  148. $this->assertInstanceOf(ClassNotFoundError::class, $error);
  149. }
  150. }