From ed17544dc8cec9a52ff41a6c559061325fbc1e41 Mon Sep 17 00:00:00 2001 From: XedinUnknown Date: Wed, 3 Feb 2021 01:45:41 +0100 Subject: [PATCH 1/2] Add iteration capability to `SegmentingContainer` --- src/SegmentingContainer.php | 83 ++++++++++++++++++- .../MultipleAccessTypesWithMapsTest.php | 4 +- 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/SegmentingContainer.php b/src/SegmentingContainer.php index bf21b40..09439de 100644 --- a/src/SegmentingContainer.php +++ b/src/SegmentingContainer.php @@ -5,8 +5,15 @@ namespace Dhii\Container; use Dhii\Collection\ContainerInterface; +use Exception; +use Iterator; +use IteratorAggregate; use Psr\Container\ContainerInterface as PsrContainerInterface; +use Traversable; + +use UnexpectedValueException; + use function array_filter; use function ltrim; @@ -53,7 +60,7 @@ * @since [*next-version*] * @see PathContainer For an implementation that achieves the opposite effect. */ -class SegmentingContainer implements ContainerInterface +class SegmentingContainer implements ContainerInterface, Iterator { /** * @var PsrContainerInterface @@ -70,6 +77,11 @@ class SegmentingContainer implements ContainerInterface */ protected $delimiter; + /** + * @var Iterator + */ + protected $iterator; + /** * Constructor. * @@ -116,4 +128,73 @@ public function has($key) { return $this->inner->has($key); } + + public function current() + { + return $this->iterator->current(); + } + + public function next() + { + $this->iterator->next(); + } + + public function key() + { + return $this->iterator->key(); + } + + public function valid() + { + return $this->startsWith($this->iterator->key(), $this->root); + } + + public function rewind() + { + if (!($this->inner instanceof Traversable)) { + throw new UnexpectedValueException('Cannot rewind: inner container not an iterator'); + } + + $this->iterator = $this->normalizeIterator($this->inner); + $this->iterator->rewind(); + } + + /** + * Normalizes a traversable into an iterator. + * + * This is helpful because an `Iterator` can be iterated over by explicitly calling known methods, + * which include those that expose the current key. + * + * @param Traversable $traversable The traversable to normalize. + * + * @return Iterator The normalized iterator. + * + * @throws Exception If problem normalizing. + */ + protected function normalizeIterator(Traversable $traversable): Iterator + { + if ($traversable instanceof Iterator) { + return $traversable; + } + + // Any `Traversable` that is not an `Iterator` is an `IteratorAggregate` + assert($traversable instanceof IteratorAggregate); + $traversable = $traversable->getIterator(); + + return $this->normalizeIterator($traversable); + } + + /** + * Determines whether the subject string has another string at the start. + * + * @param string $subject The subject to check. + * @param string $start The start string to check for. + * + * @return bool True if subject starts with specified start string; false otherwise. + */ + protected function startsWith(string $subject, string $start): bool + { + $length = strlen($start); + return substr($subject, 0, $length) === $start; + } } diff --git a/tests/system/MultipleAccessTypesWithMapsTest.php b/tests/system/MultipleAccessTypesWithMapsTest.php index d54ade7..3149cee 100644 --- a/tests/system/MultipleAccessTypesWithMapsTest.php +++ b/tests/system/MultipleAccessTypesWithMapsTest.php @@ -8,7 +8,7 @@ use Dhii\Container\SegmentingContainer; use PHPUnit\Framework\TestCase; -class MultipleAccessTypesWithMaps__ extends TestCase +class MultipleAccessTypesWithMaps extends TestCase { const DEV_DB_HOST = 'localhost'; const STAGING_DB_HOST = '123.staging.myhost'; @@ -85,8 +85,6 @@ public function testPreserveIterabilityInNamespaced(array $devData, array $stagi } { - $this->markTestIncomplete('This will fail, because the segmenting container is not iterable, ' . - 'and will return instances of itself for keys that are not found.'); $this->assertIsIterable($container->get('staging')->get('db')); } } From 15749984085304b84f51cd7817047b4806569fb8 Mon Sep 17 00:00:00 2001 From: XedinUnknown Date: Wed, 3 Feb 2021 02:12:46 +0100 Subject: [PATCH 2/2] Fix PHPCS --- src/SegmentingContainer.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/SegmentingContainer.php b/src/SegmentingContainer.php index 09439de..a406430 100644 --- a/src/SegmentingContainer.php +++ b/src/SegmentingContainer.php @@ -9,9 +9,7 @@ use Iterator; use IteratorAggregate; use Psr\Container\ContainerInterface as PsrContainerInterface; - use Traversable; - use UnexpectedValueException; use function array_filter; @@ -134,7 +132,7 @@ public function current() return $this->iterator->current(); } - public function next() + public function next(): void { $this->iterator->next(); } @@ -149,7 +147,7 @@ public function valid() return $this->startsWith($this->iterator->key(), $this->root); } - public function rewind() + public function rewind(): void { if (!($this->inner instanceof Traversable)) { throw new UnexpectedValueException('Cannot rewind: inner container not an iterator');