Browse Source

Merge pull request #9553 from eoioer/master

[PHP] Add cyberphp tests
Mike Smith 7 months ago
parent
commit
0c959df4a1

+ 26 - 0
frameworks/PHP/cyberphp/README.md

@@ -0,0 +1,26 @@
+# CyberPHP Benchmarking Test
+
+## Test URLs
+### JSON
+
+http://localhost:8080/json
+
+### PLAINTEXT
+
+http://localhost:8080/plaintext
+
+### DB
+
+http://localhost:8080/db
+
+### QUERY
+
+http://localhost:8080/queries/[count]
+
+### UPDATE
+
+http://localhost:8080/updates/[count]
+
+### FORTUNES
+
+http://localhost:8080/fortunes

+ 57 - 0
frameworks/PHP/cyberphp/app/config.php

@@ -0,0 +1,57 @@
+<?php
+return [
+    'app_name' => 'Cyber',
+    // Request middleware runs after obtaining request body and before parsing route
+    // Mainly used for blacklist, whitelist, system maintenance, request filtering, data access, etc.
+    'request_middleware' => [
+        // \app\common\middleware\IpBlacklistMiddleware::class,// IP blacklist middleware
+        // \app\middleware\RateLimitMiddleware::class,// Rate limit middleware
+        // \app\middleware\SecurityMiddleware::class, // Security protection (CSRF/XSS filtering/SQL injection) middleware
+    ],
+    // Business middleware runs after parsing route and before executing controller method
+    // Mainly used for common business such as user authentication
+    'middleware' => [
+        // \app\common\middleware\Route1Middleware::class,
+        // \app\common\middleware\Route2Middleware::class,
+    ],
+    'orm' => 'pdo',
+    'pdo' => [
+        'dsn' => 'pgsql:host=tfb-database;dbname=hello_world',
+        'username' => 'benchmarkdbuser',
+        'password' => 'benchmarkdbpass',
+        'options' => [PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,PDO::ATTR_EMULATE_PREPARES    => false]
+    ],
+    'eloquent' => [
+        'driver' => 'mysql',
+        'host' => '127.0.0.1',
+        'database' => 'lavaman',
+        'username' => 'root',
+        'password' => 'root',
+        'charset' => 'utf8mb4',
+        'prefix' => '',
+    ],
+    'thinkorm' => [
+        'default'    =>    'mysql',
+        'connections'    =>    [
+            'mysql'    =>    [
+                'type'        => 'mysql', // Database type
+                'hostname'    => '127.0.0.1',// Server address
+                'database'    => 'lavaman',// Database name
+                'username'    => 'root',// Database username
+                'password'    => 'root',// Database password
+                'hostport'    => '',// Database connection port
+                'params'      => [],
+                'charset'     => 'utf8mb4',// Database encoding default utf8
+                'prefix'      => '',// Table prefix
+            ],
+        ],
+    ],
+    'cookie' => [
+        'expires' => 0,
+        'path' => '/',
+        'domain' => '',
+        'secure' => true,
+        'httponly' => true,
+        'samesite' => 'Lax'  // None, Lax, Strict
+    ]
+];

+ 74 - 0
frameworks/PHP/cyberphp/app/controller/Index.php

@@ -0,0 +1,74 @@
+<?php
+namespace app\controller;
+
+use Cyber\Response;
+class Index {
+
+    public function json()
+    {
+        return Response::json(['message' => 'Hello, World!']);
+    }
+
+    public function plaintext()
+    {
+        return Response::text('Hello, World!');
+    }
+
+    public function db()
+    {
+        $prepare = app()->dbWorld;
+        $prepare->execute([mt_rand(1, 10000)]);
+        $data = $prepare->fetch();
+        return Response::json($data);
+    }
+    public function fortunes()
+    {
+        $fortune = app()->dbFortune;
+        $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 Response::html("<!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($q=1)
+    {
+        $statement = app()->dbWorld;
+        $query_count = max(min(intval($q), 500), 1);
+        $arr = [];
+        while ($query_count--) {
+            $statement->execute([mt_rand(1, 10000)]);
+            $arr[] = $statement->fetch();
+        }
+        return Response::json($arr);
+    }
+
+    public function updates($q=1)
+    {
+        static $updates = [];
+
+        $random = app()->dbWorld;
+        $count = max(min(intval($q), 500), 1);
+
+        $worlds = $keys = $values = [];
+        for ($i = 0; $i < $count; ++ $i) {
+            $values[] = $keys[] = $id = mt_rand(1, 10000);
+            $random->execute([$id]);
+            $row = $random->fetch();
+            $values[] = $row['randomNumber'] = mt_rand(1, 10000);
+            $worlds[] = $row;
+        }
+        if (!isset($updates[$count])) {
+            $sql = 'UPDATE World SET randomNumber = CASE id' . str_repeat(' WHEN ?::INTEGER THEN ?::INTEGER ', $count) . 'END WHERE id IN (' . str_repeat('?::INTEGER,', $count - 1) . '?::INTEGER)';
+            $updates[$count] = app()->db->prepare($sql);
+        }
+        $updates[$count]->execute([...$values, ...$keys]);
+
+        return Response::json($worlds);
+    }
+}

+ 84 - 0
frameworks/PHP/cyberphp/app/helpers.php

@@ -0,0 +1,84 @@
+<?php
+declare(strict_types=1);
+
+/**
+* Get the application instance
+* If there is a configuration file, create the application instance
+* If there is no configuration file, return the already created instance
+* If there is no configuration file and no instance has been created, throw an exception
+* @param string $bootstrap Configuration file
+* @return \Cyber\App
+*/
+if (!function_exists('app')) {
+    function app(): \Cyber\App
+    {
+        return \Cyber\App::getInstance();
+    }
+}
+
+/**
+* Get configuration information
+* config();                  // Returns all configurations
+* config('app.name');        // Gets the value of app.name
+* config('db.mysql.host');   // Gets the value of db.mysql.host
+* @param string|null $key Configuration key name, supports multi-level configuration separated by dots, e.g., 'app.debug'
+* @param mixed $default Default value, returned when the configuration item does not exist
+* @return mixed
+*/
+if (!function_exists('config')) {
+    function config(?string $key = null, $default = null)
+    {
+        return \Cyber\App::getInstance()->getConfig($key) ?? $default;
+    }
+}
+
+function renderExceptionPage($e, $debug = true, $templateFile = ''): string
+{
+    // Determine template path
+    $templateFile = !empty($templateFile) ? $templateFile : __DIR__ . '/views/errors/exception.html';
+    // Prepare template variables
+    $data = [
+        'code' => $e->getCode(),
+        'message' => $debug ? $e->getMessage() : 'The current server is experiencing an error, please contact the administrator or try again later.',
+        'error' => $e->getMessage(),
+    ];
+    // Add more information in debug mode
+    if ($debug) {
+        $data['trace'] = [];
+        $data['file'] = $e->getFile();
+        $data['line'] = $e->getLine();
+        $traceFiles = $e->getTrace();
+        array_unshift($traceFiles, ['file' => $data['file'], 'line' => $data['line']]);
+        foreach ($traceFiles as $v) {
+            try {
+                if (isset($v['file']) && isset($v['line'])) {
+                    $startline = max(1, $v['line'] - 10);
+                    $contents  = file($v['file']);
+                    $data['trace'][] = [
+                        'file' => $v['file'],
+                        'line' => $v['line'],
+                        'source0' => $contents ? array_slice($contents, 0, 1) : '',
+                        'source' => [
+                            'startline' => $startline,
+                            'content' => array_slice($contents, $startline - 1, 16)
+                        ]
+                    ];
+                }
+            } catch (\Throwable $e) {
+                continue;
+            }
+        }
+    }
+    // Render error page
+    if (!file_exists($templateFile)) {
+        $msg = '<div style="margin:100px auto 20px;text-align:center"><h1>Error ' . $data['code'] . '</h1></div>';
+        $msg .= '<div style="margin:0px auto;text-align:center">Sorry, the server encountered an error</div>';
+        $msg .= '<div style="margin:50px auto;text-align:center"><h2><pre>' . htmlspecialchars($data['message']) . '</pre></h2></div>';
+        $msg .= '<div style="margin:100px auto;text-align:center"><a href="/"><button>Return to Home</button></a></div>';
+        return $msg;
+    }
+    extract($data);
+    ob_start();
+    include $templateFile;
+    return ob_get_clean();
+}

+ 14 - 0
frameworks/PHP/cyberphp/app/route.php

@@ -0,0 +1,14 @@
+<?php
+use Cyber\Request;
+
+return [
+    ['/', 'GET', 'app\controller\Index@hello'],
+    ['/json', 'GET', 'app\controller\Index@json'],
+    ['/plaintext', 'GET', 'app\controller\Index@plaintext'],
+    ['/db', 'GET', 'app\controller\Index@db'],
+    ['/fortunes', 'GET', 'app\controller\Index@fortunes'],
+    ['/queries/', 'GET', 'app\controller\Index@queries'],
+    ['/queries/{q}', 'GET', 'app\controller\Index@queries'],
+    ['/updates/', 'GET', 'app\controller\Index@updates'],
+    ['/updates/{q}', 'GET', 'app\controller\Index@updates'],
+];

+ 62 - 0
frameworks/PHP/cyberphp/app/views/errors/exception.html

@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>An Error Occurred</title>
+    <style>
+        body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;line-height:1.6;margin:0;padding:0;background:#f8f9fa;color:#343a40}
+        pre{margin:0;padding:0;}
+        .container{max-width:80%;margin:50px auto;padding:20px;background:#fff;border-radius:8px;box-shadow:0 2px 4px rgba(0,0,0,0.1)}
+        .error-code{font-size:72px;font-weight:bold;color:#dc3545;margin:0;line-height:1}
+        .error-title{font-size:24px;color:#495057;margin:20px 0}
+        .error-message{font-size:16px;color:#6c757d;margin-bottom:30px;padding:15px;background:#f8f9fa;border-radius:4px;border-left:4px solid #dc3545}
+        .back-link{display:inline-block;margin-top:20px;padding:10px 20px;background:#007bff;color:#fff;text-decoration:none;border-radius:4px;transition:background-color 0.2s}
+        .back-link:hover{background:#0056b3}
+
+        .source-box{margin:10px 0;padding:0px;}
+        .source-box a{color:#369;font-weight:900;}
+        .source-file{padding:5px 10px;background:#f8f9fa;}
+        .source-code{padding:4px 0px;border: 0px solid #ddd;background: #f1f2f3;overflow-x: auto;}
+        .source-code ol,.source-code ul{margin: 0;color: #555;font-size:14px;}
+        .source-code code{border-left: 1px solid #ccc;padding-left:5px;font-family: "Consolas";}
+        .source-error{color:#000;background:#dc354533!important}
+        .source-error code{font-weight:900;}
+    </style>
+</head>
+<body>
+    <div class="container">
+        <h1 class="error-code">Error <?php echo htmlspecialchars($code); ?></h1>
+        <h2 class="error-title">Sorry, an error occurred</h2>
+        <div class="error-message"><?php echo htmlspecialchars($message); ?></div>
+        <!-- <?php echo htmlspecialchars($error); ?> -->
+        <?php if (isset($trace)) { ?>
+            <?php foreach ($trace as $k => $v) { ?>
+                <div class="source-box">
+                    <div class="source-file">#<?= $k ?> <a href="view-source:file:///<?php echo $v['file']; ?>"><?php echo $v['file']; ?></a> Line <?php echo $v['line']; ?></div>
+                    <div class="source-code">
+                        <pre><?php
+                        if (!empty($v['source0'])) {
+                            echo '<ol start="0"><li><code>'.htmlentities($v['source0'][0]).'</code></li></ol>';
+                        }
+                        if (!empty($v['source'])) {
+                            echo '<ol start="'.htmlentities($v['source']['startline']).'">';
+                            foreach ((array) $v['source']['content'] as $key => $value) {
+                                if (($key + $v['source']['startline']) == $v['line']) {
+                                    echo '<li class="source-error"><code>'.htmlentities($value).'</code></li>';
+                                } else {
+                                    echo '<li><code>'.htmlentities($value).'</code></li>';
+                                }
+                            }
+                            echo '</ol>';
+                        }
+                        ?></pre>
+                    </div>
+                </div>
+            <?php } ?>
+        <?php } ?>
+        <a href="/" class="back-link">Return to Home</a>
+</body>
+
+</html>

+ 30 - 0
frameworks/PHP/cyberphp/benchmark_config.json

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

+ 41 - 0
frameworks/PHP/cyberphp/bootstrap.php

@@ -0,0 +1,41 @@
+<?php
+return [
+
+    // Route configuration
+    'route_path' =>  __DIR__ .'/app/route.php',
+
+    // Application configuration file location can be modified freely
+
+    // Key represents the name of the sub-application; sub-applications not listed cannot be accessed
+    'config' => [
+        // Default configuration
+        '' => require 'app/config.php',
+        // If a sub-application does not mention a configuration, the content of the default configuration file will be used
+        // 'admin'=> (require 'app/admin/config.php') + (require 'app/config.php'),
+
+        // Or only use the default configuration
+        // 'phone'=> require 'app/config.php',
+
+        // Or do not use the default configuration, directly use your custom sub-application configuration, you can change the name freely
+        // 'phone'=> require 'app/config_phone.php',
+
+        // Or this way, each configuration item is introduced separately
+        // 'admin'=> [
+        //     'app_name' => 'admin',
+        //     'request_middleware' => require 'app/admin/config_request_middleware.php',
+        //     'middleware' => require 'app/admin/config_middleware.php',
+        //     'database' => require 'app/admin/config_database.php',
+        //     'cookie' => require 'app/admin/config_cookie.php',
+        //     'database' => require 'app/admin/config_database.php',
+        // ],
+    ],
+
+    // Create route manager
+    'Route' => \DI\create(\Cyber\Route::class),
+    'Middleware' => \DI\create(\Cyber\Middleware::class),
+
+    // Create request object for handling HTTP requests
+    'Request' => \DI\create(\Cyber\Request::class),
+    // Create response object for generating HTTP responses
+    'Response' => \DI\create(\Cyber\Response::class),
+];

+ 29 - 0
frameworks/PHP/cyberphp/composer.json

@@ -0,0 +1,29 @@
+{
+    "name": "eoioer/cyberphp",
+    "authors": [
+        {
+            "name": "eoioer",
+            "email": "[email protected]"
+        }
+    ],
+    "type": "project",
+    "description": "The Fastest and Smallest PHP Framework",
+    "license": "MIT",
+    "require": {
+        "php": ">=8.3",
+        "php-di/php-di": "^7.0",
+        "nikic/fast-route": "^1.3",
+        "workerman/workerman": "^4.1"
+    },
+    "autoload": {
+        "psr-4": {
+            "app\\": "app/",
+            "Cyber\\": "src/"
+        },
+		"files": [
+			"app/helpers.php"
+		]
+    },
+    "minimum-stability": "stable",
+    "prefer-stable": true
+}

+ 26 - 0
frameworks/PHP/cyberphp/cyberphp.dockerfile

@@ -0,0 +1,26 @@
+FROM ubuntu:24.04
+
+ENV TEST_TYPE default
+
+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 > /dev/null && \
+    apt-get update -yqq > /dev/null && apt-get upgrade -yqq > /dev/null
+
+RUN apt-get install -yqq php8.4-cli php8.4-pgsql php8.4-xml > /dev/null
+
+COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
+
+RUN apt-get update -yqq && apt-get install -y php-pear php8.4-dev libevent-dev git  > /dev/null
+RUN pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.4/cli/conf.d/30-event.ini
+
+WORKDIR /cyberphp
+COPY --link . .
+
+RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet
+COPY php.ini /etc/php/8.4/cli/conf.d/10-opcache.ini
+
+EXPOSE 8080
+
+CMD php /cyberphp/server.php start

+ 11 - 0
frameworks/PHP/cyberphp/php.ini

@@ -0,0 +1,11 @@
+zend_extension=opcache.so
+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
+opcache.jit_buffer_size=128M
+opcache.jit=tracing

BIN
frameworks/PHP/cyberphp/public/favicon.ico


+ 23 - 0
frameworks/PHP/cyberphp/public/index.php

@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+use Cyber\Response;
+use Cyber\App;
+
+require dirname(__DIR__) . '/vendor/autoload.php';
+
+
+$app = App::bootstrap(dirname(__DIR__) . '/bootstrap.php');
+
+try {
+    $response = $app->run();
+    if (!$response instanceof Response) {
+        $response = Response::html($response ?? '');
+    }
+    echo $response->send();
+} catch (Exception $e) {
+    echo renderExceptionPage($e);
+} catch (Throwable $e) {
+    echo renderExceptionPage($e);
+}

+ 85 - 0
frameworks/PHP/cyberphp/server.php

@@ -0,0 +1,85 @@
+<?php
+/**
+ * Workerman HTTP server startup file
+ * Based on Workerman to implement an HTTP server, integrated with the ThinkPHP framework
+ */
+
+use Workerman\Worker;
+use Workerman\Connection\TcpConnection;
+use Workerman\Protocols\Http\Request as WorkermanRequest;
+use Cyber\Response;
+
+// Load Composer autoload file
+require __DIR__ . '/vendor/autoload.php';
+
+// Create a Worker listening on port 8099, using HTTP protocol for communication
+$http_worker = new Worker("http://0.0.0.0:8080");
+
+// Set the number of processes (set according to the number of CPU cores, recommended to be 1-4 times the number of CPU cores)
+$http_worker->count = 4;
+
+// Initialize ThinkPHP application
+$app = \Cyber\App::bootstrap(__DIR__.'/bootstrap.php');
+
+/**
+ * Callback function to handle HTTP requests
+ * @param TcpConnection $connection Client connection object
+ * @param WorkermanRequest $request HTTP request object
+ */
+$http_worker->onMessage = function(TcpConnection $connection, WorkermanRequest $request) use ($app) {
+    // Initialize request object
+    $_GET = $request->get();  // Get GET parameters
+    $_POST = $request->post(); // Get POST parameters
+    $_FILES = $request->file(); // Get file uploads
+    $_COOKIE = $request->cookie(); // Get COOKIE
+
+    // Merge server variables
+    $_SERVER = array_merge($_SERVER, [
+        'RAW_BODY' => $request->rawBody(), // Raw request body
+        'REQUEST_METHOD' => $request->method(), // Request method
+        'REQUEST_URI' => $request->uri(), // Request URI
+        'QUERY_STRING' => $request->queryString(), // Query string
+        'REMOTE_ADDR' => $connection->getRemoteIp(), // Client IP
+        'REMOTE_PORT' => $connection->getRemotePort(), // Client port
+        'SERVER_PROTOCOL' => 'HTTP/'.$request->protocolVersion(), // Protocol version
+    ]);
+
+    // Handle request headers
+    foreach ($request->header() as $key => $value) {
+        $_SERVER['HTTP_' . strtoupper(str_replace('-', '_', $key))] = $value;
+    }
+
+    try {
+        ob_start(); // Start output buffering
+        $response = $app->run(); // Run ThinkPHP application
+
+        // Handle response
+        if(!$response instanceof Response){
+            // If not a Response object, directly output content
+            echo $response;
+            $content = ob_get_clean();
+            $connection->send($content);
+        }else{
+            // If it is a Response object, send HTTP response
+            echo $response->send();
+            $content = ob_get_clean();
+            $connection->send(new Workerman\Protocols\Http\Response(
+                $response->getStatusCode(), // Status code
+                $response->getHeaders(), // Response headers
+                $content // Response content
+            ));
+        }
+    } catch (Exception $e) {
+        // Catch exceptions and render error page
+        $connection->send(renderExceptionPage($e));
+    } catch (Throwable $e) {
+        // Catch all errors
+        $connection->send(renderExceptionPage($e));
+    }
+};
+
+/**
+ * Run all Worker instances
+ * This method will block the current process until all Workers stop
+ */
+Worker::runAll();

+ 154 - 0
frameworks/PHP/cyberphp/src/App.php

@@ -0,0 +1,154 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Cyber;
+
+use DI\Container;
+use Cyber\Request;
+use Cyber\Response;
+use Cyber\Middleware;
+use Cyber\Utility;
+use PDO;
+
+class App
+{
+    /** App instance object */
+    private static ?self $instance = null;
+    /** Container instance */
+    public Container $container;
+    /** Application configuration */
+    public array $config;
+    public  $data;
+    /** Request configuration */
+    public Request $request;
+    /** Response configuration */
+    public Response $response;
+    /** Middleware */
+    public Middleware $middleware;
+
+    /** Route configuration */
+    public array $routes;
+    /** Route manager */
+    public Route $route;
+
+    /** Application name */
+    public string $appName;
+    public $db;
+    public $dbWorld;
+    public $dbFortune;
+
+    public $start_time;
+    public $timestamps;
+    /**
+    * Constructor
+    * Configuration check, initialization configuration, container
+    */
+    public function __construct($containerConfig = null)
+    {
+        $this->start_time = time();
+
+        /* Build container instance */
+        $this->container = new Container($containerConfig);
+
+        /* Load route configuration */
+        $routes = require $this->container->get('route_path');
+        /* Create route manager */
+        $this->route = $this->container->get('Route');
+        /* Call route dispatcher */
+        $this->route->dispatcher($routes);
+
+        /* Configuration */
+        $this->config = $this->container->get('config');
+        /* Request object */
+        $this->request = $this->container->get('Request');
+
+        /* Database */
+        $pdo =  new PDO(...$this->getConfig('pdo'));
+        $this->db = $pdo;
+        $this->dbWorld = $pdo->prepare('SELECT id,randomNumber FROM World WHERE id=?');
+        $this->dbFortune = $pdo->prepare('SELECT id,message FROM Fortune');
+
+    }
+    /**
+    * Run application
+    */
+    public function run()
+    {
+        $this->timestamps = time();
+        /* cli mode maintains database connection */
+        if (php_sapi_name() === 'cli' and time() - $this->start_time > 1) {
+            $this->start_time = time();
+            $pdo =  new PDO(...$this->getConfig('pdo'));
+            $this->db = $pdo;
+            $this->dbWorld = $pdo->prepare('SELECT id,randomNumber FROM World WHERE id=?');
+            $this->dbFortune = $pdo->prepare('SELECT id,message FROM Fortune');
+        }
+
+        /* Return response */
+        return $this->route->handleRoute();
+    }
+
+    /**
+    * Get the current application configuration
+    * $app->getConfig();                         // Returns the entire configuration content of the current application
+    * $app->getConfig('app_name');               // Get the value of ['app_name'] in the current application configuration
+    * $app->getConfig('cookie.expires');         // Get the value of ['cookie']['expires']
+    * $app->getConfig(null, 'admin');            // Returns the entire configuration content of the admin application
+    * $app->getConfig('app_name', 'admin');      // Get the value of ['app_name'] in the admin application configuration
+    * $app->getConfig('cookie.expires','admin'); // Get the value of ['cookie']['expires'] in the admin application configuration
+    */
+    public function getConfig($key = null, $appName = null): mixed
+    {
+        $appName = $appName ?? $this->appName ?? '';
+        $config = $this->config[$appName] ?? null;
+        // Get the entire application configuration
+        if ($key === null) {
+            return $config;
+        }
+        // Split the key into an array
+        $keys = explode('.', $key);
+        // Traverse the key array and get the configuration layer by layer
+        foreach ($keys as $k) {
+            if (is_array($config) && array_key_exists($k, $config)) {
+                $config = $config[$k];
+            } else {
+                return null; // If a layer does not exist, return null
+            }
+        }
+        return $config; // Return the final configuration value
+    }
+    /**
+    * Initialize the application
+    * @param string $bootstrap Configuration file
+    * @return self
+    */
+    public static function bootstrap($bootstrap = null): self
+    {
+        if (!$bootstrap) {
+            throw new \Exception('App::bootstrap parameter does not exist');
+        }
+        if (self::$instance === null) {
+            /* Load container configuration file */
+            if (!file_exists($bootstrap) || !is_readable($bootstrap)) {
+                throw new \Exception("App::bootstrap parameter {$bootstrap} path error");
+            }
+            $containerConfig = require_once $bootstrap;
+            self::$instance = new self($containerConfig);
+            return self::$instance;
+        }else{
+            throw new \Exception('Application has started');
+        }
+    }
+    /**
+    * Get the application singleton instance
+    * @return self
+    */
+    public static function getInstance(): self
+    {
+        if (self::$instance === null) {
+            throw new \Exception('Application has not started');
+        }
+        return self::$instance;
+    }
+}

+ 48 - 0
frameworks/PHP/cyberphp/src/Middleware.php

@@ -0,0 +1,48 @@
+<?php
+namespace Cyber;
+
+class Middleware
+{
+
+    public function handleRequest(array $requestMiddlewares)
+    {
+        $request = app()->request;
+        foreach ($requestMiddlewares as $middleware) {
+            if (!class_exists($middleware)) {
+                throw new \Exception("The parameter class {$middleware} for processing the request middleware does not exist");
+            }
+            $instance = app()->container->get($middleware);
+            if (!method_exists($instance, 'handle')) {
+                throw new \Exception("The parameter class {$middleware} for processing the request middleware does not have a handle method");
+            }
+            /* Call the handle method of the request data middleware */
+            $request = $instance->handle($request);
+        }
+        return $request;
+    }
+
+    public function handle(array $Middlewares, callable $finalHandler)
+    {
+        $request = app()->request;
+        $container = app()->container;
+        // Start wrapping the handler from the last middleware layer by layer
+        $response = array_reduce(
+            array_reverse($Middlewares),
+            function($next, $middleware) use ($request,$container) {
+                if (!class_exists($middleware)) {
+                    throw new \Exception("The middleware parameter class {$middleware} does not exist");
+                }
+                $instance = $container->get($middleware);
+                if (!method_exists($instance, 'handle')) {
+                    throw new \Exception("The middleware parameter class {$middleware} does not have a handle method");
+                }
+                return function() use ($instance, $request, $next) {
+                    return $instance->handle($request, $next);
+                };
+            },
+            $finalHandler
+        );
+        // Execute the middleware chain
+        return $response();
+    }
+}

+ 232 - 0
frameworks/PHP/cyberphp/src/Request.php

@@ -0,0 +1,232 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Cyber;
+
+/**
+* HTTP Request Class
+*
+* This class encapsulates HTTP request information and provides a standard interface for accessing request data.
+* The design references Laravel's Request class and implements common request handling functions.
+*/
+class Request
+{
+    public static function getPathInfo(): string
+    {
+        $requestUri = $_SERVER['REQUEST_URI'] ?? '/';
+
+        // Remove query string
+        if (($pos = strpos($requestUri, '?')) !== false) {
+            $requestUri = substr($requestUri, 0, $pos);
+        }
+
+        // Remove script name (if any)
+        $scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
+        if ($scriptName && strpos($requestUri, $scriptName) === 0) {
+            $requestUri = substr($requestUri, strlen($scriptName));
+        }
+        return $requestUri ?: '/';
+    }
+    /**
+    * Get application name
+    *
+    * @return string|null
+    */
+    public function getAppName(): ?string
+    {
+        // Get the current request URI
+        $uri = static::getPathInfo();
+        $segments = explode('/', trim($uri, '/')); // Split the URI by slashes
+        // Return application name
+        return isset($segments[0]) ? $segments[0] : '';
+    }
+    /**
+    * Get request method
+    *
+    * @return string
+    */
+    public function getMethod(): string
+    {
+        return strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
+    }
+
+    /**
+    * Get GET parameter
+    *
+    * @param string|null $key Parameter name
+    * @param mixed $default Default value
+    * @return mixed
+    */
+    public function query(?string $key = null, $default = null)
+    {
+        if ($key === null) {
+            return $_GET;
+        }
+        return $_GET[$key] ?? $default;
+    }
+
+    /**
+    * Get POST parameter
+    *
+    * @param string|null $key Parameter name
+    * @param mixed $default Default value
+    * @return mixed
+    */
+    public function post(?string $key = null, $default = null)
+    {
+        if ($key === null) {
+            return $_POST;
+        }
+        return $_POST[$key] ?? $default;
+    }
+
+    /**
+    * Get all input parameters (GET + POST)
+    *
+    * @param string|null $key Parameter name
+    * @param mixed $default Default value
+    * @return mixed
+    */
+    public function all(?string $key = null, $default = null)
+    {
+        $data = array_merge($_GET, $_POST, self::getParsedContent());
+
+        if ($key === null) {
+            return $data;
+        }
+        return $data[$key] ?? $default;
+    }
+
+    /**
+    * Get parsed request content
+    *
+    * @return array
+    */
+    protected function getParsedContent(): array
+    {
+        $parsedContent = file_get_contents('php://input');
+
+        if (self::isJson()) {
+            $content = json_decode($parsedContent, true);
+            if (json_last_error() === JSON_ERROR_NONE) {
+                $parsedContent = $content;
+            }
+        }
+        return $parsedContent;
+    }
+
+    /**
+    * Get Cookie value
+    *
+    * @param string|null $key Cookie name
+    * @param mixed $default Default value
+    * @return mixed
+    */
+    public function cookie(?string $key = null, $default = null)
+    {
+        if ($key === null) {
+            return $_COOKIE;
+        }
+        return $_COOKIE[$key] ?? $default;
+    }
+
+    /**
+    * Check if it is an AJAX request
+    *
+    * @return bool
+    */
+    public function isAjax(): bool
+    {
+        return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
+    }
+
+    /**
+    * Check if it is a JSON request
+    *
+    * @return bool
+    */
+    public function isJson(): bool
+    {
+        $contentType = $_SERVER['CONTENT_TYPE'] ?? '';
+        return str_contains($contentType, '/json') || str_contains($contentType, '+json');
+    }
+    /**
+    * Check if it is a JSON request
+    *
+    * @return bool
+    */
+    public function rawBody(): bool
+    {
+        return $_SERVER['RAW_BODY'] ?? file_get_contents("php://input");
+    }
+
+    /**
+    * Get client IP address
+    *
+    * @return string
+    */
+    public function getClientIp(): string
+    {
+        $keys = ['HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'];
+
+        foreach ($keys as $key) {
+            if (isset($_SERVER[$key])) {
+                return $_SERVER[$key];
+            }
+        }
+
+        return '';
+    }
+
+    /**
+    * Get the complete URL of the request
+    *
+    * @return string
+    */
+    public function getUrl(): string
+    {
+        $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
+        $uri = $_SERVER['REQUEST_URI'] ?? '';
+        return "{$host}{$uri}";
+    }
+
+    /**
+    * Get specified request header
+    *
+    * @param string $key Request header name
+    * @param mixed $default Default value
+    * @return mixed
+    */
+    public function header($key=null, $default = null)
+    {
+        if($key === null) {
+            $headers = [];
+            foreach ($_SERVER as $key => $value) {
+                if (strpos($key, 'HTTP_') === 0) {
+                    $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))));
+                    $headers[$key] = $value;
+                }
+            }
+            ksort($headers);
+            return $headers;
+        }
+        return $_SERVER['HTTP_'.strtoupper(str_replace('-', '_', $key))] ?? $default;
+    }
+    /**
+    * Set request header
+    * @param string $key Header information name
+    * @param string $value Header information value
+    * @return void
+    */
+    public function setHeader(string $key, string $value): void
+    {
+        if(is_array($key)){
+            foreach ($key as $k => $v) {
+                $_SERVER['HTTP_' . strtoupper(str_replace('-', '_', $k))] = $v;
+            }
+        }else{
+            $_SERVER['HTTP_' . strtoupper(str_replace('-', '_', $key))] = $value;
+        }
+    }
+}

+ 218 - 0
frameworks/PHP/cyberphp/src/Response.php

@@ -0,0 +1,218 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Cyber;
+
+use Exception;
+
+class Response
+{
+    /**
+     * HTTP status codes and their descriptions
+     */
+    private const HTTP_STATUS = [
+        // 2xx Success
+        200 => 'OK',
+        201 => 'Created',
+        204 => 'No Content',
+
+        // 3xx Redirection
+        301 => 'Moved Permanently',
+        302 => 'Found',
+        304 => 'Not Modified',
+
+        // 4xx Client Errors
+        400 => 'Bad Request',
+        401 => 'Unauthorized',
+        403 => 'Forbidden',
+        404 => 'Not Found',
+        405 => 'Method Not Allowed',
+        408 => 'Request Timeout',
+        422 => 'Unprocessable Entity',
+        429 => 'Too Many Requests',
+
+        // 5xx Server Errors
+        500 => 'Internal Server Error',
+        502 => 'Bad Gateway',
+        503 => 'Service Unavailable',
+        504 => 'Gateway Timeout'
+    ];
+
+    protected string $content = '';
+    protected int $statusCode = 200;
+    protected array $headers = [];
+    protected bool $sent = false;
+
+    public function __construct(string $content = '', int $statusCode = 200, array $headers = [])
+    {
+        $this->content = $content;
+        $this->statusCode = $statusCode;
+        $this->headers = $headers;
+    }
+
+    /**
+     * Get status code description
+     */
+    public function getStatusText(): string
+    {
+        return self::HTTP_STATUS[$this->statusCode];
+    }
+    /**
+     * Get response body
+     */
+    public function getStatusCode(): int
+    {
+        return $this->statusCode;
+    }
+    /**
+     * Get response body
+     */
+    public function getContent(): string
+    {
+        return $this->content;
+    }
+
+    /**
+     * Get all response headers
+     */
+    public function getHeaders(): array
+    {
+        return $this->headers;
+    }
+
+    /**
+     * Add response header
+     *
+     * @throws Exception
+     */
+    public function withHeader(string $name, string|array $value): static
+    {
+        // Validate header name legality
+        if (!preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $name)) {
+            throw new Exception('Header name can only contain letters, numbers, and special characters');
+        }
+        if (empty($name)) {
+            throw new Exception('Header name cannot be empty');
+        }
+
+        $clone = clone $this;
+        $clone->headers[$name] = is_array($value) ? $value : [$value];
+        return $clone;
+    }
+
+    /**
+     * Add multiple response headers
+     */
+    public function withHeaders(array $headers): static
+    {
+        $clone = clone $this;
+        foreach ($headers as $name => $value) {
+            $clone = $clone->withHeader($name, $value);
+        }
+        return $clone;
+    }
+
+    /**
+     * Create JSON response
+     *
+     * @throws Exception
+     */
+    public static function json(mixed $data, int $status = 200, array $headers = []): static
+    {
+        try {
+            $json = json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
+        } catch (\JsonException $e) {
+            throw new Exception('Unable to encode data to JSON', 0, $e);
+        }
+
+        $headers['Content-Type'] = 'application/json; charset=utf-8';
+        $headers['Date'] = gmdate('D, d M Y H:i:s').' GMT';
+        return new static($json, $status, $headers);
+    }
+
+    /**
+     * Create HTML response
+     */
+    public static function html(string $html, int $status = 200, array $headers = []): static
+    {
+        $headers['Content-Type'] = 'text/html; charset=utf-8';
+        $headers['Date'] = gmdate('D, d M Y H:i:s').' GMT';
+        return new static($html, $status, $headers);
+    }
+
+    /**
+     * Create text response
+     */
+    public static function text(string $text, int $status = 200, array $headers = []): static
+    {
+        $headers['Content-Type'] = 'text/plain; charset=utf-8';
+        $headers['Date'] = gmdate('D, d M Y H:i:s').' GMT';
+        return new static($text, $status, $headers);
+    }
+    /**
+     * Create file response
+     */
+    public static function file(string $file, string $filename, int $status = 200, array $headers = []): static
+    {
+        $headers['Content-Type'] = 'application/octet-stream';
+        $headers['Date'] = gmdate('D, d M Y H:i:s').' GMT';
+        $headers['Content-Disposition'] = 'attachment; filename="' . $filename . '"';
+        return new static(file_get_contents($file), $status, $headers);
+    }
+
+    /**
+     * Create redirect response
+     *
+     * @throws Exception
+     */
+    public static function redirect(string $url, int $status = 302, array $headers = []): static
+    {
+        if (!filter_var($url, FILTER_VALIDATE_URL) && !str_starts_with($url, '/')) {
+            throw new Exception('Invalid URL format');
+        }
+        return new static('', $status, array_merge($headers, ['Location' => $url]));
+    }
+
+    /**
+     * Send response
+     *
+     * @throws Exception
+     */
+    public function send(): void
+    {
+        if ($this->isSent()) {
+            throw new Exception('Response already sent');
+        }
+        if (!headers_sent()) {
+        //     // 发送状态码
+            http_response_code($this->statusCode);
+
+        //     // 确保有 Content-Type 头
+            if (!isset($this->headers['Content-Type'])) {
+                $this->headers['Content-Type'] = ['text/html; charset=utf-8'];
+            }
+
+        //     // 发送响应头
+            foreach ($this->headers as $name => $values) {
+                $values = (array) $values;
+                foreach ($values as $value) {
+                    header($name . ': ' . $value, false);
+                }
+            }
+        }
+
+        // // 发送响应内容
+        echo $this->content;
+
+        // $this->sent = true;
+    }
+
+    /**
+     * Check if the response has been sent
+     */
+    public function isSent(): bool
+    {
+        return $this->sent;
+    }
+}

+ 61 - 0
frameworks/PHP/cyberphp/src/Route.php

@@ -0,0 +1,61 @@
+<?php
+namespace Cyber;
+
+use DI\Container;
+use FastRoute;
+use FastRoute\RouteCollector;
+
+class Route
+{
+    public $dispatcher;
+    // Route dispatch
+    public function dispatcher($routes)
+    {
+        $this->dispatcher = FastRoute\simpleDispatcher(function (RouteCollector $r) use ($routes) {
+            foreach ($routes as $route) {
+                // Check the number of array members. Three members indicate a single route configuration.
+                if (count($route) == 3) {
+                    $r->addRoute(preg_split('/\s*,\s*/', $route[1]), $route[0], $route[2]);
+                    // Two members indicate a group route.
+                } elseif (count($route) == 2) {
+                    $r->addGroup($route[0], function (RouteCollector $r) use ($route) {
+                        foreach ($route[1] as $childRoute) {
+                            $r->addRoute(preg_split('/\s*,\s*/', trim($childRoute[1])), $childRoute[0], $childRoute[2]);
+                        }
+                    });
+                }
+            }
+        });
+    }
+    public function handleRoute()
+    {
+        $request = app()->request;
+        $container = app()->container;
+        // Parse the current route
+        $routeInfo = $this->dispatcher->dispatch($request->getMethod(), $request->getPathInfo());
+        if ($routeInfo[0] == 0) {
+            throw new \Exception('Page not found', 404);
+        } elseif ($routeInfo[0] == 2) {
+            throw new \Exception('Request method error', 405);
+        } elseif ($routeInfo[0] == 1) {
+            $handler = $routeInfo[1];
+            $vars = $routeInfo[2];
+            $parameters = [...array_values($vars)];
+
+            // Create a closure to pass to your middleware to execute the final handler
+            $finalHandler = function() use ($handler, $parameters, $container) {
+                // If handler is a string (controller@method)
+                if (is_string($handler)) {
+                    list($controller, $method) = explode('@', $handler);
+                    $ctrl = $container->get($controller);
+                    return $ctrl->$method(...$parameters);
+                } elseif (is_callable($handler)) {
+                    return $handler(...$parameters);
+                } else {
+                    throw new \Exception('Route handler configuration error');
+                }
+            };
+            return $finalHandler();
+        }
+    }
+}

+ 47 - 0
frameworks/PHP/cyberphp/src/Utility.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace Cyber;
+
+class Utility
+{
+    /**
+     * Check if required PHP extensions are installed
+     *
+     * @throws Exception Throws an exception when required extensions are not installed
+     * @return bool Returns true if all extensions check passed
+     */
+    public static function checkPHPenv(): bool
+    {
+        /* Check PHP version */
+        if (version_compare(PHP_VERSION, '8.0', '<')) {
+            throw new \Exception('Current PHP (' . PHP_VERSION . ') version is too low! Minimum required PHP version is (8.0)');
+        }
+        // List of required extensions
+        $requiredExtensions =  [
+            'json' => 'Handling JSON data',
+            'mbstring' => 'Handling multibyte strings',
+            'pdo' => 'Database connection',
+            'pdo_mysql' => 'MySQL database support',
+            'openssl' => 'Encryption and HTTPS support'
+        ];
+        // Check required extensions
+        $missingExtensions = [];
+        foreach ($requiredExtensions as $extension => $purpose) {
+            if (!extension_loaded($extension)) {
+                $missingExtensions[] = sprintf(
+                    "- %s (%s)",
+                    $extension,
+                    $purpose
+                );
+            }
+        }
+        // If there are missing required extensions, throw an exception
+        if (!empty($missingExtensions)) {
+            throw new \Exception(sprintf(
+                "Missing required PHP extensions:\n%s\nPlease install these extensions before running the program.",
+                implode("\n", $missingExtensions)
+            ));
+        }
+        return true;
+    }
+}