Completed
Branch 6.0 (d30585)
by yun
04:17
created

Event::dispatch()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.0175

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 2
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
ccs 7
cts 8
cp 0.875
crap 3.0175
1
<?php
2
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
6
// +----------------------------------------------------------------------
7
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
8
// +----------------------------------------------------------------------
9
// | Author: liu21st <[email protected]>
10
// +----------------------------------------------------------------------
11
declare (strict_types = 1);
12
13
namespace think;
14
15
use ReflectionClass;
16
use ReflectionMethod;
17
18
/**
19
 * 事件管理类
20
 * @package think
21
 */
22
class Event
23
{
24
    /**
25
     * 监听者
26
     * @var array
27
     */
28
    protected $listener = [];
29
30
    /**
31
     * 事件别名
32
     * @var array
33
     */
34
    protected $bind = [
35
        'AppInit'     => event\AppInit::class,
36
        'HttpRun'     => event\HttpRun::class,
37
        'HttpEnd'     => event\HttpEnd::class,
38
        'RouteLoaded' => event\RouteLoaded::class,
39
        'LogWrite'    => event\LogWrite::class,
40
    ];
41
42
    /**
43
     * 应用对象
44
     * @var App
45
     */
46
    protected $app;
47
48 27
    public function __construct(App $app)
49
    {
50 27
        $this->app = $app;
51 27
    }
52
53
    /**
54
     * 批量注册事件监听
55
     * @access public
56
     * @param array $events 事件定义
57
     * @return $this
58
     */
59 3
    public function listenEvents(array $events)
60
    {
61 3
        foreach ($events as $event => $listeners) {
62 3
            if (isset($this->bind[$event])) {
63
                $event = $this->bind[$event];
64
            }
65
66 3
            $this->listener[$event] = array_merge($this->listener[$event] ?? [], $listeners);
67
        }
68
69 3
        return $this;
70
    }
71
72
    /**
73
     * 注册事件监听
74
     * @access public
75
     * @param string $event    事件名称
76
     * @param mixed  $listener 监听操作(或者类名)
77
     * @param bool   $first    是否优先执行
78
     * @return $this
79
     */
80 12
    public function listen(string $event, $listener, bool $first = false)
81
    {
82 12
        if (isset($this->bind[$event])) {
83 6
            $event = $this->bind[$event];
84
        }
85
86 12 View Code Duplication
        if ($first && isset($this->listener[$event])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
87
            array_unshift($this->listener[$event], $listener);
88
        } else {
89 12
            $this->listener[$event][] = $listener;
90
        }
91
92 12
        return $this;
93
    }
94
95
    /**
96
     * 是否存在事件监听
97
     * @access public
98
     * @param string $event 事件名称
99
     * @return bool
100
     */
101 6 View Code Duplication
    public function hasListener(string $event): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
102
    {
103 6
        if (isset($this->bind[$event])) {
104 3
            $event = $this->bind[$event];
105
        }
106
107 6
        return isset($this->listener[$event]);
108
    }
109
110
    /**
111
     * 移除事件监听
112
     * @access public
113
     * @param string $event 事件名称
114
     * @return void
115
     */
116 3 View Code Duplication
    public function remove(string $event): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
117
    {
118 3
        if (isset($this->bind[$event])) {
119 3
            $event = $this->bind[$event];
120
        }
121
122 3
        unset($this->listener[$event]);
123 3
    }
124
125
    /**
126
     * 指定事件别名标识 便于调用
127
     * @access public
128
     * @param array $events 事件别名
129
     * @return $this
130
     */
131 3
    public function bind(array $events)
132
    {
133 3
        $this->bind = array_merge($this->bind, $events);
134
135 3
        return $this;
136
    }
137
138
    /**
139
     * 注册事件订阅者
140
     * @access public
141
     * @param mixed $subscriber 订阅者
142
     * @return $this
143
     */
144 3
    public function subscribe($subscriber)
145
    {
146 3
        $subscribers = (array) $subscriber;
147
148 3
        foreach ($subscribers as $subscriber) {
149 3
            if (is_string($subscriber)) {
150 3
                $subscriber = $this->app->make($subscriber);
151
            }
152
153 3
            if (method_exists($subscriber, 'subscribe')) {
154
                // 手动订阅
155 3
                $subscriber->subscribe($this);
156
            } else {
157
                // 智能订阅
158 1
                $this->observe($subscriber);
159
            }
160
        }
161
162 3
        return $this;
163
    }
164
165
    /**
166
     * 自动注册事件观察者
167
     * @access public
168
     * @param string|object $observer 观察者
169
     * @param null|string   $prefix   事件名前缀
170
     * @return $this
171
     */
172 3
    public function observe($observer, string $prefix = '')
173
    {
174 3
        if (is_string($observer)) {
175 3
            $observer = $this->app->make($observer);
176
        }
177
178 3
        $reflect = new ReflectionClass($observer);
179 3
        $methods = $reflect->getMethods(ReflectionMethod::IS_PUBLIC);
180
181 3
        if (empty($prefix) && $reflect->hasProperty('eventPrefix')) {
182
            $reflectProperty = $reflect->getProperty('eventPrefix');
183
            $reflectProperty->setAccessible(true);
184
            $prefix = $reflectProperty->getValue($observer);
185
        }
186
187 3
        foreach ($methods as $method) {
188 3
            $name = $method->getName();
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
189 3
            if (0 === strpos($name, 'on')) {
190 3
                $this->listen($prefix . substr($name, 2), [$observer, $name]);
191
            }
192
        }
193
194 3
        return $this;
195
    }
196
197
    /**
198
     * 触发事件
199
     * @access public
200
     * @param string|object $event  事件名称
201
     * @param mixed         $params 传入参数
202
     * @param bool          $once   只获取一个有效返回值
203
     * @return mixed
204
     */
205 24
    public function trigger($event, $params = null, bool $once = false)
206
    {
207 24
        if (is_object($event)) {
208 6
            $params = $event;
209 6
            $event  = get_class($event);
210
        }
211
212 24
        if (isset($this->bind[$event])) {
213 3
            $event = $this->bind[$event];
214
        }
215
216 24
        $result    = [];
217 24
        $listeners = $this->listener[$event] ?? [];
218 24
        $listeners = array_unique($listeners, SORT_REGULAR);
219
220 24
        foreach ($listeners as $key => $listener) {
221 12
            $result[$key] = $this->dispatch($listener, $params);
222
223 12
            if (false === $result[$key] || (!is_null($result[$key]) && $once)) {
224 10
                break;
225
            }
226
        }
227
228 24
        return $once ? end($result) : $result;
229
    }
230
231
    /**
232
     * 触发事件(只获取一个有效返回值)
233
     * @param      $event
234
     * @param null $params
235
     * @return mixed
236
     */
237 3
    public function until($event, $params = null)
238
    {
239 3
        return $this->trigger($event, $params, true);
240
    }
241
242
    /**
243
     * 执行事件调度
244
     * @access protected
245
     * @param mixed $event  事件方法
246
     * @param mixed $params 参数
247
     * @return mixed
248
     */
249 12
    protected function dispatch($event, $params = null)
250
    {
251 12
        if (!is_string($event)) {
252 9
            $call = $event;
253 3
        } elseif (strpos($event, '::')) {
254
            $call = $event;
255
        } else {
256 3
            $obj  = $this->app->make($event);
257 3
            $call = [$obj, 'handle'];
258
        }
259
260 12
        return $this->app->invoke($call, [$params]);
261
    }
262
263
}
264