Completed
Pull Request — master (#263)
by Alain
02:42
created

AopComposerLoader::__construct()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 6
Bugs 0 Features 1
Metric Value
c 6
b 0
f 1
dl 0
loc 22
ccs 0
cts 18
cp 0
rs 8.9197
cc 4
eloc 13
nc 5
nop 3
crap 20
1
<?php
2
/*
3
 * Go! AOP framework
4
 *
5
 * @copyright Copyright 2013, Lisachenko Alexander <[email protected]>
6
 *
7
 * This source file is subject to the license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
11
namespace Go\Instrument\ClassLoading;
12
13
use Go\Core\AspectContainer;
14
use Go\Instrument\FileSystem\Enumerator;
15
use Go\Instrument\Transformer\FilterInjectorTransformer;
16
use Composer\Autoload\ClassLoader;
17
use Doctrine\Common\Annotations\AnnotationRegistry;
18
19
/**
20
 * AopComposerLoader class is responsible to use a weaver for classes instead of original one
21
 */
22
class AopComposerLoader
23
{
24
25
    /**
26
     * Instance of original autoloader
27
     *
28
     * @var ClassLoader
29
     */
30
    protected $original = null;
31
32
    /**
33
     * AOP kernel options
34
     *
35
     * @var array
36
     */
37
    protected $options = [];
38
39
    /**
40
     * File enumerator
41
     *
42
     * @var Enumerator
43
     */
44
    protected $fileEnumerator;
45
46
    /**
47
     * Cache state
48
     *
49
     * @var array
50
     */
51
    private $cacheState;
52
53
    /**
54
     * Was initialization successful or not
55
     *
56
     * @var bool
57
     */
58
    private static $wasInitialized = false;
59
60
    /**
61
     * Constructs an wrapper for the composer loader
62
     *
63
     * @param ClassLoader $original Instance of current loader
64
     * @param AspectContainer $container Instance of the container
65
     * @param array $options Configuration options
66
     */
67
    public function __construct(ClassLoader $original, AspectContainer $container, array $options = [])
68
    {
69
        $this->options  = $options;
70
        $this->original = $original;
71
72
        $prefixes     = $original->getPrefixes();
73
        $excludePaths = $options['excludePaths'];
74
75
        if (!empty($prefixes)) {
76
            // Let's exclude core dependencies from that list
77
            if (array_key_exists('Dissect', $prefixes)) {
78
                $excludePaths[] = $prefixes['Dissect'][0];
79
            }
80
            if (array_key_exists('Doctrine\\Common\\Annotations\\', $prefixes)) {
81
                $excludePaths[] = substr($prefixes['Doctrine\\Common\\Annotations\\'][0], 0, -16);
82
            }
83
        }
84
85
        $fileEnumerator       = new Enumerator($options['appDir'], $options['includePaths'], $excludePaths);
86
        $this->fileEnumerator = $fileEnumerator;
87
        $this->cacheState     = $container->get('aspect.cache.path.manager')->queryCacheState();
88
    }
89
90
    /**
91
     * Initialize aspect autoloader
92
     *
93
     * Replaces original composer autoloader with wrapper
94
     *
95
     * @param array $options Aspect kernel options
96
     * @param AspectContainer $container
97
     *
98
     * @return bool was initialization sucessful or not
99
     */
100
    public static function init(array $options = [], AspectContainer $container)
0 ignored issues
show
Coding Style introduced by
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
101
    {
102
        $loaders = spl_autoload_functions();
103
104
        foreach ($loaders as &$loader) {
105
            $loaderToUnregister = $loader;
106
            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...
107
                $originalLoader = $loader[0];
108
                // Configure library loader for doctrine annotation loader
109
                AnnotationRegistry::registerLoader(function($class) use ($originalLoader) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
110
                    $originalLoader->loadClass($class);
111
112
                    return class_exists($class, false);
113
                });
114
                $loader[0] = new AopComposerLoader($loader[0], $container, $options);
115
                self::$wasInitialized = true;
116
            }
117
            spl_autoload_unregister($loaderToUnregister);
118
        }
119
        unset($loader);
120
121
        foreach ($loaders as $loader) {
122
            spl_autoload_register($loader);
123
        }
124
125
        return self::$wasInitialized;
126
    }
127
128
    /**
129
     * Autoload a class by it's name
130
     */
131
    public function loadClass($class)
132
    {
133
        $file = $this->findFile($class);
134
135
        if ($file) {
136
            include $file;
137
        }
138
    }
139
140
    /**
141
     * {@inheritDoc}
142
     */
143
    public function findFile($class)
144
    {
145
        static $isAllowedFilter = null, $isProduction = false;
146
        if (!$isAllowedFilter) {
147
            $isAllowedFilter = $this->fileEnumerator->getFilter();
148
            $isProduction    = !$this->options['debug'];
149
        }
150
151
        $file = $this->original->findFile($class);
152
153
        if ($file) {
154
            $cacheState = isset($this->cacheState[$file]) ? $this->cacheState[$file] : null;
155
            if ($cacheState && $isProduction) {
156
                $file = $cacheState['cacheUri'] ?: $file;
157
            } elseif ($isAllowedFilter(new \SplFileInfo($file))) {
158
                // can be optimized here with $cacheState even for debug mode, but no needed right now
159
                $file = FilterInjectorTransformer::rewrite($file);
160
            }
161
        }
162
163
        return $file;
164
    }
165
166
    /**
167
     * Whether or not loader was initialized
168
     *
169
     * @return bool
170
     */
171
    public static function wasInitialized()
172
    {
173
        return self::$wasInitialized;
174
    }
175
}
176