Completed
Push — master ( 91676c...354384 )
by Alexander
02:23
created

AopComposerLoader::init()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 16
cts 16
cp 1
rs 9.1768
c 0
b 0
f 0
cc 5
nc 6
nop 2
crap 5
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)) {
0 ignored issues
show
Bug introduced by
The class Composer\Autoload\ClassLoader does not exist. Did you forget a USE statement, or did you not list all dependencies?

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 the composer.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 or require-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 ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
95 1
                $originalLoader = $loader[0];
96
                // Configure library loader for doctrine annotation loader
97
                AnnotationRegistry::registerLoader(function($class) use ($originalLoader) {
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\Common\Annotati...istry::registerLoader() has been deprecated with message: this method is deprecated and will be removed in doctrine/annotations 2.0 autoloading should be deferred to the globally registered autoloader by then. For now, use @example AnnotationRegistry::registerLoader('class_exists')

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
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