Browse Source

[PHP] Upgrade webman to 1.4 (#7558)

* 1.4

* namespace lowercase
walkor 3 years ago
parent
commit
b17628bdc1

+ 4 - 0
frameworks/PHP/webman/composer.json

@@ -33,6 +33,10 @@
     "ext-event": "For better performance. "
   },
   "autoload": {
+    "psr-4": {
+      "": "./",
+      "App\\": "./app"
+    },
     "files": [
       "./support/helpers.php"
     ]

+ 0 - 2
frameworks/PHP/webman/config/bootstrap.php

@@ -13,8 +13,6 @@
  */
 
 return [
-    support\bootstrap\Container::class,
-    support\bootstrap\Log::class,
     support\bootstrap\db\Raw::class,
     support\bootstrap\Date::class,
 ];

+ 5 - 4
frameworks/PHP/webman/config/server.php

@@ -20,8 +20,9 @@ return [
     'count'                => cpu_count() * 4,
     'user'                 => '',
     'group'                => '',
-    'pid_file'             => runtime_path() . '/webman.pid',
-    'max_request'          => 10000000000,
-    'stdout_file'          => runtime_path() . '/logs/stdout.log',
+    '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
-];
+];

+ 2 - 121
frameworks/PHP/webman/start.php

@@ -1,123 +1,4 @@
+#!/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 support\Request;
-use support\bootstrap\Log;
-use support\bootstrap\Container;
-
-Config::load(config_path(), ['route', 'container']);
-$config = config('server');
-
-if ($timezone = config('app.default_timezone')) {
-    date_default_timezone_set($timezone);
-}
-
-Worker::$onMasterReload = function (){
-    if ($status = opcache_get_status()) {
-        foreach (array_keys($status['scripts']) as $file) {
-            opcache_invalidate($file, true);
-        }
-    }
-};
-
-Worker::$pidFile                      = $config['pid_file'];
-Worker::$stdoutFile                   = $config['stdout_file'];
-TcpConnection::$defaultMaxPackageSize = $config['max_package_size'] ?? 10*1024*1024;
-
-$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) {
-    Config::reload(config_path(), ['route', 'container']);
-    foreach (config('bootstrap', []) as $class_name) {
-        /** @var \Webman\Bootstrap $class_name */
-        $class_name::start($worker);
-    }
-    $app = new App($worker, Container::instance(), Log::channel('default'), app_path(), public_path());
-    Route::load(config_path() . '/route.php');
-    Middleware::load(config('middleware', []));
-    Middleware::load(['__static__' => config('static.middleware', [])]);
-    Http::requestClass(Request::class);
-
-    $worker->onMessage = [$app, 'onMessage'];
-};
-
-
-foreach (config('process', []) as $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) {
-        Config::reload(config_path(), ['route']);
-
-        $bootstrap = $config['bootstrap'] ?? config('bootstrap', []);
-        if (!in_array(support\bootstrap\Log::class, $bootstrap)) {
-            $bootstrap[] = support\bootstrap\Log::class;
-        }
-        foreach ($bootstrap as $class_name) {
-            /** @var \Webman\Bootstrap $class_name */
-            $class_name::start($worker);
-        }
-
-        foreach ($config['services'] ?? [] as $server) {
-            if (!class_exists($server['handler'])) {
-                echo "process error: class {$config['handler']} not exists\r\n";
-                continue;
-            }
-            $listen = new Worker($server['listen'] ?? null, $server['context'] ?? []);
-            if (isset($server['listen'])) {
-                echo "listen: {$server['listen']}\n";
-            }
-            $class = Container::make($server['handler'], $server['constructor'] ?? []);
-            worker_bind($listen, $class);
-            $listen->listen();
-        }
-
-        if (isset($config['handler'])) {
-            if (!class_exists($config['handler'])) {
-                echo "process error: class {$config['handler']} not exists\r\n";
-                return;
-            }
-
-            $class = Container::make($config['handler'], $config['constructor'] ?? []);
-            worker_bind($worker, $class);
-        }
-
-    };
-}
-
-
-Worker::runAll();
+support\App::run();

+ 132 - 0
frameworks/PHP/webman/support/bootstrap.php

@@ -0,0 +1,132 @@
+<?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<[email protected]>
+ * @copyright walkor<[email protected]>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+use Dotenv\Dotenv;
+use support\Log;
+use Webman\Bootstrap;
+use Webman\Config;
+use Webman\Route;
+use Webman\Middleware;
+use Webman\Util;
+
+$worker = $worker ?? null;
+
+if ($timezone = config('app.default_timezone')) {
+    date_default_timezone_set($timezone);
+}
+
+set_error_handler(function ($level, $message, $file = '', $line = 0) {
+    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();
+    }
+}
+
+Support\App::loadAllConfig(['route']);
+
+foreach (config('autoload.files', []) as $file) {
+    include_once $file;
+}
+foreach (config('plugin', []) as $firm => $projects) {
+    foreach ($projects as $name => $project) {
+        if (!is_array($project)) {
+            continue;
+        }
+        foreach ($project['autoload']['files'] ?? [] as $file) {
+            include_once $file;
+        }
+    }
+    foreach ($projects['autoload']['files'] ?? [] as $file) {
+        include_once $file;
+    }
+}
+
+Middleware::load(config('middleware', []), '');
+foreach (config('plugin', []) as $firm => $projects) {
+    foreach ($projects as $name => $project) {
+        if (!is_array($project) || $name === 'static') {
+            continue;
+        }
+        Middleware::load($project['middleware'] ?? [], '');
+    }
+    Middleware::load($projects['middleware'] ?? [], $firm);
+    if ($static_middlewares = config("plugin.$firm.static.middleware")) {
+        Middleware::load(['__static__' => $static_middlewares], $firm);
+    }
+}
+Middleware::load(['__static__' => config('static.middleware', [])], '');
+
+foreach (config('bootstrap', []) as $class_name) {
+    if (!class_exists($class_name)) {
+        $log = "Warning: Class $class_name setting in config/bootstrap.php not found\r\n";
+        echo $log;
+        Log::error($log);
+        continue;
+    }
+    /** @var Bootstrap $class_name */
+    $class_name::start($worker);
+}
+
+foreach (config('plugin', []) as $firm => $projects) {
+    foreach ($projects as $name => $project) {
+        if (!is_array($project)) {
+            continue;
+        }
+        foreach ($project['bootstrap'] ?? [] as $class_name) {
+            if (!class_exists($class_name)) {
+                $log = "Warning: Class $class_name setting in config/plugin/$firm/$name/bootstrap.php not found\r\n";
+                echo $log;
+                Log::error($log);
+                continue;
+            }
+            /** @var Bootstrap $class_name */
+            $class_name::start($worker);
+        }
+    }
+    foreach ($projects['bootstrap'] ?? [] as $class_name) {
+        if (!class_exists($class_name)) {
+            $log = "Warning: Class $class_name setting in plugin/$firm/config/bootstrap.php not found\r\n";
+            echo $log;
+            Log::error($log);
+            continue;
+        }
+        /** @var Bootstrap $class_name */
+        $class_name::start($worker);
+    }
+}
+
+$directory = base_path() . '/plugin';
+$paths = [config_path()];
+foreach (Util::scanDir($directory) as $path) {
+    if (is_dir($path = "$path/config")) {
+        $paths[] = $path;
+    }
+}
+Route::load($paths);
+

+ 295 - 69
frameworks/PHP/webman/support/helpers.php

@@ -14,20 +14,36 @@
 
 use support\Request;
 use support\Response;
+use support\Translation;
+use support\Container;
 use support\view\Raw;
-use support\bootstrap\Translation;
+use support\view\Blade;
+use support\view\ThinkPHP;
+use support\view\Twig;
+use Workerman\Worker;
 use Webman\App;
 use Webman\Config;
-use Webman\Exception\ClassNotFoundException;
+use Webman\Route;
 
-define('BASE_PATH', realpath(__DIR__ . '/../'));
+// Phar support.
+if (\is_phar()) {
+    \define('BASE_PATH', dirname(__DIR__));
+} else {
+    \define('BASE_PATH', realpath(__DIR__ . '/../'));
+}
+\define('WEBMAN_VERSION', '1.4');
 
 /**
- * @return string
+ * @param $return_phar
+ * @return false|string
  */
-function base_path()
+function base_path(bool $return_phar = true)
 {
-    return BASE_PATH;
+    static $real_path = '';
+    if (!$real_path) {
+        $real_path = \is_phar() ? \dirname(Phar::running(false)) : BASE_PATH;
+    }
+    return $return_phar ? BASE_PATH : $real_path;
 }
 
 /**
@@ -43,7 +59,11 @@ function app_path()
  */
 function public_path()
 {
-    return BASE_PATH . DIRECTORY_SEPARATOR . 'public';
+    static $path = '';
+    if (!$path) {
+        $path = \config('app.public_path', BASE_PATH . DIRECTORY_SEPARATOR . 'public');
+    }
+    return $path;
 }
 
 /**
@@ -55,11 +75,18 @@ function config_path()
 }
 
 /**
+ * Phar support.
+ * Compatible with the 'realpath' function in the phar file.
+ *
  * @return string
  */
 function runtime_path()
 {
-    return BASE_PATH . DIRECTORY_SEPARATOR . 'runtime';
+    static $path = '';
+    if (!$path) {
+        $path = \config('app.runtime_path', BASE_PATH . DIRECTORY_SEPARATOR . 'runtime');
+    }
+    return $path;
 }
 
 /**
@@ -68,7 +95,7 @@ function runtime_path()
  * @param string $body
  * @return Response
  */
-function response($body = '', $status = 200, $headers = array())
+function response($body = '', $status = 200, $headers = [])
 {
     return new Response($status, $headers, $body);
 }
@@ -80,7 +107,7 @@ function response($body = '', $status = 200, $headers = array())
  */
 function json($data, $options = JSON_UNESCAPED_UNICODE)
 {
-    return new Response(200, ['Content-Type' => 'application/json'], json_encode($data, $options));
+    return new Response(200, ['Content-Type' => 'application/json'], \json_encode($data, $options));
 }
 
 /**
@@ -102,19 +129,19 @@ function xml($xml)
  */
 function jsonp($data, $callback_name = 'callback')
 {
-    if (!is_scalar($data) && null !== $data) {
-        $data = json_encode($data);
+    if (!\is_scalar($data) && null !== $data) {
+        $data = \json_encode($data);
     }
     return new Response(200, [], "$callback_name($data)");
 }
 
 /**
- * @param $location
+ * @param string $location
  * @param int $status
  * @param array $headers
  * @return Response
  */
-function redirect($location, $status = 302, $headers = [])
+function redirect(string $location, int $status = 302, array $headers = [])
 {
     $response = new Response($status, ['Location' => $location]);
     if (!empty($headers)) {
@@ -127,17 +154,61 @@ function redirect($location, $status = 302, $headers = [])
  * @param $template
  * @param array $vars
  * @param null $app
- * @return string
+ * @return Response
  */
-function view($template, $vars = [], $app = null)
+function view(string $template, array $vars = [], string $app = null)
 {
-    static $handler;
-    if (null === $handler) {
-        $handler = config('view.handler');
-    }
+    $request = \request();
+    $plugin =  $request->plugin ?? '';
+    $handler = \config($plugin ? "plugin.$plugin.view.handler" : 'view.handler');
     return new Response(200, [], $handler::render($template, $vars, $app));
 }
 
+/**
+ * @param string $template
+ * @param array $vars
+ * @param string|null $app
+ * @return Response
+ * @throws Throwable
+ */
+function raw_view(string $template, array $vars = [], string $app = null)
+{
+    return new Response(200, [], Raw::render($template, $vars, $app));
+}
+
+/**
+ * @param string $template
+ * @param array $vars
+ * @param string|null $app
+ * @return Response
+ */
+function blade_view(string $template, array $vars = [], string $app = null)
+{
+    return new Response(200, [], Blade::render($template, $vars, $app));
+}
+
+/**
+ * @param string $template
+ * @param array $vars
+ * @param string|null $app
+ * @return Response
+ */
+function think_view(string $template, array $vars = [], string $app = null)
+{
+    return new Response(200, [], ThinkPHP::render($template, $vars, $app));
+}
+
+/**
+ * @param string $template
+ * @param array $vars
+ * @param string|null $app
+ * @return Response
+ */
+function twig_view(string $template, array $vars = [], string $app = null)
+{
+    return new Response(200, [], Twig::render($template, $vars, $app));
+}
+
 /**
  * @return Request
  */
@@ -147,54 +218,69 @@ function request()
 }
 
 /**
- * @param $key
- * @param null $default
- * @return mixed
+ * @param string|null $key
+ * @param $default
+ * @return array|mixed|null
  */
-function config($key = null, $default = null)
+function config(string $key = null, $default = null)
 {
     return Config::get($key, $default);
 }
 
-if (!function_exists('env')) {
-    /**
-     * @param $key
-     * @param null $default
-     * @return array|bool|false|mixed|string
-     */
-    function env($key, $default = null)
-    {
-        $value = getenv($key);
-
-        if ($value === false) {
-            return $default;
-        }
+/**
+ * @param string $name
+ * @param ...$parameters
+ * @return string
+ */
+function route(string $name, ...$parameters)
+{
+    $route = Route::getByName($name);
+    if (!$route) {
+        return '';
+    }
 
-        switch (strtolower($value)) {
-            case 'true':
-            case '(true)':
-                return true;
-            case 'false':
-            case '(false)':
-                return false;
-            case 'empty':
-            case '(empty)':
-                return '';
-            case 'null':
-            case '(null)':
-                return null;
-        }
+    if (!$parameters) {
+        return $route->url();
+    }
 
-        if (($valueLength = strlen($value)) > 1 && $value[0] === '"' && $value[$valueLength - 1] === '"') {
-            return substr($value, 1, -1);
-        }
+    if (\is_array(\current($parameters))) {
+        $parameters = \current($parameters);
+    }
 
+    return $route->url($parameters);
+}
+
+/**
+ * @param mixed $key
+ * @param mixed $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;
+    }
+    if (\strpos($key, '.')) {
+        $key_array = \explode('.', $key);
+        $value = $session->all();
+        foreach ($key_array as $index) {
+            if (!isset($value[$index])) {
+                return $default;
+            }
+            $value = $value[$index];
+        }
         return $value;
     }
+    return $session->get($key, $default);
 }
 
 /**
- * @param null|string $id
+ * @param string $id
  * @param array $parameters
  * @param string|null $domain
  * @param string|null $locale
@@ -202,14 +288,15 @@ if (!function_exists('env')) {
  */
 function trans(string $id, array $parameters = [], string $domain = null, string $locale = null)
 {
-    return Translation::trans($id, $parameters, $domain, $locale);
+    $res = Translation::trans($id, $parameters, $domain, $locale);
+    return $res === '' ? $id : $res;
 }
 
 /**
  * @param null|string $locale
  * @return string
  */
-function locale(string $locale)
+function locale(string $locale = null)
 {
     if (!$locale) {
         return Translation::getLocale();
@@ -217,11 +304,65 @@ function locale(string $locale)
     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 string $source
+ * @param string $dest
+ * @param bool $overwrite
+ * @return void
+ */
+function copy_dir(string $source, string $dest, bool $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 string $dir
+ * @return bool
+ */
+function remove_dir(string $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) {
+function worker_bind($worker, $class)
+{
     $callback_map = [
         'onConnect',
         'onMessage',
@@ -233,24 +374,109 @@ function worker_bind($worker, $class) {
         'onWebSocketConnect'
     ];
     foreach ($callback_map as $name) {
-        if (method_exists($class, $name)) {
+        if (\method_exists($class, $name)) {
             $worker->$name = [$class, $name];
         }
     }
-    if (method_exists($class, 'onWorkerStart')) {
-        call_user_func([$class, 'onWorkerStart'], $worker);
+    if (\method_exists($class, 'onWorkerStart')) {
+        [$class, 'onWorkerStart']($worker);
     }
 }
 
 /**
- * @return int
+ * @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 cpu_count() {
-    if (strtolower(PHP_OS) === 'darwin') {
-        $count = shell_exec('sysctl -n machdep.cpu.core_count');
+function get_realpath(string $file_path): string
+{
+    if (\strpos($file_path, 'phar://') === 0) {
+        return $file_path;
     } else {
-        $count = shell_exec('nproc');
+        return \realpath($file_path);
     }
-    $count = (int)$count > 0 ? (int)$count : 4;
-    return $count;
-}
+}
+
+/**
+ * @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;
+    }
+    $count = 4;
+    if (\is_callable('shell_exec')) {
+        if (\strtolower(PHP_OS) === 'darwin') {
+            $count = (int)\shell_exec('sysctl -n machdep.cpu.core_count');
+        } else {
+            $count = (int)\shell_exec('nproc');
+        }
+    }
+    return $count > 0 ? $count : 4;
+}