chore: complete update

This commit is contained in:
2025-07-17 16:24:20 +02:00
parent 899227b0a4
commit 64a7051137
1300 changed files with 85570 additions and 2756 deletions

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace App\Framework\Http\Responses;
use App\Framework\Http\HeaderKey;
use App\Framework\Http\Response;
use App\Framework\Http\Status;
use App\Framework\Http\Headers;
use App\Framework\Http\Streaming\AdaptivePlaylist;
use App\Framework\Http\Streaming\StreamingFormat;
final readonly class AdaptiveStreamResponse implements Response, Streamable
{
public string $body;
public Headers $headers;
public function __construct(
public AdaptivePlaylist $playlist,
public Status $status = Status::OK,
Headers $headers = new Headers(),
public StreamingFormat $format = StreamingFormat::HLS
) {
$this->body = $this->playlist->generate($this->format);
$this->headers = $headers
->with(HeaderKey::CACHE_CONTROL, 'max-age=60', )
->with(HeaderKey::ACCESS_CONTROL_ALLOW_ORIGIN, '*' )
->with(HeaderKey::ACCESS_CONTROL_ALLOW_METHODS, 'GET, HEAD, OPTIONS')
->with(HeaderKey::ACCESS_CONTROL_ALLOW_HEADERS, 'RANGE' );
// Format-spezifische Header setzen
#$this->headers = $this->headers->with('Content-Type', $this->format->getContentType());
// Standard Streaming-Header
#$this->headers = $this->headers
# ->with('Cache-Control', 'max-age=60')
# ->with('Access-Control-Allow-Origin', '*')
# ->with('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS')
# ->with('Access-Control-Allow-Headers', 'Range');
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Framework\Http\Responses;
use App\Framework\Http\Headers;
use App\Framework\Http\Response;
use App\Framework\Http\Status;
final readonly class JsonResponse implements Response
{
public Headers $headers;
public string $body;
public function __construct(
array $body = [],
public Status $status = Status::OK,
) {
$this->headers = new Headers()->with('Content-Type', 'application/json');
$this->body = json_encode($body);
}
}

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Framework\Http\Responses;
enum MediaType: string
{
case VIDEO = 'video';
case AUDIO = 'audio';
case UNKNOWN = 'unknown';
public static function fromMimeType(string $mimeType): self
{
return match (true) {
str_starts_with($mimeType, 'video/') => self::VIDEO,
str_starts_with($mimeType, 'audio/') => self::AUDIO,
default => self::UNKNOWN
};
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Framework\Http\Responses;
use App\Framework\Http\Headers;
use App\Framework\Http\Response;
use App\Framework\Http\Status;
final readonly class NotFound implements Response
{
public Status $status;
public Headers $headers;
public function __construct(
public string $body = 'Not Found',
) {
$this->status = Status::NOT_FOUND;
$this->headers = new Headers();
}
}

View File

@@ -1,18 +0,0 @@
<?php
namespace App\Framework\Http\Responses;
use App\Framework\Http\Headers;
use App\Framework\Http\Response;
use App\Framework\Http\Status;
class Redirect implements Response
{
public private(set) string $body = '';
public private(set) Status $status = Status::FOUND;
public \App\Framework\Http\Headers $headers {
get {
return new Headers()->with('Location', $this->body);
}
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Framework\Http\Responses;
use App\Framework\Http\HeaderKey;
use App\Framework\Http\Headers;
use App\Framework\Http\Response;
use App\Framework\Http\Status;
use App\Framework\Http\Uri;
final readonly class RedirectResponse implements Response
{
public Headers $headers;
public string $body;
public Status $status;
public function __construct(
public Uri $location = new Uri('/'),
) {
$this->status = Status::FOUND;
$this->body = '';
$this->headers = new Headers()->with(HeaderKey::LOCATION, (string)$this->location);
}
}

View File

@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace App\Framework\Http\Responses;
use App\Framework\Http\Headers;
use App\Framework\Http\Response;
use App\Framework\Http\Status;
use App\Framework\Router\Result\SseEvent;
use App\Framework\Router\Result\SseResult;
use App\Framework\Router\Result\SseResultWithCallback;
use Closure;
/**
* Response-Klasse für Server-Sent Events
*/
final class SseResponse implements Response, Streamable
{
public readonly Status $status;
public readonly Headers $headers;
public readonly string $body;
public array $initialEvents = [];
/**
* @var int Maximale Streaming-Dauer in Sekunden (0 = unbegrenzt)
*/
public readonly int $maxDuration;
/**
* @var int Intervall für Heartbeats in Sekunden
*/
public readonly int $heartbeatInterval;
/**
* @param SseResult $sseResult Das SseResult, aus dem die Response erstellt wird
* @param Closure|null $streamCallback
*/
public function __construct(SseResult $sseResult, public readonly ?Closure $streamCallback = null)
{
$this->status = $sseResult->status;
$this->body = '';
// Standard SSE-Header
$headerData = [
'Content-Type' => 'text/event-stream',
'Cache-Control' => 'no-cache',
'Connection' => 'keep-alive',
'X-Accel-Buffering' => 'no', // Nginx buffering deaktivieren
];
// Benutzerdefinierte Header hinzufügen
foreach ($sseResult->headers as $name => $value) {
$headerData[$name] = $value;
}
$this->headers = new Headers($headerData);
// Initiale Events aus dem SseResult speichern
$this->initialEvents = $sseResult->getInitialEvents();
// Globales Retry-Interval setzen, falls definiert
if ($sseResult->retryInterval !== null) {
$this->initialEvents[] = new SseEvent('', null, null, $sseResult->retryInterval);
}
$this->maxDuration = $sseResult->maxDuration;
$this->heartbeatInterval = $sseResult->heartbeatInterval;;
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace App\Framework\Http\Responses;
use App\Framework\Http\Headers;
use App\Framework\Http\Range;
use App\Framework\Http\Response;
use App\Framework\Http\Status;
final readonly class StreamResponse implements Response, Streamable
{
public string $body;
public function __construct(
public Status $status = Status::OK,
public Headers $headers = new Headers(),
#public mixed $fileContent,
public string $filePath = '',
public int $fileSize = 0,
public string $mimeType = 'application/octet-stream',
public ?Range $range = null,
public MediaType $mediaType = MediaType::UNKNOWN,
) {
$this->body = '';
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace App\Framework\Http\Responses;
interface Streamable
{
}

View File

@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace App\Framework\Http\Responses;
use App\Framework\Http\Headers;
use App\Framework\Http\Response;
use App\Framework\Http\Status;
use App\Framework\Router\Result\WebSocketResult;
/**
* Response-Klasse für WebSocket-Upgrade
*/
final readonly class WebSocketResponse implements Response, Streamable
{
public Status $status;
public Headers $headers;
public string $body;
public function __construct(
private WebSocketResult $webSocketResult,
private string $websocketKey
) {
$this->status = $webSocketResult->status;
// WebSocket-Upgrade-Header
$headerData = [
'Upgrade' => 'websocket',
'Connection' => 'Upgrade',
'Sec-WebSocket-Accept' => $this->generateAcceptKey($this->websocketKey),
];
// Subprotokoll hinzufügen, falls unterstützt
$subprotocols = $webSocketResult->getSubprotocols();
if (!empty($subprotocols)) {
$headerData['Sec-WebSocket-Protocol'] = implode(', ', $subprotocols);
}
// Benutzerdefinierte Header hinzufügen
foreach ($webSocketResult->headers as $name => $value) {
$headerData[$name] = $value;
}
$this->headers = new Headers($headerData);
}
/**
* Generiert den Accept-Key für WebSocket-Handshake
*/
private function generateAcceptKey(string $key): string
{
return base64_encode(hash('sha1', $key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
}
public function getWebSocketResult(): WebSocketResult
{
return $this->webSocketResult;
}
}