Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions frameworks/PHP/ripple/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 29 additions & 0 deletions frameworks/PHP/ripple/benchmark_config.json
Original file line number Diff line number Diff line change
@@ -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 changes: 9 additions & 0 deletions frameworks/PHP/ripple/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"require": {
"ext-pdo": "*",
"cloudtay/ripple-http": "^1.0",
"cloudtay/ripple": "^1.0"
},
"minimum-stability": "beta",
"prefer-stable": true
}
18 changes: 18 additions & 0 deletions frameworks/PHP/ripple/fortunes.php
Original file line number Diff line number Diff line change
@@ -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 changes: 32 additions & 0 deletions frameworks/PHP/ripple/ripple.dockerfile
Original file line number Diff line number Diff line change
@@ -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 changes: 200 additions & 0 deletions frameworks/PHP/ripple/server.php
Original file line number Diff line number Diff line change
@@ -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();
Loading