-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
250 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
185 changes: 185 additions & 0 deletions
185
src/StaticConstructorLoader/DebugStaticConstructorLoader.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Dbalabka\StaticConstructorLoader; | ||
|
||
use Composer\Autoload\ClassLoader; | ||
use Dbalabka\StaticConstructorLoader\Exception\StaticConstructorLoaderException; | ||
|
||
class DebugStaticConstructorLoader extends ClassLoader /* extending for an contract */ | ||
{ | ||
/** | ||
* @var ClassLoader | ||
*/ | ||
private $classLoader; | ||
|
||
public function __construct(ClassLoader $classLoader) | ||
{ | ||
$this->classLoader = $classLoader; | ||
|
||
// find Composer autoloader | ||
$loaders = spl_autoload_functions(); | ||
$otherLoaders = []; | ||
$composerLoader = null; | ||
foreach ($loaders as $loader) { | ||
if (is_array($loader)) { | ||
if ($loader[0] === $classLoader) { | ||
$composerLoader = $loader; | ||
break; | ||
} | ||
if ($loader[0] instanceof self) { | ||
throw new StaticConstructorLoaderException(sprintf('%s already registered', self::class)); | ||
} | ||
} | ||
$otherLoaders[] = $loader; | ||
} | ||
|
||
if (!$composerLoader) { | ||
throw new StaticConstructorLoaderException(sprintf('%s was not found in registered autoloaders', ClassLoader::class)); | ||
} | ||
|
||
// unregister Composer autoloader and all preceding autoloaders | ||
array_map('spl_autoload_unregister', array_merge($otherLoaders, [$composerLoader])); | ||
|
||
// restore the original queue order | ||
$loadersToRestore = array_merge([[$this, 'loadClass']], array_reverse($otherLoaders)); | ||
$flagTrue = array_fill(0, count($loadersToRestore), true); | ||
array_map('spl_autoload_register', $loadersToRestore, $flagTrue, $flagTrue); | ||
} | ||
|
||
public function loadClass($className): ?bool | ||
{ | ||
return $this->classLoader->loadClass($className); | ||
} | ||
|
||
public function getPrefixes() | ||
{ | ||
return $this->classLoader->getPrefixes(); | ||
} | ||
|
||
public function getPrefixesPsr4() | ||
{ | ||
return $this->classLoader->getPrefixesPsr4(); | ||
} | ||
|
||
public function getFallbackDirs() | ||
{ | ||
return $this->classLoader->getFallbackDirs(); | ||
} | ||
|
||
public function getFallbackDirsPsr4() | ||
{ | ||
return $this->classLoader->getFallbackDirsPsr4(); | ||
} | ||
|
||
public function getClassMap() | ||
{ | ||
return $this->classLoader->getClassMap(); | ||
} | ||
|
||
public function addClassMap(array $classMap) | ||
{ | ||
$this->classLoader->addClassMap($classMap); | ||
} | ||
|
||
public function add($prefix, $paths, $prepend = false) | ||
{ | ||
$this->classLoader->add($prefix, $paths, $prepend); | ||
} | ||
|
||
public function addPsr4($prefix, $paths, $prepend = false) | ||
{ | ||
$this->classLoader->addPsr4($prefix, $paths, $prepend); | ||
} | ||
|
||
public function set($prefix, $paths) | ||
{ | ||
$this->classLoader->set($prefix, $paths); | ||
} | ||
|
||
public function setPsr4($prefix, $paths) | ||
{ | ||
$this->classLoader->setPsr4($prefix, $paths); | ||
} | ||
|
||
public function setUseIncludePath($useIncludePath) | ||
{ | ||
$this->classLoader->setUseIncludePath($useIncludePath); | ||
} | ||
|
||
public function getUseIncludePath() | ||
{ | ||
return $this->classLoader->getUseIncludePath(); | ||
} | ||
|
||
public function setClassMapAuthoritative($classMapAuthoritative) | ||
{ | ||
$this->classLoader->setClassMapAuthoritative($classMapAuthoritative); | ||
} | ||
|
||
public function isClassMapAuthoritative() | ||
{ | ||
return $this->classLoader->isClassMapAuthoritative(); | ||
} | ||
|
||
public function setApcuPrefix($apcuPrefix) | ||
{ | ||
$this->classLoader->setApcuPrefix($apcuPrefix); | ||
} | ||
|
||
public function getApcuPrefix() | ||
{ | ||
return $this->classLoader->getApcuPrefix(); | ||
} | ||
|
||
public function register($prepend = false) | ||
{ | ||
$this->classLoader->register($prepend); | ||
} | ||
|
||
public function unregister() | ||
{ | ||
$this->classLoader->unregister(); | ||
} | ||
|
||
public function findFile($class) | ||
{ | ||
$path = $this->classLoader->findFile($class); | ||
|
||
if ( | ||
class_exists('Symfony\Component\ErrorHandler\DebugClassLoader', false) | ||
|| class_exists('Symfony\Component\Debug\DebugClassLoader', false) | ||
) { | ||
return $this->handleDebugClassLoader($class, $path); | ||
} | ||
|
||
return $path; | ||
} | ||
|
||
private function handleDebugClassLoader($class, $path) | ||
{ | ||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); | ||
$debugClassLoader = ($backtrace[2] ?? null); | ||
|
||
if ($path | ||
&& is_file($path) | ||
&& \in_array( | ||
$debugClassLoader['class'] ?? null, | ||
['Symfony\Component\Debug\DebugClassLoader', 'Symfony\Component\ErrorHandler\DebugClassLoader'], | ||
true | ||
) | ||
) { | ||
include $path; | ||
|
||
if ( | ||
$class !== StaticConstructorInterface::class | ||
&& is_a($class, StaticConstructorInterface::class, true) | ||
) { | ||
$class::__constructStatic(); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
return $path; | ||
} | ||
} |
57 changes: 57 additions & 0 deletions
57
tests/StaticConstructorLoader/DebugStaticConstructorLoaderTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Dbalabka\Enumeration\Tests\StaticConstructorLoader; | ||
|
||
use Dbalabka\StaticConstructorLoader\DebugStaticConstructorLoader; | ||
use Dbalabka\StaticConstructorLoader\StaticConstructorLoader; | ||
use Dbalabka\StaticConstructorLoader\Tests\Fixtures\ChildOfAbstractEnumeration; | ||
use PHPUnit\Framework\TestCase; | ||
use Symfony\Component\ErrorHandler\DebugClassLoader; | ||
|
||
class DebugStaticConstructorLoaderTest extends TestCase | ||
{ | ||
/** @var callable */ | ||
private $defaultLoader; | ||
|
||
protected function setUp() | ||
{ | ||
$this->defaultLoader = \spl_autoload_functions()[0]; | ||
} | ||
|
||
protected function tearDown() | ||
{ | ||
DebugClassLoader::disable(); | ||
} | ||
|
||
/** | ||
* @runInSeparateProcess | ||
*/ | ||
public function testClassLoadWithDefaultStaticConstrcutorLoader() | ||
{ | ||
new StaticConstructorLoader($this->defaultLoader[0]); | ||
$x = ChildOfAbstractEnumeration::$instance; | ||
$this->assertInstanceOf(ChildOfAbstractEnumeration::class, $x); | ||
} | ||
|
||
/** | ||
* @runInSeparateProcess | ||
*/ | ||
public function testClassLoadWithDefaultStaticConstrcutorLoaderAndSymfonyDebugLoader() | ||
{ | ||
new StaticConstructorLoader($this->defaultLoader[0]); | ||
(new DebugClassLoader($this->defaultLoader))::enable(); | ||
$x = ChildOfAbstractEnumeration::$instance; | ||
$this->assertNull($x); | ||
} | ||
|
||
/** | ||
* @runInSeparateProcess | ||
*/ | ||
public function testClassLoadWithDebugStaticConstrcutorLoaderAndSymfonyDebugLoader() | ||
{ | ||
new DebugStaticConstructorLoader($this->defaultLoader[0]); | ||
(new DebugClassLoader($this->defaultLoader))::enable(); | ||
$x = ChildOfAbstractEnumeration::$instance; | ||
$this->assertInstanceOf(ChildOfAbstractEnumeration::class, $x); | ||
} | ||
} |