Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / vendor / symfony / debug / Tests / DebugClassLoaderTest.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\Debug\Tests;
13
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\Debug\DebugClassLoader;
16 use Symfony\Component\Debug\ErrorHandler;
17
18 class DebugClassLoaderTest extends TestCase
19 {
20     /**
21      * @var int Error reporting level before running tests
22      */
23     private $errorReporting;
24
25     private $loader;
26
27     protected function setUp()
28     {
29         $this->errorReporting = error_reporting(E_ALL);
30         $this->loader = new ClassLoader();
31         spl_autoload_register(array($this->loader, 'loadClass'), true, true);
32         DebugClassLoader::enable();
33     }
34
35     protected function tearDown()
36     {
37         DebugClassLoader::disable();
38         spl_autoload_unregister(array($this->loader, 'loadClass'));
39         error_reporting($this->errorReporting);
40     }
41
42     public function testIdempotence()
43     {
44         DebugClassLoader::enable();
45
46         $functions = spl_autoload_functions();
47         foreach ($functions as $function) {
48             if (is_array($function) && $function[0] instanceof DebugClassLoader) {
49                 $reflClass = new \ReflectionClass($function[0]);
50                 $reflProp = $reflClass->getProperty('classLoader');
51                 $reflProp->setAccessible(true);
52
53                 $this->assertNotInstanceOf('Symfony\Component\Debug\DebugClassLoader', $reflProp->getValue($function[0]));
54
55                 return;
56             }
57         }
58
59         $this->fail('DebugClassLoader did not register');
60     }
61
62     /**
63      * @expectedException \Exception
64      * @expectedExceptionMessage boo
65      */
66     public function testThrowingClass()
67     {
68         try {
69             class_exists(__NAMESPACE__.'\Fixtures\Throwing');
70             $this->fail('Exception expected');
71         } catch (\Exception $e) {
72             $this->assertSame('boo', $e->getMessage());
73         }
74
75         // the second call also should throw
76         class_exists(__NAMESPACE__.'\Fixtures\Throwing');
77     }
78
79     public function testUnsilencing()
80     {
81         if (\PHP_VERSION_ID >= 70000) {
82             $this->markTestSkipped('PHP7 throws exceptions, unsilencing is not required anymore.');
83         }
84         if (defined('HHVM_VERSION')) {
85             $this->markTestSkipped('HHVM is not handled in this test case.');
86         }
87
88         ob_start();
89
90         $this->iniSet('log_errors', 0);
91         $this->iniSet('display_errors', 1);
92
93         // See below: this will fail with parse error
94         // but this should not be @-silenced.
95         @class_exists(__NAMESPACE__.'\TestingUnsilencing', true);
96
97         $output = ob_get_clean();
98
99         $this->assertStringMatchesFormat('%aParse error%a', $output);
100     }
101
102     public function testStacking()
103     {
104         // the ContextErrorException must not be loaded to test the workaround
105         // for https://bugs.php.net/65322.
106         if (class_exists('Symfony\Component\Debug\Exception\ContextErrorException', false)) {
107             $this->markTestSkipped('The ContextErrorException class is already loaded.');
108         }
109         if (defined('HHVM_VERSION')) {
110             $this->markTestSkipped('HHVM is not handled in this test case.');
111         }
112
113         ErrorHandler::register();
114
115         try {
116             // Trigger autoloading + E_STRICT at compile time
117             // which in turn triggers $errorHandler->handle()
118             // that again triggers autoloading for ContextErrorException.
119             // Error stacking works around the bug above and everything is fine.
120
121             eval('
122                 namespace '.__NAMESPACE__.';
123                 class ChildTestingStacking extends TestingStacking { function foo($bar) {} }
124             ');
125             $this->fail('ContextErrorException expected');
126         } catch (\ErrorException $exception) {
127             // if an exception is thrown, the test passed
128             $this->assertStringStartsWith(__FILE__, $exception->getFile());
129             if (\PHP_VERSION_ID < 70000) {
130                 $this->assertRegExp('/^Runtime Notice: Declaration/', $exception->getMessage());
131                 $this->assertEquals(E_STRICT, $exception->getSeverity());
132             } else {
133                 $this->assertRegExp('/^Warning: Declaration/', $exception->getMessage());
134                 $this->assertEquals(E_WARNING, $exception->getSeverity());
135             }
136         } finally {
137             restore_error_handler();
138             restore_exception_handler();
139         }
140     }
141
142     /**
143      * @expectedException \RuntimeException
144      * @expectedExceptionMessage Case mismatch between loaded and declared class names
145      */
146     public function testNameCaseMismatch()
147     {
148         class_exists(__NAMESPACE__.'\TestingCaseMismatch', true);
149     }
150
151     /**
152      * @expectedException \RuntimeException
153      * @expectedExceptionMessage Case mismatch between class and real file names
154      */
155     public function testFileCaseMismatch()
156     {
157         if (!file_exists(__DIR__.'/Fixtures/CaseMismatch.php')) {
158             $this->markTestSkipped('Can only be run on case insensitive filesystems');
159         }
160
161         class_exists(__NAMESPACE__.'\Fixtures\CaseMismatch', true);
162     }
163
164     /**
165      * @expectedException \RuntimeException
166      * @expectedExceptionMessage Case mismatch between loaded and declared class names
167      */
168     public function testPsr4CaseMismatch()
169     {
170         class_exists(__NAMESPACE__.'\Fixtures\Psr4CaseMismatch', true);
171     }
172
173     public function testNotPsr0()
174     {
175         $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0', true));
176     }
177
178     public function testNotPsr0Bis()
179     {
180         $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0bis', true));
181     }
182
183     public function testClassAlias()
184     {
185         $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\ClassAlias', true));
186     }
187
188     /**
189      * @dataProvider provideDeprecatedSuper
190      */
191     public function testDeprecatedSuper($class, $super, $type)
192     {
193         set_error_handler(function () { return false; });
194         $e = error_reporting(0);
195         trigger_error('', E_USER_DEPRECATED);
196
197         class_exists('Test\\'.__NAMESPACE__.'\\'.$class, true);
198
199         error_reporting($e);
200         restore_error_handler();
201
202         $lastError = error_get_last();
203         unset($lastError['file'], $lastError['line']);
204
205         $xError = array(
206             'type' => E_USER_DEPRECATED,
207             'message' => 'The "Test\Symfony\Component\Debug\Tests\\'.$class.'" class '.$type.' "Symfony\Component\Debug\Tests\Fixtures\\'.$super.'" that is deprecated but this is a test deprecation notice.',
208         );
209
210         $this->assertSame($xError, $lastError);
211     }
212
213     public function provideDeprecatedSuper()
214     {
215         return array(
216             array('DeprecatedInterfaceClass', 'DeprecatedInterface', 'implements'),
217             array('DeprecatedParentClass', 'DeprecatedClass', 'extends'),
218         );
219     }
220
221     public function testInterfaceExtendsDeprecatedInterface()
222     {
223         set_error_handler(function () { return false; });
224         $e = error_reporting(0);
225         trigger_error('', E_USER_NOTICE);
226
227         class_exists('Test\\'.__NAMESPACE__.'\\NonDeprecatedInterfaceClass', true);
228
229         error_reporting($e);
230         restore_error_handler();
231
232         $lastError = error_get_last();
233         unset($lastError['file'], $lastError['line']);
234
235         $xError = array(
236             'type' => E_USER_NOTICE,
237             'message' => '',
238         );
239
240         $this->assertSame($xError, $lastError);
241     }
242
243     public function testDeprecatedSuperInSameNamespace()
244     {
245         set_error_handler(function () { return false; });
246         $e = error_reporting(0);
247         trigger_error('', E_USER_NOTICE);
248
249         class_exists('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent', true);
250
251         error_reporting($e);
252         restore_error_handler();
253
254         $lastError = error_get_last();
255         unset($lastError['file'], $lastError['line']);
256
257         $xError = array(
258             'type' => E_USER_NOTICE,
259             'message' => '',
260         );
261
262         $this->assertSame($xError, $lastError);
263     }
264
265     public function testReservedForPhp7()
266     {
267         if (\PHP_VERSION_ID >= 70000) {
268             $this->markTestSkipped('PHP7 already prevents using reserved names.');
269         }
270
271         set_error_handler(function () { return false; });
272         $e = error_reporting(0);
273         trigger_error('', E_USER_NOTICE);
274
275         class_exists('Test\\'.__NAMESPACE__.'\\Float', true);
276
277         error_reporting($e);
278         restore_error_handler();
279
280         $lastError = error_get_last();
281         unset($lastError['file'], $lastError['line']);
282
283         $xError = array(
284             'type' => E_USER_DEPRECATED,
285             'message' => 'The "Test\Symfony\Component\Debug\Tests\Float" class uses the reserved name "Float", it will break on PHP 7 and higher',
286         );
287
288         $this->assertSame($xError, $lastError);
289     }
290
291     public function testExtendedFinalClass()
292     {
293         set_error_handler(function () { return false; });
294         $e = error_reporting(0);
295         trigger_error('', E_USER_NOTICE);
296
297         class_exists('Test\\'.__NAMESPACE__.'\\ExtendsFinalClass', true);
298
299         error_reporting($e);
300         restore_error_handler();
301
302         $lastError = error_get_last();
303         unset($lastError['file'], $lastError['line']);
304
305         $xError = array(
306             'type' => E_USER_DEPRECATED,
307             'message' => 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass" class is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass".',
308         );
309
310         $this->assertSame($xError, $lastError);
311     }
312
313     public function testExtendedFinalMethod()
314     {
315         $deprecations = array();
316         set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
317         $e = error_reporting(E_USER_DEPRECATED);
318
319         class_exists(__NAMESPACE__.'\\Fixtures\\ExtendedFinalMethod', true);
320
321         error_reporting($e);
322         restore_error_handler();
323
324         $xError = array(
325             'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".',
326             'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod2()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".',
327         );
328
329         $this->assertSame($xError, $deprecations);
330     }
331
332     public function testExtendedDeprecatedMethodDoesntTriggerAnyNotice()
333     {
334         set_error_handler(function () { return false; });
335         $e = error_reporting(0);
336         trigger_error('', E_USER_NOTICE);
337
338         class_exists('Test\\'.__NAMESPACE__.'\\ExtendsAnnotatedClass', true);
339
340         error_reporting($e);
341         restore_error_handler();
342
343         $lastError = error_get_last();
344         unset($lastError['file'], $lastError['line']);
345
346         $this->assertSame(array('type' => E_USER_NOTICE, 'message' => ''), $lastError);
347     }
348
349     public function testInternalsUse()
350     {
351         $deprecations = array();
352         set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
353         $e = error_reporting(E_USER_DEPRECATED);
354
355         class_exists('Test\\'.__NAMESPACE__.'\\ExtendsInternals', true);
356
357         error_reporting($e);
358         restore_error_handler();
359
360         $this->assertSame($deprecations, array(
361             'The "Symfony\Component\Debug\Tests\Fixtures\InternalInterface" interface is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternalsParent".',
362             'The "Symfony\Component\Debug\Tests\Fixtures\InternalClass" class is considered internal since version 3.4. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternalsParent".',
363             'The "Symfony\Component\Debug\Tests\Fixtures\InternalTrait" trait is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".',
364             'The "Symfony\Component\Debug\Tests\Fixtures\InternalClass::internalMethod()" method is considered internal since version 3.4. It may change without further notice. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".',
365         ));
366     }
367
368     public function testUseTraitWithInternalMethod()
369     {
370         $deprecations = array();
371         set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
372         $e = error_reporting(E_USER_DEPRECATED);
373
374         class_exists('Test\\'.__NAMESPACE__.'\\UseTraitWithInternalMethod', true);
375
376         error_reporting($e);
377         restore_error_handler();
378
379         $this->assertSame(array(), $deprecations);
380     }
381 }
382
383 class ClassLoader
384 {
385     public function loadClass($class)
386     {
387     }
388
389     public function getClassMap()
390     {
391         return array(__NAMESPACE__.'\Fixtures\NotPSR0bis' => __DIR__.'/Fixtures/notPsr0Bis.php');
392     }
393
394     public function findFile($class)
395     {
396         $fixtureDir = __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR;
397
398         if (__NAMESPACE__.'\TestingUnsilencing' === $class) {
399             eval('-- parse error --');
400         } elseif (__NAMESPACE__.'\TestingStacking' === $class) {
401             eval('namespace '.__NAMESPACE__.'; class TestingStacking { function foo() {} }');
402         } elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) {
403             eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}');
404         } elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) {
405             return $fixtureDir.'psr4'.DIRECTORY_SEPARATOR.'Psr4CaseMismatch.php';
406         } elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) {
407             return $fixtureDir.'reallyNotPsr0.php';
408         } elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) {
409             return $fixtureDir.'notPsr0Bis.php';
410         } elseif ('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent' === $class) {
411             eval('namespace Symfony\Bridge\Debug\Tests\Fixtures; class ExtendsDeprecatedParent extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}');
412         } elseif ('Test\\'.__NAMESPACE__.'\DeprecatedParentClass' === $class) {
413             eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedParentClass extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}');
414         } elseif ('Test\\'.__NAMESPACE__.'\DeprecatedInterfaceClass' === $class) {
415             eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\DeprecatedInterface {}');
416         } elseif ('Test\\'.__NAMESPACE__.'\NonDeprecatedInterfaceClass' === $class) {
417             eval('namespace Test\\'.__NAMESPACE__.'; class NonDeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\NonDeprecatedInterface {}');
418         } elseif ('Test\\'.__NAMESPACE__.'\Float' === $class) {
419             eval('namespace Test\\'.__NAMESPACE__.'; class Float {}');
420         } elseif ('Test\\'.__NAMESPACE__.'\ExtendsFinalClass' === $class) {
421             eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsFinalClass extends \\'.__NAMESPACE__.'\Fixtures\FinalClass {}');
422         } elseif ('Test\\'.__NAMESPACE__.'\ExtendsAnnotatedClass' === $class) {
423             eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsAnnotatedClass extends \\'.__NAMESPACE__.'\Fixtures\AnnotatedClass {
424                 public function deprecatedMethod() { }
425             }');
426         } elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternals' === $class) {
427             eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternals extends ExtendsInternalsParent {
428                 use \\'.__NAMESPACE__.'\Fixtures\InternalTrait;
429
430                 public function internalMethod() { }
431             }');
432         } elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternalsParent' === $class) {
433             eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternalsParent extends \\'.__NAMESPACE__.'\Fixtures\InternalClass implements \\'.__NAMESPACE__.'\Fixtures\InternalInterface { }');
434         } elseif ('Test\\'.__NAMESPACE__.'\UseTraitWithInternalMethod' === $class) {
435             eval('namespace Test\\'.__NAMESPACE__.'; class UseTraitWithInternalMethod { use \\'.__NAMESPACE__.'\Fixtures\TraitWithInternalMethod; }');
436         }
437     }
438 }