Completed
Push — master ( 410465...2ed666 )
by David
03:15
created

ClassDiscovery::prependStrategy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 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
    protected static function findOneByType($type)
45
    {
46
        // Look in the cache
47
        if (null !== ($class = self::getFromCache($type))) {
48
            return $class;
49
        }
50
51
        $exceptions = [];
52
        foreach (self::$strategies as $strategy) {
53
            try {
54
                $candidates = call_user_func($strategy.'::getCandidates', $type);
55
            } catch (StrategyUnavailableException $e) {
56
                $exceptions[] = $e;
57
                continue;
58
            }
59
60
            foreach ($candidates as $candidate) {
61
                if (isset($candidate['condition'])) {
62
                    if (!self::evaluateCondition($candidate['condition'])) {
63
                        continue;
64
                    }
65
                }
66
67
                // save the result for later use
68
                self::storeInCache($type, $candidate);
69
70
                return $candidate['class'];
71
            }
72
        }
73
74
        throw new DiscoveryFailedException('Could not find resource using any discovery strategy', $exceptions);
75
    }
76
77
    /**
78
     * Get a value from cache.
79
     *
80
     * @param string $type
81
     *
82
     * @return string|null
83
     */
84
    private static function getFromCache($type)
85
    {
86
        if (!isset(self::$cache[$type])) {
87
            return;
88
        }
89
90
        $candidate = self::$cache[$type];
91
        if (isset($candidate['condition'])) {
92
            if (!self::evaluateCondition($candidate['condition'])) {
93
                return;
94
            }
95
        }
96
97
        return $candidate['class'];
98
    }
99
100
    /**
101
     * Store a value in cache.
102
     *
103
     * @param string $type
104
     * @param string $class
105
     */
106
    private static function storeInCache($type, $class)
107
    {
108
        self::$cache[$type] = $class;
109
    }
110
111
    /**
112
     * Set new strategies and clear the cache.
113
     *
114
     * @param array $strategies string array of fully qualified class name to a DiscoveryStrategy
115
     */
116
    public static function setStrategies(array $strategies)
117
    {
118
        self::$strategies = $strategies;
119
        self::clearCache();
120
    }
121
122
    /**
123
     * Append a strategy at the end of the strategy queue.
124
     *
125
     * @param string $strategy Fully qualified class name to a DiscoveryStrategy
126
     */
127
    public static function appendStrategy($strategy)
128
    {
129
        self::$strategies[] = $strategy;
130
        self::clearCache();
131
    }
132
133
    /**
134
     * Prepend a strategy at the beginning of the strategy queue.
135
     *
136
     * @param string $strategy Fully qualified class name to a DiscoveryStrategy
137
     */
138
    public static function prependStrategy($strategy)
139
    {
140
        array_unshift(self::$strategies, $strategy);
141
        self::clearCache();
142
    }
143
144
    /**
145
     * Clear the cache.
146
     */
147
    public static function clearCache()
148
    {
149
        self::$cache = [];
150
    }
151
152
    /**
153
     * Evaulates conditions to boolean.
154
     *
155
     * @param mixed $condition
156
     *
157
     * @return bool
158
     */
159
    protected static function evaluateCondition($condition)
160
    {
161
        if (is_string($condition)) {
162
            // Should be extended for functions, extensions???
163
            return class_exists($condition);
164
        } elseif (is_callable($condition)) {
165
            return $condition();
166
        } elseif (is_bool($condition)) {
167
            return $condition;
168
        } elseif (is_array($condition)) {
169
            $evaluatedCondition = true;
170
171
            // Immediately stop execution if the condition is false
172
            for ($i = 0; $i < count($condition) && false !== $evaluatedCondition; ++$i) {
173
                $evaluatedCondition &= static::evaluateCondition($condition[$i]);
174
            }
175
176
            return $evaluatedCondition;
177
        }
178
179
        return false;
180
    }
181
182
    /**
183
     * Get an instance of the $class.
184
     *
185
     * @param string|\Closure $class A FQN of a class or a closure that instantiate the class.
186
     *
187
     * @return object
188
     */
189
    protected static function instantiateClass($class)
190
    {
191
        try {
192
            if (is_string($class)) {
193
                return new $class();
194
            }
195
196
            if (is_callable($class)) {
197
                return $class();
198
            }
199
        } catch (\Exception $e) {
200
            throw new ClassinstantiationFailedException('Unexcepced exception when instantiating class.', 0, $e);
201
        }
202
203
        throw new ClassinstantiationFailedException('Could not instantiate class becuase parameter is neitehr a callable or a string');
204
    }
205
}
206