diff --git a/config/params.php b/config/params.php index 9630fc8..5351837 100644 --- a/config/params.php +++ b/config/params.php @@ -9,7 +9,7 @@ use Yiisoft\Yii\Debug\Api\Inspector\Command\PsalmCommand; $testCommands = []; -if (class_exists(\PHPUnit\Framework\Test::class)) { +if (class_exists(PHPUnit\Framework\Test::class)) { $testCommands[PHPUnitCommand::COMMAND_NAME] = PHPUnitCommand::class; } if (class_exists(Extension::class)) { diff --git a/config/routes.php b/config/routes.php index 68cc336..5034734 100644 --- a/config/routes.php +++ b/config/routes.php @@ -13,6 +13,7 @@ use Yiisoft\Yii\Debug\Api\Inspector\Controller\CacheController; use Yiisoft\Yii\Debug\Api\Inspector\Controller\CommandController; use Yiisoft\Yii\Debug\Api\Inspector\Controller\ComposerController; +use Yiisoft\Yii\Debug\Api\Inspector\Controller\DebugServerController; use Yiisoft\Yii\Debug\Api\Inspector\Controller\GitController; use Yiisoft\Yii\Debug\Api\Inspector\Controller\InspectController; use Yiisoft\Yii\Debug\Api\Inspector\Controller\OpcacheController; @@ -58,6 +59,9 @@ static function (ResponseFactoryInterface $responseFactory, ValidatorInterface $ Route::get('/event-stream') ->action([DebugController::class, 'eventStream']) ->name('event-stream'), + Route::get('/dev') + ->action([DebugServerController::class, 'stream']) + ->name('stream'), ), Group::create('/inspect/api') ->withCors(CorsAllowAll::class) diff --git a/src/Debug/Controller/DebugController.php b/src/Debug/Controller/DebugController.php index 40ce24f..0dac06d 100644 --- a/src/Debug/Controller/DebugController.php +++ b/src/Debug/Controller/DebugController.php @@ -4,6 +4,7 @@ namespace Yiisoft\Yii\Debug\Api\Debug\Controller; +use OpenApi\Attributes as OA; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; @@ -12,6 +13,7 @@ use Yiisoft\Assets\AssetPublisherInterface; use Yiisoft\DataResponse\DataResponse; use Yiisoft\DataResponse\DataResponseFactoryInterface; +use Yiisoft\Http\Header; use Yiisoft\Router\CurrentRoute; use Yiisoft\Yii\Debug\Api\Debug\Exception\NotFoundException; use Yiisoft\Yii\Debug\Api\Debug\Exception\PackageNotInstalledException; @@ -21,7 +23,6 @@ use Yiisoft\Yii\Debug\Api\ServerSentEventsStream; use Yiisoft\Yii\Debug\Storage\StorageInterface; use Yiisoft\Yii\View\ViewRenderer; -use OpenApi\Attributes as OA; /** * Debug controller provides endpoints that expose information about requests processed that debugger collected. @@ -323,9 +324,9 @@ public function eventStream( $retries = 0; return $responseFactory->createResponse() - ->withHeader('Content-Type', 'text/event-stream') - ->withHeader('Cache-Control', 'no-cache') - ->withHeader('Connection', 'keep-alive') + ->withHeader(Header::CONTENT_TYPE, 'text/event-stream') + ->withHeader(Header::CACHE_CONTROL, 'no-cache') + ->withHeader(Header::CONNECTION, 'keep-alive') ->withBody( new ServerSentEventsStream(function (array &$buffer) use ( $compareFunction, diff --git a/src/Inspector/Controller/DebugServerController.php b/src/Inspector/Controller/DebugServerController.php new file mode 100644 index 0000000..8bd9216 --- /dev/null +++ b/src/Inspector/Controller/DebugServerController.php @@ -0,0 +1,54 @@ +bind(); + + return $responseFactory->createResponse() + ->withHeader(Header::CONTENT_TYPE, 'text/event-stream') + ->withHeader(Header::CACHE_CONTROL, 'no-cache') + ->withHeader(Header::CONNECTION, 'keep-alive') + ->withBody( + new ServerSentEventsStream(function () use ($socket) { + foreach ($socket->read() as $message) { + switch ($message[0]) { + case Connection::TYPE_ERROR: + return ''; + case Connection::TYPE_RELEASE: + /** + * Break the loop if the client aborted the connection (closed the page) + */ + if (connection_aborted()) { + return ''; + } + break; + case Connection::TYPE_RESULT: + + yield $message[1]; + } + } + return ''; + }) + ); + } +} diff --git a/src/ServerSentEventsStream.php b/src/ServerSentEventsStream.php index 8956b11..a772945 100644 --- a/src/ServerSentEventsStream.php +++ b/src/ServerSentEventsStream.php @@ -5,13 +5,16 @@ namespace Yiisoft\Yii\Debug\Api; use Closure; +use Generator; use Psr\Http\Message\StreamInterface; final class ServerSentEventsStream implements StreamInterface, \Stringable { - public array $buffer = []; private bool $eof = false; + /** + * @param Closure(): Generator $stream + */ public function __construct( private Closure $stream, ) { @@ -27,9 +30,9 @@ public function detach(): void $this->eof = true; } - public function getSize(): int + public function getSize(): ?int { - return 0; + return null; } public function tell(): int @@ -77,24 +80,20 @@ public function isReadable(): bool */ public function read(int $length): string { - $continue = ($this->stream)($this->buffer); + foreach (($this->stream)($this) as $message) { + if (empty($message)) { + break; + } - if (!$continue) { - $this->eof = true; + return sprintf("data: %s\n\n", $message); } - - $output = ''; - foreach ($this->buffer as $key => $value) { - unset($this->buffer[$key]); - $output .= sprintf("data: %s\n", $value); - } - $output .= "\n"; - return $output; + $this->eof = true; + return ''; } public function getContents(): string { - return $this->read(1024); + return $this->read(8_388_608); // 8MB } public function getMetadata($key = null): array