Completed
Pull Request — 1.x (#264)
by Alain
02:31
created

AopComposerLoader   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 158
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Test Coverage

Coverage 0%

Importance

Changes 9
Bugs 0 Features 1
Metric Value
wmc 21
c 9
b 0
f 1
lcom 2
cbo 3
dl 0
loc 158
ccs 0
cts 72
cp 0
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 25 5
B init() 0 28 5
A loadClass() 0 8 2
C findFile() 0 22 8
A wasInitialized() 0 4 1
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)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
76
            // Let's exclude core dependencies from that list
77
            if(isset($prefixes['Dissect'])) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
78
                $excludePaths[] = $prefixes['Dissect'][0];
79
            }
80
            if(isset($prefixes['TokenReflection'])) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
81
                $excludePaths[] = $prefixes['TokenReflection'][0];
82
            }
83
            if(isset($prefixes['Doctrine\\Common\\Annotations\\'])) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
84
                $excludePaths[] = substr($prefixes['Doctrine\\Common\\Annotations\\'][0], 0, -16);
85
            }
86
        }
87
88
        $fileEnumerator       = new Enumerator($options['appDir'], $options['includePaths'], $excludePaths);
89
        $this->fileEnumerator = $fileEnumerator;
90
        $this->cacheState     = $container->get('aspect.cache.path.manager')->queryCacheState();
91
    }
92
93
    /**
94
     * Initialize aspect autoloader
95
     *
96
     * Replaces original composer autoloader with wrapper
97
     *
98
     * @param array $options Aspect kernel options
99
     * @param AspectContainer $container
100
     *
101
     * @return bool was initialization sucessful or not
102
     */
103
    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...
104
    {
105
        $loaders = spl_autoload_functions();
106
107
        foreach ($loaders as &$loader) {
108
            $loaderToUnregister = $loader;
109
            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...
110
                $originalLoader = $loader[0];
111
112
                // Configure library loader for doctrine annotation loader
113
                AnnotationRegistry::registerLoader(function($class) use ($originalLoader) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
114
                    $originalLoader->loadClass($class);
115
116
                    return class_exists($class, false);
117
                });
118
                $loader[0] = new AopComposerLoader($loader[0], $container, $options);
119
                self::$wasInitialized = true;
120
            }
121
            spl_autoload_unregister($loaderToUnregister);
122
        }
123
        unset($loader);
124
125
        foreach ($loaders as $loader) {
126
            spl_autoload_register($loader);
127
        }
128
129
        return self::$wasInitialized;
130
    }
131
132
    /**
133
     * Autoload a class by it's name
134
     */
135
    public function loadClass($class)
136
    {
137
        $file = $this->findFile($class);
138
139
        if ($file) {
140
            include $file;
141
        }
142
    }
143
144
    /**
145
     * {@inheritDoc}
146
     */
147
    public function findFile($class)
148
    {
149
        static $isAllowedFilter = null, $isProduction = false;
150
        if (!$isAllowedFilter) {
151
            $isAllowedFilter = $this->fileEnumerator->getFilter();
152
            $isProduction    = !$this->options['debug'];
153
        }
154
155
        $file = $this->original->findFile($class);
156
157
        if ($file) {
158
            $cacheState = isset($this->cacheState[$file]) ? $this->cacheState[$file] : null;
159
            if ($cacheState && $isProduction) {
160
                $file = $cacheState['cacheUri'] ?: $file;
161
            } elseif ($isAllowedFilter(new \SplFileInfo($file))) {
162
                // can be optimized here with $cacheState even for debug mode, but no needed right now
163
                $file = FilterInjectorTransformer::rewrite($file);
164
            }
165
        }
166
167
        return $file;
168
    }
169
170
    /**
171
     * Whether or not loader was initialized
172
     *
173
     * @return bool
174
     */
175
    public static function wasInitialized()
176
    {
177
        return self::$wasInitialized;
178
    }
179
}
180