<commit_before><?php
declare(strict_types = 1);

namespace Innmind\Http;

use Innmind\Http\{
    Message\Response,
    Header\Date,
    Header\DateValue,
    Exception\LogicException
};
use Innmind\TimeContinuum\TimeContinuumInterface;

final class ResponseSender implements Sender
{
    private $clock;

    public function __construct(TimeContinuumInterface $clock)
    {
        $this->clock = $clock;
    }

    public function __invoke(Response $response): void
    {
        if (headers_sent()) {
            throw new LogicException('Headers already sent');
        }

        header(sprintf(
            'HTTP/%s %s %s',
            $response->protocolVersion(),
            $response->statusCode(),
            $response->reasonPhrase()
        ), true, $response->statusCode()->value());

        if (!$response->headers()->has('date')) {
            header((string) new Date(new DateValue($this->clock->now())));
        }

        foreach ($response->headers() as $header) {
            header((string) $header, false);
        }

        $body = $response->body();
        $body->rewind();

        while (!$body->end()) {
            echo $body->read(4096);
            flush();
        }

        if (function_exists('fastcgi_finish_request')) {
            fastcgi_finish_request();
        }
    }
}<commit_msg>fix sending cookies<commit_after><?php
declare(strict_types = 1);

namespace Innmind\Http;

use Innmind\Http\{
    Message\Response,
    Header\Date,
    Header\DateValue,
    Header\SetCookie,
    TimeContinuum\Format\Http,
    Exception\LogicException
};
use Innmind\TimeContinuum\TimeContinuumInterface;

final class ResponseSender implements Sender
{
    private $clock;

    public function __construct(TimeContinuumInterface $clock)
    {
        $this->clock = $clock;
    }

    public function __invoke(Response $response): void
    {
        if (headers_sent()) {
            throw new LogicException('Headers already sent');
        }

        header(sprintf(
            'HTTP/%s %s %s',
            $response->protocolVersion(),
            $response->statusCode(),
            $response->reasonPhrase()
        ), true, $response->statusCode()->value());

        if (!$response->headers()->has('date')) {
            header((string) new Date(new DateValue($this->clock->now())));
        }

        foreach ($response->headers() as $header) {
            if ($header instanceof SetCookie) {
                $this->sendCookie($header);
                continue;
            }

            header((string) $header, false);
        }

        $body = $response->body();
        $body->rewind();

        while (!$body->end()) {
            echo $body->read(4096);
            flush();
        }

        if (function_exists('fastcgi_finish_request')) {
            fastcgi_finish_request();
        }
    }

    private function sendCookie(SetCookie $cookie): void
    {
        $cookie->values()->foreach(static function(CookieValue $value): void {
            $parameters = $value->parameters()->reduce(
                [],
                static function(array $parameters, Parameter $parameter): array {
                    switch ($parameter->name()) {
                        case 'Domain':
                            $parameters['domain'] = $parameter->value();
                            break;

                        case 'Expires':
                            $timestamp = \DateTimeImmutable::createFrom((string) new Http, $parameter->value())->getTimestamp();
                            // MaxAge has precedence
                            $parameters['expire'] = $parameters['expire'] !== 0 ? $parameters['expire'] : $timestamp;
                            break;

                        case 'Max-Age':
                            $parameters['expire'] = (int) $parameter->value();
                            break;

                        case 'HttpOnly':
                            $parameters['httponly'] = true;
                            break;

                        case 'Path':
                            $parameters['path'] = $parameter->value();
                            break;

                        case 'Secure':
                            $parameters['secure'] = true;
                            break;

                        case 'SameSite':
                            $parameters['samesite'] = $parameter->value();
                            break;

                        default:
                            $parameters['key'] = $parameter->name();
                            $parameters['value'] = $parameter->value();
                            break;
                    }

                    return $parameters;
                }
            );

            if (\PHP_VERSION_ID >= 70300) {
                $arguments = [
                    $parameters['key'] ?? '',
                    $parameters['value'] ?? '',
                    $parameters['expire'] ?? 0,
                    [
                        'path' => $parameters['path'] ?? '',
                        'domain' => $parameters['domain'] ?? '',
                        'secure' => $parameters['secure'] ?? false,
                        'httponly' => $parameters['httponly'] ?? false,
                    ],
                ];

                if (isset($parameters['samesite'])) {
                    $arguments[3]['samesite'] = $parameters['samesite'];
                }
            } else {
                // same site not supported
                $arguments = [
                    $parameters['key'] ?? '',
                    $parameters['value'] ?? '',
                    $parameters['expire'] ?? 0,
                    $parameters['path'] ?? '',
                    $parameters['domain'] ?? '',
                    $parameters['secure'] ?? false,
                    $parameters['httponly'] ?? false
                ];
            }

            setcookie(...$arguments);
        });
    }
}
{'fraction_non_alphanumeric': 0.13716295427901523, 'fraction_numerical': 0.003182046558365433, 'mean_word_length': 1.3698412698412699, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 52, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1167584', 'n_tokens_mistral': 1474, 'n_tokens_neox': 1391, 'n_words': 265}