Completed
Pull Request — master (#68)
by Tobias
02:31
created

ClassDiscovery::appendStrategy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
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\DiscoveryFailedException;
6
use Http\Discovery\Exception\StrategyUnavailableException;
7
use Http\Discovery\Strategy\DiscoveryStrategy;
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
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 (!self::evaluateCondition($candidate['condition'])) {
92
            return;
93
        }
94
95
        return $candidate['class'];
96
    }
97
98
    /**
99
     * Store a value in cache.
100
     *
101
     * @param string $type
102
     * @param string $class
103
     */
104
    private static function storeInCache($type, $class)
105
    {
106
        self::$cache[$type] = $class;
107
    }
108
109
    /**
110
     * Set new strategies and clear the cache.
111
     *
112
     * @param DiscoveryStrategy[] $strategies
113
     */
114
    public static function setStrategies(array $strategies)
115
    {
116
        self::$strategies = $strategies;
117
        self::clearCache();
118
    }
119
120
    /**
121
     * Append a strategy at the end of the strategy queue.
122
     *
123
     * @param DiscoveryStrategy $strategy
124
     */
125
    public static function appendStrategy(DiscoveryStrategy $strategy)
126
    {
127
        self::$strategies[] = $strategy;
128
        self::clearCache();
129
    }
130
131
    /**
132
     * Prepend a strategy at the beginning of the strategy queue.
133
     *
134
     * @param DiscoveryStrategy $strategy
135
     */
136
    public static function prependStrategy(DiscoveryStrategy $strategy)
137
    {
138
        self::$strategies = array_unshift(self::$strategies, $strategy);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_unshift(self::$strategies, $strategy) of type integer is incompatible with the declared type array of property $strategies.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
139
        self::clearCache();
140
    }
141
142
    /**
143
     * Clear the cache.
144
     */
145
    public static function clearCache()
146
    {
147
        self::$cache = [];
148
    }
149
150
    /**
151
     * Evaulates conditions to boolean.
152
     *
153
     * @param mixed $condition
154
     *
155
     * @return bool
156
     */
157
    protected static function evaluateCondition($condition)
158
    {
159
        if (is_string($condition)) {
160
            // Should be extended for functions, extensions???
161
            return class_exists($condition);
162
        } elseif (is_callable($condition)) {
163
            return $condition();
164
        } elseif (is_bool($condition)) {
165
            return $condition;
166
        } elseif (is_array($condition)) {
167
            $evaluatedCondition = true;
168
169
            // Immediately stop execution if the condition is false
170
            for ($i = 0; $i < count($condition) && false !== $evaluatedCondition; ++$i) {
171
                $evaluatedCondition &= static::evaluateCondition($condition[$i]);
172
            }
173
174
            return $evaluatedCondition;
175
        }
176
177
        return false;
178
    }
179
}
180