PRipple is a modern, high-performance native PHP coroutine framework designed to solve PHP's challenges in high concurrency, complex network communication and data operations. The framework uses an innovative architecture and efficient programming model to provide powerful and flexible backend support for modern web and web applications. By using PRipple, you will experience the advantages of managing tasks from a global view of the system and efficiently handle network traffic and data.
⚠️ This document is an AI machine translation, and there may be inaccuracies in the translation. If there are any problems, please feel free to make corrections.
composer require cclilshy/p-ripple-core
All tools of PRipple are provided by Store. Store is a global collection of tools with the namespace of
P\{Module}
, you can get all the tools through the Store, such asP\Net
,P\IO
,P\System
, etc.
\P\IO::File();
\P\IO::Socket();
\P\Net::Http();
\P\Net::WebSocket();
\P\System::Process();
In addition, basic syntax sugar is provided, such as
async
,await
, etc., to simplify asynchronous programming such as
# Promise asynchronous mode
$promise1 = \P\promise($r,$d)->then(function(){
$r('done');
});
# async/await asynchronous mode
$promise2 = \P\async(function($r,$d){
\P\sleep(1);
$r('done2');
});
\P\async(function() use ($promise1, $promise2){
$result1 = await($promise1);
$result2 = await($promise2);
echo 'result1: ' . $result1 . PHP_EOL;
echo 'result2: ' . $result2 . PHP_EOL;
});
/**
* Timer related
*/
# Do something repeatedly until cancel is called
$timerId = \P\repeat(1, function(Closure $cancel){
echo 'repeat' . PHP_EOL;
});
# Cancel Timer behavior
\P\cancel($timerId);
# Execute something after a specified time
$timerId = \P\delay(1, function(){
echo 'after' . PHP_EOL;
});
# Cancel the delay statement before it occurs
\P\cancel($timerId);
⚠️ This module relies on the Event processor of thekqueue
mechanism. The event processor under the epoll mechanism does not support monitoring of file streams. In OSX,lib-event
useskqueue
, in Linux it usesepoll
, and in Windows it usesselect
. Please make sure your system supportskqueue
mechanism,
To achieve a high level of compatibility, please install the
Ev
extension
pecl install ev
# Read file contents
async(function () {
$fileContent = await(
P\IO::File()->getContents(__FILE__)
);
$hash = hash('sha256', $fileContent);
echo "[await] File content hash: {$hash}" . PHP_EOL;
});
# Write file content
P\IO::File()->getContents(__FILE__)->then(function ($fileContent) {
$hash = hash('sha256', $fileContent);
echo "[async] File content hash: {$hash}" . PHP_EOL;
});
# open a file
$stream = P\IO::File()->open(__FILE__, 'r');
# Establish a Socket connection
/**
* @param string $uri
* @param int $flags
* @param null $context
* @return Promiss<SocketStream>
*/
P\IO::Socket()->streamSocketClient('tcp://www.baidu.com:80');
# Establish an SSL Socket connection
P\IO::Socket()->streamSocketClientSSL('tcp://www.baidu.com:443');
# Initiate 100 requests asynchronously, method 1
for ($i = 0; $i < 100; $i++) {
P\Net::Http()->Guzzle()->getAsync('https://www.baidu.com/')->then(function (Response $response) {
echo "[async] Response status code: {$response->getStatusCode()}" . PHP_EOL;
})->except(function (Exception $e) {
echo "[async] Exception: {$e->getMessage()}" . PHP_EOL;
});
}
# Initiate 100 requests asynchronously, method 2
for ($i = 0; $i < 100; $i++) {
async(function () {
try {
$response = await(P\Net::Http()->Guzzle()->getAsync('https://www.baidu.com/'));
echo "[await] Response status code: {$response->getStatusCode()}" . PHP_EOL;
} catch (Throwable $exception) {
echo "[await] Exception: {$exception->getMessage()}" . PHP_EOL;
}
});
}
# WebSocket connection
$connection = P\Net::Websocket()->connect('wss://127.0.0.1:8001/wss');
$connection->onOpen = function (Connection $connection) {
$connection->send('{"action":"sub","data":{"channel":"market:panel@8"}}');
$timerId = repeat(10, function () use ($connection) {
$connection->send('{"action":"ping","data":{}}');
});
$connection->onClose = function (Connection $connection) use ($timerId) {
P\cancel($timerId);
};
$connection->onMessage = function (string $message, int $opcode, Connection $connection) {
echo "receive: $message\n";
};
};
$connection->onError = function (Throwable $throwable) {
echo "error: {$throwable->getMessage()}\n";
};
run();
PRipple provides runtime support for parallel running, relying on multiple processes and abstracting the details of multiple processes. Users only need to care about how to use parallel running. You can use almost any PHP statement in a closure, but there are still things you need to pay attention to
- In the child process, all context resources inherit the resources of the parent process, including open files, connected databases, connected networks, and global variables
- All defined events such as
onRead
,onWrite
, etc. will be forgotten in the child process - You cannot create subprocesses in async such as
async(function(){
$task = P\System::Process()->task(function(){
// childProcess
});
$task->run();
});
This will throw an exception
ProcessException
$task = P\System::Process()->task(function(){
sleep(10);
exit(0);
});
$runtime = $task->run(); // Returns a Runtime object
$runtime->stop(); // Cancel operation (signal SIGTERM)
$runtime->stop(true); // Forced termination, equivalent to $runtime->kill()
$runtime->kill(); // Forced termination (signal SIGKILL)
$runtime->signal(SIGTERM); // Sending signals provides more granular control
$runtime->then(function($exitCode){}); // The code here will be triggered when the program exits normally, code is the exit code
$runtime->except(function(){}); // The code here will be triggered when the program exits abnormally, and exceptions can be handled here, such as process daemon/task restart
$runtime->finally(function(){}); // Whether the program exits normally or abnormally, the code here will be triggered.
$runtime->getProcessId(); // Get child process ID
$runtime->getPromise(); // Get Promise object
PRipple provides a simple HttpServer, which can be used to quickly build a simple Http server. The usage method is as follows
Among them, Request and Response inherit and implement the
RequestInterface
andResponseInterface
interface specifications ofSymfony
You can use them just like the HttpFoundation component in Symfony/Laravel
use P\IO;
use Psc\Store\Net\Http\Server\Request;
use Psc\Store\Net\Http\Server\Response;
use function P\await;
use function P\run;
$server = P\Net::Http()->server('http://127.0.0.1:8008');
$handler = function (Request $request, Response $response) {
if ($request->getMethod() === 'POST') {
$files = $request->files->get('file');
$data = [];
foreach ($files as $file) {
$data[] = [
'name' => $file->getClientOriginalName(),
'path' => $file->getPathname(),
];
}
$response->headers->set('Content-Type', 'application/json');
$response->setContent(json_encode($data));
$response->respond();
}
if ($request->getMethod() === 'GET') {
if ($request->getPathInfo() === '/') {
$response->setContent('Hello World!');
$response->respond();
}
if ($request->getPathInfo() === '/download') {
$response->setContent(
IO::File()->open(__FILE__, 'r')
);
$response->respond();
}
if ($request->getPathInfo() === '/upload') {
$template = '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Upload</title></head><body><form action="/upload" method="post" enctype="multipart/form-data"><input type="file" name="file"><button type="submit">Upload</button></form></body>';
$response->setContent($template);
$response->respond();
}
if ($request->getPathInfo() === '/qq') {
$qq = await(P\Net::Http()->Guzzle()->getAsync(
'https://www.qq.com/'
));
$response->setContent($qq->getBody()->getContents());
$response->respond();
}
}
};
$server->onRequest = $handler;
$server->listen();
run();
PRipple supports port multiplexing with
Parallel
Module
# After creating the HttpServer as above, you can replace the listening method to implement port multiplexing.
$task = P\System::Process()->task( fn() => $server->listen() );
$task->run(); //runtime1
$task->run(); //runtime2
$task->run(); //runtime3
$task->run(); //runtime4
$task->run(); //runtime5
# Guardian mode startup example
$guardRun = function($task) use (&$guardRun){
$task->run()->except(function() use ($task, &$guardRun){
$guardRun($task);
});
};
$guardRun($task);
P\run();
- Stream ~
- SocketStream ~
- Promise ~
- ...
PRipple's asynchrony follows the Promise specification, and IO operations rely on Psc\Core\Stream\Stream
and are
developed in accordance with PSR-7
Standard, for in-depth understanding, please refer to interface definitions such as StreamInterface
and PromiseInterface
Ensure the originality of the support library as much as possible without excessive encapsulation to facilitate
user-defined extensions
Developers are welcome to give it a try. I am soliciting more opinions and suggestions. You are welcome to submit a PR
or Issue and we will deal with it as soon as possible
This project is in the alpha stage and may be unstable. Please be careful when using it in a production environment
Contact information: jingnigg@gmail.com
- RevoltPHP: https://revolt.run/
- Workerman/Webman: https://www.workerman.net/
- Laravel: https://laravel.com/
- ThinkPHP: https://www.thinkphp.cn/
- Symfony: https://symfony.com/
- PHP: https://www.php.net/
- JavaScript: https://www.javascript.com/
- Jetbrains: https://www.jetbrains.com/
- OpenAI: https://www.openai.com/
- W3C: https://www.w3.org/