Completed
Push — master ( d40022...a8a6e5 )
by Arne
01:50
created

AbstractFactory   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 154
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 23
lcom 1
cbo 1
dl 0
loc 154
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 1 1
A provides() 0 6 1
A getProvidedServiceNames() 0 6 1
C create() 0 41 8
D registerFactory() 0 37 9
A requiresInstanceOf() 0 4 1
A loadFactoryMap() 0 7 2
getFactoryMap() 0 1 ?
1
<?php
2
3
namespace Archivr;
4
5
use Archivr\Exception\Exception;
6
7
/**
8
 * todo: Consider replacement by reference implementation
9
 */
10
abstract class AbstractFactory
11
{
12
    /**
13
     * Class is a singleton
14
     */
15
    private function __construct() {}
16
17
    protected static $factoryMaps = [];
18
19
    /**
20
     * Returns true if this factory provides the service of the given name.
21
     *
22
     * @param string $name
23
     * @return bool
24
     */
25
    public static function provides(string $name): bool
26
    {
27
        static::loadFactoryMap();
28
29
        return isset(static::$factoryMaps[static::class][$name]);
30
    }
31
32
    /**
33
     * Returns array of all service names that this factory provides.
34
     *
35
     * @return array
36
     */
37
    public static function getProvidedServiceNames(): array
38
    {
39
        static::loadFactoryMap();
40
41
        return array_keys(static::$factoryMaps[static::class]);
42
    }
43
44
    /**
45
     * Creates and returns the service under the given name.
46
     *
47
     * @param string $name
48
     * @param array ...$params Parameters passed to the actual factory.
49
     * @return mixed
50
     * @throws Exception
51
     */
52
    public static function create(string $name, ...$params)
53
    {
54
        static::loadFactoryMap();
55
56
        $factoryMap = static::$factoryMaps[static::class];
57
58
        if (!isset($factoryMap[$name]))
59
        {
60
            throw new \InvalidArgumentException(sprintf('Factory "%s" does not provide "%s".', static::class, $name));
61
        }
62
63
        $factory = $factoryMap[$name];
64
65
        if (is_string($factory))
66
        {
67
            $instance = new $factory(...$params);
68
        }
69
        elseif ($factory instanceof \Closure)
70
        {
71
            $instance = $factory(...$params);
72
        }
73
        elseif (is_object($factory))
74
        {
75
            $instance = $factory;
76
        }
77
        else
78
        {
79
            throw new \LogicException();
80
        }
81
82
        // check for correct interface/class if requirement is set
83
        if ($type = static::requiresInstanceOf())
84
        {
85
            if (!($instance instanceof $type))
86
            {
87
                throw new Exception(sprintf('Factory closure for "%s" does not return instance of %s. %s given.', $name, $type, is_object($instance) ? get_class($instance) : gettype($instance)));
88
            }
89
        }
90
91
        return $instance;
92
    }
93
94
    /**
95
     * Registers a n
96
     *
97
     * @param string $name
98
     * @param string|\Closure|object $factory
99
     * @param bool $allowOverride
100
     */
101
    public static function registerFactory(string $name, $factory, bool $allowOverride = false): void
102
    {
103
        static::loadFactoryMap();
104
105
        if (!$allowOverride && isset(static::$factoryMaps[static::class][$name]))
106
        {
107
            throw new \RuntimeException(sprintf('Trying to register a factory named "%s" to "%s" which already exists.', $name, static::class));
108
        }
109
110
        if (is_string($factory))
111
        {
112
            if (!class_exists($factory))
113
            {
114
                throw new \RuntimeException(sprintf('Trying to register factory named "%s" to "%s" as class name "%s" which does not exist.', $name, static::class, $factory));
115
            }
116
        }
117
        elseif ($factory instanceof \Closure)
0 ignored issues
show
Unused Code introduced by
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
118
        {
119
            // cannot really validate
120
        }
121
        elseif (is_object($factory))
122
        {
123
            if ($type = static::requiresInstanceOf())
124
            {
125
                if (!($factory instanceof $type))
126
                {
127
                    throw new \RuntimeException(sprintf('Trying to register service instance named "%s" to "%s" as which is not an instance of "%s".', $name, static::class, $type));
128
                }
129
            }
130
        }
131
        else
132
        {
133
            throw new \InvalidArgumentException(sprintf('Invalid factory of type "%s" named "%s" given to "%s".', gettype($factory), $name, static::class));
134
        }
135
136
        static::$factoryMaps[static::class][$name] = $factory;
137
    }
138
139
    /**
140
     * Can return an instance/class name that the factories have to return an instance of.
141
     *
142
     * @return string
143
     */
144
    protected static function requiresInstanceOf(): string
145
    {
146
        return null;
147
    }
148
149
    protected static function loadFactoryMap(): void
150
    {
151
        if (!isset(static::$factoryMaps[static::class]))
152
        {
153
            static::$factoryMaps[static::class] = static::getFactoryMap();
154
        }
155
    }
156
157
    /**
158
     * Has to return map of names to factory closures.
159
     *
160
     * @return array
161
     */
162
    abstract protected static function getFactoryMap(): array;
163
}
164