Completed
Pull Request — master (#5)
by Cees-Jan
11:35 queued 08:19
created

Pool::call()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 0
cts 4
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 4
crap 6
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 4
    protected function __construct(LoopInterface $loop, array $config = [])
49
    {
50 4
        $this->loop = $loop;
51
52 4
        Flexible::create(
53 4
            new Process(
54 4
                Configure::read('WyriHaximus.React.Cake.Orm.Process')
55
            ),
56 4
            $this->loop,
57 4
            $this->applyConfig($config)
58
        )->then(function (PoolInterface $pool) {
59 4
            $this->pool = $pool;
60 4
        });
61 4
    }
62
63
    /**
64
     * @param array $config
65
     * @return array
66
     */
67 4
    protected function applyConfig(array $config)
68
    {
69 4
        if (!isset($config['processOptions'])) {
70 4
            $config['processOptions'] = Configure::read('WyriHaximus.React.Cake.Orm.Line');
71
        }
72
73 4
        if (!isset($config[Options::TTL])) {
74 4
            $config[Options::TTL] = Configure::read('WyriHaximus.React.Cake.Orm.TTL');
75
        }
76
77 4
        return $config;
78
    }
79
80
    /**
81
     * @param LoopInterface|null $loop
82
     * @param array $config
83
     * @return Pool
84
     * @throws \Exception
85
     */
86 4
    public static function getInstance(LoopInterface $loop = null, array $config = [])
87
    {
88 4
        if (null === self::$instance || self::$reset) {
89 4
            if (null === $loop) {
90
                throw new \Exception('Missing event loop');
91
            }
92 4
            self::$instance = new static($loop, $config);
93 4
            self::$reset = false;
94
        }
95
96 4
        return self::$instance;
97
    }
98
99 25
    public static function reset()
100
    {
101 25
        self::$reset = true;
102 25
    }
103
104
    /**
105
     * @param $className
106
     * @param $tableName
107
     * @param $function
108
     * @param array $arguments
109
     * @return PromiseInterface
110
     */
111
    public function call($className, $tableName, $function, array $arguments)
112
    {
113
        if ($this->pool instanceof PoolInterface) {
114
            return $this->poolCall($className, $tableName, $function, $arguments);
115
        }
116
117
        return $this->waitForPoolCall($className, $tableName, $function, $arguments);
0 ignored issues
show
Unused Code introduced by
The call to Pool::waitForPoolCall() has too many arguments starting with $arguments.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
118
    }
119
120
    /**
121
     * @param $className
122
     * @param $tableName
123
     * @param $function
124
     * @param array $arguments
125
     * @return PromiseInterface
126
     */
127
    protected function poolCall($className, $tableName, $function, array $arguments)
128
    {
129
        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...
130
            'className' => $className,
131
            'function' => $function,
132
            'table' => $tableName,
133
            'arguments' => serialize($arguments),
134
        ]))->then(function ($result) {
135
            return \React\Promise\resolve($result['result']);
136
        });
137
    }
138
139
    /**
140
     * @param $tableName
141
     * @param $function
142
     * @param array $arguments
143
     * @return PromiseInterface
144
     */
145
    protected function waitForPoolCall($tableName, $function, array $arguments)
146
    {
147
        $deferred = new Deferred();
148
149
        $this->loop->addPeriodicTimer(
150
            0.1,
151
            function (TimerInterface $timer) use ($deferred, $tableName, $function, $arguments) {
152
                if ($this->pool instanceof PoolInterface) {
153
                    $timer->cancel();
154
                    $deferred->resolve($this->call($tableName, $function, $arguments));
0 ignored issues
show
Bug introduced by
The call to call() misses a required argument $arguments.

This check looks for function calls that miss required arguments.

Loading history...
155
                }
156
            }
157
        );
158
159
        return $deferred->promise();
160
    }
161
162
    /**
163
     * @inheritDoc
164
     */
165
    public function info()
166
    {
167
        if ($this->pool instanceof PoolInterface) {
168
            return $this->pool->info();
169
        }
170
171
        return [];
172
    }
173
174
    /**
175
     * @return LoopInterface
176
     */
177 1
    public function getLoop()
178
    {
179 1
        return $this->loop;
180
    }
181
182
    /**
183
     * @return PoolInfoInterface
184
     */
185 1
    public function getPool()
186
    {
187 1
        return $this->pool;
188
    }
189
}
190