Skip to content

Commit a3022c1

Browse files
committed
fixes#22 added DebugStaticConstructorLoader which adds workaround for DebugClassLoader hijacking
1 parent 8c219bd commit a3022c1

File tree

2 files changed

+194
-1
lines changed

2 files changed

+194
-1
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,14 @@ provided in this library:
187187
```php
188188
<?php
189189
use Dbalabka\StaticConstructorLoader\StaticConstructorLoader;
190+
use Dbalabka\StaticConstructorLoader\DebugStaticConstructorLoader;
190191

191192
$composer = require_once(__DIR__ . '/vendor/autoload.php');
192-
$loader = new StaticConstructorLoader($composer);
193+
if ($_ENV['APP_ENV'] === 'dev') { // if running Symfony with dev-mode
194+
$loader = new DebugStaticConstructorLoader(new StaticConstructorLoader($composer));
195+
} else {
196+
$loader = new StaticConstructorLoader($composer);
197+
}
193198
```
194199
Also, it would be very helpful to have expression based properties initialization:
195200
```php
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Dbalabka\StaticConstructorLoader;
4+
5+
use Composer\Autoload\ClassLoader;
6+
use Dbalabka\StaticConstructorLoader\Exception\StaticConstructorLoaderException;
7+
8+
class DebugStaticConstructorLoader extends ClassLoader /* extending for an contract */
9+
{
10+
/**
11+
* @var ClassLoader
12+
*/
13+
private $classLoader;
14+
15+
public function __construct(ClassLoader $classLoader)
16+
{
17+
$this->classLoader = $classLoader;
18+
19+
// find Composer autoloader
20+
$loaders = spl_autoload_functions();
21+
$otherLoaders = [];
22+
$composerLoader = null;
23+
foreach ($loaders as $loader) {
24+
if (is_array($loader)) {
25+
if ($loader[0] === $classLoader) {
26+
$composerLoader = $loader;
27+
break;
28+
}
29+
if ($loader[0] instanceof self) {
30+
throw new StaticConstructorLoaderException(sprintf('%s already registered', self::class));
31+
}
32+
}
33+
$otherLoaders[] = $loader;
34+
}
35+
36+
if (!$composerLoader) {
37+
throw new StaticConstructorLoaderException(sprintf('%s was not found in registered autoloaders', ClassLoader::class));
38+
}
39+
40+
// unregister Composer autoloader and all preceding autoloaders
41+
array_map('spl_autoload_unregister', array_merge($otherLoaders, [$composerLoader]));
42+
43+
// restore the original queue order
44+
$loadersToRestore = array_merge([[$this, 'loadClass']], array_reverse($otherLoaders));
45+
$flagTrue = array_fill(0, count($loadersToRestore), true);
46+
array_map('spl_autoload_register', $loadersToRestore, $flagTrue, $flagTrue);
47+
}
48+
49+
public function loadClass($className): ?bool
50+
{
51+
return $this->classLoader->loadClass($className);
52+
}
53+
54+
public function getPrefixes()
55+
{
56+
return $this->classLoader->getPrefixes();
57+
}
58+
59+
public function getPrefixesPsr4()
60+
{
61+
return $this->classLoader->getPrefixesPsr4();
62+
}
63+
64+
public function getFallbackDirs()
65+
{
66+
return $this->classLoader->getFallbackDirs();
67+
}
68+
69+
public function getFallbackDirsPsr4()
70+
{
71+
return $this->classLoader->getFallbackDirsPsr4();
72+
}
73+
74+
public function getClassMap()
75+
{
76+
return $this->classLoader->getClassMap();
77+
}
78+
79+
public function addClassMap(array $classMap)
80+
{
81+
$this->classLoader->addClassMap($classMap);
82+
}
83+
84+
public function add($prefix, $paths, $prepend = false)
85+
{
86+
$this->classLoader->add($prefix, $paths, $prepend);
87+
}
88+
89+
public function addPsr4($prefix, $paths, $prepend = false)
90+
{
91+
$this->classLoader->addPsr4($prefix, $paths, $prepend);
92+
}
93+
94+
public function set($prefix, $paths)
95+
{
96+
$this->classLoader->set($prefix, $paths);
97+
}
98+
99+
public function setPsr4($prefix, $paths)
100+
{
101+
$this->classLoader->setPsr4($prefix, $paths);
102+
}
103+
104+
public function setUseIncludePath($useIncludePath)
105+
{
106+
$this->classLoader->setUseIncludePath($useIncludePath);
107+
}
108+
109+
public function getUseIncludePath()
110+
{
111+
return $this->classLoader->getUseIncludePath();
112+
}
113+
114+
public function setClassMapAuthoritative($classMapAuthoritative)
115+
{
116+
$this->classLoader->setClassMapAuthoritative($classMapAuthoritative);
117+
}
118+
119+
public function isClassMapAuthoritative()
120+
{
121+
return $this->classLoader->isClassMapAuthoritative();
122+
}
123+
124+
public function setApcuPrefix($apcuPrefix)
125+
{
126+
$this->classLoader->setApcuPrefix($apcuPrefix);
127+
}
128+
129+
public function getApcuPrefix()
130+
{
131+
return $this->classLoader->getApcuPrefix();
132+
}
133+
134+
public function register($prepend = false)
135+
{
136+
$this->classLoader->register($prepend);
137+
}
138+
139+
public function unregister()
140+
{
141+
$this->classLoader->unregister();
142+
}
143+
144+
public function findFile($class)
145+
{
146+
$path = $this->classLoader->findFile($class);
147+
148+
if (
149+
class_exists(\Symfony\Component\ErrorHandler\DebugClassLoader::class, false)
150+
|| class_exists(\Symfony\Component\Debug\DebugClassLoader::class, false)
151+
) {
152+
return $this->handleDebugClassLoader($class, $path);
153+
}
154+
155+
return $path;
156+
}
157+
158+
private function handleDebugClassLoader($class, $path)
159+
{
160+
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
161+
$debugClassLoader = ($backtrace[2] ?? null);
162+
163+
if ($path
164+
&& is_file($path)
165+
&& \in_array(
166+
$debugClassLoader['class'] ?? null,
167+
[
168+
\Symfony\Component\Debug\DebugClassLoader::class,
169+
\Symfony\Component\ErrorHandler\DebugClassLoader::class
170+
],
171+
true
172+
)
173+
) {
174+
include $path;
175+
176+
if (
177+
$class !== StaticConstructorInterface::class
178+
&& is_a($class, StaticConstructorInterface::class, true)
179+
) {
180+
$class::__constructStatic();
181+
}
182+
183+
return false;
184+
}
185+
186+
return $path;
187+
}
188+
}

0 commit comments

Comments
 (0)