+
Error
+
Sorry, an error occurred
+
+
+
+ $v) { ?>
+
+
+
+
'.htmlentities($v['source0'][0]).'';
+ }
+ if (!empty($v['source'])) {
+ echo '
';
+ foreach ((array) $v['source']['content'] as $key => $value) {
+ if (($key + $v['source']['startline']) == $v['line']) {
+ echo ''.htmlentities($value).' ';
+ } else {
+ echo ''.htmlentities($value).' ';
+ }
+ }
+ echo '
';
+ }
+ ?>
+
+
+
+
+
Return to Home
+
+
+
\ No newline at end of file
diff --git a/frameworks/PHP/cyberphp/benchmark_config.json b/frameworks/PHP/cyberphp/benchmark_config.json
new file mode 100755
index 00000000000..689f889ef03
--- /dev/null
+++ b/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"
+ }
+ }
+ ]
+}
diff --git a/frameworks/PHP/cyberphp/bootstrap.php b/frameworks/PHP/cyberphp/bootstrap.php
new file mode 100644
index 00000000000..f9d1a350d25
--- /dev/null
+++ b/frameworks/PHP/cyberphp/bootstrap.php
@@ -0,0 +1,41 @@
+ __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),
+];
\ No newline at end of file
diff --git a/frameworks/PHP/cyberphp/composer.json b/frameworks/PHP/cyberphp/composer.json
new file mode 100644
index 00000000000..68678af9697
--- /dev/null
+++ b/frameworks/PHP/cyberphp/composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "eoioer/cyberphp",
+ "authors": [
+ {
+ "name": "eoioer",
+ "email": "eoioer@qq.com"
+ }
+ ],
+ "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
+}
diff --git a/frameworks/PHP/cyberphp/cyberphp.dockerfile b/frameworks/PHP/cyberphp/cyberphp.dockerfile
new file mode 100644
index 00000000000..e1c91791a05
--- /dev/null
+++ b/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
\ No newline at end of file
diff --git a/frameworks/PHP/cyberphp/php.ini b/frameworks/PHP/cyberphp/php.ini
new file mode 100644
index 00000000000..f4817cc9e3a
--- /dev/null
+++ b/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
\ No newline at end of file
diff --git a/frameworks/PHP/cyberphp/public/favicon.ico b/frameworks/PHP/cyberphp/public/favicon.ico
new file mode 100644
index 00000000000..ada4d61b845
Binary files /dev/null and b/frameworks/PHP/cyberphp/public/favicon.ico differ
diff --git a/frameworks/PHP/cyberphp/public/index.php b/frameworks/PHP/cyberphp/public/index.php
new file mode 100644
index 00000000000..cbfd55373c0
--- /dev/null
+++ b/frameworks/PHP/cyberphp/public/index.php
@@ -0,0 +1,23 @@
+run();
+ if (!$response instanceof Response) {
+ $response = Response::html($response ?? '');
+ }
+ echo $response->send();
+} catch (Exception $e) {
+ echo renderExceptionPage($e);
+} catch (Throwable $e) {
+ echo renderExceptionPage($e);
+}
diff --git a/frameworks/PHP/cyberphp/server.php b/frameworks/PHP/cyberphp/server.php
new file mode 100644
index 00000000000..e9a354b2ce9
--- /dev/null
+++ b/frameworks/PHP/cyberphp/server.php
@@ -0,0 +1,85 @@
+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();
diff --git a/frameworks/PHP/cyberphp/src/App.php b/frameworks/PHP/cyberphp/src/App.php
new file mode 100644
index 00000000000..288e969e0a9
--- /dev/null
+++ b/frameworks/PHP/cyberphp/src/App.php
@@ -0,0 +1,154 @@
+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;
+ }
+}
diff --git a/frameworks/PHP/cyberphp/src/Middleware.php b/frameworks/PHP/cyberphp/src/Middleware.php
new file mode 100644
index 00000000000..c482ebe1347
--- /dev/null
+++ b/frameworks/PHP/cyberphp/src/Middleware.php
@@ -0,0 +1,48 @@
+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();
+ }
+}
\ No newline at end of file
diff --git a/frameworks/PHP/cyberphp/src/Request.php b/frameworks/PHP/cyberphp/src/Request.php
new file mode 100644
index 00000000000..ec54d88b94c
--- /dev/null
+++ b/frameworks/PHP/cyberphp/src/Request.php
@@ -0,0 +1,232 @@
+ $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;
+ }
+ }
+}
diff --git a/frameworks/PHP/cyberphp/src/Response.php b/frameworks/PHP/cyberphp/src/Response.php
new file mode 100644
index 00000000000..7e6698231e1
--- /dev/null
+++ b/frameworks/PHP/cyberphp/src/Response.php
@@ -0,0 +1,218 @@
+ '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;
+ }
+}
diff --git a/frameworks/PHP/cyberphp/src/Route.php b/frameworks/PHP/cyberphp/src/Route.php
new file mode 100644
index 00000000000..b1b70f4f873
--- /dev/null
+++ b/frameworks/PHP/cyberphp/src/Route.php
@@ -0,0 +1,61 @@
+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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/frameworks/PHP/cyberphp/src/Utility.php b/frameworks/PHP/cyberphp/src/Utility.php
new file mode 100644
index 00000000000..17ab3948176
--- /dev/null
+++ b/frameworks/PHP/cyberphp/src/Utility.php
@@ -0,0 +1,47 @@
+ '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;
+ }
+}
diff --git a/frameworks/PHP/laravel/deploy/franken/Caddyfile b/frameworks/PHP/laravel/deploy/franken/Caddyfile
new file mode 100644
index 00000000000..6c4cc54627c
--- /dev/null
+++ b/frameworks/PHP/laravel/deploy/franken/Caddyfile
@@ -0,0 +1,24 @@
+{
+ {$CADDY_GLOBAL_OPTIONS}
+
+ admin {$CADDY_SERVER_ADMIN_HOST}:{$CADDY_SERVER_ADMIN_PORT}
+
+ frankenphp {
+ worker "{$APP_PUBLIC_PATH}/frankenphp-worker.php" {$CADDY_SERVER_WORKER_COUNT}
+ }
+}
+
+{$CADDY_SERVER_SERVER_NAME} {
+ route {
+ # Mercure configuration is injected here...
+ {$CADDY_SERVER_EXTRA_DIRECTIVES}
+
+ # FrankenPHP!
+ # disable static files for this benchmark
+ # by using php instead of php_server
+ rewrite frankenphp-worker.php
+ php {
+ root "{$APP_PUBLIC_PATH}"
+ }
+ }
+}
diff --git a/frameworks/PHP/laravel/laravel-octane-frankenphp.dockerfile b/frameworks/PHP/laravel/laravel-octane-frankenphp.dockerfile
index 2ad30a612e3..c21eeeacf8a 100644
--- a/frameworks/PHP/laravel/laravel-octane-frankenphp.dockerfile
+++ b/frameworks/PHP/laravel/laravel-octane-frankenphp.dockerfile
@@ -1,7 +1,6 @@
FROM dunglas/frankenphp
RUN install-php-extensions \
- intl \
opcache \
pcntl \
pdo_mysql \
@@ -26,4 +25,4 @@ RUN frankenphp -v
EXPOSE 8080
-ENTRYPOINT ["php", "artisan", "octane:frankenphp", "--port=8080"]
+ENTRYPOINT ["php", "artisan", "octane:frankenphp", "--port=8080", "--caddyfile=/app/deploy/franken/Caddyfile"]
diff --git a/frameworks/PHP/laravel/laravel-ripple.dockerfile b/frameworks/PHP/laravel/laravel-ripple.dockerfile
index 4f1bb8bc3a9..2fc4a480716 100644
--- a/frameworks/PHP/laravel/laravel-ripple.dockerfile
+++ b/frameworks/PHP/laravel/laravel-ripple.dockerfile
@@ -33,14 +33,13 @@ RUN mkdir -p bootstrap/cache \
storage/framework/views \
storage/framework/cache
-RUN echo "PRP_HTTP_LISTEN=http://0.0.0.0:8080" >> .env
-RUN echo "PRP_HTTP_WORKERS=64" >> .env
-RUN echo "PRP_HTTP_RELOAD=0" >> .env
-RUN echo "PRP_HTTP_SANDBOX=1" >> .env
+RUN echo "RIP_HTTP_LISTEN=http://0.0.0.0:8080" >> .env
+RUN echo "RIP_HTTP_WORKERS=64" >> .env
+RUN echo "RIP_HTTP_RELOAD=0" >> .env
# Configure
RUN composer install --quiet
-RUN composer require cloudtay/ripple-driver --quiet
+RUN composer require cloudtay/laravel-ripple --quiet
RUN php artisan vendor:publish --tag=ripple-config
RUN php artisan optimize
diff --git a/frameworks/PHP/php/deploy/franken/Caddyfile b/frameworks/PHP/php/deploy/franken/Caddyfile
index b6aa805f871..72400a7e489 100644
--- a/frameworks/PHP/php/deploy/franken/Caddyfile
+++ b/frameworks/PHP/php/deploy/franken/Caddyfile
@@ -11,11 +11,14 @@
:8080
route {
- root * /php
# FrankenPHP!
+ # disable static files for this benchmark
+ # by using php instead of php_server
@phpFiles path *.php
- php @phpFiles
+ php @phpFiles {
+ root /php
+ }
respond 404
}
\ No newline at end of file
diff --git a/frameworks/PHP/reactphp/app.php b/frameworks/PHP/reactphp/app.php
index 4f9dd816652..bea0c65038d 100644
--- a/frameworks/PHP/reactphp/app.php
+++ b/frameworks/PHP/reactphp/app.php
@@ -10,6 +10,7 @@
use React\Promise\PromiseInterface;
use function React\Promise\all;
+use function React\Promise\resolve;
/** @return Closure(Request):ResponseInterface */
function requestHandler(): Closure
@@ -29,7 +30,7 @@ function requestHandler(): Closure
};
return static function (Request $request) use ($world, $fortune, $update): ResponseInterface | PromiseInterface {
- return match($request->getUri()->getPath()) {
+ return resolve((match($request->getUri()->getPath()) {
'/plaintext' => Response::plaintext('Hello, World!'),
'/json' => Response::json(['message' => 'Hello, World!']),
'/db' => db($world),
@@ -38,7 +39,9 @@ function requestHandler(): Closure
'/update' => updateraw(queryCount($request), $world, $update),
// '/info' => info(),
default => new Response(404, [], 'Error 404'),
- };
+ }))->catch(
+ static fn (Throwable $error): PromiseInterface => resolve(Response::plaintext($error->getMessage())->withStatus(500)),
+ );
};
}
diff --git a/frameworks/PHP/reactphp/benchmark_config.json b/frameworks/PHP/reactphp/benchmark_config.json
index fb29ad3c207..e53c029d748 100644
--- a/frameworks/PHP/reactphp/benchmark_config.json
+++ b/frameworks/PHP/reactphp/benchmark_config.json
@@ -1,5 +1,6 @@
{
"framework": "reactphp",
+ "maintainers": ["WyriHaximus"],
"tests": [{
"default": {
"json_url": "/json",
@@ -16,9 +17,28 @@
"webserver": "None",
"os": "Linux",
"database_os": "Linux",
- "display_name": "reactphp",
+ "display_name": "reactphp [libevent]",
"notes": "",
"versus": "php"
+ },
+ "libuv": {
+ "json_url": "/json",
+ "plaintext_url": "/plaintext",
+ "port": 8080,
+ "approach": "Realistic",
+ "classification": "Platform",
+ "framework": "reactphp",
+ "language": "PHP",
+ "flavor": "PHP8",
+ "database": "MySQL",
+ "orm": "Raw",
+ "platform": "reactphp",
+ "webserver": "None",
+ "os": "Linux",
+ "database_os": "Linux",
+ "display_name": "reactphp [libuv]",
+ "notes": "",
+ "versus": "reactphp"
}
}]
}
diff --git a/frameworks/PHP/reactphp/reactphp-libuv.dockerfile b/frameworks/PHP/reactphp/reactphp-libuv.dockerfile
new file mode 100644
index 00000000000..48126b5c59c
--- /dev/null
+++ b/frameworks/PHP/reactphp/reactphp-libuv.dockerfile
@@ -0,0 +1,35 @@
+FROM ubuntu:24.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 > /dev/null
+RUN apt-get update -yqq > /dev/null && \
+ apt-get install -yqq git unzip wget curl build-essential \
+ php8.4-cli php8.4-mbstring php8.4-dev php8.4-xml > /dev/null
+
+# An extension is required!
+# We deal with concurrencies over 1k, which stream_select doesn't support.
+# libuv
+RUN apt-get install -yqq libuv1-dev > /dev/null \
+ && pecl install uv-beta > /dev/null \
+ && echo "extension=uv.so" > /etc/php/8.4/cli/conf.d/uv.ini
+
+# libevent
+# RUN apt-get install -y libevent-dev > /dev/null \
+# && pecl install event-3.1.4 > /dev/null \
+# && echo "extension=event.so" > /etc/php/8.4/cli/conf.d/event.ini
+
+COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
+
+COPY --link deploy/conf/* /etc/php/8.4/cli/conf.d/
+
+WORKDIR /reactphp
+COPY --link . .
+
+RUN composer install --prefer-dist --optimize-autoloader --no-dev --quiet
+
+EXPOSE 8080
+
+ENTRYPOINT ["/usr/bin/php"]
+CMD ["server.php"]
diff --git a/frameworks/PHP/reactphp/reactphp.dockerfile b/frameworks/PHP/reactphp/reactphp.dockerfile
index a46c613938f..0ecb546a1c3 100644
--- a/frameworks/PHP/reactphp/reactphp.dockerfile
+++ b/frameworks/PHP/reactphp/reactphp.dockerfile
@@ -27,7 +27,7 @@ COPY --link deploy/conf/* /etc/php/8.4/cli/conf.d/
WORKDIR /reactphp
COPY --link . .
-RUN composer install --prefer-dist --optimize-autoloader --no-dev
+RUN composer install --prefer-dist --optimize-autoloader --no-dev --quiet
EXPOSE 8080
diff --git a/frameworks/PHP/reactphp/server.php b/frameworks/PHP/reactphp/server.php
index ddfb5a07145..278208b6291 100644
--- a/frameworks/PHP/reactphp/server.php
+++ b/frameworks/PHP/reactphp/server.php
@@ -8,6 +8,9 @@
require_once __DIR__.'/app.php';
$server = new HttpServer(requestHandler());
+$server->on('error', function (\Throwable $error) {
+ echo $error->getMessage(), PHP_EOL;
+});
$socket = new SocketServer('0.0.0.0:8080');
$server->listen($socket);
diff --git a/frameworks/PHP/symfony/deploy/Caddyfile b/frameworks/PHP/symfony/deploy/Caddyfile
index f794fd3d647..6faa66909af 100644
--- a/frameworks/PHP/symfony/deploy/Caddyfile
+++ b/frameworks/PHP/symfony/deploy/Caddyfile
@@ -11,18 +11,13 @@
:8080
route {
- root * /symfony/public
- # If the requested file does not exist, try index files
- @indexFiles file {
- try_files {path} {path}/runtime.php runtime.php
- split_path .php
+ # FrankenPHP!
+ # disable static files for this benchmark
+ # by using php instead of php_server
+ rewrite runtime.php
+ php {
+ root /symfony/public
}
- rewrite @indexFiles {http.matchers.file.relative}
-
- # FrankenPHP!
- @phpFiles path *.php
- php @phpFiles
- respond 404
}
diff --git a/frameworks/PHP/symfony/symfony-franken.dockerfile b/frameworks/PHP/symfony/symfony-franken.dockerfile
index 967b9370514..05b8267409b 100644
--- a/frameworks/PHP/symfony/symfony-franken.dockerfile
+++ b/frameworks/PHP/symfony/symfony-franken.dockerfile
@@ -2,7 +2,6 @@ FROM dunglas/frankenphp
# add additional extensions here:
RUN install-php-extensions \
- intl \
opcache \
pdo_pgsql \
zip > /dev/null
diff --git a/frameworks/Perl/feersum/cpanfile b/frameworks/Perl/feersum/cpanfile
index 9b868f48ffe..12b329a825a 100644
--- a/frameworks/Perl/feersum/cpanfile
+++ b/frameworks/Perl/feersum/cpanfile
@@ -1,4 +1,4 @@
-requires 'Feersum', '== 1.503';
+requires 'Feersum', '== 1.504';
requires 'JSON::XS', '== 4.03';
requires 'DBD::MariaDB', '== 1.23';
requires 'DBD::Pg', '== 3.18.0';
diff --git a/frameworks/Python/clace/.dockerignore b/frameworks/Python/clace/.dockerignore
new file mode 100644
index 00000000000..9fce104ae8c
--- /dev/null
+++ b/frameworks/Python/clace/.dockerignore
@@ -0,0 +1,2 @@
+.venv
+README.md
diff --git a/frameworks/Python/clace/README.md b/frameworks/Python/clace/README.md
new file mode 100755
index 00000000000..f510c5c8f54
--- /dev/null
+++ b/frameworks/Python/clace/README.md
@@ -0,0 +1,11 @@
+# Introduction
+
+[Clace](https://github.com/claceio/clace) is a platform for developing and deploying internal tools.
+
+Clace is implemented in Go. Clace apps are written in [Starlark](https://starlark-lang.org/). Starlark is a thread-safe language with Python syntax, designed for embedding. Clace uses the [Starlark Go](https://github.com/google/starlark-go) implementation. Since apps are developed using a python like syntax, the benchmark is added under the Python category.
+
+# Benchmarking
+
+The JSON and plaintext tests are implemented. Clace supports SQLite database only currently, so the database tests are not implemented.
+
+The Dockerfile starts the Clace server and creates a single app which implements the benchmark apis (app.star).
diff --git a/frameworks/Python/clace/app.star b/frameworks/Python/clace/app.star
new file mode 100644
index 00000000000..86c0dd68346
--- /dev/null
+++ b/frameworks/Python/clace/app.star
@@ -0,0 +1,6 @@
+app = ace.app("testapp",
+ routes = [
+ ace.api("/json", lambda req: {'message': 'Hello, world!'}, type=ace.JSON),
+ ace.api("/plaintext", lambda req: 'Hello, world!', type=ace.TEXT)
+ ]
+)
diff --git a/frameworks/Python/clace/benchmark_config.json b/frameworks/Python/clace/benchmark_config.json
new file mode 100755
index 00000000000..c836299e939
--- /dev/null
+++ b/frameworks/Python/clace/benchmark_config.json
@@ -0,0 +1,23 @@
+{
+ "framework": "clace",
+ "tests": [
+ {
+ "default": {
+ "json_url": "/json",
+ "plaintext_url": "/plaintext",
+ "port": 8080,
+ "approach": "Realistic",
+ "classification": "Micro",
+ "framework": "Clace",
+ "language": "python",
+ "flavor": "Starlark",
+ "platform": "None",
+ "webserver": "Clace",
+ "os": "Linux",
+ "display_name": "Clace",
+ "notes": "",
+ "versus": "None"
+ }
+ }
+ ]
+}
diff --git a/frameworks/Python/clace/clace.dockerfile b/frameworks/Python/clace/clace.dockerfile
new file mode 100644
index 00000000000..40c8c69608b
--- /dev/null
+++ b/frameworks/Python/clace/clace.dockerfile
@@ -0,0 +1,11 @@
+FROM python:3.11
+WORKDIR /clace/
+
+RUN curl -L https://clace.io/install.sh | bash
+ENV CL_HOME="/root/clhome"
+ENV PATH="/root/clhome/bin:$PATH"
+
+COPY . .
+
+EXPOSE 8080
+CMD /clace/run.sh
diff --git a/frameworks/Python/clace/run.sh b/frameworks/Python/clace/run.sh
new file mode 100755
index 00000000000..d5dc87d345e
--- /dev/null
+++ b/frameworks/Python/clace/run.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+cd /root
+
+cat <
/root/clhome/clace.toml
+[logging]
+console = false
+file = false
+access_logging = false
+
+[http]
+host = "0.0.0.0"
+port = 8080
+EOF
+
+
+clace server start &
+sleep 2
+clace app create --auth=none --approve /clace tfb-server:/
+tail -f /dev/null
diff --git a/frameworks/Ruby/rack-sequel/benchmark_config.json b/frameworks/Ruby/rack-sequel/benchmark_config.json
index 30ba62a6223..64663f27632 100644
--- a/frameworks/Ruby/rack-sequel/benchmark_config.json
+++ b/frameworks/Ruby/rack-sequel/benchmark_config.json
@@ -8,7 +8,7 @@
"fortune_url": "/fortunes",
"update_url": "/updates?queries=",
"port": 8080,
- "approach": "Stripped",
+ "approach": "Realistic",
"classification": "Micro",
"database": "MySQL",
"framework": "rack",
@@ -28,7 +28,7 @@
"fortune_url": "/fortunes",
"update_url": "/updates?queries=",
"port": 8080,
- "approach": "Stripped",
+ "approach": "Realistic",
"classification": "Micro",
"database": "Postgres",
"framework": "rack",
@@ -48,7 +48,7 @@
"update_url": "/updates?queries=",
"plaintext_url": "/plaintext",
"port": 8080,
- "approach": "Stripped",
+ "approach": "Realistic",
"classification": "Micro",
"database": "MySQL",
"framework": "rack",
@@ -68,7 +68,7 @@
"fortune_url": "/fortunes",
"update_url": "/updates?queries=",
"port": 8080,
- "approach": "Stripped",
+ "approach": "Realistic",
"classification": "Micro",
"database": "Postgres",
"framework": "rack",
@@ -88,7 +88,7 @@
"fortune_url": "/fortunes",
"update_url": "/updates?queries=",
"port": 8080,
- "approach": "Stripped",
+ "approach": "Realistic",
"classification": "Micro",
"database": "MySQL",
"framework": "rack",
@@ -108,7 +108,7 @@
"fortune_url": "/fortunes",
"update_url": "/updates?queries=",
"port": 8080,
- "approach": "Stripped",
+ "approach": "Realistic",
"classification": "Micro",
"database": "Postgres",
"framework": "rack",
diff --git a/frameworks/Ruby/rack/benchmark_config.json b/frameworks/Ruby/rack/benchmark_config.json
index 97227d81e36..914d2093dbb 100644
--- a/frameworks/Ruby/rack/benchmark_config.json
+++ b/frameworks/Ruby/rack/benchmark_config.json
@@ -10,7 +10,7 @@
"fortune_url": "/fortunes",
"update_url": "/updates?queries=",
"port": 8080,
- "approach": "Stripped",
+ "approach": "Realistic",
"classification": "Micro",
"orm": "raw",
"database": "Postgres",
@@ -31,7 +31,7 @@
"fortune_url": "/fortunes",
"update_url": "/updates?queries=",
"port": 8080,
- "approach": "Stripped",
+ "approach": "Realistic",
"classification": "Micro",
"orm": "raw",
"database": "Postgres",
@@ -52,7 +52,7 @@
"fortune_url": "/fortunes",
"update_url": "/updates?queries=",
"port": 8080,
- "approach": "Stripped",
+ "approach": "Realistic",
"classification": "Micro",
"orm": "raw",
"database": "Postgres",
@@ -73,7 +73,7 @@
"fortune_url": "/fortunes",
"update_url": "/updates?queries=",
"port": 8080,
- "approach": "Stripped",
+ "approach": "Realistic",
"classification": "Micro",
"orm": "raw",
"database": "Postgres",
@@ -94,7 +94,7 @@
"fortune_url": "/fortunes",
"update_url": "/updates?queries=",
"port": 8080,
- "approach": "Stripped",
+ "approach": "Realistic",
"classification": "Micro",
"orm": "raw",
"database": "Postgres",
diff --git a/frameworks/Ruby/rack/rack-iodine.dockerfile b/frameworks/Ruby/rack/rack-iodine.dockerfile
index 999eb9fc6e7..9b4908fb177 100644
--- a/frameworks/Ruby/rack/rack-iodine.dockerfile
+++ b/frameworks/Ruby/rack/rack-iodine.dockerfile
@@ -19,4 +19,4 @@ COPY . .
EXPOSE 8080
-CMD bundle exec iodine -p 8080
+CMD bundle exec iodine -p 8080 -w $(ruby config/auto_tune.rb | grep -Eo '[0-9]+' | head -n 1)
diff --git a/frameworks/Ruby/rage-sequel/Gemfile b/frameworks/Ruby/rage-sequel/Gemfile
new file mode 100644
index 00000000000..4ee2e620f6e
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/Gemfile
@@ -0,0 +1,7 @@
+source "https://rubygems.org"
+
+gem "rage-rb", "~> 1.10"
+
+gem "pg", "~> 1.0"
+gem 'sequel', '~> 5.0'
+gem 'sequel_pg', '~> 1.6', platforms: :ruby, require: false
diff --git a/frameworks/Ruby/rage-sequel/Gemfile.lock b/frameworks/Ruby/rage-sequel/Gemfile.lock
new file mode 100644
index 00000000000..d1ad00bb988
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/Gemfile.lock
@@ -0,0 +1,37 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ bigdecimal (3.1.9)
+ pg (1.5.9)
+ rack (2.2.10)
+ rack-test (2.2.0)
+ rack (>= 1.3)
+ rage-iodine (4.0.0)
+ rage-rb (1.11.0)
+ rack (~> 2.0)
+ rack-test (~> 2.1)
+ rage-iodine (~> 4.0)
+ rake (>= 12.0)
+ thor (~> 1.0)
+ zeitwerk (~> 2.6)
+ rake (13.2.1)
+ sequel (5.88.0)
+ bigdecimal
+ sequel_pg (1.17.1)
+ pg (>= 0.18.0, != 1.2.0)
+ sequel (>= 4.38.0)
+ thor (1.3.2)
+ zeitwerk (2.7.1)
+
+PLATFORMS
+ ruby
+ x86_64-darwin-20
+
+DEPENDENCIES
+ pg (~> 1.0)
+ rage-rb (~> 1.10)
+ sequel (~> 5.0)
+ sequel_pg (~> 1.6)
+
+BUNDLED WITH
+ 2.5.6
diff --git a/frameworks/Ruby/rage-sequel/README.md b/frameworks/Ruby/rage-sequel/README.md
new file mode 100755
index 00000000000..d299d0834ba
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/README.md
@@ -0,0 +1,47 @@
+# Rage Benchmarking Test
+
+Rage is a fast web framework compatible with Rails. It uses an event-driven architecture and implements a lightweight, cooperative concurrency model based on Ruby Fibers.
+
+https://github.com/rage-rb/rage
+
+### Test Type Implementation Source Code
+
+* [JSON](app/controllers/benchmarks_controller.rb)
+* [PLAINTEXT](app/controllers/benchmarks_controller.rb)
+* [DB](app/controllers/benchmarks_controller.rb)
+* [QUERY](app/controllers/benchmarks_controller.rb)
+* [UPDATE](app/controllers/benchmarks_controller.rb)
+* [FORTUNES](app/controllers/benchmarks_controller.rb)
+
+## Important Libraries
+
+The tests were run with:
+
+* [Sequel](https://rubygems.org/gems/sequel)
+* [PG](https://rubygems.org/gems/pg)
+
+## Test URLs
+
+### JSON
+
+http://localhost:8080/json
+
+### PLAINTEXT
+
+http://localhost:8080/plaintext
+
+### DB
+
+http://localhost:8080/db
+
+### QUERY
+
+http://localhost:8080/queries?queries=
+
+### UPDATE
+
+http://localhost:8080/updates?queries=
+
+### FORTUNES
+
+http://localhost:8080/fortunes
diff --git a/frameworks/Ruby/rage-sequel/Rakefile b/frameworks/Ruby/rage-sequel/Rakefile
new file mode 100644
index 00000000000..046f1fcbd8d
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/Rakefile
@@ -0,0 +1 @@
+require_relative "config/application"
diff --git a/frameworks/Ruby/rage-sequel/app/controllers/application_controller.rb b/frameworks/Ruby/rage-sequel/app/controllers/application_controller.rb
new file mode 100644
index 00000000000..c3238c52392
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/app/controllers/application_controller.rb
@@ -0,0 +1,2 @@
+class ApplicationController < RageController::API
+end
diff --git a/frameworks/Ruby/rage-sequel/app/controllers/benchmarks_controller.rb b/frameworks/Ruby/rage-sequel/app/controllers/benchmarks_controller.rb
new file mode 100644
index 00000000000..082905ccda8
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/app/controllers/benchmarks_controller.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+class BenchmarksController < ApplicationController
+ ALL_DB_IDS = (1..10_000).to_a
+ FORTUNES_TEMPLATE = ERB.new(Rage.root.join("app/views/fortunes.html.erb").read)
+
+ before_action do
+ headers["server"] = "rage"
+ end
+
+ def db
+ render json: World.with_pk(random_id).values
+ end
+
+ def queries
+ worlds = DB.synchronize do
+ requested_ids.map do |id|
+ World.with_pk(id)
+ end
+ end
+
+ render json: worlds.map!(&:values)
+ end
+
+ def fortunes
+ records = Fortune.all
+
+ records << Fortune.new(id: 0, message: "Additional fortune added at request time.")
+ records.sort_by!(&:message)
+
+ render plain: FORTUNES_TEMPLATE.result(binding)
+ headers["content-type"] = "text/html; charset=utf-8"
+ end
+
+ def updates
+ worlds = nil
+
+ DB.synchronize do
+ worlds = requested_ids.map do |id|
+ world = World.with_pk(id)
+ new_value = random_id
+ new_value = random_id while new_value == world.randomnumber
+ world.randomnumber = new_value
+
+ world
+ end
+
+ World.batch_update(worlds)
+ end
+
+ render json: worlds.map!(&:values)
+ end
+
+ private
+
+ def requested_ids
+ num = params[:queries].to_i
+
+ if num > 500
+ num = 500
+ elsif num < 1
+ num = 1
+ end
+
+ ALL_DB_IDS.sample(num)
+ end
+
+ def random_id
+ Random.rand(9_999) + 1
+ end
+end
diff --git a/frameworks/Ruby/rage-sequel/app/views/fortunes.html.erb b/frameworks/Ruby/rage-sequel/app/views/fortunes.html.erb
new file mode 100644
index 00000000000..2614ca7a7b2
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/app/views/fortunes.html.erb
@@ -0,0 +1,12 @@
+
+
+ Fortunes
+
+
+ | id | message |
+ <% records.each do |record| %>
+ | <%= record.id %> | <%= CGI.escape_html(record.message) %> |
+ <% end %>
+
+
+
diff --git a/frameworks/Ruby/rage-sequel/benchmark_config.json b/frameworks/Ruby/rage-sequel/benchmark_config.json
new file mode 100755
index 00000000000..4cbe6d248f4
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/benchmark_config.json
@@ -0,0 +1,28 @@
+{
+ "framework": "rage-sequel",
+ "tests": [
+ {
+ "default": {
+ "db_url": "/db",
+ "query_url": "/queries?queries=",
+ "fortune_url": "/fortunes",
+ "update_url": "/updates?queries=",
+ "port": 8080,
+ "approach": "Realistic",
+ "classification": "Micro",
+ "database": "postgres",
+ "framework": "Rage",
+ "language": "Ruby",
+ "flavor": "None",
+ "orm": "Full",
+ "platform": "Rack",
+ "webserver": "Rage-Iodine",
+ "os": "Linux",
+ "database_os": "Linux",
+ "display_name": "Rage-Sequel",
+ "notes": "",
+ "versus": "None"
+ }
+ }
+ ]
+}
diff --git a/frameworks/Ruby/rage-sequel/config.ru b/frameworks/Ruby/rage-sequel/config.ru
new file mode 100644
index 00000000000..049a1ad509d
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/config.ru
@@ -0,0 +1,3 @@
+require_relative "config/application"
+
+run Rage.application
diff --git a/frameworks/Ruby/rage-sequel/config/application.rb b/frameworks/Ruby/rage-sequel/config/application.rb
new file mode 100644
index 00000000000..9af142340b3
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/config/application.rb
@@ -0,0 +1,14 @@
+require "bundler/setup"
+require "rage"
+Bundler.require(*Rage.groups)
+
+require "rage/all"
+
+Rage.configure do
+ # use this to add settings that are constant across all environments
+end
+
+require "erb"
+require "cgi"
+
+require "rage/setup"
diff --git a/frameworks/Ruby/rage-sequel/config/environments/development.rb b/frameworks/Ruby/rage-sequel/config/environments/development.rb
new file mode 100644
index 00000000000..35d9e7ae7d6
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/config/environments/development.rb
@@ -0,0 +1,4 @@
+Rage.configure do
+ config.server.workers_count = -1
+ config.logger = Rage::Logger.new(STDOUT)
+end
diff --git a/frameworks/Ruby/rage-sequel/config/environments/production.rb b/frameworks/Ruby/rage-sequel/config/environments/production.rb
new file mode 100644
index 00000000000..0189c7742fa
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/config/environments/production.rb
@@ -0,0 +1,3 @@
+Rage.configure do
+ config.logger = nil
+end
diff --git a/frameworks/Ruby/rage-sequel/config/initializers/sequel.rb b/frameworks/Ruby/rage-sequel/config/initializers/sequel.rb
new file mode 100644
index 00000000000..273694b7b15
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/config/initializers/sequel.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+SEQUEL_NO_ASSOCIATIONS = true
+Sequel.extension :fiber_concurrency
+
+# Determine thread pool size and timeout
+opts = {
+ max_connections: 512,
+ pool_timeout: 10
+}
+
+DB = Sequel.connect \
+ '%{adapter}://%{host}/%{database}?user=%{user}&password=%{password}' % {
+ :adapter=>'postgres',
+ :host=>'tfb-database',
+ :database=>'hello_world',
+ :user=>'benchmarkdbuser',
+ :password=>'benchmarkdbpass'
+ }, opts
+
+# Define ORM models
+class World < Sequel::Model(:World)
+ def self.batch_update(worlds)
+ ids = []
+ sql = String.new("UPDATE world SET randomnumber = CASE id ")
+ worlds.each do |world|
+ sql << "when #{world.id} then #{world.randomnumber} "
+ ids << world.id
+ end
+ sql << "ELSE randomnumber END WHERE id IN ( #{ids.join(',')})"
+ DB.run(sql)
+ end
+end
+
+class Fortune < Sequel::Model(:Fortune)
+ # Allow setting id to zero (0) per benchmark requirements
+ unrestrict_primary_key
+end
+
+[World, Fortune].each(&:freeze)
+DB.freeze
diff --git a/frameworks/Ruby/rage-sequel/config/routes.rb b/frameworks/Ruby/rage-sequel/config/routes.rb
new file mode 100644
index 00000000000..c03b04a7f52
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/config/routes.rb
@@ -0,0 +1,8 @@
+Rage.routes.draw do
+ root to: ->(env) { [200, {}, "It works!"] }
+
+ get "db", to: "benchmarks#db"
+ get "queries", to: "benchmarks#queries"
+ get "fortunes", to: "benchmarks#fortunes"
+ get "updates", to: "benchmarks#updates"
+end
diff --git a/frameworks/Ruby/rage-sequel/lib/.keep b/frameworks/Ruby/rage-sequel/lib/.keep
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/frameworks/Ruby/rage-sequel/rage-sequel.dockerfile b/frameworks/Ruby/rage-sequel/rage-sequel.dockerfile
new file mode 100644
index 00000000000..096b256fe18
--- /dev/null
+++ b/frameworks/Ruby/rage-sequel/rage-sequel.dockerfile
@@ -0,0 +1,13 @@
+FROM ruby:3.4
+
+EXPOSE 8080
+WORKDIR /rage-sequel
+
+COPY Gemfile* /rage-sequel/
+RUN bundle install --jobs=8
+COPY . /rage-sequel
+
+ENV RUBY_YJIT_ENABLE=1
+ENV BUNDLE_FORCE_RUBY_PLATFORM=true
+
+CMD bundle exec rage s -b 0.0.0.0 -p 8080 -e production
diff --git a/frameworks/Ruby/rails/rails-iodine.dockerfile b/frameworks/Ruby/rails/rails-iodine.dockerfile
index ffa3b078dd0..b91de6b668d 100644
--- a/frameworks/Ruby/rails/rails-iodine.dockerfile
+++ b/frameworks/Ruby/rails/rails-iodine.dockerfile
@@ -24,4 +24,4 @@ ENV RAILS_ENV=production_postgresql
ENV PORT=8080
ENV REDIS_URL=redis://localhost:6379/0
CMD service redis-server start && \
- bundle exec iodine
+ bundle exec iodine -w $(ruby config/auto_tune.rb | grep -Eo '[0-9]+' | head -n 1)
diff --git a/frameworks/Ruby/roda-sequel/roda-sequel-postgres-iodine-mri.dockerfile b/frameworks/Ruby/roda-sequel/roda-sequel-postgres-iodine-mri.dockerfile
index 05851b9f9f1..c7f9fe856d1 100644
--- a/frameworks/Ruby/roda-sequel/roda-sequel-postgres-iodine-mri.dockerfile
+++ b/frameworks/Ruby/roda-sequel/roda-sequel-postgres-iodine-mri.dockerfile
@@ -18,4 +18,4 @@ ENV DBTYPE=postgresql
EXPOSE 8080
-CMD bundle exec iodine -p 8080
+CMD bundle exec iodine -p 8080 -w $(ruby config/auto_tune.rb | grep -Eo '[0-9]+' | head -n 1)
diff --git a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-iodine-mri.dockerfile b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-iodine-mri.dockerfile
index 3f1bfb24b79..bf29d5a1e9e 100644
--- a/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-iodine-mri.dockerfile
+++ b/frameworks/Ruby/sinatra-sequel/sinatra-sequel-postgres-iodine-mri.dockerfile
@@ -17,4 +17,4 @@ ENV DBTYPE=postgresql
EXPOSE 8080
-CMD bundle exec iodine -p 8080
+CMD bundle exec iodine -p 8080 -w $(ruby config/auto_tune.rb | grep -Eo '[0-9]+' | head -n 1)