Skip to content
98 changes: 68 additions & 30 deletions src/Downloader/Downloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@

namespace RoachPHP\Downloader;

use Exception;
use RoachPHP\Events\ExceptionReceived;
use RoachPHP\Events\ExceptionReceiving;
use RoachPHP\Events\RequestDropped;
use RoachPHP\Events\RequestSending;
use RoachPHP\Events\ResponseDropped;
use RoachPHP\Events\ResponseReceived;
use RoachPHP\Events\ResponseReceiving;
use RoachPHP\Http\ClientInterface;
use RoachPHP\Http\Request;
use RoachPHP\Http\RequestException;
use RoachPHP\Http\Response;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

Expand Down Expand Up @@ -53,52 +57,56 @@ public function scheduledRequests(): int
return \count($this->requests);
}

public function prepare(Request $request): void
public function prepare(Request $request, ?callable $onRejected = null): void
{
foreach ($this->middleware as $middleware) {
$request = $middleware->handleRequest($request);
try {
foreach ($this->middleware as $middleware) {
$request = $middleware->handleRequest($request);

if ($request->wasDropped()) {
$this->eventDispatcher->dispatch(
new RequestDropped($request),
RequestDropped::NAME,
);

return;
}
}

/**
* @psalm-suppress UnnecessaryVarAnnotation
*
* @var RequestSending $event
*/
$event = $this->eventDispatcher->dispatch(
new RequestSending($request),
RequestSending::NAME,
);

if ($request->wasDropped()) {
if ($event->request->wasDropped()) {
$this->eventDispatcher->dispatch(
new RequestDropped($request),
new RequestDropped($event->request),
RequestDropped::NAME,
);

return;
}
}

/**
* @psalm-suppress UnnecessaryVarAnnotation
*
* @var RequestSending $event
*/
$event = $this->eventDispatcher->dispatch(
new RequestSending($request),
RequestSending::NAME,
);

if ($event->request->wasDropped()) {
$this->eventDispatcher->dispatch(
new RequestDropped($event->request),
RequestDropped::NAME,
);

return;
$this->requests[] = $event->request;
} catch (Exception $exception) {
$this->onExceptionReceived(new RequestException($request, $exception), $onRejected);
}

$this->requests[] = $event->request;
}

public function flush(?callable $callback = null): void
public function flush(?callable $onFullFilled = null, ?callable $onRejected = null): void
{
$requests = $this->requests;

$this->requests = [];

foreach ($requests as $key => $request) {
if ($request->getResponse() !== null) {
$this->onResponseReceived($request->getResponse(), $callback);
$this->onResponseReceived($request->getResponse(), $onFullFilled);

unset($requests[$key]);
}
Expand All @@ -108,9 +116,15 @@ public function flush(?callable $callback = null): void
return;
}

$this->client->pool(\array_values($requests), function (Response $response) use ($callback): void {
$this->onResponseReceived($response, $callback);
});
$this->client->pool(
\array_values($requests),
function (Response $response) use ($onFullFilled): void {
$this->onResponseReceived($response, $onFullFilled);
},
function (RequestException $requestException) use ($onRejected): void {
$this->onExceptionReceived($requestException, $onRejected);
}
);
}

private function onResponseReceived(Response $response, ?callable $callback): void
Expand Down Expand Up @@ -158,4 +172,28 @@ private function onResponseReceived(Response $response, ?callable $callback): vo
$callback($response);
}
}

private function onExceptionReceived(RequestException $requestException, ?callable $callback): void
{
$this->eventDispatcher->dispatch(
new ExceptionReceiving($requestException),
ExceptionReceiving::NAME,
);

foreach ($this->middleware as $middleware) {
$requestException = $middleware->handleException($requestException);
}

$this->eventDispatcher->dispatch(
new ExceptionReceived($requestException),
ExceptionReceived::NAME,
);

if (null !== $callback) {
$callback($requestException);

} else if (!$requestException->isHandled()) {
throw $requestException;
}
}
}
3 changes: 2 additions & 1 deletion src/Downloader/DownloaderMiddlewareInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@

namespace RoachPHP\Downloader;

use RoachPHP\Downloader\Middleware\ExceptionMiddlewareInterface;
use RoachPHP\Downloader\Middleware\RequestMiddlewareInterface;
use RoachPHP\Downloader\Middleware\ResponseMiddlewareInterface;

interface DownloaderMiddlewareInterface extends RequestMiddlewareInterface, ResponseMiddlewareInterface
interface DownloaderMiddlewareInterface extends RequestMiddlewareInterface, ResponseMiddlewareInterface, ExceptionMiddlewareInterface
{
}
17 changes: 14 additions & 3 deletions src/Downloader/Middleware/DownloaderMiddlewareAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@

namespace RoachPHP\Downloader\Middleware;

use Exception;
use RoachPHP\Downloader\DownloaderMiddlewareInterface;
use RoachPHP\Http\Request;
use RoachPHP\Http\RequestException;
use RoachPHP\Http\Response;

/**
Expand All @@ -23,12 +25,12 @@
final class DownloaderMiddlewareAdapter implements DownloaderMiddlewareInterface
{
private function __construct(
private RequestMiddlewareInterface|ResponseMiddlewareInterface $middleware,
private RequestMiddlewareInterface|ResponseMiddlewareInterface|ExceptionMiddlewareInterface $middleware,
) {
}

public static function fromMiddleware(
RequestMiddlewareInterface|ResponseMiddlewareInterface $middleware,
RequestMiddlewareInterface|ResponseMiddlewareInterface|ExceptionMiddlewareInterface $middleware,
): DownloaderMiddlewareInterface {
if ($middleware instanceof DownloaderMiddlewareInterface) {
return $middleware;
Expand All @@ -55,12 +57,21 @@ public function handleResponse(Response $response): Response
return $response;
}

public function handleException(RequestException $requestException): RequestException
{
if ($this->middleware instanceof ExceptionMiddlewareInterface) {
return $this->middleware->handleException($requestException);
}

return $requestException;
}

public function configure(array $options): void
{
$this->middleware->configure($options);
}

public function getMiddleware(): RequestMiddlewareInterface|ResponseMiddlewareInterface
public function getMiddleware(): RequestMiddlewareInterface|ResponseMiddlewareInterface|ExceptionMiddlewareInterface
{
return $this->middleware;
}
Expand Down
13 changes: 13 additions & 0 deletions src/Downloader/Middleware/ExceptionMiddlewareInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace RoachPHP\Downloader\Middleware;

use RoachPHP\Http\RequestException;
use RoachPHP\Support\ConfigurableInterface;

interface ExceptionMiddlewareInterface extends ConfigurableInterface
{
public function handleException(RequestException $requestException): RequestException;
}
36 changes: 36 additions & 0 deletions src/Downloader/Middleware/FakeMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@

namespace RoachPHP\Downloader\Middleware;

use Exception;
use PHPUnit\Framework\Assert;
use RoachPHP\Downloader\DownloaderMiddlewareInterface;
use RoachPHP\Http\ExceptionContext;
use RoachPHP\Http\Request;
use RoachPHP\Http\RequestException;
use RoachPHP\Http\Response;
use RoachPHP\Support\Configurable;

Expand All @@ -36,13 +39,20 @@ final class FakeMiddleware implements DownloaderMiddlewareInterface
*/
private array $responsesHandled = [];

/**
* @var array Exception[]
*/
private array $exceptionsHandled = [];

/**
* @param ?\Closure(Request): Request $requestHandler
* @param ?\Closure(Response): Response $responseHandler
* @param ?\Closure(RequestException): RequestException $exceptionHandler
*/
public function __construct(
private ?\Closure $requestHandler = null,
private ?\Closure $responseHandler = null,
private ?\Closure $exceptionHandler = null,
) {
}

Expand All @@ -68,6 +78,17 @@ public function handleResponse(Response $response): Response
return $response;
}

public function handleException(RequestException $requestException): RequestException
{
$this->exceptionsHandled[] = $requestException->getReason();

if (null !== $this->exceptionHandler) {
return ($this->exceptionHandler)($requestException);
}

return $requestException;
}

public function assertRequestHandled(Request $request): void
{
Assert::assertContains($request, $this->requestsHandled);
Expand Down Expand Up @@ -97,4 +118,19 @@ public function assertNoResponseHandled(): void
{
Assert::assertEmpty($this->responsesHandled);
}

public function assertExceptionHandled(Exception $exception): void
{
Assert::assertContains($exception, $this->exceptionsHandled);
}

public function assertExceptionNotHandled(Exception $exception): void
{
Assert::assertNotContains($exception, $this->exceptionsHandled);
}

public function assertNoExceptionHandled(): void
{
Assert::assertEmpty($this->exceptionsHandled);
}
}
17 changes: 17 additions & 0 deletions src/Events/ExceptionReceived.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace RoachPHP\Events;

use Exception;
use Symfony\Contracts\EventDispatcher\Event;

final class ExceptionReceived extends Event
{
public const NAME = 'exception.processed';

public function __construct(public Exception $exception)
{
}
}
17 changes: 17 additions & 0 deletions src/Events/ExceptionReceiving.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace RoachPHP\Events;

use Exception;
use Symfony\Contracts\EventDispatcher\Event;

final class ExceptionReceiving extends Event
{
public const NAME = 'exception.receiving';

public function __construct(public Exception $exception)
{
}
}
9 changes: 9 additions & 0 deletions src/Extensions/LoggerExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
namespace RoachPHP\Extensions;

use Psr\Log\LoggerInterface;
use RoachPHP\Events\ExceptionReceived;
use RoachPHP\Events\ItemDropped;
use RoachPHP\Events\ItemScraped;
use RoachPHP\Events\RequestDropped;
Expand All @@ -39,6 +40,7 @@ public static function getSubscribedEvents(): array
RequestDropped::NAME => ['onRequestDropped', 100],
ItemScraped::NAME => ['onItemScraped', 100],
ItemDropped::NAME => ['onItemDropped', 100],
ExceptionReceived::NAME => ['onExceptionReceived', 100],
];
}

Expand Down Expand Up @@ -81,4 +83,11 @@ public function onItemDropped(ItemDropped $event): void
'reason' => $event->item->getDropReason(),
]);
}

public function onExceptionReceived(ExceptionReceived $event): void
{
$this->logger->warning('Exception received', [
'exception' => $event->exception,
]);
}
}
Loading