Browse Source

[PHP/ripple] Added ripple coroutine engine. (#9377)

* [PHP/ripple] Added ripple coroutine engine.

* [PHP/ripple] Update dependent version
cclilshy 9 months ago
parent
commit
f897d20573

+ 75 - 0
frameworks/PHP/ripple/README.md

@@ -0,0 +1,75 @@
+<p align="center">
+<img src="https://raw.githubusercontent.com/cloudtay/ripple/refs/heads/main/assets/images/logo.png" width="420" alt="Logo">
+</p>
+<p align="center">
+<a href="#"><img src="https://img.shields.io/badge/PHP-%3E%3D%208.1-blue" alt="Build Status"></a>
+<a href="https://packagist.org/packages/cloudtay/ripple"><img src="https://img.shields.io/packagist/dt/cloudtay/ripple" alt="Download statistics"> </a>
+<a href="https://packagist.org/packages/cloudtay/ripple"><img src="https://img.shields.io/packagist/v/cloudtay/ripple" alt="Stable version"> </a>
+<a href="https://packagist.org/packages/cloudtay/ripple"><img src="https://img.shields.io/packagist/l/cloudtay/ripple" alt="License"></a>
+</p>
+<p>
+Ripple is a modern, high-performance native PHP coroutine engine designed to solve PHP's challenges in high concurrency, complex network communication and data operations.
+The engine uses an innovative architecture and efficient programming model to provide powerful and flexible backend support for modern web and web applications.
+By using ripple, you will experience the advantages of managing tasks from a global view of the system and efficiently handling network traffic and data. </p>
+
+## Install
+
+````bash
+composer require cloudtay/ripple
+````
+
+## Latest documentation
+
+You can visit `ripple`’s [documentation](https://ripple.cloudtay.com/) to start reading
+
+We recommend that you start with [Manual Installation](https://ripple.cloudtay.com/docs/install/professional) to better
+understand the workflow of ripple
+
+If you want to quickly deploy and use `ripple` services, you can directly
+visit [Quick Deployment](https://ripple.cloudtay.com/docs/install/server)
+
+## Appendix
+
+### Applicable component library
+
+> We allow users to choose applicable component libraries by themselves. All components can be used as described in the
+> document without additional configuration.
+
+**🚀 [Guzzle](https://docs.guzzlephp.org/en/stable/)**
+PHP is the most widely used HTTP client
+
+**🔥 [AmPHP](https://amphp.org/)**
+Provides rich PHP asynchronous components for users to encapsulate by themselves
+
+**🚀 [Driver](https://github.com/cloudtay/ripple-driver)**
+The official high-performance driver library provides seamless access to your traditional applications.
+
+**🚀 [Webman-coroutine](https://github.com/workbunny/webman-coroutine)**
+The workbunny team's integrated webman coroutine extension provides coroutine support for Webman.
+
+**🟢[ripple](https://github.com/cloudtay/ripple)**
+Provides standard coroutine architecture and tools for rapid development or packaging of traditional applications
+
+### Event Library Guide
+
+| Extension Types | Recommended Use | Compatibility |                                                     Description                                                      |
+|:---------------:|:---------------:|:-------------:|:--------------------------------------------------------------------------------------------------------------------:|
+|     `libev`     |       🏅️       |      🟢️      | `Ev` is a more efficient event extension that performs consistently in various systems and is recommended to be used |
+|    `Native`     |        ️        |      🟢       |                                  Support the use of PHP's built-in select mechanism                                  |
+|     `event`     |                 |      🌗       |         The event characteristics under different systems are not uniform, and their use is not recommended          |
+
+## Special thanks
+
+<a href="https://www.jetbrains.com/?from=ripple" target="__blank">
+    <img src="https://www.jetbrains.com/company/brand/img/jetbrains_logo.png" width="200" alt="jetbrains">
+</a>
+
+[Jetbrains](https://www.jetbrains.com/?from=ripple) provides free development tools for this project
+
+### Contact information
+
+`Email` [email protected]
+
+`WeChat` jingnigg
+
+---

+ 29 - 0
frameworks/PHP/ripple/benchmark_config.json

@@ -0,0 +1,29 @@
+{
+  "framework": "ripple",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Fullstack",
+        "database": "MySQL",
+        "framework": "ripple",
+        "language": "PHP",
+        "flavor": "PHP8.3",
+        "orm": "Raw",
+        "platform": "ripple",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "ripple",
+        "notes": "",
+        "versus": "php"
+      }
+    }
+  ]
+}

+ 9 - 0
frameworks/PHP/ripple/composer.json

@@ -0,0 +1,9 @@
+{
+  "require": {
+    "ext-pdo": "*",
+    "cloudtay/ripple-http": "^1.0",
+    "cloudtay/ripple": "^1.0"
+  },
+  "minimum-stability": "beta",
+  "prefer-stable": true
+}

+ 18 - 0
frameworks/PHP/ripple/fortunes.php

@@ -0,0 +1,18 @@
+<?php /*** @var array $rows */?><!DOCTYPE html>
+<html lang="en">
+<head><title>Fortunes</title></head>
+<body>
+<table>
+    <tr>
+        <th>id</th>
+        <th>message</th>
+    </tr>
+    <?php foreach ($rows as $row): ?>
+        <tr>
+            <td><?= \htmlspecialchars($row['id']) ?></td>
+            <td><?= \htmlspecialchars($row['message']) ?></td>
+        </tr>
+    <?php endforeach; ?>
+</table>
+</body>
+</html>

+ 32 - 0
frameworks/PHP/ripple/ripple.dockerfile

@@ -0,0 +1,32 @@
+FROM php:8.3-cli
+
+RUN apt-get update -yqq >> /dev/null
+RUN apt-get install -y libevent-dev \
+    libssl-dev \
+    pkg-config \
+    build-essential \
+    unzip >> /dev/null
+
+RUN docker-php-ext-install pdo_mysql \
+    opcache \
+    posix \
+    pcntl \
+    sockets >> /dev/null
+
+RUN pecl install event >> /dev/null
+
+RUN docker-php-ext-enable posix pcntl sockets
+RUN docker-php-ext-enable --ini-name zz-event.ini event
+
+COPY --from=composer --link /usr/bin/composer /usr/local/bin/composer
+
+# Initialize
+WORKDIR /ripple
+COPY --link . .
+
+# Configure
+RUN composer install --quiet
+
+# Start
+EXPOSE 8080
+ENTRYPOINT ["php","server.php"]

+ 200 - 0
frameworks/PHP/ripple/server.php

@@ -0,0 +1,200 @@
+<?php declare(strict_types=1);
+
+include __DIR__ . '/vendor/autoload.php';
+
+use Ripple\Http\Server;
+use Ripple\Worker\Manager;
+
+use function Co\repeat;
+use function Co\wait;
+
+class Setup
+{
+    public static PDO    $pdo;
+    public static string $dateFormatted;
+
+    public static PDOStatement $queryWorldWhereID;
+    public static PDOStatement $updateWorldRandomNumber;
+    public static PDOStatement $queryFortune;
+
+    public static function dateRefresh(): void
+    {
+        try {
+            $date = new DateTime('now', new DateTimeZone('GMT'));
+        } catch (Throwable $e) {
+            return;
+        }
+        Setup::$dateFormatted = $date->format('D, d M Y H:i:s T');
+    }
+
+
+    /**
+     * @return int
+     */
+    public static function randomInt(): int
+    {
+        try {
+            return \random_int(1, 10000);
+        } catch (Throwable $e) {
+            return mt_rand(1, 10000);
+        }
+    }
+
+    /**
+     * @param mixed $value
+     *
+     * @return int
+     */
+    public static function clamp(mixed $value): int
+    {
+        if (!\is_numeric($value) || $value < 1) {
+            return 1;
+        }
+        if ($value > 500) {
+            return 500;
+        }
+        return \intval($value);
+    }
+
+    /**
+     * @param string $template
+     * @param array  $data
+     *
+     * @return string
+     */
+    public static function render(string $template, array $data = []): string
+    {
+        foreach ($data as $key => $value) {
+            $$key = $value;
+        }
+
+        \ob_start();
+        include $template;
+        return \ob_get_clean();
+    }
+}
+
+$manager = new Manager();
+$worker  = new class() extends \Ripple\Worker {
+    /*** @var \Ripple\Http\Server */
+    public Server $server;
+
+    /**
+     * @param \Ripple\Worker\Manager $manager
+     *
+     * @return void
+     */
+    public function register(Manager $manager): void
+    {
+        $this->count  = 64;
+        $this->server = new Server('http://0.0.0.0:8080');
+    }
+
+    /**
+     * @return void
+     */
+    public function boot(): void
+    {
+        Setup::dateRefresh();
+        repeat(static fn () => Setup::dateRefresh(), 1);
+
+        Setup::$pdo = new \PDO(
+            'mysql:host=tfb-database;port=3306;dbname=hello_world',
+            'benchmarkdbuser',
+            'benchmarkdbpass',
+            [
+                \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
+                \PDO::ATTR_EMULATE_PREPARES   => false,
+                \PDO::ATTR_ERRMODE            => \PDO::ERRMODE_EXCEPTION,
+            ]
+        );
+
+        Setup::$queryWorldWhereID       = Setup::$pdo->prepare('SELECT id, randomNumber FROM World WHERE id = ?');
+        Setup::$updateWorldRandomNumber = Setup::$pdo->prepare('UPDATE World SET randomNumber = ? WHERE id = ?');
+        Setup::$queryFortune            = Setup::$pdo->prepare('SELECT * FROM `Fortune`');
+        $this->server->onRequest(fn (Server\Request $request) => $this->onRequest($request));
+        $this->server->listen();
+    }
+
+    /**
+     * @param \Ripple\Http\Server\Request $request
+     *
+     * @return void
+     */
+    public function onRequest(Server\Request $request): void
+    {
+        switch ($request->SERVER['REQUEST_URI']) {
+            case '/json':
+                $request->respondJson(
+                    ['message' => 'Hello, World!'],
+                    ['Date' => Setup::$dateFormatted]
+                );
+                break;
+
+            case '/db':
+                $statement = Setup::$queryWorldWhereID;
+                $statement->execute([Setup::randomInt()]);
+                $request->respondJson($statement->fetch(), ['Date' => Setup::$dateFormatted]);
+                break;
+
+            case '/queries':
+                $queries   = Setup::clamp($request->GET['queries'] ?? 1);
+                $results   = [];
+                $statement = Setup::$queryWorldWhereID;
+                while ($queries--) {
+                    $statement->execute([Setup::randomInt()]);
+                    $results[] = $statement->fetch();
+                }
+                $request->respondJson($results, ['Date' => Setup::$dateFormatted]);
+
+                break;
+            case '/fortunes':
+                $rows   = Setup::$pdo->query('SELECT * FROM `Fortune`')?->fetchAll();
+                $rows[] = ['id' => 0, 'message' => 'Additional fortune added at request time.'];
+                \usort($rows, function ($a, $b) {
+                    return $a['message'] <=> $b['message'];
+                });
+
+                $request->respondHtml(
+                    Setup::render('fortunes.php', ['rows' => $rows]),
+                    [
+                        'Date'         => Setup::$dateFormatted,
+                        'Content-Type' => 'text/html; charset=UTF-8'
+                    ]
+                );
+                break;
+
+            case '/updates':
+                $queries   = Setup::clamp($request->GET['queries'] ?? 1);
+                $results   = [];
+                $statement = Setup::$queryWorldWhereID;
+                $update    = Setup::$updateWorldRandomNumber;
+                while ($queries--) {
+                    $statement->execute([Setup::randomInt()]);
+                    $row                 = $statement->fetch();
+                    $row['randomNumber'] = Setup::randomInt();
+                    $results[]           = $row;
+                    $update->execute([$row['randomNumber'], $row['id']]);
+                }
+                $request->respondJson($results, ['Date' => Setup::$dateFormatted]);
+                break;
+
+            case '/plaintext':
+                $request->respond(
+                    'Hello, World!',
+                    [
+                        'Content-Type' => 'text/plain; charset=utf-8',
+                        'Date'         => Setup::$dateFormatted
+                    ]
+                );
+                break;
+
+            default:
+                $request->respond('Not Found', [], 404);
+        }
+    }
+};
+
+$manager->addWorker($worker);
+$manager->run();
+wait();