Completed
Push — master ( 60e9af...3a9521 )
by Cees-Jan
01:55
created

Pool::reset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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