Completed
Pull Request — master (#11)
by Clément
04:00 queued 33s
created

DelayEventDispatcher::getListeners()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
3
namespace Itkg\DelayEventBundle\EventDispatcher;
4
5
use Itkg\DelayEventBundle\Event\DelayableEvent;
6
use Itkg\DelayEventBundle\Event\DelayableEvents;
7
use Symfony\Component\EventDispatcher\Event;
8
use Itkg\DelayEventBundle\Model\Event as DelayEvent;
9
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
10
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
11
12
/**
13
 * class DelayEventDispatcher 
14
 */
15
class DelayEventDispatcher implements EventDispatcherInterface
16
{
17
    /**
18
     * @var array
19
     */
20
    private $eligibleEventNames = array();
21
22
    /**
23
     * @var EventDispatcherInterface
24
     */
25
    private $dispatcher;
26
27
    /**
28
     * @param EventDispatcherInterface $dispatcher
29
     * @param array                    $eligibleEventNames
30
     */
31
    public function __construct(EventDispatcherInterface $dispatcher, array $eligibleEventNames)
32
    {
33
        $this->dispatcher = $dispatcher;
34
        $this->eligibleEventNames = $eligibleEventNames;
35
    }
36
37
    /**
38
     * {@inheritDoc}
39
     */
40
    public function dispatch($eventName, Event $event = null)
41
    {
42
        if ($this->isEventEligible($eventName, $event)) {
0 ignored issues
show
Bug introduced by
It seems like $event defined by parameter $event on line 40 can be null; however, Itkg\DelayEventBundle\Ev...cher::isEventEligible() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
43
            $event->setOriginalName($eventName);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\EventDispatcher\Event as the method setOriginalName() does only exist in the following sub-classes of Symfony\Component\EventDispatcher\Event: Itkg\DelayEventBundle\Document\Event, Itkg\DelayEventBundle\Model\Event. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
44
            // Override event name to dispatch an delayable event
45
            $event = new DelayableEvent($event);
0 ignored issues
show
Documentation introduced by
$event is of type null|object<Symfony\Comp...\EventDispatcher\Event>, but the function expects a object<Itkg\DelayEventBundle\Model\Event>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
46
            $eventName = DelayableEvents::DELAY;
47
        }
48
49
        $this->dispatcher->dispatch($eventName, $event);
50
51
        return $event;
52
    }
53
54
    /**
55
     * {@inheritDoc}
56
     */
57
    public function addListener($eventName, $listener, $priority = 0)
58
    {
59
        $this->dispatcher->addListener($eventName, $listener, $priority);
60
    }
61
62
    /**
63
     * {@inheritDoc}
64
     */
65
    public function addSubscriber(EventSubscriberInterface $subscriber)
66
    {
67
        $this->dispatcher->addSubscriber($subscriber);
68
    }
69
70
    /**
71
     * {@inheritDoc}
72
     */
73
    public function removeListener($eventName, $listener)
74
    {
75
        $this->dispatcher->removeListener($eventName, $listener);
76
    }
77
78
    /**
79
     * {@inheritDoc}
80
     */
81
    public function removeSubscriber(EventSubscriberInterface $subscriber)
82
    {
83
        $this->dispatcher->removeSubscriber($subscriber);
84
    }
85
86
    /**
87
     * {@inheritDoc}
88
     */
89
    public function getListeners($eventName = null)
90
    {
91
        return $this->dispatcher->getListeners($eventName);
92
    }
93
94
    /**
95
     * {@inheritDoc}
96
     */
97
    public function hasListeners($eventName = null)
98
    {
99
        return $this->dispatcher->hasListeners($eventName);
100
    }
101
102
    /**
103
     * Proxies all method calls to the original event dispatcher.
104
     *
105
     * @param string $method    The method name
106
     * @param array  $arguments The method arguments
107
     *
108
     * @return mixed
109
     */
110
    public function __call($method, $arguments)
111
    {
112
        return call_user_func_array(array($this->dispatcher, $method), $arguments);
113
    }
114
115
    /**
116
     * @param String $eventName
117
     * @param Event $event
118
     *
119
     * @return bool
120
     */
121
    private function isEventEligible($eventName, $event)
122
    {
123
        return $event instanceof DelayEvent && $event->isDelayed() && in_array($eventName, $this->eligibleEventNames);
124
    }
125
}
126