LoopDecorator::addTimer()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 12
cts 12
cp 1
rs 9.7666
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 1
1
<?php declare(strict_types=1);
2
3
namespace WyriHaximus\React\Inspector;
4
5
use Evenement\EventEmitterInterface;
6
use Evenement\EventEmitterTrait;
7
use React\EventLoop\LoopInterface;
8
use React\EventLoop\Timer\TimerInterface;
9
10
class LoopDecorator implements LoopInterface, EventEmitterInterface
11
{
12
    use EventEmitterTrait;
13
14
    /**
15
     * @var LoopInterface
16
     */
17
    protected $loop;
18
19
    /**
20
     * @param LoopInterface $loop
21
     */
22 24
    public function __construct(LoopInterface $loop)
23
    {
24 24
        $this->loop = $loop;
25 24
    }
26
27
    /**
28
     * Register a listener to be notified when a stream is ready to read.
29
     *
30
     * @param stream   $stream   The PHP stream resource to check.
31
     * @param callable $listener Invoked when the stream is ready.
32
     */
33 3
    public function addReadStream($stream, callable $listener)
34
    {
35 3
        $this->emit('addReadStream', [$stream, $listener]);
36
        $this->loop->addReadStream($stream, function ($stream) use ($listener) {
37 1
            $this->emit('readStreamTick', [$stream, $listener]);
38 1
            $listener($stream, $this);
39 3
        });
40 3
    }
41
42
    /**
43
     * Register a listener to be notified when a stream is ready to write.
44
     *
45
     * @param stream   $stream   The PHP stream resource to check.
46
     * @param callable $listener Invoked when the stream is ready.
47
     */
48 3
    public function addWriteStream($stream, callable $listener)
49
    {
50 3
        $this->emit('addWriteStream', [$stream, $listener]);
51
        $this->loop->addWriteStream($stream, function ($stream) use ($listener) {
52 1
            $this->emit('writeStreamTick', [$stream, $listener]);
53 1
            $listener($stream, $this);
54 3
        });
55 3
    }
56
57
    /**
58
     * Remove the read event listener for the given stream.
59
     *
60
     * @param stream $stream The PHP stream resource.
61
     */
62 3
    public function removeReadStream($stream)
63
    {
64 3
        $this->emit('removeReadStream', [$stream]);
65 3
        $this->loop->removeReadStream($stream);
66 3
    }
67
68
    /**
69
     * Remove the write event listener for the given stream.
70
     *
71
     * @param stream $stream The PHP stream resource.
72
     */
73 3
    public function removeWriteStream($stream)
74
    {
75 3
        $this->emit('removeWriteStream', [$stream]);
76 3
        $this->loop->removeWriteStream($stream);
77 3
    }
78
79
    /**
80
     * Remove all listeners for the given stream.
81
     *
82
     * @param stream $stream The PHP stream resource.
83
     */
84 1
    public function removeStream($stream)
85
    {
86 1
        $this->emit('removeStream', [$stream]);
87 1
        $this->loop->removeStream($stream);
88 1
    }
89
90
    /**
91
     * Enqueue a callback to be invoked once after the given interval.
92
     *
93
     * The execution order of timers scheduled to execute at the same time is
94
     * not guaranteed.
95
     *
96
     * @param numeric  $interval The number of seconds to wait before execution.
97
     * @param callable $callback The callback to invoke.
98
     *
99
     * @return TimerInterface
100
     */
101 2
    public function addTimer($interval, callable $callback)
102
    {
103 2
        $loopTimer = null;
104
        $wrapper = function () use (&$loopTimer, $callback, $interval) {
105 2
            $this->emit('timerTick', [$interval, $callback, $loopTimer]);
106 2
            $callback($loopTimer);
107 2
        };
108 2
        $loopTimer = $this->loop->addTimer(
109 2
            $interval,
110 2
            $wrapper
111 2
        );
112
        $this->emit('addTimer', [$interval, $callback, $loopTimer]);
113 2
114 2
        return $loopTimer;
115 2
    }
116 2
117
    /**
118
     * Enqueue a callback to be invoked repeatedly after the given interval.
119
     *
120
     * The execution order of timers scheduled to execute at the same time is
121
     * not guaranteed.
122
     *
123
     * @param numeric  $interval The number of seconds to wait before execution.
124
     * @param callable $callback The callback to invoke.
125
     *
126
     * @return TimerInterface
127
     */
128
    public function addPeriodicTimer($interval, callable $callback)
129
    {
130 2
        $loopTimer = $this->loop->addPeriodicTimer(
131
            $interval,
132 2
            function () use (&$loopTimer, $callback, $interval) {
133 2
                $this->emit('periodicTimerTick', [$interval, $callback, $loopTimer]);
134 2
                $callback($loopTimer);
135 2
            }
136
        );
137 2
        $this->emit('addPeriodicTimer', [$interval, $callback, $loopTimer]);
138 2
139 2
        return $loopTimer;
140 2
    }
141 2
142 2
    /**
143 2
     * Cancel a pending timer.
144
     *
145
     * @param TimerInterface $timer The timer to cancel.
146
     */
147
    public function cancelTimer(TimerInterface $timer)
148
    {
149
        $this->emit('cancelTimer', [$timer]);
150
151 4
        return $this->loop->cancelTimer($timer);
152
    }
153 4
154 4
    /**
155
     * Check if a given timer is active.
156
     *
157
     * @param TimerInterface $timer The timer to check.
158
     *
159
     * @return bool True if the timer is still enqueued for execution.
160
     */
161
    public function isTimerActive(TimerInterface $timer)
162
    {
163
        return $this->loop->isTimerActive($timer);
164 2
    }
165
166 2
    /**
167
     * Schedule a callback to be invoked on a future tick of the event loop.
168
     *
169
     * Callbacks are guaranteed to be executed in the order they are enqueued.
170
     *
171
     * @param callable $listener The callback to invoke.
172
     */
173
    public function futureTick(callable $listener)
174
    {
175
        $this->emit('futureTick', [$listener]);
176
177 2
        return $this->loop->futureTick(function () use ($listener) {
178
            $this->emit('futureTickTick', [$listener]);
179 2
            $listener($this);
180
        });
181 2
    }
182 2
183 2
    /**
184
     * Perform a single iteration of the event loop.
185
     */
186
    public function tick()
187
    {
188
        $this->emit('tickStart');
189
        $this->loop->tick();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface React\EventLoop\LoopInterface as the method tick() does only exist in the following implementations of said interface: WyriHaximus\React\Inspector\LoopDecorator.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
190
        $this->emit('tickDone');
191
    }
192
193 4
    /**
194
     * Run the event loop until there are no more tasks to perform.
195 4
     */
196 4
    public function run()
197 4
    {
198 4
        $this->emit('runStart');
199 4
        $this->loop->run();
200
        $this->emit('runDone');
201
    }
202
203
    /**
204
     * Instruct a running event loop to stop.
205 1
     */
206
    public function stop()
207 1
    {
208 1
        $this->emit('stopStart');
209 1
        $this->loop->stop();
210 1
        $this->emit('stopDone');
211
    }
212
}
213