Observable::makeClosure()   B
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 27
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
cc 6
eloc 13
nc 6
nop 1
dl 0
loc 27
ccs 0
cts 22
cp 0
crap 42
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the docodeit/wechat.
5
 *
6
 * (c) docodeit <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace JinWeChat\Kernel\Traits;
13
14
use JinWeChat\Kernel\Contracts\EventHandlerInterface;
0 ignored issues
show
Bug introduced by
The type JinWeChat\Kernel\Contracts\EventHandlerInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use JinWeChat\Kernel\Decorators\FinallyResult;
16
use JinWeChat\Kernel\Decorators\TerminateResult;
17
use JinWeChat\Kernel\Exceptions\InvalidArgumentException;
18
use JinWeChat\Kernel\ServiceContainer;
19
20
/**
21
 * Trait Observable.
22
 *
23
 * @author docodeit <[email protected]>
24
 */
25
trait Observable
26
{
27
    /**
28
     * @var array
29
     */
30
    protected $handlers = [];
31
32
    /**
33
     * @param \Closure|EventHandlerInterface|string $handler
34
     * @param \Closure|EventHandlerInterface|string $condition
35
     *
36
     * @throws \JinWeChat\Kernel\Exceptions\InvalidArgumentException
37
     */
38
    public function push($handler, $condition = '*')
39
    {
40
        list($handler, $condition) = $this->resolveHandlerAndCondition($handler, $condition);
41
42
        if (!isset($this->handlers[$condition])) {
43
            $this->handlers[$condition] = [];
44
        }
45
46
        array_push($this->handlers[$condition], $handler);
47
    }
48
49
    /**
50
     * @param \Closure|EventHandlerInterface|string $handler
51
     * @param \Closure|EventHandlerInterface|string $condition
52
     *
53
     * @throws \JinWeChat\Kernel\Exceptions\InvalidArgumentException
54
     */
55
    public function unshift($handler, $condition = '*')
56
    {
57
        list($handler, $condition) = $this->resolveHandlerAndCondition($handler, $condition);
58
59
        if (!isset($this->handlers[$condition])) {
60
            $this->handlers[$condition] = [];
61
        }
62
63
        array_unshift($this->handlers[$condition], $handler);
64
    }
65
66
    /**
67
     * @param string                                $condition
68
     * @param \Closure|EventHandlerInterface|string $handler
69
     *
70
     * @throws \JinWeChat\Kernel\Exceptions\InvalidArgumentException
71
     */
72
    public function observe($condition, $handler)
73
    {
74
        $this->push($handler, $condition);
75
    }
76
77
    /**
78
     * @param string                                $condition
79
     * @param \Closure|EventHandlerInterface|string $handler
80
     *
81
     * @throws \JinWeChat\Kernel\Exceptions\InvalidArgumentException
82
     */
83
    public function on($condition, $handler)
84
    {
85
        $this->push($handler, $condition);
86
    }
87
88
    /**
89
     * @param string|int $event
90
     * @param mixed      ...$payload
91
     *
92
     * @return mixed|null
93
     */
94
    public function dispatch($event, $payload)
95
    {
96
        return $this->notify($event, $payload);
97
    }
98
99
    /**
100
     * @param string|int $event
101
     * @param mixed      ...$payload
102
     *
103
     * @return mixed|null
104
     */
105
    public function notify($event, $payload)
106
    {
107
        $result = null;
108
109
        foreach ($this->handlers as $condition => $handlers) {
110
            if ('*' === $condition || ($condition & $event) === $event) {
111
                foreach ($handlers as $handler) {
112
                    $response = $this->callHandler($handler, $payload);
113
114
                    switch (true) {
115
                        case $response instanceof TerminateResult:
116
                            return $response->content;
117
                        case true === $response:
118
                            continue 2;
119
                        case false === $response:
120
                            break 2;
121
                        case !empty($response) && !($result instanceof FinallyResult):
122
                            $result = $response;
123
                    }
124
                }
125
            }
126
        }
127
128
        return $result instanceof FinallyResult ? $result->content : $result;
129
    }
130
131
    /**
132
     * @return array
133
     */
134
    public function getHandlers()
135
    {
136
        return $this->handlers;
137
    }
138
139
    /**
140
     * @param callable $handler
141
     * @param mixed    $payload
142
     *
143
     * @return mixed
144
     */
145
    protected function callHandler(callable $handler, $payload)
146
    {
147
        try {
148
            return $handler($payload);
149
        } catch (\Exception $e) {
150
            if (property_exists($this, 'app') && $this->app instanceof ServiceContainer) {
151
                $this->app['logger']->error($e->getCode().': '.$e->getMessage(), [
152
                    'code' => $e->getCode(),
153
                    'message' => $e->getMessage(),
154
                    'file' => $e->getFile(),
155
                    'line' => $e->getLine(),
156
                ]);
157
            }
158
        }
159
    }
160
161
    /**
162
     * @param $handler
163
     *
164
     * @throws \JinWeChat\Kernel\Exceptions\InvalidArgumentException
165
     *
166
     * @return \Closure
167
     */
168
    protected function makeClosure($handler)
169
    {
170
        if (is_callable($handler)) {
171
            return $handler;
172
        }
173
174
        if (is_string($handler)) {
175
            if (!class_exists($handler)) {
176
                throw new InvalidArgumentException(sprintf('Class "%s" not exists.', $handler));
177
            }
178
179
            if (!in_array(EventHandlerInterface::class, (new \ReflectionClass($handler))->getInterfaceNames(), true)) {
180
                throw new InvalidArgumentException(sprintf('Class "%s" not an instance of "%s".', $handler, EventHandlerInterface::class));
181
            }
182
183
            return function ($payload) use ($handler) {
184
                return (new $handler($this->app ?? null))->handle($payload);
185
            };
186
        }
187
188
        if ($handler instanceof EventHandlerInterface) {
189
            return function () use ($handler) {
190
                return $handler->handle(...func_get_args());
191
            };
192
        }
193
194
        throw new InvalidArgumentException('No valid handler is found in arguments.');
195
    }
196
197
    /**
198
     * @param $handler
199
     * @param $condition
200
     *
201
     * @throws \JinWeChat\Kernel\Exceptions\InvalidArgumentException
202
     *
203
     * @return array
204
     */
205
    protected function resolveHandlerAndCondition($handler, $condition): array
206
    {
207
        if (is_int($handler) || (is_string($handler) && !class_exists($handler))) {
208
            list($handler, $condition) = [$condition, $handler];
209
        }
210
211
        return [$this->makeClosure($handler), $condition];
212
    }
213
}
214