├── runtime
├── logs
│ └── .gitignore
├── views
│ └── .gitignore
└── .gitignore
├── windows.bat
├── app
├── functions.php
├── process
│ ├── Http.php
│ └── Monitor.php
├── view
│ └── index
│ │ └── view.html
├── model
│ └── Test.php
├── controller
│ └── IndexController.php
└── middleware
│ └── StaticFile.php
├── public
└── favicon.ico
├── .gitignore
├── start.php
├── support
├── bootstrap.php
├── Request.php
└── Response.php
├── docker-compose.yml
├── config
├── dependence.php
├── middleware.php
├── container.php
├── route.php
├── bootstrap.php
├── exception.php
├── view.php
├── autoload.php
├── static.php
├── translation.php
├── server.php
├── app.php
├── log.php
├── session.php
└── process.php
├── Dockerfile
├── LICENSE
├── composer.json
├── README.md
└── windows.php
/runtime/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/runtime/views/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/windows.bat:
--------------------------------------------------------------------------------
1 | CHCP 65001
2 | php windows.php
3 | pause
--------------------------------------------------------------------------------
/runtime/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !logs
3 | !views
4 | !.gitignore
5 |
--------------------------------------------------------------------------------
/app/functions.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | webman
9 |
10 |
11 |
12 | hello =htmlspecialchars($name)?>
13 |
14 |
15 |
--------------------------------------------------------------------------------
/config/dependence.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | return [];
--------------------------------------------------------------------------------
/config/middleware.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | return [];
--------------------------------------------------------------------------------
/config/container.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | return new Webman\Container;
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:8.3.22-cli-alpine
2 |
3 | RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
4 |
5 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
6 | && apk update --no-cache \
7 | && docker-php-source extract
8 |
9 | # install extensions
10 | RUN docker-php-ext-install pdo pdo_mysql -j$(nproc) pcntl
11 |
12 | # enable opcache and pcntl
13 | RUN docker-php-ext-enable opcache pcntl
14 | RUN docker-php-source delete \
15 | rm -rf /var/cache/apk/*
16 |
17 | RUN mkdir -p /app
18 | WORKDIR /app
--------------------------------------------------------------------------------
/config/route.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | use Webman\Route;
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/config/bootstrap.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | return [
16 | support\bootstrap\Session::class,
17 | ];
18 |
--------------------------------------------------------------------------------
/config/exception.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | return [
16 | '' => support\exception\Handler::class,
17 | ];
--------------------------------------------------------------------------------
/app/model/Test.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | namespace support;
16 |
17 | /**
18 | * Class Request
19 | * @package support
20 | */
21 | class Request extends \Webman\Http\Request
22 | {
23 |
24 | }
--------------------------------------------------------------------------------
/support/Response.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | namespace support;
16 |
17 | /**
18 | * Class Response
19 | * @package support
20 | */
21 | class Response extends \Webman\Http\Response
22 | {
23 |
24 | }
--------------------------------------------------------------------------------
/config/view.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | use support\view\Raw;
16 | use support\view\Twig;
17 | use support\view\Blade;
18 | use support\view\ThinkPHP;
19 |
20 | return [
21 | 'handler' => Raw::class
22 | ];
23 |
--------------------------------------------------------------------------------
/config/autoload.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | return [
16 | 'files' => [
17 | base_path() . '/app/functions.php',
18 | base_path() . '/support/Request.php',
19 | base_path() . '/support/Response.php',
20 | ]
21 | ];
22 |
--------------------------------------------------------------------------------
/config/static.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | /**
16 | * Static file settings
17 | */
18 | return [
19 | 'enable' => true,
20 | 'middleware' => [ // Static file Middleware
21 | //app\middleware\StaticFile::class,
22 | ],
23 | ];
--------------------------------------------------------------------------------
/config/translation.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | /**
16 | * Multilingual configuration
17 | */
18 | return [
19 | // Default language
20 | 'locale' => 'zh_CN',
21 | // Fallback language
22 | 'fallback_locale' => ['zh_CN', 'en'],
23 | // Folder where language files are stored
24 | 'path' => base_path() . '/resource/translations',
25 | ];
--------------------------------------------------------------------------------
/config/server.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | return [
16 | 'event_loop' => '',
17 | 'stop_timeout' => 2,
18 | 'pid_file' => runtime_path() . '/webman.pid',
19 | 'status_file' => runtime_path() . '/webman.status',
20 | 'stdout_file' => runtime_path() . '/logs/stdout.log',
21 | 'log_file' => runtime_path() . '/logs/workerman.log',
22 | 'max_package_size' => 10 * 1024 * 1024
23 | ];
24 |
--------------------------------------------------------------------------------
/app/controller/IndexController.php:
--------------------------------------------------------------------------------
1 |
13 | * {
14 | padding: 0;
15 | margin: 0;
16 | }
17 | iframe {
18 | border: none;
19 | overflow: scroll;
20 | }
21 |
22 |
29 | EOF;
30 | }
31 |
32 | public function view(Request $request)
33 | {
34 | return view('index/view', ['name' => 'webman']);
35 | }
36 |
37 | public function json(Request $request)
38 | {
39 | return json(['code' => 0, 'msg' => 'ok']);
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/config/app.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | use support\Request;
16 |
17 | return [
18 | 'debug' => true,
19 | 'error_reporting' => E_ALL,
20 | 'default_timezone' => 'Asia/Shanghai',
21 | 'request_class' => Request::class,
22 | 'public_path' => base_path() . DIRECTORY_SEPARATOR . 'public',
23 | 'runtime_path' => base_path(false) . DIRECTORY_SEPARATOR . 'runtime',
24 | 'controller_suffix' => 'Controller',
25 | 'controller_reuse' => false,
26 | ];
27 |
--------------------------------------------------------------------------------
/config/log.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | return [
16 | 'default' => [
17 | 'handlers' => [
18 | [
19 | 'class' => Monolog\Handler\RotatingFileHandler::class,
20 | 'constructor' => [
21 | runtime_path() . '/logs/webman.log',
22 | 7, //$maxFiles
23 | Monolog\Logger::DEBUG,
24 | ],
25 | 'formatter' => [
26 | 'class' => Monolog\Formatter\LineFormatter::class,
27 | 'constructor' => [null, 'Y-m-d H:i:s', true],
28 | ],
29 | ]
30 | ],
31 | ],
32 | ];
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 walkor and contributors (see https://github.com/walkor/webman/contributors)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app/middleware/StaticFile.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | namespace app\middleware;
16 |
17 | use Webman\MiddlewareInterface;
18 | use Webman\Http\Response;
19 | use Webman\Http\Request;
20 |
21 | /**
22 | * Class StaticFile
23 | * @package app\middleware
24 | */
25 | class StaticFile implements MiddlewareInterface
26 | {
27 | public function process(Request $request, callable $handler): Response
28 | {
29 | // Access to files beginning with. Is prohibited
30 | if (strpos($request->path(), '/.') !== false) {
31 | return response('403 forbidden
', 403);
32 | }
33 | /** @var Response $response */
34 | $response = $handler($request);
35 | // Add cross domain HTTP header
36 | /*$response->withHeaders([
37 | 'Access-Control-Allow-Origin' => '*',
38 | 'Access-Control-Allow-Credentials' => 'true',
39 | ]);*/
40 | return $response;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "workerman/webman",
3 | "type": "project",
4 | "keywords": [
5 | "high performance",
6 | "http service"
7 | ],
8 | "homepage": "https://www.workerman.net",
9 | "license": "MIT",
10 | "description": "High performance HTTP Service Framework.",
11 | "authors": [
12 | {
13 | "name": "walkor",
14 | "email": "walkor@workerman.net",
15 | "homepage": "https://www.workerman.net",
16 | "role": "Developer"
17 | }
18 | ],
19 | "support": {
20 | "email": "walkor@workerman.net",
21 | "issues": "https://github.com/walkor/webman/issues",
22 | "forum": "https://wenda.workerman.net/",
23 | "wiki": "https://workerman.net/doc/webman",
24 | "source": "https://github.com/walkor/webman"
25 | },
26 | "require": {
27 | "php": ">=8.1",
28 | "workerman/webman-framework": "^2.1",
29 | "monolog/monolog": "^2.0"
30 | },
31 | "suggest": {
32 | "ext-event": "For better performance. "
33 | },
34 | "autoload": {
35 | "psr-4": {
36 | "": "./",
37 | "app\\": "./app",
38 | "App\\": "./app",
39 | "app\\View\\Components\\": "./app/view/components"
40 | }
41 | },
42 | "scripts": {
43 | "post-package-install": [
44 | "support\\Plugin::install"
45 | ],
46 | "post-package-update": [
47 | "support\\Plugin::install"
48 | ],
49 | "pre-package-uninstall": [
50 | "support\\Plugin::uninstall"
51 | ]
52 | },
53 | "minimum-stability": "dev",
54 | "prefer-stable": true
55 | }
56 |
--------------------------------------------------------------------------------
/config/session.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | use Webman\Session\FileSessionHandler;
16 | use Webman\Session\RedisSessionHandler;
17 | use Webman\Session\RedisClusterSessionHandler;
18 |
19 | return [
20 |
21 | 'type' => 'file', // or redis or redis_cluster
22 |
23 | 'handler' => FileSessionHandler::class,
24 |
25 | 'config' => [
26 | 'file' => [
27 | 'save_path' => runtime_path() . '/sessions',
28 | ],
29 | 'redis' => [
30 | 'host' => '127.0.0.1',
31 | 'port' => 6379,
32 | 'auth' => '',
33 | 'timeout' => 2,
34 | 'database' => '',
35 | 'prefix' => 'redis_session_',
36 | ],
37 | 'redis_cluster' => [
38 | 'host' => ['127.0.0.1:7000', '127.0.0.1:7001', '127.0.0.1:7001'],
39 | 'timeout' => 2,
40 | 'auth' => '',
41 | 'prefix' => 'redis_session_',
42 | ]
43 | ],
44 |
45 | 'session_name' => 'PHPSID',
46 |
47 | 'auto_update_timestamp' => false,
48 |
49 | 'lifetime' => 7*24*60*60,
50 |
51 | 'cookie_lifetime' => 365*24*60*60,
52 |
53 | 'cookie_path' => '/',
54 |
55 | 'domain' => '',
56 |
57 | 'http_only' => true,
58 |
59 | 'secure' => false,
60 |
61 | 'same_site' => '',
62 |
63 | 'gc_probability' => [1, 1000],
64 |
65 | ];
66 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
webman
3 |
4 | 基于
workerman开发的超高性能PHP框架
5 |
6 |
7 |
学习
8 |
9 |
32 |
33 |
34 |
35 |
赞助商
36 |
37 |
特别赞助
38 |
39 |
40 |
41 |
42 |
铂金赞助
43 |

44 |

45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
请作者喝咖啡
53 |
54 |

55 |

56 |
57 |
如果您觉得webman对您有所帮助,欢迎捐赠。
58 |
59 |
60 |
61 |
62 |
63 |
64 |
LICENSE
65 | The webman is open-sourced software licensed under the MIT.
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/config/process.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | use support\Log;
16 | use support\Request;
17 | use app\process\Http;
18 |
19 | global $argv;
20 |
21 | return [
22 | 'webman' => [
23 | 'handler' => Http::class,
24 | 'listen' => 'http://0.0.0.0:8787',
25 | 'count' => cpu_count() * 4,
26 | 'user' => '',
27 | 'group' => '',
28 | 'reusePort' => false,
29 | 'eventLoop' => '',
30 | 'context' => [],
31 | 'constructor' => [
32 | 'requestClass' => Request::class,
33 | 'logger' => Log::channel('default'),
34 | 'appPath' => app_path(),
35 | 'publicPath' => public_path()
36 | ]
37 | ],
38 | // File update detection and automatic reload
39 | 'monitor' => [
40 | 'handler' => app\process\Monitor::class,
41 | 'reloadable' => false,
42 | 'constructor' => [
43 | // Monitor these directories
44 | 'monitorDir' => array_merge([
45 | app_path(),
46 | config_path(),
47 | base_path() . '/process',
48 | base_path() . '/support',
49 | base_path() . '/resource',
50 | base_path() . '/.env',
51 | ], glob(base_path() . '/plugin/*/app'), glob(base_path() . '/plugin/*/config'), glob(base_path() . '/plugin/*/api')),
52 | // Files with these suffixes will be monitored
53 | 'monitorExtensions' => [
54 | 'php', 'html', 'htm', 'env'
55 | ],
56 | 'options' => [
57 | 'enable_file_monitor' => !in_array('-d', $argv) && DIRECTORY_SEPARATOR === '/',
58 | 'enable_memory_monitor' => DIRECTORY_SEPARATOR === '/',
59 | ]
60 | ]
61 | ]
62 | ];
63 |
--------------------------------------------------------------------------------
/windows.php:
--------------------------------------------------------------------------------
1 | load();
18 | } else {
19 | Dotenv::createMutable(base_path())->load();
20 | }
21 | }
22 |
23 | App::loadAllConfig(['route']);
24 |
25 | $errorReporting = config('app.error_reporting');
26 | if (isset($errorReporting)) {
27 | error_reporting($errorReporting);
28 | }
29 |
30 | $runtimeProcessPath = runtime_path() . DIRECTORY_SEPARATOR . '/windows';
31 | $paths = [
32 | $runtimeProcessPath,
33 | runtime_path('logs'),
34 | runtime_path('views')
35 | ];
36 | foreach ($paths as $path) {
37 | if (!is_dir($path)) {
38 | mkdir($path, 0777, true);
39 | }
40 | }
41 |
42 | $processFiles = [];
43 | if (config('server.listen')) {
44 | $processFiles[] = __DIR__ . DIRECTORY_SEPARATOR . 'start.php';
45 | }
46 | foreach (config('process', []) as $processName => $config) {
47 | if ($config['enable'] ?? true) {
48 | $processFiles[] = write_process_file($runtimeProcessPath, $processName, '');
49 | }
50 | }
51 |
52 | foreach (config('plugin', []) as $firm => $projects) {
53 | foreach ($projects as $name => $project) {
54 | if (!is_array($project)) {
55 | continue;
56 | }
57 | foreach ($project['process'] ?? [] as $processName => $config) {
58 | if ($config['enable'] ?? true) {
59 | $processFiles[] = write_process_file($runtimeProcessPath, $processName, "$firm.$name");
60 | }
61 | }
62 | }
63 | foreach ($projects['process'] ?? [] as $processName => $config) {
64 | if ($config['enable'] ?? true) {
65 | $processFiles[] = write_process_file($runtimeProcessPath, $processName, $firm);
66 | }
67 | }
68 | }
69 |
70 | function write_process_file($runtimeProcessPath, $processName, $firm): string
71 | {
72 | $processParam = $firm ? "plugin.$firm.$processName" : $processName;
73 | $configParam = $firm ? "config('plugin.$firm.process')['$processName']" : "config('process')['$processName']";
74 | $fileContent = << true]);
125 | if (!$resource) {
126 | exit("Can not execute $cmd\r\n");
127 | }
128 | return $resource;
129 | }
130 |
131 | $resource = popen_processes($processFiles);
132 | echo "\r\n";
133 | while (1) {
134 | sleep(1);
135 | if (!empty($monitor) && $monitor->checkAllFilesChange()) {
136 | $status = proc_get_status($resource);
137 | $pid = $status['pid'];
138 | shell_exec("taskkill /F /T /PID $pid");
139 | proc_close($resource);
140 | $resource = popen_processes($processFiles);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/app/process/Monitor.php:
--------------------------------------------------------------------------------
1 |
10 | * @copyright walkor
11 | * @link http://www.workerman.net/
12 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
13 | */
14 |
15 | namespace app\process;
16 |
17 | use FilesystemIterator;
18 | use RecursiveDirectoryIterator;
19 | use RecursiveIteratorIterator;
20 | use SplFileInfo;
21 | use Workerman\Timer;
22 | use Workerman\Worker;
23 |
24 | /**
25 | * Class FileMonitor
26 | * @package process
27 | */
28 | class Monitor
29 | {
30 | /**
31 | * @var array
32 | */
33 | protected array $paths = [];
34 |
35 | /**
36 | * @var array
37 | */
38 | protected array $extensions = [];
39 |
40 | /**
41 | * @var array
42 | */
43 | protected array $loadedFiles = [];
44 |
45 | /**
46 | * @var int
47 | */
48 | protected int $ppid = 0;
49 |
50 | /**
51 | * Pause monitor
52 | * @return void
53 | */
54 | public static function pause(): void
55 | {
56 | file_put_contents(static::lockFile(), time());
57 | }
58 |
59 | /**
60 | * Resume monitor
61 | * @return void
62 | */
63 | public static function resume(): void
64 | {
65 | clearstatcache();
66 | if (is_file(static::lockFile())) {
67 | unlink(static::lockFile());
68 | }
69 | }
70 |
71 | /**
72 | * Whether monitor is paused
73 | * @return bool
74 | */
75 | public static function isPaused(): bool
76 | {
77 | clearstatcache();
78 | return file_exists(static::lockFile());
79 | }
80 |
81 | /**
82 | * Lock file
83 | * @return string
84 | */
85 | protected static function lockFile(): string
86 | {
87 | return runtime_path('monitor.lock');
88 | }
89 |
90 | /**
91 | * FileMonitor constructor.
92 | * @param $monitorDir
93 | * @param $monitorExtensions
94 | * @param array $options
95 | */
96 | public function __construct($monitorDir, $monitorExtensions, array $options = [])
97 | {
98 | $this->ppid = function_exists('posix_getppid') ? posix_getppid() : 0;
99 | static::resume();
100 | $this->paths = (array)$monitorDir;
101 | $this->extensions = $monitorExtensions;
102 | foreach (get_included_files() as $index => $file) {
103 | $this->loadedFiles[$file] = $index;
104 | if (strpos($file, 'webman-framework/src/support/App.php')) {
105 | break;
106 | }
107 | }
108 | if (!Worker::getAllWorkers()) {
109 | return;
110 | }
111 | $disableFunctions = explode(',', ini_get('disable_functions'));
112 | if (in_array('exec', $disableFunctions, true)) {
113 | echo "\nMonitor file change turned off because exec() has been disabled by disable_functions setting in " . PHP_CONFIG_FILE_PATH . "/php.ini\n";
114 | } else {
115 | if ($options['enable_file_monitor'] ?? true) {
116 | Timer::add(1, function () {
117 | $this->checkAllFilesChange();
118 | });
119 | }
120 | }
121 |
122 | $memoryLimit = $this->getMemoryLimit($options['memory_limit'] ?? null);
123 | if ($memoryLimit && ($options['enable_memory_monitor'] ?? true)) {
124 | Timer::add(60, [$this, 'checkMemory'], [$memoryLimit]);
125 | }
126 | }
127 |
128 | /**
129 | * @param $monitorDir
130 | * @return bool
131 | */
132 | public function checkFilesChange($monitorDir): bool
133 | {
134 | static $lastMtime, $tooManyFilesCheck;
135 | if (!$lastMtime) {
136 | $lastMtime = time();
137 | }
138 | clearstatcache();
139 | if (!is_dir($monitorDir)) {
140 | if (!is_file($monitorDir)) {
141 | return false;
142 | }
143 | $iterator = [new SplFileInfo($monitorDir)];
144 | } else {
145 | // recursive traversal directory
146 | $dirIterator = new RecursiveDirectoryIterator($monitorDir, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS);
147 | $iterator = new RecursiveIteratorIterator($dirIterator);
148 | }
149 | $count = 0;
150 | foreach ($iterator as $file) {
151 | $count ++;
152 | /** var SplFileInfo $file */
153 | if (is_dir($file->getRealPath())) {
154 | continue;
155 | }
156 | // check mtime
157 | if (in_array($file->getExtension(), $this->extensions, true) && $lastMtime < $file->getMTime()) {
158 | $lastMtime = $file->getMTime();
159 | if (DIRECTORY_SEPARATOR === '/' && isset($this->loadedFiles[$file->getRealPath()])) {
160 | echo "$file updated but cannot be reloaded because only auto-loaded files support reload.\n";
161 | continue;
162 | }
163 | $var = 0;
164 | exec('"'.PHP_BINARY . '" -l ' . $file, $out, $var);
165 | if ($var) {
166 | continue;
167 | }
168 | // send SIGUSR1 signal to master process for reload
169 | if (DIRECTORY_SEPARATOR === '/') {
170 | if ($masterPid = $this->getMasterPid()) {
171 | echo $file . " updated and reload\n";
172 | posix_kill($masterPid, SIGUSR1);
173 | } else {
174 | echo "Master process has gone away and can not reload\n";
175 | }
176 | return true;
177 | }
178 | echo $file . " updated and reload\n";
179 | return true;
180 | }
181 | }
182 | if (!$tooManyFilesCheck && $count > 1000) {
183 | echo "Monitor: There are too many files ($count files) in $monitorDir which makes file monitoring very slow\n";
184 | $tooManyFilesCheck = 1;
185 | }
186 | return false;
187 | }
188 |
189 | /**
190 | * @return int
191 | */
192 | public function getMasterPid(): int
193 | {
194 | if ($this->ppid === 0) {
195 | return 0;
196 | }
197 | if (function_exists('posix_kill') && !posix_kill($this->ppid, 0)) {
198 | echo "Master process has gone away\n";
199 | return $this->ppid = 0;
200 | }
201 | if (PHP_OS_FAMILY !== 'Linux') {
202 | return $this->ppid;
203 | }
204 | $cmdline = "/proc/$this->ppid/cmdline";
205 | if (!is_readable($cmdline) || !($content = file_get_contents($cmdline)) || (!str_contains($content, 'WorkerMan') && !str_contains($content, 'php'))) {
206 | // Process not exist
207 | $this->ppid = 0;
208 | }
209 | return $this->ppid;
210 | }
211 |
212 | /**
213 | * @return bool
214 | */
215 | public function checkAllFilesChange(): bool
216 | {
217 | if (static::isPaused()) {
218 | return false;
219 | }
220 | foreach ($this->paths as $path) {
221 | if ($this->checkFilesChange($path)) {
222 | return true;
223 | }
224 | }
225 | return false;
226 | }
227 |
228 | /**
229 | * @param $memoryLimit
230 | * @return void
231 | */
232 | public function checkMemory($memoryLimit): void
233 | {
234 | if (static::isPaused() || $memoryLimit <= 0) {
235 | return;
236 | }
237 | $masterPid = $this->getMasterPid();
238 | if ($masterPid <= 0) {
239 | echo "Master process has gone away\n";
240 | return;
241 | }
242 |
243 | $childrenFile = "/proc/$masterPid/task/$masterPid/children";
244 | if (!is_file($childrenFile) || !($children = file_get_contents($childrenFile))) {
245 | return;
246 | }
247 | foreach (explode(' ', $children) as $pid) {
248 | $pid = (int)$pid;
249 | $statusFile = "/proc/$pid/status";
250 | if (!is_file($statusFile) || !($status = file_get_contents($statusFile))) {
251 | continue;
252 | }
253 | $mem = 0;
254 | if (preg_match('/VmRSS\s*?:\s*?(\d+?)\s*?kB/', $status, $match)) {
255 | $mem = $match[1];
256 | }
257 | $mem = (int)($mem / 1024);
258 | if ($mem >= $memoryLimit) {
259 | posix_kill($pid, SIGINT);
260 | }
261 | }
262 | }
263 |
264 | /**
265 | * Get memory limit
266 | * @param $memoryLimit
267 | * @return int
268 | */
269 | protected function getMemoryLimit($memoryLimit): int
270 | {
271 | if ($memoryLimit === 0) {
272 | return 0;
273 | }
274 | $usePhpIni = false;
275 | if (!$memoryLimit) {
276 | $memoryLimit = ini_get('memory_limit');
277 | $usePhpIni = true;
278 | }
279 |
280 | if ($memoryLimit == -1) {
281 | return 0;
282 | }
283 | $unit = strtolower($memoryLimit[strlen($memoryLimit) - 1]);
284 | $memoryLimit = (int)$memoryLimit;
285 | if ($unit === 'g') {
286 | $memoryLimit = 1024 * $memoryLimit;
287 | } else if ($unit === 'k') {
288 | $memoryLimit = ($memoryLimit / 1024);
289 | } else if ($unit === 'm') {
290 | $memoryLimit = (int)($memoryLimit);
291 | } else if ($unit === 't') {
292 | $memoryLimit = (1024 * 1024 * $memoryLimit);
293 | } else {
294 | $memoryLimit = ($memoryLimit / (1024 * 1024));
295 | }
296 | if ($memoryLimit < 50) {
297 | $memoryLimit = 50;
298 | }
299 | if ($usePhpIni) {
300 | $memoryLimit = (0.8 * $memoryLimit);
301 | }
302 | return (int)$memoryLimit;
303 | }
304 |
305 | }
306 |
--------------------------------------------------------------------------------