EventsTrait::unbind()   C
last analyzed

Complexity

Conditions 12
Paths 62

Size

Total Lines 50
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 28
nc 62
nop 3
dl 0
loc 50
rs 5.3904
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Webino™ (http://webino.sk)
4
 *
5
 * @link        https://github.com/webino for the canonical source repository
6
 * @copyright   Copyright (c) 2015-2017 Webino, s.r.o. (http://webino.sk)
7
 * @author      Peter Bačinský <[email protected]>
8
 * @license     BSD-3-Clause
9
 */
10
11
namespace WebinoAppLib\Application\Traits;
12
13
use WebinoAppLib\Application;
14
use WebinoAppLib\Exception;
15
use WebinoAppLib\Log;
16
use Zend\EventManager\Event;
17
use Zend\EventManager\EventManagerInterface;
18
use Zend\EventManager\ListenerAggregateInterface;
19
use Zend\Stdlib\CallbackHandler;
20
21
/**
22
 * Trait EventEmitterTrait
23
 * @TODO redesign, remove CallbackHandler support, cause is deprecated by Zend
24
 */
25
trait EventsTrait
26
{
27
    /**
28
     * @var EventManagerInterface
29
     */
30
    private $events;
31
32
    /**
33
     * @var \Zend\EventManager\ListenerAggregateInterface[]
34
     */
35
    protected $listeners = [];
36
37
    /**
38
     * @param string $service
39
     * @return mixed
40
     */
41
    abstract public function get($service);
42
43
    /**
44
     * @param mixed $service
45
     * @param null $factory
46
     * @return $this
47
     */
48
    abstract public function set($service, $factory = null);
49
50
    /**
51
     * @param $service
52
     * @return bool
53
     */
54
    abstract public function has($service);
55
56
    /**
57
     * @param mixed|\WebinoLogLib\Message\MessageInterface $level
58
     * @param mixed ...$args
59
     * @return \Psr\Log\LoggerInterface
60
     */
61
    abstract public function log($level = null, ...$args);
62
63
    /**
64
     * Require service from services into application
65
     *
66
     * @param string $service Service name
67
     * @throws Exception\DomainException Unable to get service
68
     */
69
    abstract protected function requireService($service);
70
71
    /**
72
     * @return EventManagerInterface
73
     */
74
    public function getEvents()
75
    {
76
        if (null === $this->events) {
77
            $this->requireService(Application::EVENTS);
78
        }
79
        return $this->events;
80
    }
81
82
    /**
83
     * @param EventManagerInterface $events
84
     */
85
    protected function setEvents(EventManagerInterface $events)
86
    {
87
        $this->events = $events;
88
    }
89
90
    /**
91
     * Attach a listener to an event
92
     *
93
     * @param string|\Zend\EventManager\ListenerAggregateInterface $event
94
     * @param string|callable|int $callback If string $event provided, expects PHP callback;
95
     * @param int $priority Invocation priority
96
     * @return \Zend\Stdlib\CallbackHandler|mixed CallbackHandler if attaching callable
97
     *                          (to allow later unsubscribe); mixed if attaching aggregate
98
     */
99
    public function bind($event, $callback = null, $priority = 1)
100
    {
101
        $aggregate = (null === $callback) ? $this->resolveListenerAggregate($event) : null;
102
103
        if ($aggregate instanceof ListenerAggregateInterface) {
104
            $this->log(Log\AttachAggregateListener::class, [$aggregate]);
105
            $aggregate->attach($this->getEvents());
106
            return $this;
107
        }
108
109
        unset($aggregate);
110
111
        if ($event instanceof CallbackHandler) {
112
            $callback = $event->getCallback();
113
            $mData = $event->getMetadata();
114
            $this->log(Log\AttachListener::class, [$mData['event'], $callback, $mData['priority']]);
115
            return $this->listeners[] = $this->getEvents()->attach(
116
                $mData['event'],
117
                $callback,
118
                $mData['priority']
119
            );
120
        }
121
122
        $normalized = $this->normalizeCallback($callback);
123
        $this->log(Log\AttachListener::class, [$event, $normalized, $priority]);
124
        $this->listeners[] = $this->getEvents()->attach($event, $normalized, $priority);
0 ignored issues
show
Bug introduced by
It seems like $event defined by parameter $event on line 99 can also be of type object<Zend\EventManager...enerAggregateInterface>; however, Zend\EventManager\EventManagerInterface::attach() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
125
126
        return $this;
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132
    public function unbind($event = null, $callback = null, $priority = null)
133
    {
134
        $aggregate = (null === $callback) ? $this->resolveListenerAggregate($event) : null;
135
136
        if ($aggregate instanceof ListenerAggregateInterface) {
137
            $this->log(Log\DetachAggregateListener::class, [$aggregate]);
138
            $aggregate->detach($this->getEvents());
139
            return $this;
140
        }
141
142
        unset($aggregate);
143
144
        if (is_callable($event)) {
145
            $_callback = $event;
146
            $event = null;
147
148
        } elseif ($callback instanceof CallbackHandler) {
149
            $_callback = $callback->getCallback();
150
151
        } else {
152
            $_callback = $this->normalizeCallback($callback);
153
        }
154
155
        if ($_callback instanceof CallbackHandler) {
156
            $_callback = $_callback->getCallback();
157
        }
158
159
        /** @var \Zend\Stdlib\CallbackHandler $listener */
160
        foreach ($this->listeners as $index => $listener) {
161
            $mData = $listener->getMetadata();
162
163
            if ($event && $event !== $mData['event']) {
164
                continue;
165
            }
166
167
            if ($_callback !== $listener->getCallback()) {
168
                continue;
169
            }
170
171
            if (null !== $priority && $priority !== $mData['priority']) {
172
                continue;
173
            }
174
175
            $this->log(Log\DetachListener::class, [$event, $_callback, $priority]);
176
            $this->getEvents()->detach($listener);
177
            unset($this->listeners[$index]);
178
        }
179
180
        return $this;
181
    }
182
183
    /**
184
     * {@inheritdoc}
185
     */
186
    public function emit($event, $argv = [], $callback = null)
187
    {
188
        $target = $this;
189
        if ($event instanceof Event) {
190
            $event->getTarget() or $event->setTarget($this);
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
191
            $target = $callback;
192
        }
193
194
        $this->log(Log\TriggerEvent::class, [$event]);
195
196
        if (is_callable($argv)) {
197
            return $this->getEvents()->trigger($event, $target, [], $argv);
198
        }
199
200
        return $this->getEvents()->trigger($event, $target, $argv, $callback);
201
    }
202
203
    /**
204
     * Normalize the callback argument
205
     *
206
     * If the callback is a string, register it to the services then return it.
207
     *
208
     * @param mixed $callback
209
     * @return mixed
210
     */
211
    private function normalizeCallback($callback)
212
    {
213
        if (is_string($callback)) {
214
            $this->has($callback) || $this->set($callback);
215
            return $this->get($callback);
216
        }
217
        return $callback;
218
    }
219
220
    /**
221
     * @param mixed $event
222
     * @return mixed|string
223
     */
224
    private function resolveListenerAggregate($event)
225
    {
226
        if (is_string($event) && class_exists($event)) {
227
            if (empty(class_implements($event)[ListenerAggregateInterface::class])) {
228
                return $event;
229
            }
230
            return $this->normalizeCallback($event);
231
        }
232
233
        return $event;
234
    }
235
}
236