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
15 changes: 14 additions & 1 deletion src/Connector/CachingConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
/**
* Wraps a connector to cache fetched data using PSR-6-compliant objects.
*/
class CachingConnector implements Connector
class CachingConnector implements Connector, ConnectorWrapper
{
/**
* @var Connector
Expand All @@ -37,6 +37,14 @@ public function __construct(
$this->cacheKeyGenerator = $cacheKeyGenerator ?: new JsonCacheKeyGenerator;
}

public function __clone()
{
$this->connector = clone $this->connector;

/* It doesn't make sense to clone the cache because we want cache state to be shared between imports.
We're also not cloning the CacheKeyGenerator because they're expected to be stateless algorithms. */
}

/**
* @param ConnectionContext $context
* @param string $source
Expand Down Expand Up @@ -87,4 +95,9 @@ private function validateCacheKey($key)
));
}
}

public function getWrappedConnector()
{
return $this->connector;
}
}
15 changes: 15 additions & 0 deletions src/Connector/ConnectorWrapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
namespace ScriptFUSION\Porter\Connector;

/**
* Designates a connector decorator object and provides a method to access the wrapped connector.
*/
interface ConnectorWrapper
{
/**
* Gets the wrapped connector.
*
* @return Connector
*/
public function getWrappedConnector();
}
22 changes: 19 additions & 3 deletions src/Connector/ImportConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*
* Do not store references to this connector that would prevent it expiring when an import operation ends.
*/
final class ImportConnector
final class ImportConnector implements ConnectorWrapper
{
private $connector;

Expand All @@ -22,7 +22,7 @@ public function __construct(Connector $connector, ConnectionContext $context)
throw CacheUnavailableException::createUnsupported();
}

$this->connector = $connector;
$this->connector = clone $connector;
$this->context = $context;
}

Expand All @@ -34,13 +34,29 @@ public function fetch($source)
/**
* Gets the wrapped connector. Useful for resources to reconfigure connector options during this import.
*
* @return Connector
* @return Connector Wrapped connector.
*/
public function getWrappedConnector()
{
return $this->connector;
}

/**
* Finds the base connector by traversing the stack of wrapped connectors.
*
* @return Connector Base connector.
*/
public function findBaseConnector()
{
$connector = $this->connector;

while ($connector instanceof ConnectorWrapper) {
$connector = $connector->getWrappedConnector();
}

return $connector;
}

/**
* Sets the exception handler to be called when a recoverable exception is thrown by Connector::fetch().
*
Expand Down
2 changes: 1 addition & 1 deletion src/Porter.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ private function fetch(ProviderResource $resource, $providerName, ConnectionCont
);
}

$records = $resource->fetch(new ImportConnector(clone $connector, $context));
$records = $resource->fetch(new ImportConnector($connector, $context));

if (!$records instanceof \Iterator) {
throw new ImportException(get_class($resource) . '::fetch() did not return an Iterator.');
Expand Down
8 changes: 4 additions & 4 deletions src/Specification/ImportSpecification.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ final public function getResource()
}

/**
* Gets the provider name.
* Gets the provider service name.
*
* @return string Provider name.
*/
Expand All @@ -90,7 +90,7 @@ final public function getProviderName()
}

/**
* Sets the provider name.
* Sets the provider service name.
*
* @param string $providerName Provider name.
*
Expand Down Expand Up @@ -211,7 +211,7 @@ final public function disableCache()
}

/**
* Gets the maximum number of fetch attempts per import.
* Gets the maximum number of fetch attempts per connection.
*
* @return int Maximum fetch attempts.
*/
Expand All @@ -221,7 +221,7 @@ final public function getMaxFetchAttempts()
}

/**
* Sets the maximum number of fetch attempts per import.
* Sets the maximum number of fetch attempts per connection before failure is considered permanent.
*
* @param int $attempts Maximum fetch attempts.
*
Expand Down
21 changes: 21 additions & 0 deletions test/Integration/Porter/Connector/CachingConnectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
use ScriptFUSIONTest\FixtureFactory;
use ScriptFUSIONTest\Stubs\TestOptions;

/**
* @see CachingConnector
*/
final class CachingConnectorTest extends \PHPUnit_Framework_TestCase
{
use MockeryPHPUnitIntegration;
Expand Down Expand Up @@ -193,6 +196,24 @@ public function testFetchThrowsInvalidCacheKeyExceptionOnNonPSR6CompliantCacheKe
$connector->fetch($this->context, 'baz');
}

/**
* Tests that getting the wrapped connector returns exactly the same connector as constructed with.
*/
public function testGetWrappedConnector()
{
self::assertSame($this->wrappedConnector, $this->connector->getWrappedConnector());
}

/**
* Tests that cloning the caching connector also clones the wrapped connector.
*/
public function testClone()
{
$clone = clone $this->connector;

self::assertNotSame($this->wrappedConnector, $clone->getWrappedConnector());
}

private function createConnector(MockInterface $cache = null, MockInterface $cacheKeyGenerator = null)
{
return new CachingConnector($this->wrappedConnector, $cache, $cacheKeyGenerator);
Expand Down
24 changes: 21 additions & 3 deletions test/Unit/Porter/Connector/ImportConnectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use ScriptFUSION\Porter\Cache\CacheUnavailableException;
use ScriptFUSION\Porter\Connector\CachingConnector;
use ScriptFUSION\Porter\Connector\Connector;
use ScriptFUSION\Porter\Connector\ConnectorWrapper;
use ScriptFUSION\Porter\Connector\FetchExceptionHandler\FetchExceptionHandler;
use ScriptFUSION\Porter\Connector\ImportConnector;
use ScriptFUSIONTest\FixtureFactory;
Expand Down Expand Up @@ -55,7 +56,7 @@ public function testFetchCacheDisabled()
public function testFetchCacheEnabled()
{
$connector = new ImportConnector(
\Mockery::mock(CachingConnector::class)
\Mockery::mock(CachingConnector::class, [\Mockery::mock(Connector::class)])
->shouldReceive('fetch')
->andReturn($output = 'foo')
->getMock(),
Expand All @@ -79,7 +80,7 @@ public function testFetchCacheEnabledButNotAvailable()
}

/**
* Tests that getting the wrapped connector returns exactly the same connector as constructed with.
* Tests that getting the wrapped connector returns a clone of the original connector passed to the constructor.
*/
public function testGetWrappedConnector()
{
Expand All @@ -88,7 +89,8 @@ public function testGetWrappedConnector()
FixtureFactory::buildConnectionContext()
);

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

/**
Expand All @@ -106,4 +108,20 @@ public function testSetExceptionHandlerTwice()
$this->setExpectedException(\LogicException::class);
$connector->setExceptionHandler($handler);
}

/**
* Tests that finding the base connector returns the connector at the bottom of a ConnectorWrapper stack.
*/
public function testFindBaseConnector()
{
$connector = new ImportConnector(
\Mockery::mock(Connector::class, ConnectorWrapper::class)
->shouldReceive('getWrappedConnector')
->andReturn($baseConnector = \Mockery::mock(Connector::class))
->getMock(),
FixtureFactory::buildConnectionContext()
);

self::assertSame($baseConnector, $connector->findBaseConnector());
}
}