-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add AbstractCookieDispatchTranscriptor
It uses black magic excessively: - wraps & replaces the EventDispatcher of the Symfony Response - high-jacks logging-events to unwind present day cookies
- Loading branch information
Showing
17 changed files
with
618 additions
and
5 deletions.
There are no files selected for viewing
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
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace brnc\Symfony1\Message\Transcriptor; | ||
|
||
use brnc\Symfony1\Message\Transcriptor\Response\CookieDispatch\CookieContainerInterface; | ||
use brnc\Symfony1\Message\Transcriptor\Response\CookieDispatch\DispatchSubstitutor; | ||
|
||
/** | ||
* Wraps sfEventDispatcher to fire PSR7 cookies in sfWebRequest | ||
* | ||
* There is no interface to implement and this class deliberately doesn't extend the sfEventDispatcher. | ||
* As there is to type coercion in the Sf1 code, that should be fine. | ||
*/ | ||
class CookieDispatcher | ||
{ | ||
public const APPLICATION_LOG = 'application.log'; | ||
private const LOGGING_PRIORITY = null; // For debugging: overrides event's priority, @see \sfLogger | ||
|
||
private ?\sfEventDispatcher $dispatcher; | ||
private bool $logging; | ||
|
||
private ?int $headerCountdown = null; | ||
|
||
public function __construct(?\sfEventDispatcher $dispatcher, bool $logging) | ||
{ | ||
$this->dispatcher = $dispatcher; | ||
$this->logging = $logging; | ||
} | ||
|
||
/** | ||
* @param array{0: \sfEvent} $arguments # |array{0: \sfEvent, 1: mixed}|array{0: string}|array{0: string, 1: callable} | ||
* | ||
* @return mixed # void|false|\sfEvent|bool|callable[] | ||
*/ | ||
public function __call(string $name, array $arguments) | ||
{ | ||
$this->dispatcher ??= new \sfEventDispatcher(); | ||
|
||
return call_user_func_array([$this->dispatcher, $name], $arguments); // @phpstan-ignore argument.type | ||
} | ||
|
||
public function notify(\sfEvent $event): \sfEvent | ||
{ // We are only interested in logging events from the response, and pass-through everything else. | ||
if (self::APPLICATION_LOG !== $event->getName() || !$event->getSubject() instanceof \sfWebResponse) { | ||
return $this->passNotify($event); | ||
} | ||
// Override local logging for debug purposes | ||
if (null !== self::LOGGING_PRIORITY) { // @phpstan-ignore notIdentical.alwaysFalse | ||
$event->offsetSet('priority', self::LOGGING_PRIORITY); // Force logging | ||
} | ||
|
||
if ($this->logging) { | ||
$this->passNotify($event); // Notify is not expected to change the event; Sticking to in-order logging, over preserving possible return-event. | ||
} | ||
/** @var \sfWebResponse $response */ | ||
$response = $event->getSubject(); | ||
|
||
/** @var string $logMessage */ | ||
$logMessage = $event->offsetGet(0); | ||
|
||
// There is always at least on header, as Content-Type is forced in sfWebResponseX::sendHttpHeaders | ||
if (str_starts_with($logMessage, 'Send header "')) { | ||
// initialize countdown with the number of header lines, after the very first header was sent out… | ||
$this->headerCountdown ??= count($response->getHttpHeaders()); | ||
--$this->headerCountdown; // decrease right away… | ||
if (0 === $this->headerCountdown) { // so that we'll reach 0, after the last header was sent | ||
/** @var array{__psr7cookies: CookieContainerInterface} $options */ | ||
$options = $response->getOptions(); | ||
foreach ($options[DispatchSubstitutor::PSR_7_COOKIES]->getCookies() as $cookie) { | ||
$cookie->apply(); | ||
if ($this->logging) { | ||
$params = ["Send PSR7 cookie \"{$cookie->getName()}\": \"{$cookie->getValue()}\""]; | ||
|
||
if (null !== self::LOGGING_PRIORITY) { // @phpstan-ignore notIdentical.alwaysFalse | ||
$params['priority'] = self::LOGGING_PRIORITY; // Force logging | ||
} | ||
$this->passNotify( | ||
new \sfEvent($this, self::APPLICATION_LOG, $params) | ||
); | ||
} | ||
} | ||
} | ||
} | ||
|
||
return $event; | ||
} | ||
|
||
private function passNotify(\sfEvent $event): \sfEvent | ||
{ | ||
if ($this->dispatcher) { | ||
return $this->dispatcher->notify($event); | ||
} | ||
|
||
return $event; | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
src/Transcriptor/Response/AbstractCookieDispatchTranscriptor.php
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace brnc\Symfony1\Message\Transcriptor\Response; | ||
|
||
use brnc\Symfony1\Message\Transcriptor\Response\CookieDispatch\CookieContainerInterface; | ||
use brnc\Symfony1\Message\Transcriptor\Response\CookieDispatch\DispatchSubstitutor; | ||
use Psr\Http\Message\ResponseInterface; | ||
|
||
abstract class AbstractCookieDispatchTranscriptor implements CookieTranscriptorInterface | ||
{ | ||
private DispatchSubstitutor $substitutor; | ||
|
||
public function __construct(DispatchSubstitutor $substitutor) | ||
{ | ||
$this->substitutor = $substitutor; | ||
} | ||
|
||
final public function transcribeCookies(ResponseInterface $psrResponse, \sfWebResponse $sfWebResponse): void | ||
{ | ||
$this->substitutor->wrapDispatcher($sfWebResponse, $this->getCookieContainer($psrResponse)); | ||
} | ||
|
||
/** Implement this method to obtain a CookieContainer from your PSR-7 Response! */ | ||
abstract protected function getCookieContainer(ResponseInterface $psrResponse): CookieContainerInterface; | ||
} |
32 changes: 32 additions & 0 deletions
32
src/Transcriptor/Response/CookieDispatch/AbstractCookie.php
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace brnc\Symfony1\Message\Transcriptor\Response\CookieDispatch; | ||
|
||
abstract class AbstractCookie implements CookieInterface | ||
{ | ||
/** @var array{expires?: int, path?: string, domain?: string, secure?: bool, httponly?: bool, samesite?: 'Lax'|'None'|'Strict'} */ | ||
protected array $options; | ||
|
||
private string $name; | ||
private string $value; | ||
|
||
/** @param array{expires?: int, path?: string, domain?: string, secure?: bool, httponly?: bool, samesite?: 'Lax'|'None'|'Strict'} $options */ | ||
public function __construct(string $name, string $value, array $options) | ||
{ | ||
$this->name = $name; | ||
$this->value = $value; | ||
$this->options = $options; | ||
} | ||
|
||
public function getName(): string | ||
{ | ||
return $this->name; | ||
} | ||
|
||
public function getValue(): string | ||
{ | ||
return $this->value; | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
src/Transcriptor/Response/CookieDispatch/CookieContainer.php
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace brnc\Symfony1\Message\Transcriptor\Response\CookieDispatch; | ||
|
||
class CookieContainer | ||
{ | ||
/** @var CookieInterface[] */ | ||
private array $cookies; | ||
|
||
/** @param CookieInterface[] $cookies */ | ||
public function __construct(array $cookies) | ||
{ | ||
$this->cookies = $cookies; | ||
} | ||
|
||
/** @return CookieInterface[] */ | ||
public function getCookies(): array | ||
{ | ||
return $this->cookies; | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
src/Transcriptor/Response/CookieDispatch/CookieContainerInterface.php
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace brnc\Symfony1\Message\Transcriptor\Response\CookieDispatch; | ||
|
||
interface CookieContainerInterface | ||
{ | ||
/** @return CookieInterface[] */ | ||
public function getCookies(): array; | ||
} |
14 changes: 14 additions & 0 deletions
14
src/Transcriptor/Response/CookieDispatch/CookieInterface.php
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace brnc\Symfony1\Message\Transcriptor\Response\CookieDispatch; | ||
|
||
interface CookieInterface | ||
{ | ||
public function getName(): string; | ||
|
||
public function getValue(): string; | ||
|
||
public function apply(): bool; | ||
} |
Oops, something went wrong.