Completed
Push — master ( eacba4...44ea6a )
by Cees-Jan
06:48
created

Pool::getLoop()   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 1 Features 1
Metric Value
c 1
b 1
f 1
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
3
namespace WyriHaximus\React\Cake\Orm;
4
5
use Cake\Core\Configure;
6
use React\ChildProcess\Process;
7
use React\EventLoop\LoopInterface;
8
use React\EventLoop\Timer\TimerInterface;
9
use React\Promise\Deferred;
10
use React\Promise\PromiseInterface;
11
use WyriHaximus\React\ChildProcess\Messenger\Messages\Factory;
12
use WyriHaximus\React\ChildProcess\Pool\Factory\Flexible;
13
use WyriHaximus\React\ChildProcess\Pool\PoolInfoInterface;
14
use WyriHaximus\React\ChildProcess\Pool\PoolInterface;
15
use WyriHaximus\React\ChildProcess\Pool\PoolUtilizerInterface;
16
17
/**
18
 * Class Pool
19
 * @package WyriHaximus\React\Cake\Orm
20
 */
21
class Pool implements PoolUtilizerInterface
22
{
23
    /**
24
     * @var LoopInterface
25
     */
26
    protected $loop;
27
28
    /**
29
     * @var PoolInfoInterface
30
     */
31
    protected $pool;
32
33
    /**
34
     * @var Pool
35
     */
36 1
    protected static $instance = null;
37
38 1
    /**
39 1
     * @var boolean
40 1
     */
41 1
    protected static $reset = false;
42 1
43 1
    /**
44
     * @param LoopInterface $loop
45 1
     */
46
    protected function __construct(LoopInterface $loop)
47
    {
48 1
        $this->loop = $loop;
49 1
        Flexible::create(
50 1
            new Process(
51
                Configure::read('WyriHaximus.React.Cake.Orm.Process')
52
            ),
53
            $this->loop,
54
            [
55
                'processOptions' => Configure::read('WyriHaximus.React.Cake.Orm.Line'),
56
            ]
57 2
        )->then(function (PoolInterface $pool) {
58
            $this->pool = $pool;
59 2
        });
60 2
    }
61 1
62
    /**
63
     * @param LoopInterface $loop
64 1
     * @return Pool
65 1
     * @throws \Exception
66
     */
67 2
    public static function getInstance(LoopInterface $loop = null)
68
    {
69
        if (null === self::$instance || self::$reset) {
70 18
            if (null === $loop) {
71
                throw new \Exception('Missing event loop');
72 18
            }
73 18
            self::$instance = new static($loop);
74
            self::$reset = false;
75
        }
76
77
        return self::$instance;
78
    }
79
80
    public static function reset()
81
    {
82
        self::$reset = true;
83
    }
84
85
    /**
86
     * @param $tableName
87
     * @param $function
88
     * @param array $arguments
89
     * @return PromiseInterface
90
     */
91
    public function call($tableName, $function, array $arguments)
92
    {
93
        if ($this->pool instanceof PoolInterface) {
94
            return $this->poolCall($tableName, $function, $arguments);
95
        }
96
97
        return $this->waitForPoolCall($tableName, $function, $arguments);
98
    }
99
100
    /**
101
     * @param $tableName
102
     * @param $function
103
     * @param array $arguments
104
     * @return PromiseInterface
105
     */
106
    protected function poolCall($tableName, $function, array $arguments)
107
    {
108
        return $this->pool->rpc(Factory::rpc('table.call', [
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface WyriHaximus\React\ChildP...\Pool\PoolInfoInterface as the method rpc() does only exist in the following implementations of said interface: WyriHaximus\React\ChildProcess\Pool\Pool\Dummy, WyriHaximus\React\ChildProcess\Pool\Pool\Fixed, WyriHaximus\React\ChildProcess\Pool\Pool\Flexible.

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...
109
            'function' => $function,
110
            'table' => $tableName,
111
            'arguments' => serialize($arguments),
112
        ]))->then(function ($result) {
113 1
            return \React\Promise\resolve($result['result']);
114
        });
115
    }
116
117
    /**
118
     * @param $tableName
119 1
     * @param $function
120
     * @param array $arguments
121
     * @return PromiseInterface
122
     */
123
    protected function waitForPoolCall($tableName, $function, array $arguments)
124 1
    {
125
        $deferred = new Deferred();
126
127
        $this->loop->addPeriodicTimer(
128
            0.1,
129
            function (TimerInterface $timer) use ($deferred, $tableName, $function, $arguments) {
130
                if ($this->pool instanceof PoolInterface) {
131
                    $timer->cancel();
132
                    $deferred->resolve($this->call($tableName, $function, $arguments));
133 1
                }
134
            }
135 1
        );
136 1
137
        return $deferred->promise();
138
    }
139
140
    /**
141
     * @inheritDoc
142
     */
143
    public function info()
144
    {
145
        if ($this->pool instanceof PoolInterface) {
146
            return $this->pool->info();
147
        }
148
149
        return [];
150
    }
151
152
    /**
153
     * @return LoopInterface
154
     */
155
    public function getLoop()
156
    {
157
        return $this->loop;
158
    }
159
160
    /**
161
     * @return PoolInfoInterface
162
     */
163
    public function getPool()
164
    {
165
        return $this->pool;
166
    }
167
}
168