Last active
July 13, 2022 10:23
-
-
Save ace411/13bea03eaccc11cded3aed882c4abc87 to your computer and use it in GitHub Desktop.
Streaming ReactPHP in ReactJS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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, | |
), | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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', | |
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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], | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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