Generic::codepoint()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
nc 2
nop 1
1
<?php
2
namespace PHPDaemon\Request;
3
4
use PHPDaemon\Core\AppInstance;
5
use PHPDaemon\Core\Daemon;
6
use PHPDaemon\Core\EventLoop;
7
8
abstract class Generic
9
{
10
    use \PHPDaemon\Traits\ClassWatchdog;
11
    use \PHPDaemon\Traits\StaticObjectWatchdog;
12
    use \PHPDaemon\Traits\EventHandlers;
13
14
    /**
15
     * State: finished.
16
     * @var integer
17
     */
18
    const STATE_FINISHED = 1;
19
20
    /**
21
     * State: waiting.
22
     * @var integer
23
     */
24
    const STATE_WAITING = 2;
25
26
    /**
27
     * State: running.
28
     * @var integer
29
     */
30
    const STATE_RUNNING = 3;
31
32
    /**
33
     * Related Application instance
34
     * @var \PHPDaemon\Core\AppInstance
35
     */
36
    public $appInstance;
37
38
    /**
39
     * Is this request aborted?
40
     * @var boolean
41
     */
42
    protected $aborted = false;
43
44
    /**
45
     * State
46
     * @var integer (self::STATE_*)
47
     */
48
    protected $state = self::STATE_WAITING;
49
50
    /**
51
     * Attributes
52
     * @var \StdCLass
53
     */
54
    public $attrs;
55
56
    /**
57
     * Registered shutdown functions
58
     * @var array
59
     */
60
    protected $shutdownFuncs = [];
61
62
    /**
63
     * Is this request running?
64
     * @var boolean
65
     */
66
    protected $running = false;
67
68
    /**
69
     * Upstream
70
     * @var object
71
     */
72
    protected $upstream;
73
74
    /**
75
     * Event
76
     * @var object
77
     */
78
    protected $ev;
79
80
    /**
81
     * Current sleep() time
82
     * @var float
83
     */
84
    protected $sleepTime = 0;
85
86
    /**
87
     * Priority
88
     * @var integer
89
     */
90
    protected $priority = null;
91
92
    /**
93
     * Current code point
94
     * @var mixed
95
     */
96
    protected $codepoint;
97
98
    protected $autoinit = false; // @TODO: remove this option in future version
99
100
    /**
101
     * Log
102
     * @param string $msg Message
103
     * @return void
104
     */
105
    public function log($msg)
106
    {
107
        Daemon::log(get_class($this) . ': ' . $msg);
108
    }
109
110
    /**
111
     * Constructor
112
     * @param AppInstance $appInstance Parent AppInstance.
113
     * @param IRequestUpstream $upstream Upstream.
114
     * @param object $parent Source request.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $parent not be object|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
115
     */
116
    public function __construct($appInstance, IRequestUpstream $upstream, $parent = null)
117
    {
118
        ++Daemon::$process->reqCounter;
119
        $this->appInstance = $appInstance;
120
        $this->upstream = $upstream;
121
        $this->ev = EventLoop::$instance->timer([$this, 'eventCall']);
122
        if ($this->priority !== null) {
123
            $this->ev->priority = $this->priority;
124
        }
125
        $this->preinit($parent);
126
        if ($this->autoinit) {
127
            $this->callInit();
128
        }
129
    }
130
131
    public function callInit()
132
    {
133
        $this->onWakeup();
134
        try {
135
            $this->init();
136
        } catch (\Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
137
            Daemon::uncaughtExceptionHandler($e);
138
            $this->finish();
139
            return;
140
        }
141
        $this->onSleep();
142
    }
143
144
    /**
145
     * Uncaught exception handler
146
     * @param $e
147
     * @return boolean|null Handled?
148
     */
149
    public function handleException($e)
0 ignored issues
show
Unused Code introduced by
The parameter $e is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
150
    {
151
    }
152
153
    /**
154
     * Is this request aborted?
155
     * @return boolean
156
     */
157
    public function isAborted()
158
    {
159
        return $this->aborted;
160
    }
161
162
    /**
163
     * Is this request finished?
164
     * @return boolean
165
     */
166
    public function isFinished()
167
    {
168
        return $this->state === static::STATE_FINISHED;
169
    }
170
171
    /**
172
     * Is this request running?
173
     * @return boolean
174
     */
175
    public function isRunning()
176
    {
177
        return $this->running;
178
    }
179
180
    /**
181
     * Output some data
182
     * @param string $s String to out
183
     * @param bool $flush
184
     * @return boolean|null Success
185
     */
186
    public function out($s, $flush = true)
187
    {
188
    }
189
190
    /**
191
     * Called when request iterated.
192
     * @return integer Status.
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
193
     */
194
    protected function run()
195
    {
196
    }
197
198
    /**
199
     * Event handler of Request, called by Evtimer
200
     * @param $arg
201
     * @return void
202
     */
203
    public function eventCall($arg)
0 ignored issues
show
Unused Code introduced by
The parameter $arg is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
204
    {
205
        try {
206
            if ($this->state === Generic::STATE_FINISHED) {
207
                $this->finish();
208
                $this->free();
209
                return;
210
            }
211
            $this->state = Generic::STATE_RUNNING;
212
            $this->onWakeup();
213
            $throw = false;
214
            try {
215
                $ret = $this->run();
216
                if (($ret === Generic::STATE_FINISHED) || ($ret === null)) {
217
                    $this->finish();
218
                } elseif ($ret === Generic::STATE_WAITING) {
219
                    $this->state = $ret;
220
                }
221
            } catch (RequestSleep $e) {
222
                $this->state = Generic::STATE_WAITING;
223
            } catch (RequestTerminated $e) {
224
                $this->state = Generic::STATE_FINISHED;
225
            } catch (\Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
226
                if (!$this->handleException($e)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->handleException($e) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
227
                    $throw = true;
228
                }
229
            }
230
            if ($this->state === Generic::STATE_FINISHED) {
231
                $this->finish();
232
            }
233
            $this->onSleep();
234
            if ($throw) {
235
                throw $e;
236
            }
237
        } catch (\Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
238
            Daemon::uncaughtExceptionHandler($e);
239
            $this->finish();
240
            return;
241
        }
242
        if ($this->state === Generic::STATE_WAITING) {
243
            $this->ev->add($this->sleepTime);
244
        }
245
    }
246
247
    /**
248
     * Frees the request
249
     * @return void
250
     */
251
    public function free()
252
    {
253
        if ($this->ev) {
254
            $this->ev->free();
255
            $this->ev = null;
256
        }
257
        if (isset($this->upstream)) {
258
            $this->upstream->freeRequest($this);
259
            $this->upstream = null;
260
        }
261
    }
262
263
    /**
264
     * Sets the priority
265
     * @param integer Priority
266
     * @return void
267
     */
268
    public function setPriority($p)
269
    {
270
        $this->priority = $p;
271
        if ($this->ev !== null) {
272
            $this->ev->priority = $p;
273
        }
274
    }
275
276
    /**
277
     * Preparing before init
278
     * @param object Source request
279
     * @return void
280
     */
281
    protected function preinit($req)
282
    {
283
        if ($req === null) {
284
            $req = new \stdClass;
285
            $req->attrs = new \stdClass;
286
        }
287
288
        $this->attrs = $req->attrs;
289
    }
290
291
    /**
292
     * This magic method called when the object casts to string
293
     * @return string Description
294
     */
295
    public function __toString()
296
    {
297
        return 'Request of type ' . get_class($this);
298
    }
299
300
    /**
301
     * Called when request constructs
302
     * @return void
303
     */
304
    protected function init()
305
    {
306
    }
307
308
    /**
309
     * Get string value from the given variable
310
     * @param Reference of variable.
311
     * @param array     Optional. Possible values.
312
     * @return string Value.
313
     */
314
    public static function getString(&$var, $values = null)
315
    {
316
        if (!is_string($var)) {
317
            $var = '';
318
        }
319 View Code Duplication
        if ($values !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
320
            return in_array($var, $values, true) ? $var : $values[0];
321
        }
322
323
        return $var;
324
    }
325
326
    /**
327
     * Get string value from the given variable
328
     * @param Reference of variable.
329
     * @return string Value.
330
     */
331
    public static function getMixed(&$var)
332
    {
333
        return $var;
334
    }
335
336
    /**
337
     * Get array value from the given variable
338
     * @param Reference of variable.
339
     * @param array     Optional. Filter callback.
340
     * @return string Value.
0 ignored issues
show
Documentation introduced by
Should the return type not be array?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
341
     */
342
    public static function getArray(&$var, $filter = null)
343
    {
344
        if (!is_array($var)) {
345
            return [];
346
        }
347
        if ($filter !== null) {
348
            return array_filter($var, $filter);
349
        }
350
351
        return $var;
352
    }
353
354
    /**
355
     * Get integer value from the given variable
356
     * @param Reference of variable.
357
     * @param array     Optional. Possible values.
358
     * @return string Value.
359
     */
360
    public static function getInteger(&$var, $values = null)
361
    {
362
        if (is_string($var) && ctype_digit($var)) {
363
            $var = (int)$var;
364
        }
365
        if (!is_int($var)) {
366
            return 0;
367
        }
368 View Code Duplication
        if ($values !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
369
            return in_array($var, $values, true) ? $var : $values[0];
370
        }
371
372
        return $var;
373
    }
374
375
    /**
376
     * Called when the connection is ready to accept new data
377
     * @return void
378
     */
379
    public function onWrite()
380
    {
381
    }
382
383
    /**
384
     * Adds new callback called before the request finished
385
     * @param callable $callback
386
     * @return void
387
     */
388
    public function registerShutdownFunction($callback)
389
    {
390
        $this->shutdownFuncs[] = $callback;
391
    }
392
393
    /**
394
     * Remove the given callback
395
     * @param callable $callback
396
     * @return void
397
     */
398
    public function unregisterShutdownFunction($callback)
399
    {
400
        if (($k = array_search($callback, $this->shutdownFuncs)) !== false) {
401
            unset($this->shutdownFuncs[$k]);
402
        }
403
    }
404
405
    /**
406
     * Helper for easy switching between several interruptable stages of request's execution
407
     * @param string Name
408
     * @return boolean Execute
409
     */
410
    public function codepoint($p)
411
    {
412
        if ($this->codepoint !== $p) {
413
            $this->codepoint = $p;
414
            return true;
415
        }
416
417
        return false;
418
    }
419
420
    /**
421
     * Delays the request execution for the given number of seconds
422
     *
423
     * @param integer $time Time to sleep in seconds
424
     * @param boolean $set Set this parameter to true when use call it outside of Request->run() or if you don't want to interrupt execution now
425
     * @throws RequestSleep
426
     * @return void
427
     */
428
    public function sleep($time = 0, $set = false)
429
    {
430
        if ($this->state === Generic::STATE_FINISHED) {
431
            return;
432
        }
433
        if ($this->state !== Generic::STATE_RUNNING) {
434
            $set = true;
435
        }
436
437
        $this->sleepTime = $time;
0 ignored issues
show
Documentation Bug introduced by
The property $sleepTime was declared of type double, but $time is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
438
439
        if (!$set) {
440
            throw new RequestSleep;
441
        } else {
442
            $this->ev->del();
443
            $this->ev->add($this->sleepTime);
444
        }
445
446
        $this->state = Generic::STATE_WAITING;
447
    }
448
449
    /**
450
     * Throws terminating exception
451
     * @param $s
452
     * @throws RequestTerminated
453
     * @return void
454
     */
455
    public function terminate($s = null)
456
    {
457
        if (is_string($s)) {
458
            $this->out($s);
459
        }
460
461
        throw new RequestTerminated;
462
    }
463
464
    /**
465
     * Cancel current sleep
466
     * @return void
467
     */
468
    public function wakeup()
469
    {
470
        if ($this->state === Generic::STATE_WAITING) {
471
            $this->ev->del();
472
            $this->ev->add(0);
473
        }
474
    }
475
476
    /**
477
     * Called to check if Request is ready
478
     * @return boolean Ready?
479
     */
480
    public function checkIfReady()
481
    {
482
        return true;
483
    }
484
485
    /**
486
     * Called when the request aborted
487
     * @return void
488
     */
489
    public function onAbort()
490
    {
491
    }
492
493
    /**
494
     * Called when the request finished
495
     * @return void
496
     */
497
    public function onFinish()
498
    {
499
    }
500
501
    /**
502
     * Called when the request wakes up
503
     * @return void
504
     */
505
    public function onWakeup()
506
    {
507
        $this->running = true;
508
        Daemon::$req = $this;
509
        Daemon::$context = $this;
510
        Daemon::$process->setState(Daemon::WSTATE_BUSY);
0 ignored issues
show
Bug introduced by
The method setState does only exist in PHPDaemon\Thread\Worker, but not in PHPDaemon\Thread\IPC and PHPDaemon\Thread\Master.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
511
    }
512
513
    /**
514
     * Called when the request starts sleep
515
     * @return void
516
     */
517
    public function onSleep()
518
    {
519
        Daemon::$req = null;
520
        Daemon::$context = null;
521
        $this->running = false;
522
        Daemon::$process->setState(Daemon::WSTATE_IDLE);
0 ignored issues
show
Bug introduced by
The method setState does only exist in PHPDaemon\Thread\Worker, but not in PHPDaemon\Thread\IPC and PHPDaemon\Thread\Master.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
523
    }
524
525
    /**
526
     * Aborts the request
527
     * @return void
528
     */
529
    public function abort()
530
    {
531
        if ($this->aborted) {
532
            return;
533
        }
534
535
        $this->aborted = true;
536
        $this->onWakeup();
537
        $this->onAbort();
538
539
        if ((ignore_user_abort() === 1)
540
            && (($this->state === Generic::STATE_RUNNING) || ($this->state === Generic::STATE_WAITING))
541
            && !Daemon::$compatMode
542
        ) {
543
            $this->upstream->endRequest($this);
544
        } else {
545
            $this->finish(-1);
546
        }
547
548
        $this->onSleep();
549
    }
550
551
    /**
552
     * Finish the request
553
     * @param integer Optional. Status. 0 - normal, -1 - abort, -2 - termination
554
     * @param boolean Optional. Zombie. Default is false
555
     * @return void
556
     */
557
    public function finish($status = 0, $zombie = false)
558
    {
559
        if ($this->state === Generic::STATE_FINISHED) {
560
            return;
561
        }
562
563
        if (!$zombie) {
564
            $this->state = Generic::STATE_FINISHED;
565
        }
566
567
        if (!($r = $this->running)) {
568
            $this->onWakeup();
569
        }
570
571
        while (($cb = array_shift($this->shutdownFuncs)) !== null) {
572
            try {
573
                $cb($this);
574
            } catch (\Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
575
                Daemon::uncaughtExceptionHandler($e);
576
                // @TODO: break?
577
            }
578
        }
579
580
        if (!$r) {
581
            $this->onSleep();
582
        }
583
584
        $this->event('finish');
585
        $this->onFinish();
586
        $this->cleanupEventHandlers();
587
588
        if (Daemon::$compatMode) {
589
            return;
590
        }
591
592
        ++Daemon::$process->counterGC;
593
594
        if (Daemon::$compatMode) {
595
            return;
596
        }
597
598
        if (!Daemon::$obInStack) { // preventing recursion
599
            ob_flush();
600
        }
601
602
        if ($status !== -1) {
603
            $appStatus = 0;
604
            $this->postFinishHandler(function () use ($appStatus, $status) {
605
                $this->upstream->endRequest($this, $appStatus, $status);
606
                $this->free();
607
            });
608
        } else {
609
            $this->free();
610
        }
611
    }
612
613
    /**
614
     * Called after request finish
615
     * @param callable $cb
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
616
     * @return void
617
     */
618
    protected function postFinishHandler($cb = null)
619
    {
620
        if ($cb !== null) {
621
            $cb();
622
        }
623
    }
624
}
625