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 |
|
|
|
|
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) { |
|
|
|
|
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) { |
|
|
|
|
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 = []) |
|
|
|
|
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) { |
|
|
|
|
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) { |
|
|
|
|
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
|
|
|
|
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.