Event   A
last analyzed

Complexity

Total Complexity 37

Size/Duplication

Total Lines 248
Duplicated Lines 0 %

Test Coverage

Coverage 88.61%

Importance

Changes 8
Bugs 0 Features 0
Metric Value
eloc 79
c 8
b 0
f 0
dl 0
loc 248
ccs 70
cts 79
cp 0.8861
rs 9.44
wmc 37

11 Methods

Rating   Name   Duplication   Size   Complexity  
A listen() 0 13 4
A hasListener() 0 7 2
A listenEvents() 0 11 3
A subscribe() 0 19 4
A bind() 0 5 1
A __construct() 0 3 1
A remove() 0 7 2
A observe() 0 23 6
A until() 0 3 1
A dispatch() 0 12 3
B trigger() 0 32 10
1
<?php
2
// +----------------------------------------------------------------------
3
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
4
// +----------------------------------------------------------------------
5
// | Copyright (c) 2006~2021 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
        'LogRecord'   => event\LogRecord::class,
41
    ];
42
43
    /**
44
     * 应用对象
45
     * @var App
46
     */
47
    protected $app;
48
49 27
    public function __construct(App $app)
50
    {
51 27
        $this->app = $app;
52 27
    }
53
54
    /**
55
     * 批量注册事件监听
56
     * @access public
57
     * @param array $events 事件定义
58
     * @return $this
59
     */
60 3
    public function listenEvents(array $events)
61
    {
62 3
        foreach ($events as $event => $listeners) {
63 3
            if (isset($this->bind[$event])) {
64
                $event = $this->bind[$event];
65
            }
66
67 3
            $this->listener[$event] = array_merge($this->listener[$event] ?? [], $listeners);
68
        }
69
70 3
        return $this;
71
    }
72
73
    /**
74
     * 注册事件监听
75
     * @access public
76
     * @param string $event    事件名称
77
     * @param mixed  $listener 监听操作(或者类名)
78
     * @param bool   $first    是否优先执行
79
     * @return $this
80
     */
81 12
    public function listen(string $event, $listener, bool $first = false)
82
    {
83 12
        if (isset($this->bind[$event])) {
84 6
            $event = $this->bind[$event];
85
        }
86
87 12
        if ($first && isset($this->listener[$event])) {
88
            array_unshift($this->listener[$event], $listener);
89
        } else {
90 12
            $this->listener[$event][] = $listener;
91
        }
92
93 12
        return $this;
94
    }
95
96
    /**
97
     * 是否存在事件监听
98
     * @access public
99
     * @param string $event 事件名称
100
     * @return bool
101
     */
102 6
    public function hasListener(string $event): bool
103
    {
104 6
        if (isset($this->bind[$event])) {
105 3
            $event = $this->bind[$event];
106
        }
107
108 6
        return isset($this->listener[$event]);
109
    }
110
111
    /**
112
     * 移除事件监听
113
     * @access public
114
     * @param string $event 事件名称
115
     * @return void
116
     */
117 3
    public function remove(string $event): void
118
    {
119 3
        if (isset($this->bind[$event])) {
120 3
            $event = $this->bind[$event];
121
        }
122
123 3
        unset($this->listener[$event]);
124 3
    }
125
126
    /**
127
     * 指定事件别名标识 便于调用
128
     * @access public
129
     * @param array $events 事件别名
130
     * @return $this
131
     */
132 3
    public function bind(array $events)
133
    {
134 3
        $this->bind = array_merge($this->bind, $events);
135
136 3
        return $this;
137
    }
138
139
    /**
140
     * 注册事件订阅者
141
     * @access public
142
     * @param mixed $subscriber 订阅者
143
     * @return $this
144
     */
145 3
    public function subscribe($subscriber)
146
    {
147 3
        $subscribers = (array) $subscriber;
148
149 3
        foreach ($subscribers as $subscriber) {
0 ignored issues
show
introduced by
$subscriber is overwriting one of the parameters of this function.
Loading history...
150 3
            if (is_string($subscriber)) {
151 3
                $subscriber = $this->app->make($subscriber);
152
            }
153
154 3
            if (method_exists($subscriber, 'subscribe')) {
155
                // 手动订阅
156 3
                $subscriber->subscribe($this);
157
            } else {
158
                // 智能订阅
159 1
                $this->observe($subscriber);
160
            }
161
        }
162
163 3
        return $this;
164
    }
165
166
    /**
167
     * 自动注册事件观察者
168
     * @access public
169
     * @param string|object $observer 观察者
170
     * @param null|string   $prefix   事件名前缀
171
     * @return $this
172
     */
173 3
    public function observe($observer, string $prefix = '')
174
    {
175 3
        if (is_string($observer)) {
176 3
            $observer = $this->app->make($observer);
177
        }
178
179 3
        $reflect = new ReflectionClass($observer);
180 3
        $methods = $reflect->getMethods(ReflectionMethod::IS_PUBLIC);
181
182 3
        if (empty($prefix) && $reflect->hasProperty('eventPrefix')) {
183
            $reflectProperty = $reflect->getProperty('eventPrefix');
184
            $reflectProperty->setAccessible(true);
185
            $prefix = $reflectProperty->getValue($observer);
186
        }
187
188 3
        foreach ($methods as $method) {
189 3
            $name = $method->getName();
190 3
            if (0 === strpos($name, 'on')) {
191 3
                $this->listen($prefix . substr($name, 2), [$observer, $name]);
192
            }
193
        }
194
195 3
        return $this;
196
    }
197
198
    /**
199
     * 触发事件
200
     * @access public
201
     * @param string|object $event  事件名称
202
     * @param mixed         $params 传入参数
203
     * @param bool          $once   只获取一个有效返回值
204
     * @return mixed
205
     */
206 24
    public function trigger($event, $params = null, bool $once = false)
207
    {
208 24
        if (is_object($event)) {
209 6
            $params = $event;
210 6
            $event  = get_class($event);
211
        }
212
213 24
        if (isset($this->bind[$event])) {
214 3
            $event = $this->bind[$event];
215
        }
216
217 24
        $result    = [];
218 24
        $listeners = $this->listener[$event] ?? [];
219
220 24
        if (strpos($event, '.')) {
221
            [$prefix, $event] = explode('.', $event, 2);
222
            if (isset($this->listener[$prefix . '.*'])) {
223
                $listeners = array_merge($listeners, $this->listener[$prefix . '.*']);
224
            }
225
        }
226
227 24
        $listeners = array_unique($listeners, SORT_REGULAR);
228
229 24
        foreach ($listeners as $key => $listener) {
230 12
            $result[$key] = $this->dispatch($listener, $params);
231
232 12
            if (false === $result[$key] || (!is_null($result[$key]) && $once)) {
233 10
                break;
234
            }
235
        }
236
237 24
        return $once ? end($result) : $result;
238
    }
239
240
    /**
241
     * 触发事件(只获取一个有效返回值)
242
     * @param      $event
243
     * @param null $params
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $params is correct as it would always require null to be passed?
Loading history...
244
     * @return mixed
245
     */
246 3
    public function until($event, $params = null)
247
    {
248 3
        return $this->trigger($event, $params, true);
249
    }
250
251
    /**
252
     * 执行事件调度
253
     * @access protected
254
     * @param mixed $event  事件方法
255
     * @param mixed $params 参数
256
     * @return mixed
257
     */
258 12
    protected function dispatch($event, $params = null)
259
    {
260 12
        if (!is_string($event)) {
261 9
            $call = $event;
262 3
        } elseif (strpos($event, '::')) {
263
            $call = $event;
264
        } else {
265 3
            $obj  = $this->app->make($event);
266 3
            $call = [$obj, 'handle'];
267
        }
268
269 12
        return $this->app->invoke($call, [$params]);
270
    }
271
272
}
273