ManagesIocTrait   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 195
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 0
loc 195
c 0
b 0
f 0
wmc 26
lcom 1
cbo 2
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
B get() 0 27 5
A fetch() 0 4 1
B add() 0 25 5
A share() 0 5 1
A setup() 0 5 1
C produceDependency() 0 64 13
1
<?php
2
namespace Michaels\Manager\Traits;
3
4
use Michaels\Manager\Contracts\IocContainerInterface;
5
use Michaels\Manager\Exceptions\ItemNotFoundException;
6
use Michaels\Manager\Messages\NoItemFoundMessage;
7
8
/**
9
 * Manages complex, nested data
10
 *
11
 * @implements Michaels\Manager\Contracts\ManagesItemsInterface
12
 * @package Michaels\Manager
13
 */
14
trait ManagesIocTrait
15
{
16
    use DependsOnManagesItemsTrait;
17
18
    /**
19
     * Returns the request object with all dependencies
20
     *
21
     * Overrides the `get()` method on ManagesItemsTrait
22
     * Use getRaw() to return the raw value
23
     *
24
     * string      Full class name for a new object each time
25
     * callable    Factory to create new object (passed manager)
26
     * object      The exact object to be returned
27
     *
28
     * @param string $alias
29
     * @param string|mixed $fallback
30
     * @return mixed
31
     * @throws \Exception
32
     */
33
    public function get($alias, $fallback = '_michaels_no_fallback')
34
    {
35
        // If this is a link, just go back to the master
36
        $link = $this->getIfExists("$alias");
37
        if (is_string($link) && strpos($link, '_michaels_link_') !== false) {
38
            return $this->get(str_replace('_michaels_link_', '', $link));
39
        }
40
41
        // Otherwise, continue
42
        $shared = $this->getIfExists("_singletons.$alias");
43
44
        if ($shared instanceof NoItemFoundMessage) {
45
            // This is not a shared item. We want a new one each time
46
            return $this->produceDependency($alias, $fallback);
47
        } else {
48
            // This is shared, and object has already been cached
49
            if (is_object($shared)) {
50
                return $shared;
51
52
            // This is shared, but we must produce and cache it
53
            } else {
54
                $object = $this->produceDependency($alias, $fallback);
55
                $this->set("_singletons.$alias", $object);
56
                return $object;
57
            }
58
        }
59
    }
60
61
    /**
62
     * Alias of get() for backwards comparability
63
     *
64
     * @param string $alias
65
     * @param string|mixed $fallback
66
     * @return mixed
67
     * @throws \Exception
68
     */
69
    public function fetch($alias, $fallback = '_michaels_no_fallback')
70
    {
71
        return $this->get($alias, $fallback);
72
    }
73
74
    /**
75
     * Adds a dependency to the manager
76
     *
77
     * $factory can be a:
78
     *      string      Full class name for a new object each time
79
     *      callable    Factory to create new object (passed manager)
80
     *      object      The exact object to be returned
81
     *
82
     * @param string $alias
83
     * @param callable|string|object $factory
84
     * @param array $declared
85
     * @return $this
86
     */
87
    public function add($alias, $factory = null, array $declared = null)
88
    {
89
        // Setup links, if necessary
90
        if (is_array($alias)) {
91
            $links = $alias;
92
            $alias = $alias[0];
93
            unset($links[0]);
94
        }
95
96
        $this->set("$alias", $factory);
0 ignored issues
show
Bug introduced by
It seems like $factory defined by parameter $factory on line 87 can also be of type callable; however, Michaels\Manager\Traits\...anagesItemsTrait::set() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
97
98
        // Setup any declared dependencies
99
        if ($declared) {
100
            $this->set("_declarations.$alias", $declared);
101
        }
102
103
        // Add Links
104
        if (!empty($links)) {
105
            foreach ($links as $link) {
106
                $this->set("$link", "_michaels_link_$alias");
107
            }
108
        }
109
110
        return $this;
111
    }
112
113
    /**
114
     * Turns a dependency into a singleton.
115
     * @param $alias
116
     * @return mixed
117
     */
118
    public function share($alias)
119
    {
120
        $this->set("_singletons.$alias", true);
121
        return $this;
122
    }
123
124
    /**
125
     * Add a pipeline to to the que
126
     * @param $alias
127
     * @param $pipeline
128
     * @return $this
129
     */
130
    public function setup($alias, $pipeline)
131
    {
132
        $this->set("_pipelines.$alias", $pipeline);
133
        return $this;
134
    }
135
136
    /**
137
     * Produces the object from an alias
138
     * @param string $alias
139
     * @param mixed|string $fallback
140
     * @return mixed
141
     * @throws ItemNotFoundException
142
     * @throws \Exception
143
     */
144
    protected function produceDependency($alias, $fallback = '_michaels_no_fallback')
145
    {
146
        /* Get the registered factory (string, closure, object, container, NoItemFoundMessage) */
147
        $factory = $this->getIfExists("$alias");
148
149
        /* Manage not founds and fallback */
150
        if ($factory instanceof NoItemFoundMessage) {
151
            if ($fallback !== '_michaels_no_fallback') {
152
                return $fallback;
153
            } elseif (class_exists($alias)) {
154
                return new $alias;
155
            } else {
156
                throw new ItemNotFoundException("$alias not found");
157
            }
158
        }
159
160
        /* Get any declared dependencies */
161
        $declared = $this->getIfExists("_declarations.$alias");
162
        $dependencies = [];
163
164
        // Now setup those dependencies into an array
165
        if (!$declared instanceof NoItemFoundMessage) {
166
            $dependencies = array_map(function(&$value) use ($alias) {
167
                if (is_string($value) && $this->exists("$alias")) {
168
                    return $this->get($value);
169
                }
170
                return $value;
171
            }, $declared);
172
        }
173
174
        /* Produce the object itself */
175
        if ($factory instanceof IocContainerInterface) {
176
            $object = $factory->get($alias);
177
178
        } elseif (is_string($factory)) {
179
            $class = new \ReflectionClass($factory);
180
            $object = $class->newInstanceArgs($dependencies);
181
182
        } elseif (is_callable($factory)) {
183
            array_unshift($dependencies, $this);
184
            $object = call_user_func_array($factory, $dependencies);
185
186
        } elseif (is_object($factory)) {
187
            $object = $factory;
188
189
            if (method_exists($object, "needs")) {
190
                call_user_func_array([$object, 'needs'], $dependencies);
191
            }
192
193
        } else {
194
            throw new \Exception("`get()` can only return from strings, callables, or objects");
195
        }
196
197
        /* Run the object through the pipeline, if desired */
198
        $pipeline = $this->getIfExists("_pipelines.$alias");
199
200
        if (!$pipeline instanceof NoItemFoundMessage) {
201
            /** @var \Closure $pipeline */
202
            $object = $pipeline($object, $this);
203
        }
204
205
        /* Return the final object */
206
        return $object;
207
    }
208
}
209