Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/Connector/ConnectionContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,20 @@ final class ConnectionContext
{
private $mustCache;

/**
* User-defined exception handler called when a recoverable exception is thrown by Connector::fetch().
*
* @var callable
*/
private $fetchExceptionHandler;

/**
* Provider-defined exception handler called when a recoverable exception is thrown by Connector::fetch().
*
* @var callable
*/
private $providerFetchExceptionHandler;

private $maxFetchAttempts;

public function __construct($mustCache, callable $fetchExceptionHandler, $maxFetchAttempts)
Expand Down Expand Up @@ -47,9 +59,30 @@ function (\Exception $exception) {
throw $exception;
}

// Call provider's exception handler, if defined.
if ($this->providerFetchExceptionHandler) {
call_user_func($this->providerFetchExceptionHandler, $exception);
}

// TODO Clone exception handler to avoid persisting state between calls.
call_user_func($this->fetchExceptionHandler, $exception);
}
);
}

/**
* Sets an exception handler to be called when a recoverable exception is thrown by Connector::fetch().
*
* This handler is intended to be set by provider resources only and is called before the user-defined handler.
*
* @param callable $providerFetchExceptionHandler Exception handler.
*/
public function setProviderFetchExceptionHandler(callable $providerFetchExceptionHandler)
{
if ($this->providerFetchExceptionHandler !== null) {
throw new \LogicException('Cannot set provider fetch exception handler: already set!');
}

$this->providerFetchExceptionHandler = $providerFetchExceptionHandler;
}
}
10 changes: 10 additions & 0 deletions src/Connector/ImportConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,14 @@ public function getWrappedConnector()
{
return $this->connector;
}

/**
* Sets the exception handler to be called when a recoverable exception is thrown by Connector::fetch().
*
* @param callable $exceptionHandler Exception handler.
*/
public function setExceptionHandler(callable $exceptionHandler)
{
$this->context->setProviderFetchExceptionHandler($exceptionHandler);
}
}
31 changes: 31 additions & 0 deletions test/Integration/Porter/PorterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use ScriptFUSION\Porter\Connector\ConnectionContext;
use ScriptFUSION\Porter\Connector\Connector;
use ScriptFUSION\Porter\Connector\ConnectorOptions;
use ScriptFUSION\Porter\Connector\ImportConnector;
use ScriptFUSION\Porter\Connector\RecoverableConnectorException;
use ScriptFUSION\Porter\ImportException;
use ScriptFUSION\Porter\Porter;
Expand Down Expand Up @@ -303,6 +304,36 @@ public function testCustomFetchExceptionHandler()
$this->porter->import($this->specification);
}

/**
* Tests that when a provider fetch exception handler is specified and the connector throws a recoverable
* exception, the handler is called before the user handler.
*/
public function testCustomProviderFetchExceptionHandler()
{
$this->specification->setFetchExceptionHandler(function () {
throw new \LogicException('This exception must not be thrown!');
});

$this->arrangeConnectorException($connectorException =
new RecoverableConnectorException('This exception is caught by the provider handler.'));

$this->resource
->shouldReceive('fetch')
->andReturnUsing(function (ImportConnector $connector) use ($connectorException) {
$connector->setExceptionHandler(function (\Exception $exception) use ($connectorException) {
self::assertSame($connectorException, $exception);

throw new \RuntimeException('This exception is thrown by the provider handler.');
});

yield $connector->fetch('foo');
})
;

$this->setExpectedException(\RuntimeException::class);
$this->porter->importOne($this->specification);
}

#endregion

public function testFilter()
Expand Down
16 changes: 16 additions & 0 deletions test/Unit/Porter/Connector/ImportConnectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,20 @@ public function testGetWrappedConnector()

self::assertSame($wrappedConnector, $connector->getWrappedConnector());
}

/**
* Tests that setting the provider exception handler twice produces an exception the second time.
*/
public function testSetExceptionHandlerTwice()
{
$connector = new ImportConnector(
$wrappedConnector = \Mockery::mock(Connector::class),
FixtureFactory::buildConnectionContext()
);

$connector->setExceptionHandler([$this, __FUNCTION__]);

$this->setExpectedException(\LogicException::class);
$connector->setExceptionHandler([$this, __FUNCTION__]);
}
}