Completed
Pull Request — master (#463)
by Alexander
30:17 queued 05:15
created

AopComposerLoader   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 142
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Test Coverage

Coverage 92.16%

Importance

Changes 0
Metric Value
wmc 20
lcom 2
cbo 5
dl 0
loc 142
ccs 47
cts 51
cp 0.9216
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types = 1);
4
/*
5
 * Go! AOP framework
6
 *
7
 * @copyright Copyright 2013, Lisachenko Alexander <[email protected]>
8
 *
9
 * This source file is subject to the license that is bundled
10
 * with this source code in the file LICENSE.
11
 */
12
13
namespace Go\Instrument\ClassLoading;
14
15
use SplFileInfo;
16
use Go\Core\AspectContainer;
17
use Go\Instrument\FileSystem\Enumerator;
18
use Go\Instrument\PathResolver;
19
use Go\Instrument\Transformer\FilterInjectorTransformer;
20
use Composer\Autoload\ClassLoader;
21
use Doctrine\Common\Annotations\AnnotationRegistry;
22
23
/**
24
 * AopComposerLoader class is responsible to use a weaver for classes instead of original one
25
 */
26
class AopComposerLoader
27
{
28
    /**
29
     * Instance of original autoloader
30
     */
31
    protected ClassLoader $original;
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_STRING, expecting T_FUNCTION or T_CONST
Loading history...
32
33
    /**
34
     * AOP kernel options
35
     */
36
    protected array $options = [];
37
38
    /**
39
     * File enumerator
40
     */
41
    protected Enumerator $fileEnumerator;
42
43
    /**
44
     * Cache state
45
     */
46
    private array $cacheState;
47
48
    /**
49
     * Was initialization successful or not
50
     */
51
    private static bool $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