mx 3 years ago
commit
8eb134ee05

+ 4 - 0
.github/FUNDING.yml

@@ -0,0 +1,4 @@
+# These are supported funding model platforms
+
+open_collective: walkor
+patreon: walkor

+ 7 - 0
.gitignore

@@ -0,0 +1,7 @@
+/.idea
+/.vscode
+/vendor
+*.log
+.env
+/tests/tmp
+/tests/.phpunit.result.cache

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 walkor<walkor@workerman.net> and contributors (see https://github.com/walkor/webman/contributors)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 10 - 0
README.md

@@ -0,0 +1,10 @@
+## webman testing project
+
+1. run as debug
+
+`php start.php start`
+
+2. run as production
+
+`php start.php start -d`
+

+ 24 - 0
app/controller/Index.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace app\controller;
+
+use support\Request;
+
+class Index
+{
+    public function index(Request $request)
+    {
+        return response('hello webman');
+    }
+
+    public function view(Request $request)
+    {
+        return view('index/view', ['name' => 'webman']);
+    }
+
+    public function json(Request $request)
+    {
+        return json(['code' => 0, 'msg' => 'ok']);
+    }
+
+}

+ 4 - 0
app/functions.php

@@ -0,0 +1,4 @@
+<?php
+/**
+ * Here is your custom functions.
+ */

+ 42 - 0
app/middleware/StaticFile.php

@@ -0,0 +1,42 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+namespace app\middleware;
+
+use Webman\MiddlewareInterface;
+use Webman\Http\Response;
+use Webman\Http\Request;
+
+/**
+ * Class StaticFile
+ * @package app\middleware
+ */
+class StaticFile implements MiddlewareInterface
+{
+    public function process(Request $request, callable $next): Response
+    {
+        // Access to files beginning with. Is prohibited
+        if (strpos($request->path(), '/.') !== false) {
+            return response('<h1>403 forbidden</h1>', 403);
+        }
+        /** @var Response $response */
+        $response = $next($request);
+        // Add cross domain HTTP header
+        /*$response->withHeaders([
+            'Access-Control-Allow-Origin'      => '*',
+            'Access-Control-Allow-Credentials' => 'true',
+        ]);*/
+        return $response;
+    }
+}

+ 29 - 0
app/model/Test.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace app\model;
+
+use support\Model;
+
+class Test extends Model
+{
+    /**
+     * The table associated with the model.
+     *
+     * @var string
+     */
+    protected $table = 'test';
+
+    /**
+     * The primary key associated with the table.
+     *
+     * @var string
+     */
+    protected $primaryKey = 'id';
+
+    /**
+     * Indicates if the model should be timestamped.
+     *
+     * @var bool
+     */
+    public $timestamps = false;
+}

+ 14 - 0
app/view/index/view.html

@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="shortcut icon" href="/favicon.ico"/>
+    <title>webman</title>
+
+</head>
+<body>
+hello <?=htmlspecialchars($name)?>
+</body>
+</html>

+ 54 - 0
composer.json

@@ -0,0 +1,54 @@
+{
+  "name": "workerman/webman",
+  "type": "project",
+  "keywords": [
+    "high performance",
+    "http service"
+  ],
+  "homepage": "http://www.workerman.net",
+  "license": "MIT",
+  "description": "High performance HTTP Service Framework.",
+  "authors": [
+    {
+      "name": "walkor",
+      "email": "walkor@workerman.net",
+      "homepage": "http://www.workerman.net",
+      "role": "Developer"
+    }
+  ],
+  "support": {
+    "email": "walkor@workerman.net",
+    "issues": "https://github.com/walkor/webman/issues",
+    "forum": "http://wenda.workerman.net/",
+    "wiki": "http://workerman.net/doc/webman",
+    "source": "https://github.com/walkor/webman"
+  },
+  "require": {
+    "php": ">=7.2",
+    "workerman/webman-framework": "^1.3.0",
+    "monolog/monolog": "^2.0"
+  },
+  "suggest": {
+    "ext-event": "For better performance. "
+  },
+  "autoload": {
+    "psr-4": {
+      "": "./",
+      "App\\": "./app"
+    },
+    "files": [
+      "./support/helpers.php"
+    ]
+  },
+  "scripts": {
+    "post-package-install": [
+      "support\\Plugin::install"
+    ],
+    "post-package-update": [
+      "support\\Plugin::install"
+    ],
+    "pre-package-uninstall": [
+      "support\\Plugin::uninstall"
+    ]
+  }
+}

+ 407 - 0
composer.lock

@@ -0,0 +1,407 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "6dc6ac51f2736b5302038d2aff7cd5c8",
+    "packages": [
+        {
+            "name": "monolog/monolog",
+            "version": "2.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Seldaek/monolog.git",
+                "reference": "d7fd7450628561ba697b7097d86db72662f54aef"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d7fd7450628561ba697b7097d86db72662f54aef",
+                "reference": "d7fd7450628561ba697b7097d86db72662f54aef",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2",
+                "psr/log": "^1.0.1 || ^2.0 || ^3.0"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0"
+            },
+            "require-dev": {
+                "aws/aws-sdk-php": "^2.4.9 || ^3.0",
+                "doctrine/couchdb": "~1.0@dev",
+                "elasticsearch/elasticsearch": "^7",
+                "graylog2/gelf-php": "^1.4.2",
+                "mongodb/mongodb": "^1.8",
+                "php-amqplib/php-amqplib": "~2.4 || ^3",
+                "php-console/php-console": "^3.1.3",
+                "phpspec/prophecy": "^1.6.1",
+                "phpstan/phpstan": "^0.12.91",
+                "phpunit/phpunit": "^8.5",
+                "predis/predis": "^1.1",
+                "rollbar/rollbar": "^1.3 || ^2 || ^3",
+                "ruflin/elastica": ">=0.90@dev",
+                "swiftmailer/swiftmailer": "^5.3|^6.0"
+            },
+            "suggest": {
+                "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+                "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+                "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
+                "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+                "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
+                "ext-mbstring": "Allow to work properly with unicode symbols",
+                "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
+                "ext-openssl": "Required to send log messages using SSL",
+                "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
+                "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+                "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
+                "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+                "php-console/php-console": "Allow sending log messages to Google Chrome",
+                "rollbar/rollbar": "Allow sending log messages to Rollbar",
+                "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Monolog\\": "src/Monolog"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "https://seld.be"
+                }
+            ],
+            "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+            "homepage": "https://github.com/Seldaek/monolog",
+            "keywords": [
+                "log",
+                "logging",
+                "psr-3"
+            ],
+            "support": {
+                "issues": "https://github.com/Seldaek/monolog/issues",
+                "source": "https://github.com/Seldaek/monolog/tree/2.4.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/Seldaek",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-03-14T12:44:37+00:00"
+        },
+        {
+            "name": "nikic/fast-route",
+            "version": "v1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nikic/FastRoute.git",
+                "reference": "181d480e08d9476e61381e04a71b34dc0432e812"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812",
+                "reference": "181d480e08d9476e61381e04a71b34dc0432e812",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.35|~5.7"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "FastRoute\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Nikita Popov",
+                    "email": "nikic@php.net"
+                }
+            ],
+            "description": "Fast request router for PHP",
+            "keywords": [
+                "router",
+                "routing"
+            ],
+            "support": {
+                "issues": "https://github.com/nikic/FastRoute/issues",
+                "source": "https://github.com/nikic/FastRoute/tree/master"
+            },
+            "time": "2018-02-13T20:26:39+00:00"
+        },
+        {
+            "name": "psr/container",
+            "version": "2.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+                "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/container/issues",
+                "source": "https://github.com/php-fig/container/tree/2.0.2"
+            },
+            "time": "2021-11-05T16:47:00+00:00"
+        },
+        {
+            "name": "psr/log",
+            "version": "1.1.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/log.git",
+                "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
+                "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Log\\": "Psr/Log/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for logging libraries",
+            "homepage": "https://github.com/php-fig/log",
+            "keywords": [
+                "log",
+                "psr",
+                "psr-3"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/log/tree/1.1.4"
+            },
+            "time": "2021-05-03T11:20:27+00:00"
+        },
+        {
+            "name": "workerman/webman-framework",
+            "version": "v1.3.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/walkor/webman-framework.git",
+                "reference": "cbc82cd13477492787122be9ccbd20124eff88d5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/walkor/webman-framework/zipball/cbc82cd13477492787122be9ccbd20124eff88d5",
+                "reference": "cbc82cd13477492787122be9ccbd20124eff88d5",
+                "shasum": ""
+            },
+            "require": {
+                "nikic/fast-route": "^1.3",
+                "php": ">=7.2",
+                "psr/container": ">=1.0",
+                "workerman/workerman": "^4.0.4"
+            },
+            "suggest": {
+                "ext-event": "For better performance. "
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Webman\\": "./src",
+                    "Support\\": "./src/support",
+                    "support\\": "./src/support",
+                    "Support\\View\\": "./src/support/view",
+                    "Support\\Bootstrap\\": "./src/support/bootstrap",
+                    "Support\\Exception\\": "./src/support/exception"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "walkor",
+                    "email": "walkor@workerman.net",
+                    "homepage": "http://www.workerman.net",
+                    "role": "Developer"
+                }
+            ],
+            "description": "High performance HTTP Service Framework.",
+            "homepage": "http://www.workerman.net",
+            "keywords": [
+                "High Performance",
+                "http service"
+            ],
+            "support": {
+                "email": "walkor@workerman.net",
+                "forum": "http://wenda.workerman.net/",
+                "issues": "https://github.com/walkor/webman/issues",
+                "source": "https://github.com/walkor/webman-framework",
+                "wiki": "http://doc.workerman.net/"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/walkor",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://www.patreon.com/walkor",
+                    "type": "patreon"
+                }
+            ],
+            "time": "2022-03-25T08:15:45+00:00"
+        },
+        {
+            "name": "workerman/workerman",
+            "version": "v4.0.31",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/walkor/workerman.git",
+                "reference": "39df5a1627345678e8b83ac86efabafdf31ca9d5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/walkor/workerman/zipball/39df5a1627345678e8b83ac86efabafdf31ca9d5",
+                "reference": "39df5a1627345678e8b83ac86efabafdf31ca9d5",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3"
+            },
+            "suggest": {
+                "ext-event": "For better performance. "
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Workerman\\": "./"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "walkor",
+                    "email": "walkor@workerman.net",
+                    "homepage": "http://www.workerman.net",
+                    "role": "Developer"
+                }
+            ],
+            "description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.",
+            "homepage": "http://www.workerman.net",
+            "keywords": [
+                "asynchronous",
+                "event-loop"
+            ],
+            "support": {
+                "email": "walkor@workerman.net",
+                "forum": "http://wenda.workerman.net/",
+                "issues": "https://github.com/walkor/workerman/issues",
+                "source": "https://github.com/walkor/workerman",
+                "wiki": "http://doc.workerman.net/"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/workerman",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://www.patreon.com/walkor",
+                    "type": "patreon"
+                }
+            ],
+            "time": "2022-03-23T02:14:58+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": {
+        "php": ">=7.2"
+    },
+    "platform-dev": [],
+    "plugin-api-version": "2.2.0"
+}

+ 24 - 0
config/app.php

@@ -0,0 +1,24 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+use support\Request;
+
+return [
+    'debug' => true,
+    'default_timezone' => 'Asia/Shanghai',
+    'request_class' => Request::class,
+    'public_path' => base_path() . DIRECTORY_SEPARATOR . 'public',
+    'runtime_path' => base_path(false) . DIRECTORY_SEPARATOR . 'runtime',
+    'controller_suffix' => '',
+];

+ 19 - 0
config/autoload.php

@@ -0,0 +1,19 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+return [
+    'files' => [
+        base_path() . '/app/functions.php'
+    ]
+];

+ 18 - 0
config/bootstrap.php

@@ -0,0 +1,18 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+return [
+    support\bootstrap\Session::class,
+    support\bootstrap\LaravelDb::class,
+];

+ 15 - 0
config/container.php

@@ -0,0 +1,15 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+return new Webman\Container;

+ 15 - 0
config/database.php

@@ -0,0 +1,15 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+return [];

+ 15 - 0
config/dependence.php

@@ -0,0 +1,15 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+return [];

+ 17 - 0
config/exception.php

@@ -0,0 +1,17 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+return [
+    '' => support\exception\Handler::class,
+];

+ 32 - 0
config/log.php

@@ -0,0 +1,32 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+return [
+    'default' => [
+        'handlers' => [
+            [
+                'class' => Monolog\Handler\RotatingFileHandler::class,
+                'constructor' => [
+                    runtime_path() . '/logs/webman.log',
+                    7, //$maxFiles
+                    Monolog\Logger::DEBUG,
+                ],
+                'formatter' => [
+                    'class' => Monolog\Formatter\LineFormatter::class,
+                    'constructor' => [null, 'Y-m-d H:i:s', true],
+                ],
+            ]
+        ],
+    ],
+];

+ 15 - 0
config/middleware.php

@@ -0,0 +1,15 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+return [];

+ 37 - 0
config/process.php

@@ -0,0 +1,37 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+
+return [
+    // File update detection and automatic reload
+    'monitor' => [
+        'handler' => process\Monitor::class,
+        'reloadable' => false,
+        'constructor' => [
+            // Monitor these directories
+            'monitor_dir' => [
+                app_path(),
+                config_path(),
+                base_path() . '/process',
+                base_path() . '/support',
+                base_path() . '/resource',
+                base_path() . '/.env',
+            ],
+            // Files with these suffixes will be monitored
+            'monitor_extensions' => [
+                'php', 'html', 'htm', 'env'
+            ]
+        ]
+    ]
+];

+ 22 - 0
config/redis.php

@@ -0,0 +1,22 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+return [
+    'default' => [
+        'host' => '127.0.0.1',
+        'password' => null,
+        'port' => 6379,
+        'database' => 0,
+    ],
+];

+ 21 - 0
config/route.php

@@ -0,0 +1,21 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+use Webman\Route;
+
+
+
+
+
+

+ 30 - 0
config/server.php

@@ -0,0 +1,30 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+return [
+    'listen' => 'http://0.0.0.0:8787',
+    'transport' => 'tcp',
+    'context' => [],
+    'name' => 'webman',
+    'count' => cpu_count() * 2,
+    'user' => '',
+    'group' => '',
+    'reusePort' => false,
+    'event_loop' => '',
+    'pid_file' => runtime_path() . '/webman.pid',
+    'status_file' => runtime_path() . '/webman.status',
+    'stdout_file' => runtime_path() . '/logs/stdout.log',
+    'log_file' => runtime_path() . '/logs/workerman.log',
+    'max_package_size' => 10 * 1024 * 1024
+];

+ 42 - 0
config/session.php

@@ -0,0 +1,42 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+return [
+
+    'type' => 'file', // or redis or redis_cluster
+
+    'handler' => Webman\FileSessionHandler::class,
+
+    'config' => [
+        'file' => [
+            'save_path' => runtime_path() . '/sessions',
+        ],
+        'redis' => [
+            'host' => '127.0.0.1',
+            'port' => 6379,
+            'auth' => '',
+            'timeout' => 2,
+            'database' => '',
+            'prefix' => 'redis_session_',
+        ],
+        'redis_cluster' => [
+            'host' => ['127.0.0.1:7000', '127.0.0.1:7001', '127.0.0.1:7001'],
+            'timeout' => 2,
+            'auth' => '',
+            'prefix' => 'redis_session_',
+        ]
+    ],
+
+    'session_name' => 'PHPSID',
+];

+ 23 - 0
config/static.php

@@ -0,0 +1,23 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+/**
+ * Static file settings
+ */
+return [
+    'enable' => true,
+    'middleware' => [     // Static file Middleware
+        //app\middleware\StaticFile::class,
+    ],
+];

+ 25 - 0
config/translation.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+/**
+ * Multilingual configuration
+ */
+return [
+    // Default language
+    'locale' => 'zh_CN',
+    // Fallback language
+    'fallback_locale' => ['zh_CN', 'en'],
+    // Folder where language files are stored
+    'path' => base_path() . '/resource/translations',
+];

+ 22 - 0
config/view.php

@@ -0,0 +1,22 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+use support\view\Raw;
+use support\view\Twig;
+use support\view\Blade;
+use support\view\ThinkPHP;
+
+return [
+    'handler' => Raw::class
+];

+ 189 - 0
process/Monitor.php

@@ -0,0 +1,189 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+namespace process;
+
+use Workerman\Timer;
+use Workerman\Worker;
+
+/**
+ * Class FileMonitor
+ * @package process
+ */
+class Monitor
+{
+    /**
+     * @var array
+     */
+    protected $_paths = [];
+
+    /**
+     * @var array
+     */
+    protected $_extensions = [];
+
+    /**
+     * FileMonitor constructor.
+     * @param $monitor_dir
+     * @param $monitor_extensions
+     * @param $memory_limit
+     */
+    public function __construct($monitor_dir, $monitor_extensions, $memory_limit = null)
+    {
+        $this->_paths = (array)$monitor_dir;
+        $this->_extensions = $monitor_extensions;
+        if (!Worker::getAllWorkers()) {
+            return;
+        }
+        $disable_functions = explode(',', ini_get('disable_functions'));
+        if (in_array('exec', $disable_functions, true)) {
+            echo "\nMonitor file change turned off because exec() has been disabled by disable_functions setting in " . PHP_CONFIG_FILE_PATH . "/php.ini\n";
+        } else {
+            if (!Worker::$daemonize) {
+                Timer::add(1, function () {
+                    $this->checkAllFilesChange();
+                });
+            }
+        }
+
+        $memory_limit = $this->getMemoryLimit($memory_limit);
+        if ($memory_limit && DIRECTORY_SEPARATOR === '/') {
+            Timer::add(60, [$this, 'checkMemory'], [$memory_limit]);
+        }
+    }
+
+    /**
+     * @param $monitor_dir
+     */
+    public function checkFilesChange($monitor_dir)
+    {
+        static $last_mtime;
+        if (!$last_mtime) {
+            $last_mtime = time();
+        }
+        clearstatcache();
+        if (!is_dir($monitor_dir)) {
+            if (!is_file($monitor_dir)) {
+                return;
+            }
+            $iterator = [new \SplFileInfo($monitor_dir)];
+        } else {
+            // recursive traversal directory
+            $dir_iterator = new \RecursiveDirectoryIterator($monitor_dir, \FilesystemIterator::FOLLOW_SYMLINKS);
+            $iterator = new \RecursiveIteratorIterator($dir_iterator);
+        }
+        foreach ($iterator as $file) {
+            /** var SplFileInfo $file */
+            if (is_dir($file)) {
+                continue;
+            }
+            // check mtime
+            if ($last_mtime < $file->getMTime() && in_array($file->getExtension(), $this->_extensions, true)) {
+                $var = 0;
+                exec(PHP_BINARY . " -l " . $file, $out, $var);
+                if ($var) {
+                    $last_mtime = $file->getMTime();
+                    continue;
+                }
+                $last_mtime = $file->getMTime();
+                echo $file . " update and reload\n";
+                // send SIGUSR1 signal to master process for reload
+                if (DIRECTORY_SEPARATOR === '/') {
+                    posix_kill(posix_getppid(), SIGUSR1);
+                } else {
+                    return true;
+                }
+                break;
+            }
+        }
+    }
+
+    /**
+     * @return bool
+     */
+    public function checkAllFilesChange()
+    {
+        foreach ($this->_paths as $path) {
+            if ($this->checkFilesChange($path)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @param $memory_limit
+     * @return void
+     */
+    public function checkMemory($memory_limit)
+    {
+        $ppid = posix_getppid();
+        $children_file = "/proc/$ppid/task/$ppid/children";
+        if (!is_file($children_file) || !($children = file_get_contents($children_file))) {
+            return;
+        }
+        foreach (explode(' ', $children) as $pid) {
+            $pid = (int)$pid;
+            $status_file = "/proc/$pid/status";
+            if (!is_file($status_file) || !($status = file_get_contents($status_file))) {
+                continue;
+            }
+            $mem = 0;
+            if (preg_match('/VmRSS\s*?:\s*?(\d+?)\s*?kB/', $status, $match)) {
+                $mem = $match[1];
+            }
+            $mem = (int)($mem / 1024);
+            if ($mem >= $memory_limit) {
+                posix_kill($pid, SIGINT);
+            }
+        }
+    }
+
+    /**
+     * Get memory limit
+     * @return float
+     */
+    protected function getMemoryLimit($memory_limit)
+    {
+        if ($memory_limit === 0) {
+            return 0;
+        }
+        $use_php_ini = false;
+        if (!$memory_limit) {
+            $memory_limit = ini_get('memory_limit');
+            $use_php_ini = true;
+        }
+
+        if ($memory_limit == -1) {
+            return 0;
+        }
+        $unit = $memory_limit[strlen($memory_limit) - 1];
+        if ($unit == 'G') {
+            $memory_limit = 1024 * (int)$memory_limit;
+        } else if ($unit == 'M') {
+            $memory_limit = (int)$memory_limit;
+        } else if ($unit == 'K') {
+            $memory_limit = (int)($memory_limit / 1024);
+        } else {
+            $memory_limit = (int)($memory_limit / (1024 * 1024));
+        }
+        if ($memory_limit < 30) {
+            $memory_limit = 30;
+        }
+        if ($use_php_ini) {
+            $memory_limit = (int)(0.8 * $memory_limit);
+        }
+        return $memory_limit;
+    }
+}

+ 12 - 0
public/404.html

@@ -0,0 +1,12 @@
+<html>
+<head>
+    <title>404 Not Found - webman</title>
+</head>
+<body>
+<center>
+    <h1>404 Not Found</h1>
+</center>
+<hr>
+<center><a href="https://www.workerman.net">webman</a></center>
+</body>
+</html>

BIN
public/favicon.ico


+ 4 - 0
runtime/.gitignore

@@ -0,0 +1,4 @@
+*
+!logs
+!views
+!.gitignore

+ 2 - 0
runtime/logs/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 2 - 0
runtime/views/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 108 - 0
start.php

@@ -0,0 +1,108 @@
+#!/usr/bin/env php
+<?php
+require_once __DIR__ . '/vendor/autoload.php';
+
+use Workerman\Worker;
+use Workerman\Protocols\Http;
+use Workerman\Connection\TcpConnection;
+use Webman\App;
+use Webman\Config;
+use Webman\Route;
+use Webman\Middleware;
+use Dotenv\Dotenv;
+use support\Request;
+use support\Log;
+use support\Container;
+
+ini_set('display_errors', 'on');
+error_reporting(E_ALL);
+
+if (class_exists('Dotenv\Dotenv') && file_exists(base_path() . '/.env')) {
+    if (method_exists('Dotenv\Dotenv', 'createUnsafeImmutable')) {
+        Dotenv::createUnsafeImmutable(base_path())->load();
+    } else {
+        Dotenv::createMutable(base_path())->load();
+    }
+}
+
+Config::load(config_path(), ['route', 'container']);
+
+if ($timezone = config('app.default_timezone')) {
+    date_default_timezone_set($timezone);
+}
+
+$runtime_logs_path = runtime_path() . DIRECTORY_SEPARATOR . 'logs';
+if ( !file_exists($runtime_logs_path) || !is_dir($runtime_logs_path) ) {
+    if (!mkdir($runtime_logs_path,0777,true)) {
+        throw new \RuntimeException("Failed to create runtime logs directory. Please check the permission.");
+    }
+}
+
+$runtime_views_path = runtime_path() . DIRECTORY_SEPARATOR . 'views';
+if ( !file_exists($runtime_views_path) || !is_dir($runtime_views_path) ) {
+    if (!mkdir($runtime_views_path,0777,true)) {
+        throw new \RuntimeException("Failed to create runtime views directory. Please check the permission.");
+    }
+}
+
+Worker::$onMasterReload = function () {
+    if (function_exists('opcache_get_status')) {
+        if ($status = opcache_get_status()) {
+            if (isset($status['scripts']) && $scripts = $status['scripts']) {
+                foreach (array_keys($scripts) as $file) {
+                    opcache_invalidate($file, true);
+                }
+            }
+        }
+    }
+};
+
+$config = config('server');
+Worker::$pidFile = $config['pid_file'];
+Worker::$stdoutFile = $config['stdout_file'];
+Worker::$logFile = $config['log_file'];
+Worker::$eventLoopClass = $config['event_loop'] ?? '';
+TcpConnection::$defaultMaxPackageSize = $config['max_package_size'] ?? 10 * 1024 * 1024;
+if (property_exists(Worker::class, 'statusFile')) {
+    Worker::$statusFile = $config['status_file'] ?? '';
+}
+
+if ($config['listen']) {
+    $worker = new Worker($config['listen'], $config['context']);
+    $property_map = [
+        'name',
+        'count',
+        'user',
+        'group',
+        'reusePort',
+        'transport',
+    ];
+    foreach ($property_map as $property) {
+        if (isset($config[$property])) {
+            $worker->$property = $config[$property];
+        }
+    }
+
+    $worker->onWorkerStart = function ($worker) {
+        require_once base_path() . '/support/bootstrap.php';
+        $app = new App($worker, Container::instance(), Log::channel('default'), app_path(), public_path());
+        Http::requestClass(config('app.request_class', config('server.request_class', Request::class)));
+        $worker->onMessage = [$app, 'onMessage'];
+    };
+}
+
+// Windows does not support custom processes.
+if (\DIRECTORY_SEPARATOR === '/') {
+    foreach (config('process', []) as $process_name => $config) {
+        worker_start($process_name, $config);
+    }
+    foreach (config('plugin', []) as $firm => $projects) {
+        foreach ($projects as $name => $project) {
+            foreach ($project['process'] ?? [] as $process_name => $config) {
+                worker_start("plugin.$firm.$name.$process_name", $config);
+            }
+        }
+    }
+}
+
+Worker::runAll();

+ 55 - 0
support/Plugin.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace support;
+
+class Plugin
+{
+    public static function install($event)
+    {
+        static::findHepler();
+        $operation = $event->getOperation();
+        $autoload = method_exists($operation, 'getPackage') ? $operation->getPackage()->getAutoload() : $operation->getTargetPackage()->getAutoload();
+        if (!isset($autoload['psr-4'])) {
+            return;
+        }
+        $namespace = key($autoload['psr-4']);
+        $install_function = "\\{$namespace}Install::install";
+        $plugin_const = "\\{$namespace}Install::WEBMAN_PLUGIN";
+        if (defined($plugin_const) && is_callable($install_function)) {
+            $install_function();
+        }
+    }
+
+    public static function update($event)
+    {
+        static::install($event);
+    }
+
+    public static function uninstall($event)
+    {
+        static::findHepler();
+        $autoload = $event->getOperation()->getPackage()->getAutoload();
+        if (!isset($autoload['psr-4'])) {
+            return;
+        }
+        $namespace = key($autoload['psr-4']);
+        $uninstall_function = "\\{$namespace}Install::uninstall";
+        $plugin_const = "\\{$namespace}Install::WEBMAN_PLUGIN";
+        if (defined($plugin_const) && is_callable($uninstall_function)) {
+            $uninstall_function();
+        }
+    }
+
+    protected static function findHepler()
+    {
+        // Plugin.php in vendor
+        $file = __DIR__ . '/../../../../../support/helpers.php';
+        if (is_file($file)) {
+            require_once $file;
+            return;
+        }
+        // Plugin.php in webman
+        require_once __DIR__ . '/helpers.php';
+    }
+
+}

+ 90 - 0
support/bootstrap.php

@@ -0,0 +1,90 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+use Dotenv\Dotenv;
+use support\Container;
+use Webman\Config;
+use Webman\Route;
+use Webman\Middleware;
+
+$worker = $worker ?? null;
+
+if ($timezone = config('app.default_timezone')) {
+    date_default_timezone_set($timezone);
+}
+
+set_error_handler(function ($level, $message, $file = '', $line = 0, $context = []) {
+    if (error_reporting() & $level) {
+        throw new ErrorException($message, 0, $level, $file, $line);
+    }
+});
+
+if ($worker) {
+    register_shutdown_function(function ($start_time) {
+        if (time() - $start_time <= 1) {
+            sleep(1);
+        }
+    }, time());
+}
+
+if (class_exists('Dotenv\Dotenv') && file_exists(base_path() . '/.env')) {
+    if (method_exists('Dotenv\Dotenv', 'createUnsafeImmutable')) {
+        Dotenv::createUnsafeImmutable(base_path())->load();
+    } else {
+        Dotenv::createMutable(base_path())->load();
+    }
+}
+
+Config::reload(config_path(), ['route', 'container']);
+
+foreach (config('plugin', []) as $firm => $projects) {
+    foreach ($projects as $name => $project) {
+        foreach ($project['autoload']['files'] ?? [] as $file) {
+            include_once $file;
+        }
+    }
+}
+
+foreach (config('autoload.files', []) as $file) {
+    include_once $file;
+}
+
+$container = Container::instance();
+Route::container($container);
+Middleware::container($container);
+
+Middleware::load(config('middleware', []));
+foreach (config('plugin', []) as $firm => $projects) {
+    foreach ($projects as $name => $project) {
+        Middleware::load($project['middleware'] ?? []);
+    }
+}
+Middleware::load(['__static__' => config('static.middleware', [])]);
+
+foreach (config('bootstrap', []) as $class_name) {
+    /** @var \Webman\Bootstrap $class_name */
+    $class_name::start($worker);
+}
+
+foreach (config('plugin', []) as $firm => $projects) {
+    foreach ($projects as $name => $project) {
+        foreach ($project['bootstrap'] ?? [] as $class_name) {
+            /** @var \Webman\Bootstrap $class_name */
+            $class_name::start($worker);
+        }
+    }
+}
+
+Route::load(config_path());
+

+ 458 - 0
support/helpers.php

@@ -0,0 +1,458 @@
+<?php
+/**
+ * This file is part of webman.
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the MIT-LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author    walkor<walkor@workerman.net>
+ * @copyright walkor<walkor@workerman.net>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+use support\Request;
+use support\Response;
+use support\Translation;
+use support\Container;
+use support\view\Raw;
+use support\view\Blade;
+use support\view\ThinkPHP;
+use support\view\Twig;
+use Workerman\Worker;
+use Webman\App;
+use Webman\Config;
+use Webman\Route;
+
+// Phar support.
+if (is_phar()) {
+    define('BASE_PATH', dirname(__DIR__));
+} else {
+    define('BASE_PATH', realpath(__DIR__ . '/../'));
+}
+define('WEBMAN_VERSION', '1.3.0');
+
+/**
+ * @param $return_phar
+ * @return false|string
+ */
+function base_path($return_phar = true)
+{
+    static $real_path = '';
+    if (!$real_path) {
+        $real_path = is_phar() ? dirname(Phar::running(false)) : BASE_PATH;
+    }
+    return $return_phar ? BASE_PATH : $real_path;
+}
+
+/**
+ * @return string
+ */
+function app_path()
+{
+    return BASE_PATH . DIRECTORY_SEPARATOR . 'app';
+}
+
+/**
+ * @return string
+ */
+function public_path()
+{
+    static $path = '';
+    if (!$path) {
+        $path = get_realpath(config('app.public_path', BASE_PATH . DIRECTORY_SEPARATOR . 'public'));
+    }
+    return $path;
+}
+
+/**
+ * @return string
+ */
+function config_path()
+{
+    return BASE_PATH . DIRECTORY_SEPARATOR . 'config';
+}
+
+/**
+ * Phar support.
+ * Compatible with the 'realpath' function in the phar file.
+ *
+ * @return string
+ */
+function runtime_path()
+{
+    static $path = '';
+    if (!$path) {
+        $path = get_realpath(config('app.runtime_path', BASE_PATH . DIRECTORY_SEPARATOR . 'runtime'));
+    }
+    return $path;
+}
+
+/**
+ * @param int $status
+ * @param array $headers
+ * @param string $body
+ * @return Response
+ */
+function response($body = '', $status = 200, $headers = array())
+{
+    return new Response($status, $headers, $body);
+}
+
+/**
+ * @param $data
+ * @param int $options
+ * @return Response
+ */
+function json($data, $options = JSON_UNESCAPED_UNICODE)
+{
+    return new Response(200, ['Content-Type' => 'application/json'], json_encode($data, $options));
+}
+
+/**
+ * @param $xml
+ * @return Response
+ */
+function xml($xml)
+{
+    if ($xml instanceof SimpleXMLElement) {
+        $xml = $xml->asXML();
+    }
+    return new Response(200, ['Content-Type' => 'text/xml'], $xml);
+}
+
+/**
+ * @param $data
+ * @param string $callback_name
+ * @return Response
+ */
+function jsonp($data, $callback_name = 'callback')
+{
+    if (!is_scalar($data) && null !== $data) {
+        $data = json_encode($data);
+    }
+    return new Response(200, [], "$callback_name($data)");
+}
+
+/**
+ * @param $location
+ * @param int $status
+ * @param array $headers
+ * @return Response
+ */
+function redirect($location, $status = 302, $headers = [])
+{
+    $response = new Response($status, ['Location' => $location]);
+    if (!empty($headers)) {
+        $response->withHeaders($headers);
+    }
+    return $response;
+}
+
+/**
+ * @param $template
+ * @param array $vars
+ * @param null $app
+ * @return Response
+ */
+function view($template, $vars = [], $app = null)
+{
+    static $handler;
+    if (null === $handler) {
+        $handler = config('view.handler');
+    }
+    return new Response(200, [], $handler::render($template, $vars, $app));
+}
+
+/**
+ * @param $template
+ * @param array $vars
+ * @param null $app
+ * @return Response
+ */
+function raw_view($template, $vars = [], $app = null)
+{
+    return new Response(200, [], Raw::render($template, $vars, $app));
+}
+
+/**
+ * @param $template
+ * @param array $vars
+ * @param null $app
+ * @return Response
+ */
+function blade_view($template, $vars = [], $app = null)
+{
+    return new Response(200, [], Blade::render($template, $vars, $app));
+}
+
+/**
+ * @param $template
+ * @param array $vars
+ * @param null $app
+ * @return Response
+ */
+function think_view($template, $vars = [], $app = null)
+{
+    return new Response(200, [], ThinkPHP::render($template, $vars, $app));
+}
+
+/**
+ * @param $template
+ * @param array $vars
+ * @param null $app
+ * @return Response
+ */
+function twig_view($template, $vars = [], $app = null)
+{
+    return new Response(200, [], Twig::render($template, $vars, $app));
+}
+
+/**
+ * @return Request
+ */
+function request()
+{
+    return App::request();
+}
+
+/**
+ * @param $key
+ * @param null $default
+ * @return mixed
+ */
+function config($key = null, $default = null)
+{
+    return Config::get($key, $default);
+}
+
+/**
+ * @param $name
+ * @param array $parameters
+ * @return string
+ */
+function route($name, $parameters = [])
+{
+    $route = Route::getByName($name);
+    if (!$route) {
+        return '';
+    }
+    return $route->url($parameters);
+}
+
+/**
+ * @param null $key
+ * @param null $default
+ * @return mixed
+ */
+function session($key = null, $default = null)
+{
+    $session = request()->session();
+    if (null === $key) {
+        return $session;
+    }
+    if (\is_array($key)) {
+        $session->put($key);
+        return null;
+    }
+    return $session->get($key, $default);
+}
+
+/**
+ * @param null|string $id
+ * @param array $parameters
+ * @param string|null $domain
+ * @param string|null $locale
+ * @return string
+ */
+function trans(string $id, array $parameters = [], string $domain = null, string $locale = null)
+{
+    $res = Translation::trans($id, $parameters, $domain, $locale);
+    return $res === '' ? $id : $res;
+}
+
+/**
+ * @param null|string $locale
+ * @return string
+ */
+function locale(string $locale = null)
+{
+    if (!$locale) {
+        return Translation::getLocale();
+    }
+    Translation::setLocale($locale);
+}
+
+/**
+ * 404 not found
+ *
+ * @return Response
+ */
+function not_found()
+{
+    return new Response(404, [], file_get_contents(public_path() . '/404.html'));
+}
+
+/**
+ * Copy dir.
+ * @param $source
+ * @param $dest
+ * @param bool $overwrite
+ * @return void
+ */
+function copy_dir($source, $dest, $overwrite = false)
+{
+    if (is_dir($source)) {
+        if (!is_dir($dest)) {
+            mkdir($dest);
+        }
+        $files = scandir($source);
+        foreach ($files as $file) {
+            if ($file !== "." && $file !== "..") {
+                copy_dir("$source/$file", "$dest/$file");
+            }
+        }
+    } else if (file_exists($source) && ($overwrite || !file_exists($dest))) {
+        copy($source, $dest);
+    }
+}
+
+/**
+ * Remove dir.
+ * @param $dir
+ * @return bool
+ */
+function remove_dir($dir)
+{
+    if (is_link($dir) || is_file($dir)) {
+        return unlink($dir);
+    }
+    $files = array_diff(scandir($dir), array('.', '..'));
+    foreach ($files as $file) {
+        (is_dir("$dir/$file") && !is_link($dir)) ? remove_dir("$dir/$file") : unlink("$dir/$file");
+    }
+    return rmdir($dir);
+}
+
+/**
+ * @param $worker
+ * @param $class
+ */
+function worker_bind($worker, $class)
+{
+    $callback_map = [
+        'onConnect',
+        'onMessage',
+        'onClose',
+        'onError',
+        'onBufferFull',
+        'onBufferDrain',
+        'onWorkerStop',
+        'onWebSocketConnect'
+    ];
+    foreach ($callback_map as $name) {
+        if (method_exists($class, $name)) {
+            $worker->$name = [$class, $name];
+        }
+    }
+    if (method_exists($class, 'onWorkerStart')) {
+        call_user_func([$class, 'onWorkerStart'], $worker);
+    }
+}
+
+/**
+ * @param $process_name
+ * @param $config
+ * @return void
+ */
+function worker_start($process_name, $config)
+{
+    $worker = new Worker($config['listen'] ?? null, $config['context'] ?? []);
+    $property_map = [
+        'count',
+        'user',
+        'group',
+        'reloadable',
+        'reusePort',
+        'transport',
+        'protocol',
+    ];
+    $worker->name = $process_name;
+    foreach ($property_map as $property) {
+        if (isset($config[$property])) {
+            $worker->$property = $config[$property];
+        }
+    }
+
+    $worker->onWorkerStart = function ($worker) use ($config) {
+        require_once base_path() . '/support/bootstrap.php';
+
+        foreach ($config['services'] ?? [] as $server) {
+            if (!class_exists($server['handler'])) {
+                echo "process error: class {$server['handler']} not exists\r\n";
+                continue;
+            }
+            $listen = new Worker($server['listen'] ?? null, $server['context'] ?? []);
+            if (isset($server['listen'])) {
+                echo "listen: {$server['listen']}\n";
+            }
+            $instance = Container::make($server['handler'], $server['constructor'] ?? []);
+            worker_bind($listen, $instance);
+            $listen->listen();
+        }
+
+        if (isset($config['handler'])) {
+            if (!class_exists($config['handler'])) {
+                echo "process error: class {$config['handler']} not exists\r\n";
+                return;
+            }
+
+            $instance = Container::make($config['handler'], $config['constructor'] ?? []);
+            worker_bind($worker, $instance);
+        }
+
+    };
+}
+
+/**
+ * Phar support.
+ * Compatible with the 'realpath' function in the phar file.
+ *
+ * @param string $file_path
+ * @return string
+ */
+function get_realpath(string $file_path): string
+{
+    if (strpos($file_path, 'phar://') === 0) {
+        return $file_path;
+    } else {
+        return realpath($file_path);
+    }
+}
+
+/**
+ * @return bool
+ */
+function is_phar()
+{
+    return class_exists(\Phar::class, false) && Phar::running();
+}
+
+/**
+ * @return int
+ */
+function cpu_count()
+{
+    // Windows does not support the number of processes setting.
+    if (\DIRECTORY_SEPARATOR === '\\') {
+        return 1;
+    }
+    if (strtolower(PHP_OS) === 'darwin') {
+        $count = shell_exec('sysctl -n machdep.cpu.core_count');
+    } else {
+        $count = shell_exec('nproc');
+    }
+    $count = (int)$count > 0 ? (int)$count : 4;
+    return $count;
+}

+ 3 - 0
windows.bat

@@ -0,0 +1,3 @@
+CHCP 65001
+php windows.php
+pause

+ 96 - 0
windows.php

@@ -0,0 +1,96 @@
+<?php
+/**
+ * Start file for windows
+ */
+require_once __DIR__ . '/vendor/autoload.php';
+
+use process\Monitor;
+use Workerman\Worker;
+use Webman\Config;
+
+ini_set('display_errors', 'on');
+error_reporting(E_ALL);
+
+Config::load(config_path(), ['route', 'container']);
+
+$runtime_process_path = runtime_path() . DIRECTORY_SEPARATOR . '/windows';
+if (!is_dir($runtime_process_path)) {
+    mkdir($runtime_process_path);
+}
+$process_files = [
+    __DIR__ . DIRECTORY_SEPARATOR . 'start.php'
+];
+foreach (config('process', []) as $process_name => $config) {
+    $file_content = <<<EOF
+<?php
+require_once __DIR__ . '/../../vendor/autoload.php';
+
+use Workerman\Worker;
+use Webman\Config;
+
+ini_set('display_errors', 'on');
+error_reporting(E_ALL);
+
+Config::load(config_path(), ['route', 'container']);
+
+worker_start('$process_name', config('process')['$process_name']);
+Worker::runAll();
+
+EOF;
+
+    $process_file = $runtime_process_path . DIRECTORY_SEPARATOR . "start_$process_name.php";
+    $process_files[] = $process_file;
+    file_put_contents($process_file, $file_content);
+}
+
+foreach (config('plugin', []) as $firm => $projects) {
+    foreach ($projects as $name => $project) {
+        foreach ($project['process'] ?? [] as $process_name => $config) {
+            $file_content = <<<EOF
+<?php
+require_once __DIR__ . '/../../vendor/autoload.php';
+
+use Workerman\Worker;
+use Webman\Config;
+
+ini_set('display_errors', 'on');
+error_reporting(E_ALL);
+
+Config::load(config_path(), ['route', 'container']);
+
+worker_start("plugin.$firm.$name.$process_name", config("plugin.$firm.$name.process")['$process_name']);
+Worker::runAll();
+
+EOF;
+            $process_file = $runtime_process_path . DIRECTORY_SEPARATOR . "start_$process_name.php";
+            $process_files[] = $process_file;
+            file_put_contents($process_file, $file_content);
+        }
+    }
+}
+
+$monitor = new Monitor(...array_values(config('process.monitor.constructor')));
+
+function popen_processes($process_files)
+{
+    $cmd = "php " . implode(' ', $process_files);
+    $descriptorspec = [STDIN, STDOUT, STDOUT];
+    $resource = proc_open($cmd, $descriptorspec, $pipes);
+    if (!$resource) {
+        exit("Can not execute $cmd\r\n");
+    }
+    return $resource;
+}
+
+$resource = popen_processes($process_files);
+echo "\r\n";
+while (1) {
+    sleep(1);
+    if ($monitor->checkAllFilesChange()) {
+        $status = proc_get_status($resource);
+        $pid = $status['pid'];
+        shell_exec("taskkill /F /T /PID $pid");
+        proc_close($resource);
+        $resource = popen_processes($process_files);
+    }
+}