Daemon::glob()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
dl 0
loc 8
rs 10
c 0
b 0
f 0
nc 2
nop 2
1
<?php
2
namespace PHPDaemon\Core;
3
4
use PHPDaemon\Config;
5
use PHPDaemon\FS\FileSystem;
6
use PHPDaemon\Thread;
7
use PHPDaemon\Thread\Collection;
8
use PHPDaemon\Utils\ShmEntity;
9
10
/**
11
 * Daemon "namespace"
12
 *
13
 * @package Core
14
 *
15
 * @author  Vasily Zorin <[email protected]>
16
 */
17
class Daemon
18
{
19
    use \PHPDaemon\Traits\ClassWatchdog;
20
    use \PHPDaemon\Traits\StaticObjectWatchdog;
21
22
    /**
23
     * Support of runkit sandbox functionality
24
     * @var integer
25
     */
26
    const SUPPORT_RUNKIT_SANDBOX = 0;
27
28
    /**
29
     * Support of runkit on-the-fly userland code modification functionality
30
     * @var integer
31
     */
32
    const SUPPORT_RUNKIT_MODIFY = 1;
33
34
    /**
35
     * Support of runkit on-the-fly internal code modification functionality
36
     * @var integer
37
     */
38
    const SUPPORT_RUNKIT_INTERNAL_MODIFY = 2;
39
40
    /**
41
     * Support of runkit on-the-fly userland code import functionality
42
     * @var integer
43
     */
44
    const SUPPORT_RUNKIT_IMPORT = 3;
45
46
    /**
47
     * Worker state: idle. It means that the worker IS NOT in the middle of execution valuable callback (e.g. request) at this moment of time. Currently, it does not mean that worker not have any pending operations.
48
     * @var integer
49
     */
50
    const WSTATE_IDLE = 1;
51
52
    /**
53
     * Worker state: busy. It means that the worker IS in the middle of execution valuable callback.
54
     */
55
    const WSTATE_BUSY = 2;
56
57
    /**
58
     * Worker state: shutdown. It means that worker is shutdown. Nothing special.
59
     * @var integer
60
     */
61
    const WSTATE_SHUTDOWN = 3;
62
63
    /**
64
     * Worker state: shutdown. It means that worker is shutdown.
65
     * @var integer
66
     */
67
    const WSTATE_PREINIT = 4;
68
69
    /**
70
     * Worker state: initialization. It means that worker is starting right now.
71
     * @var integer
72
     */
73
    const WSTATE_INIT = 5;
74
75
    /**
76
     * Hash of possible worker states
77
     * @var array
78
     */
79
    public static $wstateRev = [
80
        1 => 'IDLE',
81
        2 => 'BUSY',
82
        3 => 'SHUTDOWN',
83
        4 => 'PREINIT',
84
        5 => 'INIT',
85
    ];
86
87
    /**
88
     * Shared memory WSTATE segment size
89
     * @var integer
90
     */
91
    const SHM_WSTATE_SIZE = 1024;
92
93
    /**
94
     * PHPDaemon version
95
     * @var string
96
     */
97
    public static $version;
98
99
    /**
100
     * PHPDaemon start time
101
     * @var integer
102
     */
103
    public static $startTime;
104
105
    /**
106
     * Log file resource
107
     * @var resource
108
     */
109
    public static $logpointer;
110
111
    /**
112
     * Log file async. resource
113
     * @var object
114
     */
115
    public static $logpointerAsync;
116
117
    /**
118
     * Supported things array
119
     * @var string
120
     */
121
    protected static $support = [];
122
123
    /**
124
     * Current thread object
125
     * @var \PHPDaemon\Thread\Master|\PHPDaemon\Thread\IPC|\PHPDaemon\Thread\Worker
126
     */
127
    public static $process;
128
129
    /**
130
     * AppResolver
131
     * @var \PHPDaemon\Core\AppResolver
132
     */
133
    public static $appResolver;
134
135
    /**
136
     * Running application instances
137
     * @var array
138
     */
139
    public static $appInstances = [];
140
141
    /**
142
     * Running request
143
     * @var \PHPDaemon\Request\Generic
144
     */
145
    public static $req;
146
147
    /**
148
     * Running context
149
     * @var object
150
     */
151
    public static $context;
152
153
    /**
154
     * Collection of workers
155
     * @var \PHPDaemon\Thread\Collection
156
     */
157
    protected static $workers;
158
159
    /**
160
     * Collection of masters
161
     * @var \PHPDaemon\Thread\Collection
162
     */
163
    protected static $masters;
164
165
    /**
166
     * Copy of $_SERVER on the daemon start
167
     * @var array
168
     */
169
    protected static $initservervar;
170
171
    /**
172
     * Shared memory 'WSTATE' entity
173
     * @var ShmEntity
174
     */
175
    public static $shm_wstate;
176
177
    /**
178
     * Running under Apache/PHP-FPM in compatibility mode?
179
     * @var boolean
180
     */
181
    public static $compatMode = false;
182
183
    /**
184
     * Base name of daemon instance
185
     * @var string
186
     */
187
    public static $runName = 'phpdaemon';
188
189
    /**
190
     * Configuration object
191
     * @var \PHPDaemon\Config\_Object
192
     */
193
    public static $config;
194
195
    /**
196
     * Path to application resolver
197
     * @var string
198
     */
199
    public static $appResolverPath;
200
201
202
    /**
203
     * Restrict error control. When true, operator '@' means nothing.
204
     * @var boolean
205
     */
206
    public static $restrictErrorControl = false;
207
208
    /**
209
     * Default error reporting level
210
     * @var integer
211
     */
212
    public static $defaultErrorLevel;
213
214
    /**
215
     * Is it running under master-less 'runworker' mode?
216
     * @var bool
217
     */
218
    public static $runworkerMode = false;
219
220
    /**
221
     * Whether if the current execution stack contains ob-filter
222
     * @var bool
223
     */
224
    public static $obInStack = false;
225
226
    /**
227
     * Mechanism of catching errors. Set it to true, then run your suspect code, and then check this property again. If false, there was error message.
228
     * @var bool
229
     */
230
    public static $noError = false;
231
232
    /**
233
     * Loads default setting.
234
     * @return void
235
     */
236
    public static function initSettings()
237
    {
238
        Daemon::$version = file_get_contents('VERSION', true);
239
240
        Daemon::$config = new Config\_Object;
241
242
        if (!defined('SO_REUSEPORT') && mb_orig_strpos(php_uname('s'), 'BSD') !== false) {
243
            // @TODO: better if-BSD check
244
            define('SO_REUSEPORT', 0x200);
245
        }
246
    }
247
248
    /**
249
     * Glob function with support of include_path
250
     * @param string $pattern
251
     * @param int $flags
252
     * @return array
253
     */
254
    public static function glob($pattern, $flags = 0)
255
    {
256
        $r = [];
257
        foreach (explode(':', get_include_path()) as $path) {
258
            $r = array_merge($r, glob($p = $path . '/' . $pattern, $flags));
259
        }
260
        return array_unique($r);
261
    }
262
263
    /**
264
     * Generate a unique ID.
265
     * @return string Returns the unique identifier, as a string.
266
     */
267
    public static function uniqid()
268
    {
269
        static $n = 0;
270
        return str_shuffle(
271
            md5(
272
                str_shuffle(
273
                    microtime(true) . chr(mt_rand(0, 0xFF))
274
                    . Daemon::$process->getPid() . chr(mt_rand(0, 0xFF))
275
                    . (++$n) . mt_rand(0, mt_getrandmax())
276
                )
277
            )
278
        );
279
    }
280
281
    /**
282
     * Load PHP extension (module) if absent
283
     * @param string $mod
284
     * @param string $version
0 ignored issues
show
Documentation introduced by
Should the type for parameter $version not be string|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...
285
     * @param string $compare
286
     * @return bool $success
287
     */
288
    public static function loadModuleIfAbsent($mod, $version = null, $compare = '>=')
289
    {
290
        if (!extension_loaded($mod)) {
291
            if (!get_cfg_var('enable_dl')) {
292
                return false;
293
            }
294
            if (!@dl(basename($mod) . '.so')) {
295
                return false;
296
            }
297
        }
298
        if (!$version) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $version of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
299
            return true;
300
        }
301
        try {
302
            $ext = new \ReflectionExtension($mod);
303
            return version_compare($ext->getVersion(), $version, $compare);
304
        } catch (\ReflectionException $e) {
305
            return false;
306
        }
307
    }
308
309
    /**
310
     * Call automatic garbage collector
311
     * @return void
312
     */
313
    public static function callAutoGC()
314
    {
315
        if (self::checkAutoGC()) {
316
            gc_collect_cycles();
317
        }
318
    }
319
320
    /**
321
     * Check if we need to run automatic garbage collector
322
     * @return bool
323
     */
324
    public static function checkAutoGC()
325
    {
326
        if ((Daemon::$config->autogc->value > 0)
327
            && (Daemon::$process->counterGC > 0)
328
            && (Daemon::$process->counterGC >= Daemon::$config->autogc->value)
329
        ) {
330
            Daemon::$process->counterGC = 0;
331
            return true;
332
        }
333
        return false;
334
    }
335
336
    /**
337
     * Output filter
338
     * @return string Output
339
     */
340
    public static function outputFilter($s)
341
    {
342
        static $n = 0; // recursion counter
343
344
        if ($s === '') {
345
            return '';
346
        }
347
        ++$n;
348
        Daemon::$obInStack = true;
349
        if (Daemon::$config->obfilterauto->value && Daemon::$context instanceof \PHPDaemon\Request\Generic) {
350
            Daemon::$context->out($s, false);
351
        } else {
352
            Daemon::log('Unexcepted output (len. ' . mb_orig_strlen($s) . '): \'' . $s . '\'');
353
        }
354
        --$n;
355
        Daemon::$obInStack = $n > 0;
356
        return '';
357
    }
358
359
    /**
360
     * Uncaught exception handler
361
     * @param \Throwable $e
362
     * @return void
363
     */
364
    public static function uncaughtExceptionHandler(\Throwable $e)
365
    {
366
        if (Daemon::$context !== null) {
367
            if (Daemon::$context->handleException($e)) {
368
                return;
369
            }
370
        }
371
        $msg = $e->getMessage();
372
        Daemon::log('Uncaught ' . get_class($e) . ' (' . $e->getCode() . ')' . (mb_orig_strlen($msg) ? ': ' . $msg : '') . ".\n" . $e->getTraceAsString());
373 View Code Duplication
        if (Daemon::$context instanceof \PHPDaemon\Request\Generic) {
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...
374
            Daemon::$context->out('<b>Uncaught ' . get_class($e) . ' (' . $e->getCode() . ')</b>' . (mb_orig_strlen($msg) ? ': ' . $msg : '') . '.<br />');
375
        }
376
    }
377
378
    /**
379
     * Error handler
380
     * @param integer $errno
381
     * @param string $errstr
382
     * @param string $errfile
383
     * @param integer $errline
384
     * @param array $errcontext
385
     */
386
    public static function errorHandler($errno, $errstr, $errfile, $errline, $errcontext = [])
0 ignored issues
show
Unused Code introduced by
The parameter $errcontext 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...
387
    {
388
        Daemon::$noError = false;
389
        $l = error_reporting();
390
        if ($l === 0) {
391
            if (!Daemon::$restrictErrorControl) {
392
                return;
393
            }
394
        } elseif (!($l & $errno)) {
395
            return;
396
        }
397
398
        static $errtypes = [
399
            E_ERROR => 'Fatal error',
400
            E_WARNING => 'Warning',
401
            E_PARSE => 'Parse error',
402
            E_NOTICE => 'Notice',
403
            E_PARSE => 'Parse error',
404
            E_USER_ERROR => 'Fatal error (userland)',
405
            E_USER_WARNING => 'Warning (userland)',
406
            E_USER_NOTICE => 'Notice (userland)',
407
            E_STRICT => 'Notice (userland)',
408
            E_RECOVERABLE_ERROR => 'Fatal error (recoverable)',
409
            E_DEPRECATED => 'Deprecated',
410
            E_USER_DEPRECATED => 'Deprecated (userland)',
411
        ];
412
        $errtype = $errtypes[$errno];
413
        Daemon::log($errtype . ': ' . $errstr . ' in ' . $errfile . ':' . $errline . "\n" . Debug::backtrace());
414 View Code Duplication
        if (Daemon::$context instanceof \PHPDaemon\Request\Generic) {
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...
415
            Daemon::$context->out('<strong>' . $errtype . '</strong>: ' . $errstr . ' in ' . basename($errfile) . ':' . $errline . '<br />');
416
        }
417
    }
418
419
    /**
420
     * Performs initial actions.
421
     * @return void
422
     */
423
    public static function init(bool $master = true)
424
    {
425
        Daemon::$startTime = time();
426
        set_time_limit(0);
427
428
        Daemon::$defaultErrorLevel = error_reporting();
429
        Daemon::$restrictErrorControl = (bool)Daemon::$config->restricterrorcontrol->value;
430
        ob_start(['\PHPDaemon\Core\Daemon', 'outputFilter']);
431
        set_error_handler(['\PHPDaemon\Core\Daemon', 'errorHandler']);
432
433
        Daemon::checkSupports();
434
435
        Daemon::$initservervar = $_SERVER;
436
        Daemon::$masters = new Collection;
437
        
438
        if ($master) {
439
            Daemon::$shm_wstate = new ShmEntity(
440
                Daemon::$config->pidfile->value,
441
                Daemon::SHM_WSTATE_SIZE,
442
                'wstate',
443
                true
444
            );
445
        }
446
        Daemon::openLogs();
447
    }
448
449
    /**
450
     * Is thing supported
451
     * @param integer $what Thing to check
452
     * @return boolean
453
     */
454
    public static function supported($what)
455
    {
456
        return isset(self::$support[$what]);
457
    }
458
459
    /**
460
     * Method to fill $support array
461
     * @return void
462
     */
463
    protected static function checkSupports()
464
    {
465
        if (is_callable('runkit_lint_file')) {
466
            self::$support[self::SUPPORT_RUNKIT_SANDBOX] = true;
467
        }
468
469
        if (is_callable('runkit_function_add')) {
470
            self::$support[self::SUPPORT_RUNKIT_MODIFY] = true;
471
        }
472
473
        if (is_callable('runkit_import')) {
474
            self::$support[self::SUPPORT_RUNKIT_IMPORT] = true;
475
        }
476
477
        if (self::supported(self::SUPPORT_RUNKIT_MODIFY) && ini_get('runkit.internal_override')) {
478
            self::$support[self::SUPPORT_RUNKIT_INTERNAL_MODIFY] = true;
479
        }
480
    }
481
482
    /**
483
     * Check file syntax via runkit_lint_file if supported or via php -l
484
     * @param string File name
485
     * @return boolean
486
     */
487
    public static function lintFile($filename)
488
    {
489
        if (!file_exists($filename)) {
490
            return false;
491
        }
492
493
        if (self::supported(self::SUPPORT_RUNKIT_SANDBOX)) {
494
            /** @noinspection PhpUndefinedFunctionInspection */
495
            return runkit_lint_file($filename);
496
        }
497
498
        $cmd = 'php -l ' . escapeshellcmd($filename) . ' 2>&1';
499
500
        exec($cmd, $output, $retcode);
501
502
        return 0 === $retcode;
503
    }
504
505
    /**
506
     * It allows to run your simple web-apps in spawn-fcgi/php-fpm environment.
507
     * @return boolean|null - Success.
508
     */
509
    public static function compatRunEmul()
510
    {
511
        Daemon::$compatMode = true;
512
        Daemon::initSettings();
513
514
        $argv = isset($_SERVER['CMD_ARGV']) ? $_SERVER['CMD_ARGV'] : '';
515
516
        $args = \PHPDaemon\Core\Bootstrap::getArgs($argv);
517
518
        if (isset($args[$k = 'configfile'])) {
519
            Daemon::$config[$k] = $args[$k];
520
        }
521
522
        if (!Daemon::$config->loadCmdLineArgs($args)) {
523
            $error = true;
524
        }
525
526
        if (isset(Daemon::$config->configfile->value) && !Daemon::loadConfig(Daemon::$config->configfile->value)) {
527
            $error = true;
528
        }
529
530
        if (!isset(Daemon::$config->path->value)) {
531
            exit('\'path\' is not defined');
532
        }
533
534
        if ($error) {
0 ignored issues
show
Bug introduced by
The variable $error 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...
535
            exit;
536
        }
537
538
        $appResolver = require Daemon::$config->path->value;
539
        $appResolver->init();
540
541
        $req = new \stdClass;
542
        $req->attrs = new \stdClass;
543
        $req->attrs->request = $_REQUEST;
544
        $req->attrs->get = $_GET;
545
        $req->attrs->post = $_REQUEST;
546
        $req->attrs->cookie = $_REQUEST;
547
        $req->attrs->server = $_SERVER;
548
        $req->attrs->files = $_FILES;
549
        $req->attrs->session = isset($_SESSION) ? $_SESSION : null;
550
        $req->attrs->connId = 1;
551
        $req->attrs->trole = 'RESPONDER';
552
        $req->attrs->flags = 0;
553
        $req->attrs->id = 1;
554
        $req->attrs->paramsDone = true;
555
        $req->attrs->inputDone = true;
556
        $req = $appResolver->getRequest($req);
557
558
        while (true) {
559
            $ret = $req->call();
560
561
            if ($ret === 1) {
562
                return;
563
            }
564
        }
565
    }
566
567
    /**
568
     * Load config-file
569
     * @param string $paths Path
570
     * @return boolean - Success.
571
     */
572
    public static function loadConfig($paths)
573
    {
574
        $apaths = explode(';', $paths);
575
        foreach ($apaths as $path) {
576
            if (is_file($p = realpath($path))) {
577
                $ext = strtolower(pathinfo($p, PATHINFO_EXTENSION));
578
                if ($ext === 'conf') {
579
                    return Daemon::$config->loadFile($p);
580
                } else {
581
                    Daemon::log('Config file \'' . $p . '\' has unsupported file extension.');
582
                    return false;
583
                }
584
            }
585
        }
586
        Daemon::log('Config file not found in \'' . $paths . '\'.');
587
        return false;
588
    }
589
590
    /**
591
     * Open logs
592
     * @return void
593
     */
594
    public static function openLogs()
595
    {
596
        if (Daemon::$config->logging->value) {
597
            $dir = dirname(Daemon::$config->logstorage->value);
598
            is_dir($dir) || mkdir($dir, 0755, true);
599
            Daemon::$logpointer = fopen(Daemon::$config->logstorage->value, 'a');
600
            if (isset(Daemon::$config->group->value)) {
601
                chgrp(Daemon::$config->logstorage->value, Daemon::$config->group->value); // @TODO: rewrite to async I/O
602
            }
603
            if (isset(Daemon::$config->user->value)) {
604
                chown(Daemon::$config->logstorage->value, Daemon::$config->user->value); // @TODO: rewrite to async I/O
605
            }
606
            if ((Daemon::$process instanceof Thread\Worker) && FileSystem::$supported) {
607
                FileSystem::open(Daemon::$config->logstorage->value, 'a!', function ($file) {
608
                    Daemon::$logpointerAsync = $file;
609
                    if (!$file) {
610
                        return;
611
                    }
612
                });
613
            }
614
        } else {
615
            Daemon::$logpointer = null;
616
            Daemon::$logpointerAsync = null;
617
        }
618
    }
619
620
    /**
621
     * Get state of workers.
622
     * @return array - information.
623
     */
624
    public static function getStateOfWorkers()
625
    {
626
        $offset = 0;
627
628
        $stat = [
629
            'idle' => 0,
630
            'busy' => 0,
631
            'alive' => 0,
632
            'shutdown' => 0,
633
            'preinit' => 0,
634
            'init' => 0,
635
            'reloading' => 0,
636
        ];
637
        $readed = 0;
638
        while (($buf = Daemon::$shm_wstate->read($readed, 1024)) !== false) {
639
            $buflen = mb_orig_strlen($buf);
640
            $readed += $buflen;
641
            for ($i = 0; $i < $buflen; ++$i) {
642
                $code = ord($buf[$i]);
643
                if ($code === 0) {
644
                    break 2;
645
                }
646
                if ($code >= 100) {
647
                    // reloaded (shutdown)
648
                    $code -= 100;
649
                    if ($code !== Daemon::WSTATE_SHUTDOWN) {
650
                        ++$stat['alive'];
651
                        if (Daemon::$process instanceof Thread\Master) {
652
                            Daemon::$process->reloadWorker($offset + $i + 1);
653
                            ++$stat['reloading'];
654
                            continue;
655
                        }
656
                    }
657
                }
658
                if ($code === Daemon::WSTATE_IDLE) {
659
                    // idle
660
                    ++$stat['alive'];
661
                    ++$stat['idle'];
662
                } elseif ($code === Daemon::WSTATE_BUSY) {
663
                    // busy
664
                    ++$stat['alive'];
665
                    ++$stat['busy'];
666
                } elseif ($code === Daemon::WSTATE_SHUTDOWN) {
667
                    // shutdown
668
                    ++$stat['shutdown'];
669
                } elseif ($code === Daemon::WSTATE_PREINIT) {
670
                    // pre-init
671
                    ++$stat['alive'];
672
                    ++$stat['preinit'];
673
                    ++$stat['idle'];
674
                } elseif ($code === Daemon::WSTATE_INIT) { // init
675
                    ++$stat['alive'];
676
                    ++$stat['init'];
677
                    ++$stat['idle'];
678
                }
679
            }
680
        }
681
        return $stat;
682
    }
683
684
    /**
685
     *  Send message to the log
686
     *
687
     * @param array ...$args
688
     */
689
    public static function log(...$args)
690
    {
691
        if (sizeof($args) === 1) {
692
            $msg = is_scalar($args[0]) ? $args[0] : Debug::dump($args[0]);
693
        } else {
694
            $msg = Debug::dump($args);
695
        }
696
697
        $mt = explode(' ', microtime());
698
699
        if (is_resource(STDERR)) {
700
            fwrite(STDERR, '[PHPD] ' . $msg . "\n");
701
        }
702
703
        $msg = str_replace("\x01", $msg, date(strtr(Daemon::$config->logformat->value,
704
                ['%msg%' => "\x01", '\\u' => '\\u', 'u' => sprintf('%06d', $mt[0] * 1000000)]))) . "\n";
705
706
        if (Daemon::$logpointerAsync) {
707
            Daemon::$logpointerAsync->write($msg);
708
        } elseif (Daemon::$logpointer) {
709
            fwrite(Daemon::$logpointer, $msg);
710
        }
711
    }
712
713
    /**
714
     * Spawn a master process
715
     * @return null|integer - success
716
     */
717
    public static function spawnMaster()
718
    {
719
        Daemon::$masters->push($thread = new Thread\Master);
720
        $thread->start();
721
722
        if (-1 === $thread->getPid()) {
723
            Daemon::log('could not start master');
724
            exit(0);
725
        }
726
727
        return $thread->getPid();
728
    }
729
730
    /**
731
     * Run worker thread
732
     * @return void
733
     */
734
    public static function runWorker()
735
    {
736
        Daemon::$runworkerMode = true;
737
        $thread = new Thread\Worker;
738
        $thread();
739
    }
740
}
741