Completed
Pull Request — master (#67)
by Eric
01:58
created

Jarvis::offsetSet()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 27
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 16
nc 5
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Jarvis;
6
7
use Jarvis\Skill\DependencyInjection\Container;
8
use Jarvis\Skill\DependencyInjection\ContainerProvider;
9
use Jarvis\Skill\EventBroadcaster\BroadcasterInterface;
10
use Jarvis\Skill\EventBroadcaster\BroadcasterTrait;
11
use Jarvis\Skill\EventBroadcaster\ControllerEvent;
12
use Jarvis\Skill\EventBroadcaster\EventInterface;
13
use Jarvis\Skill\EventBroadcaster\ExceptionEvent;
14
use Jarvis\Skill\EventBroadcaster\PermanentEventInterface;
15
use Jarvis\Skill\EventBroadcaster\ResponseEvent;
16
use Jarvis\Skill\EventBroadcaster\RunEvent;
17
use Jarvis\Skill\EventBroadcaster\SimpleEvent;
18
use Symfony\Component\HttpFoundation\Request;
19
use Symfony\Component\HttpFoundation\Response;
20
21
/**
22
 * Jarvis. Minimalist dependency injection container.
23
 *
24
 * @property bool                                              $debug
25
 * @property Request                                           $request
26
 * @property \Jarvis\Skill\Routing\Router                      $router
27
 * @property \Symfony\Component\HttpFoundation\Session\Session $session
28
 * @property \Jarvis\Skill\Core\CallbackResolver               $callbackResolver
29
 *
30
 * @author Eric Chau <[email protected]>
31
 */
32
class Jarvis extends Container implements BroadcasterInterface
33
{
34
    use BroadcasterTrait {
35
        broadcast as traitBroadcast;
36
    };
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected ';', expecting T_FUNCTION or T_CONST
Loading history...
37
38
    const DEFAULT_DEBUG = false;
39
    const CONTAINER_PROVIDER_FQCN = ContainerProvider::class;
40
41
    private $masterSetter = false;
42
43
    /**
44
     * Creates an instance of Jarvis. It can take settings as first argument.
45
     * List of accepted options:
46
     *   - providers (type: string|array): fqcn of your container provider
47
     *   - extra
48
     *
49
     * @param  array $settings Your own settings to modify Jarvis behavior
50
     */
51
    public function __construct(array $settings = [])
52
    {
53
        parent::__construct();
54
55
        $this['settings'] = $settings;
56
        $providers = array_merge([static::CONTAINER_PROVIDER_FQCN], (array) ($settings['providers'] ?? []));
57
        foreach (array_unique($providers) as $classname) {
58
            $this->hydrate(new $classname());
59
        }
60
    }
61
62
    public function __destruct()
63
    {
64
        $this->masterBroadcast(BroadcasterInterface::TERMINATE_EVENT);
65
    }
66
67
    /**
68
     * This method is an another way to get a locked value.
69
     *
70
     * Example: $this['foo'] is equal to $this->foo, but it ONLY works for locked values.
71
     *
72
     * @param  string $key The key of the locked value
73
     * @return mixed
74
     * @throws \InvalidArgumentException if the requested key is not associated to a locked service
75
     */
76
    public function __get(string $key)
77
    {
78
        if (!isset($this->locked[$key])) {
79
            throw new \InvalidArgumentException(sprintf('"%s" is not a key of a locked value.', $key));
80
        }
81
82
        $this->masterSet($key, $this[$key]);
83
84
        return $this->$key;
85
    }
86
87
    /**
88
     * Sets new attributes to Jarvis. Note that this method is reserved to Jarvis itself only.
89
     *
90
     * @param string $key   The key name of the new attribute
91
     * @param mixed  $value The value to associate to provided key
92
     * @throws \LogicException if this method is not called by Jarvis itself
93
     */
94
    public function __set(string $key, $value)
95
    {
96
        if (!$this->masterSetter) {
97
            throw new \LogicException('You are not allowed to set new attribute into Jarvis.');
98
        }
99
100
        $this->$key = $value;
101
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106
    public function offsetSet($id, $v): void
107
    {
108
        parent::offsetSet($id, $v);
109
110
        if (!($v instanceof \Closure)) {
111
            return;
112
        }
113
114
        $refMethod = new \ReflectionMethod($v, '__invoke');
115
        if (null === $returntype = $refMethod->getReturnType()) {
116
            return;
117
        }
118
119
        $alias = $returntype->getName();
120
        if (
121
            $alias === $id
122
            || (!class_exists($alias) && !interface_exists($alias))
123
        ) {
124
            return;
125
        }
126
127
        if (!isset($this[$alias])) {
128
            $this->alias($alias, $id);
129
        } else {
130
            unset($this[$alias]);
131
        }
132
    }
133
134
    /**
135
     * @param  Request|null $request
136
     * @return Response
137
     */
138
    public function run(Request $request = null): Response
139
    {
140
        $request = $request ?? $this['request'];
141
        $event = $event = new RunEvent($request);
142
143
        try {
144
            $this->masterBroadcast(BroadcasterInterface::RUN_EVENT, $event);
145
            if ($response = $event->response()) {
146
                return $response;
147
            }
148
149
            [$callback, $arguments] = $this['router']->match($request->getMethod(), $request->getPathInfo());
150
            $event = new ControllerEvent($this['callbackResolver']->resolve($callback), $arguments);
151
            $this->masterBroadcast(BroadcasterInterface::CONTROLLER_EVENT, $event);
152
153
            $response = $this['callbackResolver']->resolveAndCall($event->callback(), $event->arguments());
154
            $event = new ResponseEvent($request, $response);
155
            $this->masterBroadcast(BroadcasterInterface::RESPONSE_EVENT, $event);
156
        } catch (\Throwable $throwable) {
157
            $event = new ExceptionEvent($throwable);
158
            $this->masterBroadcast(BroadcasterInterface::EXCEPTION_EVENT, $event);
159
        }
160
161
        return $event->response();
162
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167
    public function broadcast(string $name, EventInterface $event = null): void
168
    {
169
        if (!$this->masterEmitter && in_array($name, BroadcasterInterface::RESERVED_EVENT_NAMES)) {
170
            throw new \LogicException(sprintf(
171
                'You\'re trying to broadcast "$name" but "%s" are reserved event names.',
172
                implode('|', BroadcasterInterface::RESERVED_EVENT_NAMES)
173
            ));
174
        }
175
176
        $this->traitBroadcast($name, $event);
177
    }
178
179
    /**
180
     * Sets new attribute into Jarvis.
181
     *
182
     * @param  string $key   The name of the new attribute
183
     * @param  mixed  $value The value of the new attribute
184
     */
185
    private function masterSet(string $key, $value): void
186
    {
187
        $this->masterSetter = true;
188
        $this->$key = $value;
189
        $this->masterSetter = false;
190
    }
191
192
    /**
193
     * Enables master emitter mode.
194
     */
195
    private function masterBroadcast(string $name, EventInterface $event = null): void
196
    {
197
        $this->masterEmitter = true;
198
        $this->broadcast($name, $event);
199
        $this->masterEmitter = false;
200
    }
201
}
202