Completed
Push — master ( 5c965c...c70f50 )
by Vuong
02:09
created

Async   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 203
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Test Coverage

Coverage 85.25%

Importance

Changes 0
Metric Value
wmc 17
lcom 2
cbo 5
dl 0
loc 203
ccs 52
cts 61
cp 0.8525
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
B run() 0 32 6
A success() 0 9 1
A error() 0 9 1
A timeout() 0 6 1
A wait() 0 4 1
A setConcurrency() 0 4 1
A setTimeout() 0 4 1
A setSleepTimeWait() 0 4 1
A setAutoload() 0 4 1
A setAppConfigFile() 0 4 1
A createProcess() 0 4 1
1
<?php
2
/**
3
 * @link https://github.com/vuongxuongminh/yii2-async
4
 * @copyright Copyright (c) 2019 Vuong Xuong Minh
5
 * @license [New BSD License](http://www.opensource.org/licenses/bsd-license.php)
6
 */
7
8
namespace vxm\async;
9
10
use Yii;
11
use Closure;
12
use Throwable;
13
14
use yii\base\Component;
15
16
use Spatie\Async\Pool;
17
use Spatie\Async\Process\Runnable;
18
19
use vxm\async\event\Event;
20
use vxm\async\event\ErrorEvent;
21
use vxm\async\event\SuccessEvent;
22
use vxm\async\runtime\ParentRuntime;
23
24
/**
25
 * Support run code async. To use it, you just config it to your application components in configure file:
26
 *
27
 * ```php
28
 * 'components' => [
29
 *      'async' => 'vxm\async\Async'
30
 * ]
31
 *
32
 * ```
33
 *
34
 * And after that you can run an async code:
35
 *
36
 * ```php
37
 *
38
 * Yii::$app->async->run(function () {
39
 *
40
 *      sleep(15);
41
 * });
42
 *
43
 * ```
44
 *
45
 * If you want to wait until task done, you just to call [[wait]].
46
 *
47
 * ```php
48
 *
49
 * Yii::$app->async->run(function () {
50
 *
51
 *      sleep(15);
52
 * })->wait();
53
 *
54
 * ```
55
 *
56
 * Run multi tasks:
57
 *
58
 * ```php
59
 *
60
 * Yii::$app->async->run(function () {
61
 *
62
 *      sleep(15);
63
 * });
64
 *
65
 * Yii::$app->async->run(function () {
66
 *
67
 *      sleep(15);
68
 * });
69
 *
70
 * Yii::$app->async->wait(); // sleep 30s
71
 * ```
72
 *
73
 * @property string $autoload path of autoload file for runtime task execute environment.
74
 * @property int $sleepTimeWait time to sleep on wait tasks execute.
75
 * @property int $concurrency tasks executable.
76
 * @property int $timeout of task executable.
77
 *
78
 * @author Vuong Minh <[email protected]>
79
 * @since 1.0.0
80
 */
81
class Async extends Component
82
{
83
84
    /**
85
     * @event SuccessEvent an event that is triggered when task done.
86
     */
87
    const EVENT_SUCCESS = 'success';
88
89
    /**
90
     * @event ErrorEvent an event that is triggered when task error.
91
     */
92
    const EVENT_ERROR = 'error';
93
94
    /**
95
     * @event \yii\base\Event an event that is triggered when task timeout.
96
     */
97
    const EVENT_TIMEOUT = 'timeout';
98
99
    /**
100
     * @var Pool handling tasks.
101
     */
102
    protected $pool;
103
104
    /**
105
     * @var string a file config of an application run in child process.
106
     * Note: If an autoload file's set, it will not affect, you need to invoke an app in your autoload if needed.
107
     */
108
    protected $appConfigFile;
109
110
    /**
111
     * Async constructor.
112
     *
113
     * @param array $config
114
     * @throws \yii\base\InvalidConfigException
115
     */
116 8
    public function __construct($config = [])
117
    {
118 8
        $pool = $this->pool = Yii::createObject(Pool::class);
119 8
        $pool->autoload(__DIR__ . '/runtime/AutoloadRuntime.php');
120
121 8
        parent::__construct($config);
122 8
    }
123
124
    /**
125
     * Execute async task.
126
     *
127
     * @param callable|\Spatie\Async\Task|Task $callable need to execute.
128
     * @param array $callbacks event. Have key is an event name, value is a callable triggered when event happen,
129
     * have three events `error`, `success`, `timeout`.
130
     * @return static
131
     */
132 8
    public function run($callable, array $callbacks = []): self
133
    {
134 8
        $process = $this->createProcess($callable);
135
136 8
        foreach ($callbacks as $name => $callback) {
137
138 4
            switch (strtolower($name)) {
139 4
                case 'success':
140 2
                    $process->then(Closure::fromCallable($callback));
141 2
                    break;
142 2
                case 'error':
143 1
                case 'catch':
144 1
                    $process->catch(Closure::fromCallable($callback));
145 1
                    break;
146 1
                case 'timeout':
147 1
                    $process->timeout(Closure::fromCallable($callback));
148 1
                    break;
149
                default:
150 4
                    break;
151
            }
152
153
        }
154
155
        $process
156 8
            ->then(Closure::fromCallable([$this, 'success']))
157 8
            ->catch(Closure::fromCallable([$this, 'error']))
158 8
            ->timeout(Closure::fromCallable([$this, 'timeout']));
159
160 8
        $this->pool->add($process);
0 ignored issues
show
Documentation introduced by
$process is of type object<Spatie\Async\Process\Runnable>, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
161
162 8
        return $this;
163
    }
164
165
    /**
166
     * This method is called when task executed success.
167
     * When overriding this method, make sure you call the parent implementation to ensure the
168
     * event is triggered.
169
     *
170
     * @param mixed $output of task executed.
171
     * @throws \yii\base\InvalidConfigException
172
     */
173 3
    public function success($output): void
174
    {
175 3
        $event = Yii::createObject([
176 3
            'class' => SuccessEvent::class,
177 3
            'output' => $output
178
        ]);
179
180 3
        $this->trigger(self::EVENT_SUCCESS, $event);
181 3
    }
182
183
    /**
184
     * This method is called when task executed error.
185
     * When overriding this method, make sure you call the parent implementation to ensure the
186
     * event is triggered.
187
     *
188
     * @param Throwable $throwable when executing task.
189
     * @throws \yii\base\InvalidConfigException
190
     */
191 1
    public function error(Throwable $throwable): void
192
    {
193 1
        $event = Yii::createObject([
194 1
            'class' => ErrorEvent::class,
195 1
            'throwable' => $throwable
196
        ]);
197
198 1
        $this->trigger(self::EVENT_ERROR, $event);
199 1
    }
200
201
    /**
202
     * This method is called when task executed timeout.
203
     * When overriding this method, make sure you call the parent implementation to ensure the
204
     * event is triggered.
205
     *
206
     * @throws \yii\base\InvalidConfigException
207
     */
208 2
    public function timeout(): void
209
    {
210 2
        $event = Yii::createObject(Event::class);
211
212 2
        $this->trigger(self::EVENT_TIMEOUT, $event);
213 2
    }
214
215
    /**
216
     * Wait until all tasks done.
217
     */
218 7
    public function wait(): void
219
    {
220 7
        $this->pool->wait();
221 7
    }
222
223
    /**
224
     * Set concurrency process do tasks.
225
     *
226
     * @param int $concurrency
227
     */
228
    public function setConcurrency(int $concurrency): void
229
    {
230
        $this->pool->concurrency($concurrency);
231
    }
232
233
    /**
234
     * Set timeout of task when execute.
235
     *
236
     * @param int $timeout
237
     */
238 8
    public function setTimeout(int $timeout): void
239
    {
240 8
        $this->pool->timeout($timeout);
241 8
    }
242
243
    /**
244
     * Set sleep time when wait tasks execute.
245
     *
246
     * @param int $sleepTimeWait
247
     */
248
    public function setSleepTimeWait(int $sleepTimeWait): void
249
    {
250
        $this->pool->sleepTime($sleepTimeWait);
251
    }
252
253
    /**
254
     * Set autoload for environment tasks execute.
255
     * @param string $autoload it can use at an alias.
256
     */
257
    public function setAutoload(string $autoload): void
258
    {
259
        $this->pool->autoload(Yii::getAlias($autoload));
0 ignored issues
show
Bug introduced by
It seems like \Yii::getAlias($autoload) targeting yii\BaseYii::getAlias() can also be of type boolean; however, Spatie\Async\Pool::autoload() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
260
    }
261
262
    /**
263
     * Set an application config file for invoke in child runtime process.
264
     *
265
     * @param string $appConfigFile it can use at an alias.
266
     */
267 8
    public function setAppConfigFile(string $appConfigFile): void
268
    {
269 8
        $this->appConfigFile = Yii::getAlias($appConfigFile);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Yii::getAlias($appConfigFile) can also be of type boolean. However, the property $appConfigFile is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
270 8
    }
271
272
    /**
273
     * Create an async process.
274
     *
275
     * @param callable|\Spatie\Async\Task|Task $callable need to execute.
276
     * @return Runnable process.
277
     */
278 8
    protected function createProcess($callable): Runnable
279
    {
280 8
        return ParentRuntime::createProcess([$callable, $this->appConfigFile]);
281
    }
282
283
}
284