Ver código fonte

[PHP] Add Webman (#5819)

* Add Webman

* Remove unnecessary files

* versus:workerman
walkor 5 anos atrás
pai
commit
580c8749d3

+ 1 - 1
.travis.yml

@@ -63,7 +63,7 @@ env:
     - "TESTLANG=Nim"
     - "TESTLANG=Perl"
     - 'TESTDIR="PHP/php"'
-    - 'TESTDIR="PHP/comet PHP/kumbiaphp PHP/workerman"'
+    - 'TESTDIR="PHP/comet PHP/kumbiaphp PHP/workerman PHP/webman"'
     - 'TESTDIR="PHP/cakephp PHP/codeigniter PHP/fat-free PHP/fuel PHP/phpixie PHP/slim PHP/symfony PHP/yii2 PHP/zend PHP/spiral"'
     - 'TESTDIR="PHP/amp PHP/hhvm PHP/peachpie PHP/php-ngx PHP/phalcon"'
     - 'TESTDIR="PHP/hamlet PHP/laravel PHP/lumen PHP/swoole PHP/ubiquity PHP/hyperf PHP/sw-fw-less PHP/imi PHP/simps"'

+ 28 - 0
frameworks/PHP/webman/README.md

@@ -0,0 +1,28 @@
+# Webman Benchmarking Test
+
+[Webman](https://github.com/walkor/webman) is high performance HTTP Service Framework for PHP based on [Workerman](https://github.com/walkor/workerman).
+
+
+## Infrastructure Software Versions
+The tests were run with:
+
+* [Webman](https://github.com/walkor/webman)
+* [PHP-CLI](http://www.php.net/)
+
+
+### JSON Encoding Test
+Using the PHP standard [JSON encoder](http://www.php.net/manual/en/function.json-encode.php)
+
+## Test URLs
+
+### JSON Encoding Test
+http://localhost:8080/json
+
+### Data-Store/Database Mapping Test
+http://localhost:8080/updates
+
+### Variable Query Test
+http://localhost:8080/queries/5
+
+### Fortune Test
+http://localhost:8080/fortunes

+ 124 - 0
frameworks/PHP/webman/app/controller/Index.php

@@ -0,0 +1,124 @@
+<?php
+namespace app\controller;
+
+use support\Request;
+use support\bootstrap\Date;
+use support\bootstrap\db\Raw as Db;
+use support\Response;
+use PDO;
+
+class Index
+{
+    public function plaintext($request)
+    {
+        return new Response(200, [
+            'Content-Type' => 'text/plain',
+            'Date'         => Date::$date
+        ], 'Hello, World!');
+    }
+
+
+    public function json($request)
+    {
+        return new Response(200, [
+            'Content-Type' => 'application/json',
+            'Date'         => Date::$date
+        ], json_encode(['message' => 'Hello, World!']));
+    }
+
+    public function db($request)
+    {
+        $statement = Db::$statement;
+        $statement->execute([\mt_rand(1, 10000)]);
+
+        return new Response(200, [
+            'Content-Type' => 'application/json',
+            'Date'         => Date::$date
+        ], json_encode($statement->fetch()));
+    }
+
+    public function fortunes($request)
+    {
+        $fortune = Db::$fortune;
+
+        $fortune->execute();
+
+        $arr    = $fortune->fetchAll(PDO::FETCH_KEY_PAIR);
+        $arr[0] = 'Additional fortune added at request time.';
+        \asort($arr);
+
+        $html = '';
+        foreach ($arr as $id => $message) {
+            $message = \htmlspecialchars($message, ENT_QUOTES, 'UTF-8');
+            $html .= "<tr><td>$id</td><td>$message</td></tr>";
+        }
+
+        return new Response(200, [
+            'Date'         => Date::$date
+        ], "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>$html</table></body></html>"
+        );
+    }
+
+    public function queries($request, $q = 1)
+    {
+        $statement = Db::$statement;
+
+        $query_count = 1;
+        if ($q > 1) {
+            $query_count = \min($q, 500);
+        }
+
+        $arr = [];
+        while ($query_count--) {
+            $statement->execute([\mt_rand(1, 10000)]);
+            $arr[] = $statement->fetch();
+        }
+
+        return new Response(200, [
+            'Content-Type' => 'application/json',
+            'Date'         => Date::$date
+        ], json_encode($arr));
+    }
+
+    public function updates($request, $q = 1)
+    {
+        $random = Db::$random;
+        $update = Db::$update;
+
+        $query_count = 1;
+        if ($q > 1) {
+            $query_count = \min($q, 500);
+        }
+
+        $arr = [];
+
+        while ($query_count--) {
+            $id = \mt_rand(1, 10000);
+            $random->execute([$id]);
+
+            //$random->fetchColumn(); //
+            //$world = ['id' => $id, 'randomNumber' => \mt_rand(1, 10000)]; //
+
+            $world = ['id' => $id, 'randomNumber' => $random->fetchColumn()];
+            $update->execute(
+                [$world['randomNumber'] = mt_rand(1, 10000), $id]
+            );
+
+            $arr[] = $world;
+        }
+
+        /*$pdo = Db::$pdo;
+        $pdo->beginTransaction();
+        foreach($arr as $world) {
+             $update->execute([$world['randomNumber'], $world['id']]);
+        }
+        $pdo->commit();*/
+
+        return new Response(200, [
+            'Content-Type' => 'application/json',
+            'Date'         => Date::$date
+        ], \json_encode($arr));
+    }
+
+
+}

+ 28 - 0
frameworks/PHP/webman/benchmark_config.json

@@ -0,0 +1,28 @@
+{
+  "framework": "webman",
+  "tests": [{
+    "default": {
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "db_url": "/db",
+      "query_url": "/queries/",
+      "update_url": "/updates/",
+      "fortune_url": "/fortunes",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "Postgres",
+      "framework": "None",
+      "language": "PHP",
+      "flavor": "PHP7",
+      "orm": "Raw",
+      "platform": "workerman",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "webman",
+      "notes": "",
+      "versus": "workerman"
+    }
+  }]
+}

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

@@ -0,0 +1,45 @@
+{
+  "name": "workerman/webman",
+  "type": "library",
+  "keywords": [
+    "high performance",
+    "http service"
+  ],
+  "homepage": "http://www.workerman.net",
+  "license": "MIT",
+  "description": "High performance HTTP Service Framework.",
+  "authors": [
+    {
+      "name": "walkor",
+      "email": "[email protected]",
+      "homepage": "http://www.workerman.net",
+      "role": "Developer"
+    }
+  ],
+  "support": {
+    "email": "[email protected]",
+    "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": "dev-master",
+    "monolog/monolog": "^2.0",
+    "vlucas/phpdotenv": "^4.1"
+  },
+  "suggest": {
+    "ext-event": "For better performance. "
+  },
+  "autoload": {
+    "files": [
+      "./support/helpers.php"
+    ]
+  },
+  "scripts": {
+    "post-autoload-dump": [
+      "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
+    ]
+  }
+}

+ 18 - 0
frameworks/PHP/webman/config/app.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<[email protected]>
+ * @copyright walkor<[email protected]>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+return [
+    'debug' => true,
+    'default_timezone' => 'Asia/Shanghai',
+];

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

@@ -0,0 +1,20 @@
+<?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
+ */
+
+return [
+    support\bootstrap\Container::class,
+    support\bootstrap\Log::class,
+    support\bootstrap\db\Raw::class,
+    support\bootstrap\Date::class,
+];

+ 26 - 0
frameworks/PHP/webman/config/container.php

@@ -0,0 +1,26 @@
+<?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
+ */
+
+
+// 如果你需要自动依赖注入(包括注解注入)。
+// 请先运行 composer require php-di/php-di && composer require doctrine/annotations
+// 并将下面的代码注释解除,并注释掉最后一行 return new Webman\Container;
+/*$builder = new \DI\ContainerBuilder();
+$builder->addDefinitions(config('dependence', []));
+$builder->useAutowiring(true);
+$builder->useAnnotations(true);
+return $builder->build();*/
+
+
+return new Webman\Container;

+ 31 - 0
frameworks/PHP/webman/config/log.php

@@ -0,0 +1,31 @@
+<?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
+ */
+
+return [
+    'default' => [
+        'handlers' => [
+            [
+                'class' => Monolog\Handler\RotatingFileHandler::class,
+                'constructor' => [
+                    runtime_path() . '/logs/webman.log',
+                    Monolog\Logger::DEBUG,
+                ],
+                'formatter' => [
+                    'class' => Monolog\Formatter\LineFormatter::class,
+                    'constructor' => [ null, 'Y-m-d H:i:s', true],
+                ],
+            ]
+        ],
+    ],
+];

+ 23 - 0
frameworks/PHP/webman/config/route.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<[email protected]>
+ * @copyright walkor<[email protected]>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+
+use Webman\Route;
+
+
+Route::any('/json', 'app\controller\Index@json');
+Route::any('/plaintext', 'app\controller\Index@plaintext');
+Route::any('/fortunes', 'app\controller\Index@fortunes');
+Route::any('/db', 'app\controller\Index@db');
+Route::any('/queries[/[{q}]]', 'app\controller\Index@queries');
+Route::any('/updates[/[{q}]]', 'app\controller\Index@updates');

+ 27 - 0
frameworks/PHP/webman/config/server.php

@@ -0,0 +1,27 @@
+<?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
+ */
+
+return [
+    'listen'               => 'http://0.0.0.0:8080',
+    'transport'            => 'tcp',
+    'context'              => [],
+    'name'                 => 'webman',
+    'count'                => cpu_count() * 4,
+    'user'                 => '',
+    'group'                => '',
+    'pid_file'             => runtime_path() . '/webman.pid',
+    'max_request'          => 10000000000,
+    'stdout_file'          => runtime_path() . '/logs/stdout.log',
+    'max_package_size'     => 10*1024*1024
+];

+ 10 - 0
frameworks/PHP/webman/php.ini

@@ -0,0 +1,10 @@
+opcache.enable=1
+opcache.enable_cli=1
+opcache.validate_timestamps=0
+opcache.save_comments=0
+opcache.enable_file_override=1
+opcache.huge_code_pages=1
+
+mysqlnd.collect_statistics = Off
+
+memory_limit = 512M

+ 10 - 0
frameworks/PHP/webman/public/404.html

@@ -0,0 +1,10 @@
+<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>

+ 3 - 0
frameworks/PHP/webman/runtime/.gitignore

@@ -0,0 +1,3 @@
+*
+!logs
+!.gitignore

+ 2 - 0
frameworks/PHP/webman/runtime/logs/.gitignore

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

+ 123 - 0
frameworks/PHP/webman/start.php

@@ -0,0 +1,123 @@
+<?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();

+ 23 - 0
frameworks/PHP/webman/support/Request.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<[email protected]>
+ * @copyright walkor<[email protected]>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace support;
+
+/**
+ * Class Request
+ * @package support
+ */
+class Request extends \Webman\Http\Request
+{
+
+}

+ 23 - 0
frameworks/PHP/webman/support/Response.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<[email protected]>
+ * @copyright walkor<[email protected]>
+ * @link      http://www.workerman.net/
+ * @license   http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace support;
+
+/**
+ * Class Response
+ * @package support
+ */
+class Response extends \Webman\Http\Response
+{
+
+}

+ 61 - 0
frameworks/PHP/webman/support/bootstrap/Container.php

@@ -0,0 +1,61 @@
+<?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
+ */
+namespace support\bootstrap;
+
+use Workerman\Worker;
+use Webman\Bootstrap;
+use Psr\Container\ContainerInterface;
+
+/**
+ * Class Container
+ * @package support
+ * @method static mixed get($name)
+ * @method static mixed make($name, array $parameters)
+ * @method static bool has($name)
+ */
+class Container implements Bootstrap
+{
+    /**
+     * @var ContainerInterface
+     */
+    protected static $_instance = null;
+
+    /**
+     * @param Worker $worker
+     * @return void
+     */
+    public static function start($worker)
+    {
+        static::$_instance = include config_path() . '/container.php';
+    }
+
+    /**
+     * @param $name
+     * @param $arguments
+     * @return mixed
+     */
+    public static function __callStatic($name, $arguments)
+    {
+        return static::$_instance->{$name}(... $arguments);
+    }
+
+    /**
+     * instance
+     * @return
+     */
+    public static function instance()
+    {
+        return static::$_instance;
+    }
+}

+ 39 - 0
frameworks/PHP/webman/support/bootstrap/Date.php

@@ -0,0 +1,39 @@
+<?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
+ */
+namespace support\bootstrap;
+
+use Webman\Bootstrap;
+use Workerman\Timer;
+
+/**
+ * Class Date
+ * @package support\bootstrap
+ */
+class Date implements Bootstrap {
+
+    public static $date = '';
+
+    /**
+     * @param \Workerman\Worker $worker
+     * @return void
+     */
+    public static function start($worker)
+    {
+        self::$date = gmdate('D, d M Y H:i:s').' GMT';
+        Timer::add(1, function() {
+            self::$date = gmdate('D, d M Y H:i:s').' GMT';
+        });
+    }
+
+}

+ 79 - 0
frameworks/PHP/webman/support/bootstrap/Log.php

@@ -0,0 +1,79 @@
+<?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
+ */
+namespace support\bootstrap;
+
+use Webman\Bootstrap;
+use Monolog\Logger;
+
+/**
+ * Class Redis
+ * @package support
+ *
+ * @method static void log($level, $message, array $context = [])
+ * @method static void debug($message, array $context = [])
+ * @method static void info($message, array $context = [])
+ * @method static void notice($message, array $context = [])
+ * @method static void warning($message, array $context = [])
+ * @method static void error($message, array $context = [])
+ * @method static void critical($message, array $context = [])
+ * @method static void alert($message, array $context = [])
+ * @method static void emergency($message, array $context = [])
+ */
+class Log implements Bootstrap {
+
+    /**
+     * @var array
+     */
+    protected static $_instance = [];
+
+    /**
+     * @param \Workerman\Worker $worker
+     * @return void
+     */
+    public static function start($worker)
+    {
+        $configs = config('log', []);
+        foreach ($configs as $channel => $config) {
+            $logger = static::$_instance[$channel] = new Logger($channel);
+            foreach ($config['handlers'] as $handler_config) {
+                $handler = new $handler_config['class'](... \array_values($handler_config['constructor']));
+                if (isset($handler_config['formatter'])) {
+                    $formatter = new $handler_config['formatter']['class'](... \array_values($handler_config['formatter']['constructor']));
+                    $handler->setFormatter($formatter);
+                }
+                $logger->pushHandler($handler);
+            }
+        }
+    }
+
+    /**
+     * @param string $name
+     * @return Logger;
+     */
+    public static function channel($name = 'default')
+    {
+        return static::$_instance[$name] ?? null;
+    }
+
+
+    /**
+     * @param $name
+     * @param $arguments
+     * @return mixed
+     */
+    public static function __callStatic($name, $arguments)
+    {
+        return static::channel('default')->{$name}(... $arguments);
+    }
+}

+ 70 - 0
frameworks/PHP/webman/support/bootstrap/db/Raw.php

@@ -0,0 +1,70 @@
+<?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
+ */
+namespace support\bootstrap\db;
+
+use Webman\Bootstrap;
+use Workerman\Worker;
+use PDOStatement;
+use PDO;
+
+/**
+ * Class Raw
+ * @package support\bootstrap\db
+ */
+class Raw implements Bootstrap
+{
+    /**
+     * @var \PDO
+     */
+    public static $pdo;
+
+    /**
+     * @var PDOStatement
+     */
+    public static $statement;
+
+    /**
+     * @var PDOStatement
+     */
+    public static $fortune;
+
+    /**
+     * @var PDOStatement
+     */
+    public static $random;
+
+    /**
+     * @var PDOStatement
+     */
+    public static $update;
+
+    /**
+     * @param Worker $worker
+     *
+     * @return void
+     */
+    public static function start($worker)
+    {
+        $pdo = new PDO('pgsql:host=tfb-database;dbname=hello_world',
+            'benchmarkdbuser', 'benchmarkdbpass',
+            [PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+                PDO::ATTR_EMULATE_PREPARES    => false]
+        );
+        self::$statement = $pdo->prepare('SELECT id,randomNumber FROM World WHERE id=?');
+        self::$fortune   = $pdo->prepare('SELECT id,message FROM Fortune');
+        self::$random    = $pdo->prepare('SELECT randomNumber FROM World WHERE id=?');
+        self::$update    = $pdo->prepare('UPDATE World SET randomNumber=? WHERE id=?');
+        self::$pdo = $pdo;
+    }
+}

+ 256 - 0
frameworks/PHP/webman/support/helpers.php

@@ -0,0 +1,256 @@
+<?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 support\Request;
+use support\Response;
+use support\view\Raw;
+use support\bootstrap\Translation;
+use Webman\App;
+use Webman\Config;
+use Webman\Exception\ClassNotFoundException;
+
+define('BASE_PATH', realpath(__DIR__ . '/../'));
+
+/**
+ * @return string
+ */
+function base_path()
+{
+    return BASE_PATH;
+}
+
+/**
+ * @return string
+ */
+function app_path()
+{
+    return BASE_PATH . DIRECTORY_SEPARATOR . 'app';
+}
+
+/**
+ * @return string
+ */
+function public_path()
+{
+    return BASE_PATH . DIRECTORY_SEPARATOR . 'public';
+}
+
+/**
+ * @return string
+ */
+function config_path()
+{
+    return BASE_PATH . DIRECTORY_SEPARATOR . 'config';
+}
+
+/**
+ * @return string
+ */
+function runtime_path()
+{
+    return BASE_PATH . DIRECTORY_SEPARATOR . 'runtime';
+}
+
+/**
+ * @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 string
+ */
+function view($template, $vars = [], $app = null)
+{
+    static $handler;
+    if (null === $handler) {
+        $handler = config('view.handler');
+    }
+    return new Response(200, [], $handler::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);
+}
+
+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;
+        }
+
+        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 (($valueLength = strlen($value)) > 1 && $value[0] === '"' && $value[$valueLength - 1] === '"') {
+            return substr($value, 1, -1);
+        }
+
+        return $value;
+    }
+}
+
+/**
+ * @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)
+{
+    return Translation::trans($id, $parameters, $domain, $locale);
+}
+
+/**
+ * @param null|string $locale
+ * @return string
+ */
+function locale(string $locale)
+{
+    if (!$locale) {
+        return Translation::getLocale();
+    }
+    Translation::setLocale($locale);
+}
+
+/**
+ * @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);
+    }
+}
+
+/**
+ * @return int
+ */
+function cpu_count() {
+    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;
+}

+ 23 - 0
frameworks/PHP/webman/webman.dockerfile

@@ -0,0 +1,23 @@
+FROM ubuntu:20.04
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null
+RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php
+RUN apt-get update -yqq > /dev/null && \
+    apt-get install -yqq php7.4 php7.4-common php7.4-cli php7.4-pgsql php7.4-xml > /dev/null
+
+RUN apt-get install -yqq composer > /dev/null
+
+RUN apt-get install -y php-pear php-dev libevent-dev > /dev/null
+RUN printf "\n\n /usr/lib/x86_64-linux-gnu/\n\n\nno\n\n\n" | pecl install event > /dev/null && echo "extension=event.so" > /etc/php/7.4/cli/conf.d/event.ini
+
+COPY php.ini /etc/php/7.4/cli/php.ini
+
+ADD ./ /webman
+WORKDIR /webman
+
+
+RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet
+
+CMD php /webman/start.php start