Completed
Pull Request — master (#66)
by Tobias
02:47
created

ClassDiscovery::instantiateClass()   A

Complexity

Conditions 4
Paths 7

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 16
rs 9.2
cc 4
eloc 9
nc 7
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 (!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 array $strategies
113
     */
114
    public static function setStrategies(array $strategies)
115
    {
116
        self::$strategies = $strategies;
117
        self::clearCache();
118
    }
119
120
    /**
121
     * Clear the cache.
122
     */
123
    public static function clearCache()
124
    {
125
        self::$cache = [];
126
    }
127
128
    /**
129
     * Evaulates conditions to boolean.
130
     *
131
     * @param mixed $condition
132
     *
133
     * @return bool
134
     */
135
    protected static function evaluateCondition($condition)
136
    {
137
        if (is_string($condition)) {
138
            // Should be extended for functions, extensions???
139
            return class_exists($condition);
140
        } elseif (is_callable($condition)) {
141
            return $condition();
142
        } elseif (is_bool($condition)) {
143
            return $condition;
144
        } elseif (is_array($condition)) {
145
            $evaluatedCondition = true;
146
147
            // Immediately stop execution if the condition is false
148
            for ($i = 0; $i < count($condition) && false !== $evaluatedCondition; ++$i) {
149
                $evaluatedCondition &= static::evaluateCondition($condition[$i]);
150
            }
151
152
            return $evaluatedCondition;
153
        }
154
155
        return false;
156
    }
157
158
    /**
159
     * Get an instance of the $class.
160
     *
161
     * @param string|\Closure $class A FQN of a class or a closure that instantiate the class.
162
     *
163
     * @return object
164
     */
165
    protected static function instantiateClass($class)
166
    {
167
        try {
168
            if (is_string($class)) {
169
                return new $class();
170
            }
171
172
            if (is_callable($class)) {
173
                return $class();
174
            }
175
        } catch (\Exception $e) {
176
            throw new ClassinstantiationFailedException('Unexcepced exception when instantiating class.', 0, $e);
177
        }
178
        
179
        throw new ClassinstantiationFailedException('Could not instantiate class becuase parameter is neitehr a callable or a string');
180
    }
181
}
182