Completed
Push — master ( 9acf59...6e6660 )
by Vasily
03:56
created

Worker.php ➔ isUploadedFile()   A

Complexity

Conditions 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 2
dl 0
loc 7
rs 9.4285
1
<?php
2
namespace PHPDaemon\Thread;
3
4
use PHPDaemon\Core\AppInstance;
5
use PHPDaemon\Core\Daemon;
6
use PHPDaemon\Core\Debug;
7
use PHPDaemon\Core\Timer;
8
use PHPDaemon\FS\FileSystem;
9
use PHPDaemon\Structures\StackCallbacks;
10
use PHPDaemon\Cache\CappedStorageHits;
11
12
/**
13
 * Implementation of the worker thread
14
 *
15
 * @package Core
16
 *
17
 * @author  Vasily Zorin <[email protected]>
18
 */
19
class Worker extends Generic
20
{
21
22
    /**
23
     * Update?
24
     * @var boolean
25
     */
26
    public $update = false;
27
28
    /**
29
     * Reload?
30
     * @var boolean
31
     */
32
    public $reload = false;
33
34
    protected $graceful = false;
35
36
    /**
37
     * Reload time
38
     * @var integer
39
     */
40
    protected $reloadTime = 0;
41
42
    /**
43
     * Reload delay
44
     * @var integer
45
     */
46
    protected $reloadDelay = 2;
47
48
    /**
49
     * Reloaded?
50
     * @var boolean
51
     */
52
    public $reloaded = false;
53
54
    /**
55
     * Time of last activity
56
     * @var integer
57
     */
58
    public $timeLastActivity = 0;
59
60
    /**
61
     * Last time of auto reload
62
     * @var integer
63
     */
64
    protected $autoReloadLast = 0;
65
66
    /**
67
     * Event base
68
     * @var EventBase
69
     */
70
    public $eventBase;
71
72
    /**
73
     * DNS base
74
     * @var EventDnsBase
75
     */
76
    public $dnsBase;
77
78
    /**
79
     * State
80
     * @var integer
81
     */
82
    public $state = 0;
83
84
    /**
85
     * Request counter
86
     * @var integer
87
     */
88
    public $reqCounter = 0;
89
90
    /**
91
     * Break main loop?
92
     * @var boolean
93
     */
94
    public $breakMainLoop = false;
95
96
    /**
97
     * Reload ready?
98
     * @var boolean
99
     */
100
    public $reloadReady = false;
101
102
    /**
103
     * If true, we do not register signals automatically at start
104
     * @var boolean
105
     */
106
    protected $delayedSigReg = false;
107
108
    /**
109
     * Instances count
110
     * @var array
111
     */
112
    public $instancesCount = [];
113
114
    /**
115
     * Connection
116
     * @var Connection
117
     */
118
    public $connection;
119
120
    /**
121
     * Counter GC
122
     * @var integer
123
     */
124
    public $counterGC = 0;
125
126
    /**
127
     * Stack of callbacks to execute
128
     * @var \PHPDaemon\Structures\StackCallbacks
129
     */
130
    public $callbacks;
131
132
    /** @var \PHPDaemon\IPCManager\IPCManager */
133
    public $IPCManager;
134
135
    public $lambdaCache;
136
137
    /**
138
     * Runtime of Worker process.
139
     * @return void
140
     */
141
    protected function run()
142
    {
143
        $this->lambdaCache = new CappedStorageHits;
144
        $this->lambdaCache->setMaxCacheSize(Daemon::$config->lambdacachemaxsize->value);
145
        $this->lambdaCache->setCapWindow(Daemon::$config->lambdacachecapwindow->value);
146
147
        $this->callbacks = new StackCallbacks();
148
        if (Daemon::$process instanceof Master) {
149
            Daemon::$process->unregisterSignals();
0 ignored issues
show
Bug introduced by
The method unregisterSignals() cannot be called from this context as it is declared protected in class PHPDaemon\Thread\Generic.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
150
        }
151
        if (Daemon::$process && Daemon::$process->eventBase) {
152
            Daemon::$process->eventBase->reinit();
153
            $this->eventBase = Daemon::$process->eventBase;
154
        } else {
155
            $this->eventBase = new \EventBase();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \EventBase() of type object<EventBase> is incompatible with the declared type object<PHPDaemon\Thread\EventBase> of property $eventBase.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
156
        }
157
        Daemon::$process = $this;
158
        if (Daemon::$logpointerAsync) {
159
            $oldfd                       = Daemon::$logpointerAsync->fd;
0 ignored issues
show
Unused Code introduced by
$oldfd is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
160
            Daemon::$logpointerAsync->fd = null;
161
            Daemon::$logpointerAsync     = null;
162
        }
163
        class_exists('Timer');
164
        $this->autoReloadLast = time();
165
        $this->reloadDelay    = Daemon::$config->mpmdelay->value + 2;
166
        $this->setState(Daemon::WSTATE_PREINIT);
167
168
        if (Daemon::$config->autogc->value > 0) {
169
            gc_enable();
170
            gc_collect_cycles();
171
        } else {
172
            gc_disable();
173
        }
174
175
        if (Daemon::$runworkerMode) {
176 View Code Duplication
            if (!Daemon::$config->verbosetty->value) {
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...
177
                fclose(STDIN);
178
                fclose(STDOUT);
179
                fclose(STDERR);
180
            }
181
182
            Daemon::$appResolver->preload(true);
183
        }
184
185
        $this->prepareSystemEnv();
186
        $this->overrideNativeFuncs();
187
188
        $this->setState(Daemon::WSTATE_INIT);
189
        ;
190
        $this->dnsBase = new \EventDnsBase($this->eventBase, false); // @TODO: test with true
0 ignored issues
show
Documentation Bug introduced by
It seems like new \EventDnsBase($this->eventBase, false) of type object<EventDnsBase> is incompatible with the declared type object<PHPDaemon\Thread\EventDnsBase> of property $dnsBase.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
191
        $this->registerEventSignals();
192
193
        FileSystem::init();
194
        FileSystem::initEvent();
195
        Daemon::openLogs();
196
197
        $this->IPCManager = Daemon::$appResolver->getInstanceByAppName('\PHPDaemon\IPCManager\IPCManager');
198
        if (!$this->IPCManager) {
199
            $this->log('cannot instantiate IPCManager');
200
        }
201
202
        Daemon::$appResolver->preload();
203
204
        foreach (Daemon::$appInstances as $app) {
205
            foreach ($app as $appInstance) {
206
                if (!$appInstance->ready) {
207
                    $appInstance->ready = true;
208
                    $appInstance->onReady();
209
                }
210
            }
211
        }
212
213
        $this->setState(Daemon::WSTATE_IDLE);
214
215
        Timer::add(function ($event) {
216
217
            if (!Daemon::$runworkerMode) {
218
                if ($this->IPCManager) {
219
                    $this->IPCManager->ensureConnection();
220
                }
221
            }
222
223
            $this->breakMainLoopCheck();
224
            if ($this->breakMainLoop) {
225
                $this->eventBase->exit();
226
                return;
227
            }
228
229
            if (Daemon::checkAutoGC()) {
230
                $this->callbacks->push(function ($thread) {
0 ignored issues
show
Unused Code introduced by
The parameter $thread 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...
231
                    gc_collect_cycles();
232
                });
233
                $this->eventBase->exit();
234
            }
235
236
            $event->timeout();
237
        }, 1e6 * 1, 'breakMainLoopCheck');
238
        if (Daemon::$config->autoreload->value > 0) {
239
            Timer::add(function ($event) {
240
                static $n = 0;
241
                $list = get_included_files();
242
                $s    = sizeof($list);
243
                if ($s > $n) {
244
                    $slice = array_map('realpath', array_slice($list, $n));
245
                    Daemon::$process->IPCManager->sendPacket(['op' => 'addIncludedFiles', 'files' => $slice]);
246
                    $n = $s;
247
                }
248
                $event->timeout();
249
            }, 1e6 * Daemon::$config->autoreload->value, 'watchIncludedFiles');
250
        }
251
252
        while (!$this->breakMainLoop) {
253
            $this->callbacks->executeAll($this);
254
            if (!$this->eventBase->dispatch()) {
255
                break;
256
            }
257
        }
258
        $this->shutdown();
259
    }
260
261
    /**
262
     * Override a standard PHP function
263
     * @param string $camelCase e.g. isUploadedFile
264
     * @param string $real e.g. is_uploaded_file
265
     */
266
    protected function override($camelCase, $real)
267
    {
268
        runkit_function_rename($real, $real . '_native');
269
        runkit_function_rename('PHPDaemon\\Thread\\' . $camelCase, $real);
270
    }
271
272
    /**
273
     * Overrides native PHP functions.
274
     * @return void
275
     */
276
    protected function overrideNativeFuncs()
277
    {
278
        if (Daemon::supported(Daemon::SUPPORT_RUNKIT_INTERNAL_MODIFY)) {
279
            function define($k, $v)
280
            {
281
                if (defined($k)) {
282
                    runkit_constant_redefine($k, $v);
283
                } else {
284
                    runkit_constant_add($k, $v);
285
                }
286
            }
287
            $this->override('define');
0 ignored issues
show
Bug introduced by
The call to override() misses a required argument $real.

This check looks for function calls that miss required arguments.

Loading history...
288
289
            function header()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
290
            {
291
                if (!Daemon::$context instanceof \PHPDaemon\Request\Generic) {
292
                    return false;
293
                }
294
                return Daemon::$context->header(...func_get_args());
0 ignored issues
show
Documentation Bug introduced by
The method header does not exist on object<PHPDaemon\Request\Generic>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
295
            }
296
            $this->override('header');
0 ignored issues
show
Bug introduced by
The call to override() misses a required argument $real.

This check looks for function calls that miss required arguments.

Loading history...
297
298
            function isUploadedFile()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
299
            {
300
                if (!Daemon::$context instanceof \PHPDaemon\Request\Generic) {
301
                    return false;
302
                }
303
                return Daemon::$context->isUploadedFile(...func_get_args());
0 ignored issues
show
Documentation Bug introduced by
The method isUploadedFile does not exist on object<PHPDaemon\Request\Generic>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
304
            }
305
            $this->override('isUploadedFile', 'is_uploaded_file');
306
307
            function moveUploadedFile()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
308
            {
309
                if (!Daemon::$context instanceof \PHPDaemon\Request\Generic) {
310
                    return false;
311
                }
312
                return Daemon::$context->moveUploadedFile(...func_get_args());
0 ignored issues
show
Documentation Bug introduced by
The method moveUploadedFile does not exist on object<PHPDaemon\Request\Generic>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
313
            }
314
            $this->override('moveUploadedFile', 'move_uploaded_file');
315
316
            function headersSent(&$file = null, &$line = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
317
            {
318
                if (!Daemon::$context instanceof \PHPDaemon\Request\Generic) {
319
                    return false;
320
                }
321
                return Daemon::$context->headers_sent($file, $line);
0 ignored issues
show
Documentation Bug introduced by
The method headers_sent does not exist on object<PHPDaemon\Request\Generic>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
322
            }
323
324
            //$this->override('headersSent', 'headers_sent); // Commented out due to a runkit bug
325
326
            function headersList()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
327
            {
328
                if (!Daemon::$context instanceof \PHPDaemon\Request\Generic) {
329
                    return false;
330
                }
331
                return Daemon::$context->headers_list();
0 ignored issues
show
Documentation Bug introduced by
The method headers_list does not exist on object<PHPDaemon\Request\Generic>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
332
            }
333
            $this->override('headersList', 'headers_list');
334
335
            function setcookie()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
336
            {
337
                if (!Daemon::$context instanceof \PHPDaemon\Request\Generic) {
338
                    return false;
339
                }
340
                return Daemon::$context->setcookie(...func_get_args());
0 ignored issues
show
Documentation Bug introduced by
The method setcookie does not exist on object<PHPDaemon\Request\Generic>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
341
            }
342
            $this->override('setcookie');
0 ignored issues
show
Bug introduced by
The call to override() misses a required argument $real.

This check looks for function calls that miss required arguments.

Loading history...
343
344
            /**
345
             * @param callable $cb
346
             */
347
            function registerShutdownFunction($cb)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
348
            {
349
                if (!Daemon::$context instanceof \PHPDaemon\Request\Generic) {
350
                    return false;
351
                }
352
                return Daemon::$context->registerShutdownFunction($cb);
353
            }
354
            $this->override('registerShutdownFunction', 'register_shutdown_function');
355
356
            runkit_function_copy('create_function', 'create_function_native');
357
            runkit_function_redefine('create_function', '$arg,$body', 'return \PHPDaemon\Core\Daemon::$process->createFunction($arg,$body);');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 142 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
358
        }
359
    }
360
361
    /**
362
     * Creates anonymous function (old-fashioned) like create_function()
363
     * @return void
364
     */
365
    public function createFunction($args, $body, $ttl = null)
366
    {
367
        $key = $args . "\x00" . $body;
368
        if (($f = $this->lambdaCache->getValue($key)) !== null) {
369
            return $f;
370
        }
371
        $f = eval('return function(' . $args . '){' . $body . '};');
372
        if ($ttl === null && Daemon::$config->lambdacachettl->value) {
373
            $ttl = Daemon::$config->lambdacachettl->value;
374
        }
375
        $this->lambdaCache->put($key, $f, $ttl);
376
        return $f;
377
    }
378
379
    /**
380
     * Setup settings on start.
381
     * @return void
382
     */
383
    protected function prepareSystemEnv()
384
    {
385
        proc_nice(Daemon::$config->workerpriority->value);
386
387
        register_shutdown_function(function () {
388
            $this->shutdown(true);
389
        });
390
391
        $this->setTitle(
392
            Daemon::$runName . ': worker process'
393
            . (Daemon::$config->pidfile->value !== Daemon::$config->defaultpidfile->value
394
                    ? ' (' . Daemon::$config->pidfile->value . ')' : '')
395
        );
396
397
        if (isset(Daemon::$config->group->value)) {
398
            $sg = posix_getgrnam(Daemon::$config->group->value);
399
        }
400
401
        if (isset(Daemon::$config->user->value)) {
402
            $su = posix_getpwnam(Daemon::$config->user->value);
403
        }
404
405
        $flushCache = false;
406
        if (Daemon::$config->chroot->value !== '/') {
407 View Code Duplication
            if (posix_getuid() != 0) {
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...
408
                Daemon::log('You must have the root privileges to change root.');
409
                exit(0);
410
            } elseif (!chroot(Daemon::$config->chroot->value)) {
411
                Daemon::log('Couldn\'t change root to \'' . Daemon::$config->chroot->value . '\'.');
412
                exit(0);
413
            }
414
            $flushCache = true;
415
        }
416
417
        if (isset(Daemon::$config->group->value)) {
418
            if ($sg === false) {
419
                Daemon::log('Couldn\'t change group to \'' . Daemon::$config->group->value . '\'. You must replace config-variable \'group\' with existing group.');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 164 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
420
                exit(0);
421
            } elseif (($sg['gid'] != posix_getgid()) && (!posix_setgid($sg['gid']))) {
0 ignored issues
show
Bug introduced by
The variable $sg does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
422
                Daemon::log('Couldn\'t change group to \'' . Daemon::$config->group->value . "'. Error (" . ($errno = posix_get_last_error()) . '): ' . posix_strerror($errno));
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 176 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
423
                exit(0);
424
            }
425
            $flushCache = true;
426
        }
427
428
        if (isset(Daemon::$config->user->value)) {
429
            if ($su === false) {
430
                Daemon::log('Couldn\'t change user to \'' . Daemon::$config->user->value . '\', user not found. You must replace config-variable \'user\' with existing username.');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 180 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
431
                exit(0);
432
            } elseif ($su['uid'] != posix_getuid() & !posix_setuid($su['uid'])) {
0 ignored issues
show
Bug introduced by
The variable $su does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
433
                Daemon::log('Couldn\'t change user to \'' . Daemon::$config->user->value . "'. Error (" . ($errno = posix_get_last_error()) . '): ' . posix_strerror($errno));
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 174 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
434
                exit(0);
435
            }
436
            $flushCache = true;
437
        }
438
        if ($flushCache) {
439
            clearstatcache(true);
440
        }
441
        if (Daemon::$config->cwd->value !== '.') {
442 View Code Duplication
            if (!@chdir(Daemon::$config->cwd->value)) {
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...
443
                Daemon::log('Couldn\'t change directory to \'' . Daemon::$config->cwd->value . '.');
444
            }
445
            clearstatcache(true);
446
        }
447
    }
448
449
    /**
450
     * Log something
451
     * @param string - Message.
452
     * @param string $message
453
     * @return void
454
     */
455
    public function log($message)
456
    {
457
        Daemon::log('W#' . $this->pid . ' ' . $message);
458
    }
459
460
    /**
461
     * Reloads additional config-files on-the-fly.
462
     * @return void
463
     */
464
    protected function update()
465
    {
466
        FileSystem::updateConfig();
467
        foreach (Daemon::$appInstances as $app) {
468
            foreach ($app as $appInstance) {
469
                $appInstance->handleStatus(AppInstance::EVENT_CONFIG_UPDATED);
470
            }
471
        }
472
    }
473
474
    /**
475
     * Check if we should break main loop
476
     * @return void
477
     */
478
    protected function breakMainLoopCheck()
479
    {
480
        $time = microtime(true);
481
482
        if ($this->terminated || $this->breakMainLoop) {
483
            return;
484
        }
485
486
        if ($this->shutdown) {
487
            $this->breakMainLoop = true;
488
            return;
489
        }
490
491
        if ($this->reload) {
492
            if ($time > $this->reloadTime) {
493
                $this->breakMainLoop = true;
494
            }
495
            return;
496
        }
497
498
        if (Daemon::$config->maxmemoryusage->value > 0
499
            && (memory_get_usage(true) > Daemon::$config->maxmemoryusage->value)) {
500
            $this->log('\'maxmemory\' exceed. Graceful shutdown.');
501
502
            $this->gracefulRestart();
503
        }
504
505
        if (Daemon::$config->maxrequests->value > 0
506
                && ($this->reqCounter >= Daemon::$config->maxrequests->value)) {
507
            $this->log('\'max-requests\' exceed. Graceful shutdown.');
508
509
            $this->gracefulRestart();
510
        }
511
512
        if (Daemon::$config->maxidle->value
513
            && $this->timeLastActivity && ($time - $this->timeLastActivity > Daemon::$config->maxidle->value)) {
514
            $this->log('\'maxworkeridle\' exceed. Graceful shutdown.');
515
516
            $this->gracefulRestart();
517
        }
518
519
        if ($this->update === true) {
520
            $this->update = false;
521
            $this->update();
522
        }
523
    }
524
525
    /**
526
     * Graceful restart
527
     * @return void
528
     */
529 View Code Duplication
    public function gracefulRestart()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
530
    {
531
        $this->reload     = true;
532
        $this->graceful = true;
533
        $this->reloadTime = microtime(true) + $this->reloadDelay;
0 ignored issues
show
Documentation Bug introduced by
The property $reloadTime was declared of type integer, but microtime(true) + $this->reloadDelay is of type double. 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...
534
        $this->setState($this->state);
0 ignored issues
show
Documentation introduced by
$this->state is of type integer, but the function expects a object<PHPDaemon\Thread\Constant>.

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...
535
    }
536
537
    /**
538
     * Graceful stop
539
     * @return void
540
     */
541 View Code Duplication
    public function gracefulStop()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
542
    {
543
        $this->breakMainLoop = true;
544
        $this->graceful = true;
545
        $this->reloadTime = microtime(true) + $this->reloadDelay;
0 ignored issues
show
Documentation Bug introduced by
The property $reloadTime was declared of type integer, but microtime(true) + $this->reloadDelay is of type double. 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...
546
        $this->setState($this->state);
0 ignored issues
show
Documentation introduced by
$this->state is of type integer, but the function expects a object<PHPDaemon\Thread\Constant>.

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...
547
    }
548
549
    /**
550
     * Asks the running applications the whether we can go to shutdown current (old) worker.
551
     * @return boolean - Ready?
552
     */
553
    protected function appInstancesReloadReady()
554
    {
555
        $ready = true;
556
557
        foreach (Daemon::$appInstances as $k => $app) {
558
            foreach ($app as $name => $appInstance) {
559
                if (!$appInstance->handleStatus($this->graceful ? AppInstance::EVENT_GRACEFUL_SHUTDOWN : AppInstance::EVENT_SHUTDOWN)) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 136 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
560
                    $this->log(__METHOD__ . ': waiting for ' . $k . ':' . $name);
561
                    $ready = false;
562
                }
563
            }
564
        }
565
        return $ready;
566
    }
567
568
    /**
569
     * Shutdown this worker
570
     * @param boolean Hard? If hard, we shouldn't wait for graceful shutdown of the running applications.
571
     * @return boolean|null Ready?
572
     */
573
    protected function shutdown($hard = false)
574
    {
575
        $error = error_get_last();
576 View Code Duplication
        if ($error) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $error of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
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...
577
            if ($error['type'] === E_ERROR) {
578
                Daemon::log('W#' . $this->pid . ' crashed by error \'' . $error['message'] . '\' at ' . $error['file'] . ':' . $error['line']);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 143 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
579
            }
580
        }
581
        if (Daemon::$config->logevents->value) {
582
            $this->log('event shutdown(' . ($hard ? 'HARD' : '') . ') invoked.');
583
        }
584
585
        if (Daemon::$config->throwexceptiononshutdown->value) {
586
            throw new \Exception('event shutdown');
587
        }
588
589
        @ob_flush();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
590
591
        if ($this->terminated === true) {
592
            if ($hard) {
593
                exit(0);
594
            }
595
596
            return;
597
        }
598
599
        $this->terminated = true;
600
601
        if ($hard) {
602
            $this->setState(Daemon::WSTATE_SHUTDOWN);
603
            exit(0);
604
        }
605
606
        $this->reloadReady = $this->appInstancesReloadReady();
607
608
        if ($this->reload && $this->graceful) {
609
            $this->reloadReady = $this->reloadReady && (microtime(true) > $this->reloadTime);
610
        }
611
612
        if (Daemon::$config->logevents->value) {
613
            $this->log('reloadReady = ' . Debug::dump($this->reloadReady));
614
        }
615
616
        Timer::remove('breakMainLoopCheck');
617
618
        Timer::add(function ($event) {
619
            $self = Daemon::$process;
620
621
            $self->reloadReady = $self->appInstancesReloadReady();
0 ignored issues
show
Bug introduced by
The method appInstancesReloadReady 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...
622
623
            if ($self->reload === true) {
624
                $self->reloadReady = $self->reloadReady && (microtime(true) > $self->reloadTime);
625
            }
626
            if (!$self->reloadReady) {
627
                $event->timeout();
628
            } else {
629
                $self->eventBase->exit();
630
            }
631
        }, 1e6, 'checkReloadReady');
632
        while (!$this->reloadReady) {
633
            $this->eventBase->loop();
634
        }
635
        FileSystem::waitAllEvents(); // ensure that all I/O events completed before suicide
636
        exit(0); // R.I.P.
637
    }
638
639
    /**
640
     * Set current status of worker
641
     * @param int Constant
642
     * @return boolean Success.
643
     */
644
    public function setState($int)
645
    {
646
        if (Daemon::$compatMode) {
647
            return true;
648
        }
649
        if (!$this->id) {
650
            return false;
651
        }
652
653
        if (Daemon::$config->logworkersetstate->value) {
654
            $this->log('state is ' . Daemon::$wstateRev[$int]);
655
        }
656
657
        $this->state = $int;
0 ignored issues
show
Documentation Bug introduced by
It seems like $int of type object<PHPDaemon\Thread\Constant> is incompatible with the declared type integer of property $state.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
658
659
        if ($this->reload) {
660
            $int += 100;
661
        }
662
        Daemon::$shm_wstate->write(chr($int), $this->id - 1);
0 ignored issues
show
Documentation Bug introduced by
The method write does not exist on object<PHPDaemon\Thread\Collection>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
663
        return true;
664
    }
665
666
    /**
667
     * Handler of the SIGINT (hard shutdown) signal in worker process.
668
     * @return void
669
     */
670
    protected function sigint()
671
    {
672
        if (Daemon::$config->logsignals->value) {
673
            $this->log('caught SIGINT.');
674
        }
675
676
        $this->shutdown(true);
677
    }
678
679
    /**
680
     * Handler of the SIGTERM (graceful shutdown) signal in worker process.
681
     * @return void
682
     */
683
    protected function sigterm()
684
    {
685
        if (Daemon::$config->logsignals->value) {
686
            $this->log('caught SIGTERM.');
687
        }
688
689
        $this->graceful = false;
690
        $this->breakMainLoop = true;
691
        $this->eventBase->exit();
692
    }
693
694
    /**
695
     * Handler of the SIGQUIT (graceful shutdown) signal in worker process.
696
     * @return void
697
     */
698
    protected function sigquit()
699
    {
700
        if (Daemon::$config->logsignals->value) {
701
            $this->log('caught SIGQUIT.');
702
        }
703
704
        parent::sigquit();
705
    }
706
707
    /**
708
     * Handler of the SIGHUP (reload config) signal in worker process.
709
     * @return void
710
     */
711 View Code Duplication
    protected function sighup()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
712
    {
713
        if (Daemon::$config->logsignals->value) {
714
            $this->log('caught SIGHUP (reload config).');
715
        }
716
717
        if (isset(Daemon::$config->configfile->value)) {
718
            Daemon::loadConfig(Daemon::$config->configfile->value);
719
        }
720
721
        $this->update = true;
722
    }
723
724
    /**
725
     * Handler of the SIGUSR1 (re-open log-file) signal in worker process.
726
     * @return void
727
     */
728
    protected function sigusr1()
729
    {
730
        if (Daemon::$config->logsignals->value) {
731
            $this->log('caught SIGUSR1 (re-open log-file).');
732
        }
733
734
        Daemon::openLogs();
735
    }
736
737
    /**
738
     * Handler of the SIGUSR2 (graceful shutdown for update) signal in worker process.
739
     * @return void
740
     */
741
    protected function sigusr2()
742
    {
743
        if (Daemon::$config->logsignals->value) {
744
            $this->log('caught SIGUSR2 (graceful shutdown for update).');
745
        }
746
747
        $this->gracefulRestart();
748
    }
749
750
    /**
751
     * Handler of the SIGTSTP (graceful stop) signal in worker process.
752
     * @return void
753
     */
754
    protected function sigtstp()
755
    {
756
        if (Daemon::$config->logsignals->value) {
757
            $this->log('caught SIGTSTP (graceful stop).');
758
        }
759
760
        $this->gracefulStop();
761
    }
762
763
    /**
764
     * Handler of the SIGTTIN signal in worker process.
765
     * @return void
766
     */
767
    protected function sigttin()
768
    {
769
    }
770
771
    /**
772
     * Handler of the SIGPIPE signal in worker process.
773
     * @return void
774
     */
775
    protected function sigpipe()
776
    {
777
    }
778
779
    /**
780
     * Handler of non-known signals.
781
     * @return void
782
     */
783 View Code Duplication
    protected function sigunknown($signo)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
784
    {
785
        if (isset(Generic::$signals[$signo])) {
786
            $sig = Generic::$signals[$signo];
787
        } else {
788
            $sig = 'UNKNOWN';
789
        }
790
791
        $this->log('caught signal #' . $signo . ' (' . $sig . ').');
792
    }
793
794
    /**
795
     * Called (in master) when process is terminated
796
     * @return void
797
     */
798
    public function onTerminated()
799
    {
800
        $this->setState(Daemon::WSTATE_SHUTDOWN);
801
    }
802
803
    /**
804
     * Destructor of worker thread.
805
     * @return void
806
     */
807
    public function __destruct()
808
    {
809
        if (posix_getpid() === $this->pid) {
810
            $this->setState(Daemon::WSTATE_SHUTDOWN);
811
        }
812
    }
813
}
814