Completed
Push — master ( 0ecc08...4d9b3a )
by David
9s
created

ClassDiscovery::storeInCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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