Passed
Pull Request — master (#1252)
by Keal
02:21
created

Observable::observe()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 0
cts 1
cp 0
crap 2
1
<?php
2
3
/*
4
 * This file is part of the overtrue/wechat.
5
 *
6
 * (c) overtrue <[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 EasyWeChat\Kernel\Traits;
13
14
use EasyWeChat\Kernel\Contracts\EventHandlerInterface;
15
use EasyWeChat\Kernel\Decorators\FinallyResult;
16
use EasyWeChat\Kernel\Decorators\TerminateResult;
17
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
18
use EasyWeChat\Kernel\ServiceContainer;
19
20
/**
21
 * Trait Observable.
22
 *
23
 * @author overtrue <[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 \EasyWeChat\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 \EasyWeChat\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 \EasyWeChat\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 \EasyWeChat\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
     * @return \Closure
165
     *
166
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
167
     * @throws \ReflectionException
168
     */
169
    protected function makeClosure($handler)
170
    {
171
        if (is_callable($handler)) {
172
            return $handler;
173
        }
174
175
        if (is_string($handler)) {
176
            if (!class_exists($handler)) {
177
                throw new InvalidArgumentException(sprintf('Class "%s" not exists.', $handler));
178
            }
179
180
            if (!in_array(EventHandlerInterface::class, (new \ReflectionClass($handler))->getInterfaceNames(), true)) {
181
                throw new InvalidArgumentException(sprintf('Class "%s" not an instance of "%s".', $handler, EventHandlerInterface::class));
182
            }
183
184
            return function ($payload) use ($handler) {
185
                return (new $handler($this->app ?? null))->handle($payload);
186
            };
187
        }
188
189
        if ($handler instanceof EventHandlerInterface) {
190
            return function () use ($handler) {
191
                return $handler->handle(...func_get_args());
0 ignored issues
show
Bug introduced by
func_get_args() is expanded, but the parameter $payload of EasyWeChat\Kernel\Contra...dlerInterface::handle() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

191
                return $handler->handle(/** @scrutinizer ignore-type */ ...func_get_args());
Loading history...
192
            };
193
        }
194
195
        throw new InvalidArgumentException('No valid handler is found in arguments.');
196
    }
197
198
    /**
199
     * @param $handler
200
     * @param $condition
201
     *
202
     * @return array
203
     *
204
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
205
     * @throws \ReflectionException
206
     */
207
    protected function resolveHandlerAndCondition($handler, $condition): array
208
    {
209
        if (is_int($handler) || (is_string($handler) && !class_exists($handler))) {
210
            list($handler, $condition) = [$condition, $handler];
211
        }
212
213
        return [$this->makeClosure($handler), $condition];
214
    }
215
}
216