Completed
Push — master ( ba5abe...d52dbf )
by Cees-Jan
02:14
created

Pool::applyConfig()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
ccs 8
cts 8
cp 1
rs 9.4285
cc 3
eloc 6
nc 4
nop 1
crap 3
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\Options;
14
use WyriHaximus\React\ChildProcess\Pool\PoolInfoInterface;
15
use WyriHaximus\React\ChildProcess\Pool\PoolInterface;
16
use WyriHaximus\React\ChildProcess\Pool\PoolUtilizerInterface;
17
18
/**
19
 * Class Pool
20
 * @package WyriHaximus\React\Cake\Orm
21
 */
22
class Pool implements PoolUtilizerInterface
23
{
24
    /**
25
     * @var LoopInterface
26
     */
27
    protected $loop;
28
29
    /**
30
     * @var PoolInfoInterface
31
     */
32
    protected $pool;
33
34
    /**
35
     * @var Pool
36
     */
37
    protected static $instance = null;
38
39
    /**
40
     * @var boolean
41
     */
42
    protected static $reset = false;
43
44
    /**
45
     * @param LoopInterface $loop
46
     * @param array $config
47
     */
48 6
    protected function __construct(LoopInterface $loop, array $config = [])
49
    {
50 6
        $this->loop = $loop;
51
52 6
        Flexible::create(
53 6
            new Process(
54 6
                Configure::read('WyriHaximus.React.Cake.Orm.Process')
55 6
            ),
56 6
            $this->loop,
57 6
            $this->applyConfig($config)
58
        )->then(function (PoolInterface $pool) {
59 6
            $this->pool = $pool;
60 6
        });
61 6
    }
62
63
    /**
64
     * @param array $config
65
     * @return array
66
     */
67 6
    protected function applyConfig(array $config)
68
    {
69 6
        if (!isset($config['processOptions'])) {
70 6
            $config['processOptions'] = Configure::read('WyriHaximus.React.Cake.Orm.Line');
71 6
        }
72
73 6
        if (!isset($config[Options::TTL])) {
74 6
            $config[Options::TTL] = Configure::read('WyriHaximus.React.Cake.Orm.TTL');
75 6
        }
76
77 6
        return $config;
78
    }
79
80
    /**
81
     * @param LoopInterface|null $loop
82
     * @param array $config
83
     * @return Pool
84
     * @throws \Exception
85
     */
86 6
    public static function getInstance(LoopInterface $loop = null, array $config = [])
87
    {
88 6
        if (null === self::$instance || self::$reset) {
89 6
            if (null === $loop) {
90
                throw new \Exception('Missing event loop');
91
            }
92 6
            self::$instance = new static($loop, $config);
93 6
            self::$reset = false;
94 6
        }
95
96 6
        return self::$instance;
97
    }
98
99 23
    public static function reset()
100
    {
101 23
        self::$reset = true;
102 23
    }
103
104
    /**
105
     * @param $tableName
106
     * @param $function
107
     * @param array $arguments
108
     * @return PromiseInterface
109
     */
110
    public function call($tableName, $function, array $arguments)
111
    {
112
        if ($this->pool instanceof PoolInterface) {
113
            return $this->poolCall($tableName, $function, $arguments);
114
        }
115
116
        return $this->waitForPoolCall($tableName, $function, $arguments);
117
    }
118
119
    /**
120
     * @param $tableName
121
     * @param $function
122
     * @param array $arguments
123
     * @return PromiseInterface
124
     */
125
    protected function poolCall($tableName, $function, array $arguments)
126
    {
127
        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...
128
            'function' => $function,
129
            'table' => $tableName,
130
            'arguments' => serialize($arguments),
131
        ]))->then(function ($result) {
132
            return \React\Promise\resolve($result['result']);
133
        });
134
    }
135
136
    /**
137
     * @param $tableName
138
     * @param $function
139
     * @param array $arguments
140
     * @return PromiseInterface
141
     */
142
    protected function waitForPoolCall($tableName, $function, array $arguments)
143
    {
144
        $deferred = new Deferred();
145
146
        $this->loop->addPeriodicTimer(
147
            0.1,
148
            function (TimerInterface $timer) use ($deferred, $tableName, $function, $arguments) {
149
                if ($this->pool instanceof PoolInterface) {
150
                    $timer->cancel();
151
                    $deferred->resolve($this->call($tableName, $function, $arguments));
152
                }
153
            }
154
        );
155
156
        return $deferred->promise();
157
    }
158
159
    /**
160
     * @inheritDoc
161
     */
162 1
    public function info()
163
    {
164 1
        if ($this->pool instanceof PoolInterface) {
165 1
            return $this->pool->info();
166
        }
167
168
        return [];
169
    }
170
171
    /**
172
     * @return LoopInterface
173
     */
174 1
    public function getLoop()
175
    {
176 1
        return $this->loop;
177
    }
178
179
    /**
180
     * @return PoolInfoInterface
181
     */
182 1
    public function getPool()
183
    {
184 1
        return $this->pool;
185
    }
186
}
187