Skip to content

Instantly share code, notes, and snippets.

@ace411
Last active July 13, 2022 10:23
Show Gist options
  • Save ace411/13bea03eaccc11cded3aed882c4abc87 to your computer and use it in GitHub Desktop.
Save ace411/13bea03eaccc11cded3aed882c4abc87 to your computer and use it in GitHub Desktop.
Streaming ReactPHP in ReactJS
<?php
declare(strict_types=1);
namespace App\Api;
use App\Constants;
use App\Filesystem;
use React\EventLoop\Loop;
use React\Http\Message\Response;
use React\Promise\PromiseInterface;
use React\Stream\ThroughStream;
use function React\Promise\resolve;
function streamLyrics(): PromiseInterface
{
$line = 1;
$stream = new ThroughStream();
$timer = Loop::addPeriodicTimer(
2,
function () use (&$line, $stream, &$timer) {
Filesystem\freadlAsync(
__DIR__ . '/../lyrics.txt',
$line++,
)->then(
function (string $data) use ($stream, $timer) {
if (empty($data)) {
$stream->end();
Loop::cancelTimer($timer);
}
$stream->write($data);
},
);
},
);
$stream->on(
'close',
function () use ($timer) {
Loop::cancelTimer($timer);
},
);
return resolve(
new Response(
200,
\array_merge(Constants\CORS_HEADERS, Constants\TEXT_TYPE),
$stream,
),
);
}
<?php
declare(strict_types=1);
namespace App\Constants;
const CORS_HEADERS = [
'access-control-allow-origin' => '*',
'access-control-allow-methods' => 'GET, OPTIONS',
];
const JSON_TYPE = [
'content-type' => 'application/json',
];
const TEXT_TYPE = [
'content-type' => 'text/plain',
];
<?php
declare(strict_types=1);
namespace App\Filesystem;
use React\EventLoop\Loop;
use React\Promise\PromiseInterface;
use function Chemem\Asyncify\call;
function freadl(string $file, int $line): string
{
$fp = \fopen($file, 'r');
$count = 0;
while ($count < $line) {
$contents = \fgets($fp, 1024);
\fseek($fp, \ftell($fp));
$count++;
}
\fclose($fp);
return !$contents ? '' : $contents;
}
function freadlAsync(string $file, int $line): PromiseInterface
{
$async = call(Loop::get());
return $async(
__NAMESPACE__ . '\\freadl',
[$file, $line],
);
}
// pages/index.js
import React, { useEffect, useState } from 'react'
const Home = () => {
const [stop, stopStream] = useState(false)
const [lyrics, addLyrics] = useState([])
useEffect(() => {
const streamLyrics = async () => {
const response = await fetch('http://localhost:5000/api/lyrics')
const reader = response.body
.pipeThrough(new TextDecoderStream())
.getReader()
while (true) {
const { value, done } = await reader.read()
if (done) {
stopStream(true)
break
}
addLyrics((prev) => [...new Set(prev.concat([value]))])
}
stopStream(true)
}
if (!stop) {
streamLyrics()
}
}, [lyrics, stop])
return (
<>
<div>
<h1>Lana Del Rey - West Coast lyrics</h1>
<ul style={{ listStyleType: 'none' }}>
{lyrics.length > 0 &&
lyrics.map((lyric, idx) => (
<li key={idx} style={{ lineHeight: '2.5rem' }}>
{lyric}
</li>
))}
</ul>
</div>
</>
)
}
export default Home
[Verse 1]
Down on the West coast, they got a sayin'
"If you're not drinkin', then you're not playin'"
But you've got the music
You've got the music in you, don't you?
Down on the West coast, I get this feeling like
It all could happen, that's why I'm leaving
You for the moment, you for the moment
Boy Blue, yeah, you
[Pre-Chorus 1]
You're falling hard, I push away, I'm feelin' hot to the touch
You say you miss me and I wanna say I miss you so much
But something keeps me really quiet, I'm alive, I'm a lush
Your love, your love, my love
[Chorus]
I can see my baby swinging
His Parliament's on fire and his hands are up
On the balcony and I'm singing
Ooh, baby, ooh, baby, I'm in love
I can see my sweet boy swaying
He's crazy y cubano como yo, la-la
On the balcony and I'm saying
Move, baby, move, baby, I'm in love
I'm in love (I'm in love)
I'm in love (I'm in love)
[Post-Chorus]
(Mic-check)
(One, two)
(Two, two)
(Get it, girl)
[Verse 2]
Down on the West coast, they got their icons
Their silver starlets, their Queens of Saigons
And you've got the music
You've got the music in you, don't you?
Down on the West coast, they love their movies
Their golden gods and Rock 'n' Roll groupies
And you've got the music
You've got the music in you, don't you?
[Pre-Chorus 2]
You push it hard, I pull away, I'm feeling hotter than fire
I guess that no one ever really made me feel that much higher
Te deseo, cariño, boy, it's you I desire
Your love, your love, my love
[Chorus]
I can see my baby swinging
His Parliament's on fire and his hands are up
On the balcony and I'm singing
Ooh, baby, ooh, baby, I'm in love
I can see my sweet boy swaying
He's crazy y cubano como yo, la-la
On the balcony and I'm saying
Move, baby, move, baby, I'm in love
[Chorus]
I can see my baby swinging
His Parliament's on fire and his hands are up
On the balcony and I'm singing
Ooh, baby, ooh, baby, I'm in love
I can see my sweet boy swaying
He's crazy y cubano como yo, la-la
On the balcony and I'm saying
Move, baby, move, baby, I'm in love
[Outro]
I'm in love
I'm in love
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use App\Api;
use App\Constants;
use FastRoute\Dispatcher;
use FastRoute\RouteCollector;
use React\Http\HttpServer;
use React\Http\Message\Response;
use React\Promise\PromiseInterface;
use React\Socket\SocketServer;
use Psr\Http\Message\ServerRequestInterface;
use function FastRoute\simpleDispatcher;
use function React\Promise\resolve;
$http = new HttpServer(
function (ServerRequestInterface $request): PromiseInterface {
$dispatcher = simpleDispatcher(
function (RouteCollector $route) {
$route->get('/api/lyrics', fn () => Api\streamLyrics());
return $route;
},
);
$response = $dispatcher->dispatch(
$request->getMethod(),
$request->getUri()->getPath(),
);
$info = $response[0];
if ($info === Dispatcher::METHOD_NOT_ALLOWED) {
return resolve(
new Response(
405,
array_merge(Constants\JSON_TYPE, Constants\CORS_HEADERS),
json_encode(['message' => 'Invalid request method']),
),
);
}
if ($info === Dispatcher::NOT_FOUND) {
return resolve(
new Response(
404,
array_merge(Constants\JSON_TYPE, Constants\CORS_HEADERS),
json_encode(['message' => 'Route not found']),
),
);
}
return $response[1]();
},
);
$socket = new SocketServer('127.0.0.1:5000');
$http->listen($socket);
echo 'Listening on port 5000' . PHP_EOL;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment