Skip to content

Commit

Permalink
Merge 94513e5 into c53daca
Browse files Browse the repository at this point in the history
  • Loading branch information
oleg-andreyev committed Jun 10, 2020
2 parents c53daca + 94513e5 commit ae5ca8c
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 2 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,14 @@ provided in this library:
```php
<?php
use Dbalabka\StaticConstructorLoader\StaticConstructorLoader;
use Dbalabka\StaticConstructorLoader\DebugStaticConstructorLoader;

$composer = require_once(__DIR__ . '/vendor/autoload.php');
$loader = new StaticConstructorLoader($composer);
if ($_ENV['APP_ENV'] === 'dev') { // if running Symfony with dev-mode
$loader = new DebugStaticConstructorLoader(new StaticConstructorLoader($composer));
} else {
$loader = new StaticConstructorLoader($composer);
}
```
Also, it would be very helpful to have expression based properties initialization:
```php
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"myclabs/php-enum": "^1.0",
"phpunit/phpunit": "^7.5",
"phpbench/phpbench": "^0.16.9",
"vimeo/psalm": "^3.5"
"vimeo/psalm": "^3.5",
"symfony/error-handler": "^5.1"
},
"autoload": {
"psr-4": {
Expand Down
185 changes: 185 additions & 0 deletions src/StaticConstructorLoader/DebugStaticConstructorLoader.php
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 tests/StaticConstructorLoader/DebugStaticConstructorLoaderTest.php
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);
}
}

0 comments on commit ae5ca8c

Please sign in to comment.