1
|
|
|
<?php |
2
|
|
|
declare(strict_types = 1); |
3
|
|
|
/* |
4
|
|
|
* Go! AOP framework |
5
|
|
|
* |
6
|
|
|
* @copyright Copyright 2013, Lisachenko Alexander <[email protected]> |
7
|
|
|
* |
8
|
|
|
* This source file is subject to the license that is bundled |
9
|
|
|
* with this source code in the file LICENSE. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Go\Instrument\ClassLoading; |
13
|
|
|
|
14
|
|
|
use Go\Core\AspectContainer; |
15
|
|
|
use Go\Instrument\FileSystem\Enumerator; |
16
|
|
|
use Go\Instrument\PathResolver; |
17
|
|
|
use Go\Instrument\Transformer\FilterInjectorTransformer; |
18
|
|
|
use Composer\Autoload\ClassLoader; |
19
|
|
|
use Doctrine\Common\Annotations\AnnotationRegistry; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* AopComposerLoader class is responsible to use a weaver for classes instead of original one |
23
|
|
|
*/ |
24
|
|
|
class AopComposerLoader |
25
|
|
|
{ |
26
|
|
|
/** |
27
|
|
|
* Instance of original autoloader |
28
|
|
|
*/ |
29
|
|
|
protected $original; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* AOP kernel options |
33
|
|
|
*/ |
34
|
|
|
protected $options = []; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* File enumerator |
38
|
|
|
*/ |
39
|
|
|
protected $fileEnumerator; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Cache state |
43
|
|
|
* |
44
|
|
|
* @var array |
45
|
|
|
*/ |
46
|
|
|
private $cacheState; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Was initialization successful or not |
50
|
|
|
*/ |
51
|
|
|
private static $wasInitialized = false; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Constructs an wrapper for the composer loader |
55
|
|
|
* |
56
|
|
|
* @param array $options Configuration options |
57
|
|
|
*/ |
58
|
1 |
|
public function __construct(ClassLoader $original, AspectContainer $container, array $options = []) |
59
|
|
|
{ |
60
|
1 |
|
$this->options = $options; |
61
|
1 |
|
$this->original = $original; |
62
|
|
|
|
63
|
1 |
|
$prefixes = $original->getPrefixes(); |
64
|
1 |
|
$excludePaths = $options['excludePaths']; |
65
|
|
|
|
66
|
1 |
|
if (!empty($prefixes)) { |
67
|
|
|
// Let's exclude core dependencies from that list |
68
|
1 |
|
if (isset($prefixes['Dissect'])) { |
69
|
1 |
|
$excludePaths[] = $prefixes['Dissect'][0]; |
70
|
|
|
} |
71
|
1 |
|
if (isset($prefixes['Doctrine\\Common\\Annotations\\'])) { |
72
|
|
|
$excludePaths[] = substr($prefixes['Doctrine\\Common\\Annotations\\'][0], 0, -16); |
73
|
|
|
} |
74
|
|
|
} |
75
|
|
|
|
76
|
1 |
|
$fileEnumerator = new Enumerator($options['appDir'], $options['includePaths'], $excludePaths); |
77
|
1 |
|
$this->fileEnumerator = $fileEnumerator; |
78
|
1 |
|
$this->cacheState = $container->get('aspect.cache.path.manager')->queryCacheState(); |
79
|
1 |
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Initialize aspect autoloader and returns status whether initialization was successful or not |
83
|
|
|
* |
84
|
|
|
* Replaces original composer autoloader with wrapper |
85
|
|
|
* |
86
|
|
|
* @param array $options Aspect kernel options |
87
|
|
|
*/ |
88
|
1 |
|
public static function init(array $options, AspectContainer $container): bool |
89
|
|
|
{ |
90
|
1 |
|
$loaders = spl_autoload_functions(); |
91
|
|
|
|
92
|
1 |
|
foreach ($loaders as &$loader) { |
93
|
1 |
|
$loaderToUnregister = $loader; |
94
|
1 |
|
if (is_array($loader) && ($loader[0] instanceof ClassLoader)) { |
|
|
|
|
95
|
1 |
|
$originalLoader = $loader[0]; |
96
|
|
|
// Configure library loader for doctrine annotation loader |
97
|
|
|
AnnotationRegistry::registerLoader(function($class) use ($originalLoader) { |
|
|
|
|
98
|
1 |
|
$originalLoader->loadClass($class); |
99
|
|
|
|
100
|
1 |
|
return class_exists($class, false); |
101
|
1 |
|
}); |
102
|
1 |
|
$loader[0] = new AopComposerLoader($loader[0], $container, $options); |
103
|
1 |
|
self::$wasInitialized = true; |
104
|
|
|
} |
105
|
1 |
|
spl_autoload_unregister($loaderToUnregister); |
106
|
|
|
} |
107
|
1 |
|
unset($loader); |
108
|
|
|
|
109
|
1 |
|
foreach ($loaders as $loader) { |
110
|
1 |
|
spl_autoload_register($loader); |
111
|
|
|
} |
112
|
|
|
|
113
|
1 |
|
return self::$wasInitialized; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* Autoload a class by it's name |
118
|
|
|
*/ |
119
|
18 |
|
public function loadClass(string $class): void |
120
|
|
|
{ |
121
|
18 |
|
$file = $this->findFile($class); |
122
|
|
|
|
123
|
18 |
|
if ($file !== false) { |
124
|
18 |
|
include $file; |
125
|
|
|
} |
126
|
18 |
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Finds either the path to the file where the class is defined, |
130
|
|
|
* or gets the appropriate php://filter stream for the given class. |
131
|
|
|
* |
132
|
|
|
* @return string|false The path/resource if found, false otherwise. |
133
|
|
|
*/ |
134
|
18 |
|
public function findFile(string $class) |
135
|
|
|
{ |
136
|
18 |
|
static $isAllowedFilter = null, $isProduction = false; |
137
|
18 |
|
if (!$isAllowedFilter) { |
138
|
1 |
|
$isAllowedFilter = $this->fileEnumerator->getFilter(); |
139
|
1 |
|
$isProduction = !$this->options['debug']; |
140
|
|
|
} |
141
|
|
|
|
142
|
18 |
|
$file = $this->original->findFile($class); |
143
|
|
|
|
144
|
18 |
|
if ($file !== false) { |
145
|
18 |
|
$file = PathResolver::realpath($file)?:$file; |
146
|
18 |
|
$cacheState = $this->cacheState[$file] ?? null; |
147
|
18 |
|
if ($cacheState && $isProduction) { |
148
|
|
|
$file = $cacheState['cacheUri'] ?: $file; |
149
|
18 |
|
} elseif ($isAllowedFilter(new \SplFileInfo($file))) { |
150
|
|
|
// can be optimized here with $cacheState even for debug mode, but no needed right now |
151
|
1 |
|
$file = FilterInjectorTransformer::rewrite($file); |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
|
155
|
18 |
|
return $file; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* Whether or not loader was initialized |
160
|
|
|
*/ |
161
|
|
|
public static function wasInitialized(): bool |
162
|
|
|
{ |
163
|
|
|
return self::$wasInitialized; |
164
|
|
|
} |
165
|
|
|
} |
166
|
|
|
|
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.