Completed
Push — master ( a6beda...e78e0b )
by David
14:30 queued 07:07
created

ClassDiscovery::evaluateCondition()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 8.1426

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 10
cts 14
cp 0.7143
rs 8.5866
c 0
b 0
f 0
cc 7
nc 7
nop 1
crap 8.1426
1
<?php
2
3
namespace Http\Discovery;
4
5
use Http\Discovery\Exception\ClassInstantiationFailedException;
6
use Http\Discovery\Exception\DiscoveryFailedException;
7
use Http\Discovery\Exception\NoCandidateFoundException;
8
use Http\Discovery\Exception\StrategyUnavailableException;
9
10
/**
11
 * Registry that based find results on class existence.
12
 *
13
 * @author David de Boer <[email protected]>
14
 * @author Márk Sági-Kazár <[email protected]>
15
 * @author Tobias Nyholm <[email protected]>
16
 */
17
abstract class ClassDiscovery
18
{
19
    /**
20
     * A list of strategies to find classes.
21
     *
22
     * @var array
23
     */
24
    private static $strategies = [
25
        Strategy\PuliBetaStrategy::class,
26
        Strategy\CommonClassesStrategy::class,
27
    ];
28
29
    /**
30
     * Discovery cache to make the second time we use discovery faster.
31
     *
32
     * @var array
33
     */
34
    private static $cache = [];
35
36
    /**
37
     * Finds a class.
38
     *
39
     * @param string $type
40
     *
41
     * @return string|\Closure
42
     *
43
     * @throws DiscoveryFailedException
44
     */
45 15
    protected static function findOneByType($type)
46
    {
47
        // Look in the cache
48 15
        if (null !== ($class = self::getFromCache($type))) {
49
            return $class;
50
        }
51
52 15
        $exceptions = [];
53 15
        foreach (self::$strategies as $strategy) {
54
            try {
55 15
                $candidates = call_user_func($strategy.'::getCandidates', $type);
56 15
            } catch (StrategyUnavailableException $e) {
57
                $exceptions[] = $e;
58
59
                continue;
60
            }
61
62 15
            foreach ($candidates as $candidate) {
63 9
                if (isset($candidate['condition'])) {
64 7
                    if (!self::evaluateCondition($candidate['condition'])) {
65 1
                        continue;
66
                    }
67 7
                }
68
69
                // save the result for later use
70 9
                self::storeInCache($type, $candidate);
71
72 9
                return $candidate['class'];
73 7
            }
74
75 7
            $exceptions[] = new NoCandidateFoundException($strategy, $candidates);
76 7
        }
77
78 6
        throw DiscoveryFailedException::create($exceptions);
79
    }
80
81
    /**
82
     * Get a value from cache.
83
     *
84
     * @param string $type
85
     *
86
     * @return string|null
87
     */
88 15
    private static function getFromCache($type)
89
    {
90 15
        if (!isset(self::$cache[$type])) {
91 15
            return;
92
        }
93
94
        $candidate = self::$cache[$type];
95
        if (isset($candidate['condition'])) {
96
            if (!self::evaluateCondition($candidate['condition'])) {
97
                return;
98
            }
99
        }
100
101
        return $candidate['class'];
102
    }
103
104
    /**
105
     * Store a value in cache.
106
     *
107
     * @param string $type
108
     * @param string $class
109
     */
110 9
    private static function storeInCache($type, $class)
111
    {
112 9
        self::$cache[$type] = $class;
113 9
    }
114
115
    /**
116
     * Set new strategies and clear the cache.
117
     *
118
     * @param array $strategies string array of fully qualified class name to a DiscoveryStrategy
119
     */
120 27
    public static function setStrategies(array $strategies)
121
    {
122 27
        self::$strategies = $strategies;
123 27
        self::clearCache();
124 27
    }
125
126
    /**
127
     * Append a strategy at the end of the strategy queue.
128
     *
129
     * @param string $strategy Fully qualified class name to a DiscoveryStrategy
130
     */
131 1
    public static function appendStrategy($strategy)
132
    {
133 1
        self::$strategies[] = $strategy;
134 1
        self::clearCache();
135 1
    }
136
137
    /**
138
     * Prepend a strategy at the beginning of the strategy queue.
139
     *
140
     * @param string $strategy Fully qualified class name to a DiscoveryStrategy
141
     */
142 1
    public static function prependStrategy($strategy)
143
    {
144 1
        array_unshift(self::$strategies, $strategy);
145 1
        self::clearCache();
146 1
    }
147
148
    /**
149
     * Clear the cache.
150
     */
151 27
    public static function clearCache()
152
    {
153 27
        self::$cache = [];
154 27
    }
155
156
    /**
157
     * Evaluates conditions to boolean.
158
     *
159
     * @param mixed $condition
160
     *
161
     * @return bool
162
     */
163 7
    protected static function evaluateCondition($condition)
164
    {
165 7
        if (is_string($condition)) {
166
            // Should be extended for functions, extensions???
167
            return self::safeClassExists($condition);
168
        }
169 7
        if (is_callable($condition)) {
170
            return (bool) $condition();
171
        }
172 7
        if (is_bool($condition)) {
173 7
            return $condition;
174
        }
175 1
        if (is_array($condition)) {
176 1
            foreach ($condition as $c) {
177 1
                if (false === static::evaluateCondition($c)) {
178
                    // Immediately stop execution if the condition is false
179 1
                    return false;
180
                }
181 1
            }
182
183
            return true;
184
        }
185
186
        return false;
187
    }
188
189
    /**
190
     * Get an instance of the $class.
191
     *
192
     * @param string|\Closure $class A FQCN of a class or a closure that instantiate the class.
193
     *
194
     * @return object
195
     *
196
     * @throws ClassInstantiationFailedException
197
     */
198 5
    protected static function instantiateClass($class)
199
    {
200
        try {
201 5
            if (is_string($class)) {
202 5
                return new $class();
203
            }
204
205
            if (is_callable($class)) {
206
                return $class();
207
            }
208
        } catch (\Exception $e) {
209
            throw new ClassInstantiationFailedException('Unexpected exception when instantiating class.', 0, $e);
210
        }
211
212
        throw new ClassInstantiationFailedException('Could not instantiate class because parameter is neither a callable nor a string');
213
    }
214
215
    /**
216
     * We want to do a "safe" version of PHP's "class_exists" because Magento has a bug
217
     * (or they call it a "feature"). Magento is throwing an exception if you do class_exists()
218
     * on a class that ends with "Factory" and if that file does not exits.
219
     *
220
     * This function will catch all potential exceptions and make sure it returns a boolean.
221
     *
222
     * @param string $class
223
     * @param bool   $autoload
0 ignored issues
show
Bug introduced by
There is no parameter named $autoload. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
224
     *
225
     * @return bool
226
     */
227
    public static function safeClassExists($class)
228
    {
229
        try {
230
            return class_exists($class);
231
        } catch (\Exception $e) {
232
            return false;
233
        }
234
    }
235
}
236