Completed
Push — master ( 548a13...2bd7b3 )
by Cees-Jan
04:44
created

Pool::poolCall()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 7

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 10
ccs 0
cts 7
cp 0
rs 9.4285
cc 1
eloc 7
nc 1
nop 3
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
     * @param LoopInterface $loop
35
     */
36
    protected function __construct(LoopInterface $loop)
37
    {
38
        $this->loop = $loop;
39
        Flexible::create(
40
            new Process(
41
                Configure::read('WyriHaximus.React.Cake.Orm.Process')
42
            ),
43
            $this->loop,
44
            [
45
                'processOptions' => Configure::read('WyriHaximus.React.Cake.Orm.Line'),
46
            ]
47
        )->then(function (PoolInterface $pool) {
48
            $this->pool = $pool;
49
        });
50
    }
51
52
    /**
53
     * @param LoopInterface $loop
54
     * @return Pool
55
     * @throws \Exception
56
     */
57
    public static function getInstance(LoopInterface $loop = null)
58
    {
59
        static $instance = null;
60
        if (null === $instance) {
61
            if (null === $loop) {
62
                throw new \Exception('Missing event loop');
63
            }
64
            $instance = new static($loop);
65
        }
66
67
        return $instance;
68
    }
69
70
    /**
71
     * @param $tableName
72
     * @param $function
73
     * @param array $arguments
74
     * @return PromiseInterface
75
     */
76
    public function call($tableName, $function, array $arguments)
77
    {
78
        if ($this->pool instanceof PoolInterface) {
79
            return $this->poolCall($tableName, $function, $arguments);
80
        }
81
82
        return $this->waitForPoolCall($tableName, $function, $arguments);
83
    }
84
85
    /**
86
     * @param $tableName
87
     * @param $function
88
     * @param array $arguments
89
     * @return PromiseInterface
90
     */
91
    protected function poolCall($tableName, $function, array $arguments)
92
    {
93
        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...
94
            'function' => $function,
95
            'table' => $tableName,
96
            'arguments' => serialize($arguments),
97
        ]))->then(function ($result) {
98
            return \React\Promise\resolve($result['result']);
99
        });
100
    }
101
102
    /**
103
     * @param $tableName
104
     * @param $function
105
     * @param array $arguments
106
     * @return PromiseInterface
107
     */
108
    protected function waitForPoolCall($tableName, $function, array $arguments)
109
    {
110
        $deferred = new Deferred();
111
112
        $this->loop->addPeriodicTimer(
113
            0.1,
114
            function (TimerInterface $timer) use ($deferred, $tableName, $function, $arguments) {
115
                if ($this->pool instanceof PoolInterface) {
116
                    $timer->cancel();
117
                    $deferred->resolve($this->call($tableName, $function, $arguments));
118
                }
119
            }
120
        );
121
122
        return $deferred->promise();
123
    }
124
125
    /**
126
     * @inheritDoc
127
     */
128
    public function info()
129
    {
130
        if ($this->pool instanceof PoolInterface) {
131
            return $this->pool->info();
132
        }
133
134
        return [];
135
    }
136
}
137