diff --git a/src/ArrayUtils.php b/src/ArrayUtils.php index 454a7a5b..2cf61e08 100644 --- a/src/ArrayUtils.php +++ b/src/ArrayUtils.php @@ -5,6 +5,7 @@ namespace Laminas\Stdlib; +use Iterator; use Laminas\Stdlib\ArrayUtils\MergeRemoveKey; use Laminas\Stdlib\ArrayUtils\MergeReplaceKeyInterface; use Traversable; @@ -240,7 +241,11 @@ public static function iteratorToArray($iterator, $recursive = true) return iterator_to_array($iterator); } - if (is_object($iterator) && method_exists($iterator, 'toArray')) { + if ( + is_object($iterator) + && ! $iterator instanceof Iterator + && method_exists($iterator, 'toArray') + ) { return $iterator->toArray(); } diff --git a/src/Glob.php b/src/Glob.php index dd187758..5f7fc094 100644 --- a/src/Glob.php +++ b/src/Glob.php @@ -109,7 +109,7 @@ protected static function systemGlob($pattern, $flags) */ protected static function fallbackGlob($pattern, $flags) { - if (! $flags & self::GLOB_BRACE) { + if (self::flagsIsEqualTo($flags, self::GLOB_BRACE)) { return static::systemGlob($pattern, $flags); } @@ -195,14 +195,19 @@ protected static function nextBraceSub($pattern, $begin, $flags) $current = $begin; while ($current < $length) { - if (! $flags & self::GLOB_NOESCAPE && $pattern[$current] === '\\') { + $flagsEqualsNoEscape = self::flagsIsEqualTo($flags, self::GLOB_NOESCAPE); + + if ($flagsEqualsNoEscape && $pattern[$current] === '\\') { if (++$current === $length) { break; } $current++; } else { - if (($pattern[$current] === '}' && $depth-- === 0) || ($pattern[$current] === ',' && $depth === 0)) { + if ( + ($pattern[$current] === '}' && $depth-- === 0) + || ($pattern[$current] === ',' && $depth === 0) + ) { break; } elseif ($pattern[$current++] === '{') { $depth++; @@ -212,4 +217,10 @@ protected static function nextBraceSub($pattern, $begin, $flags) return $current < $length ? $current : null; } + + /** @internal */ + public static function flagsIsEqualTo(int $flags, int $otherFlags): bool + { + return (bool) ($flags & $otherFlags); + } } diff --git a/test/ArrayUtilsTest.php b/test/ArrayUtilsTest.php index 9982d904..cac6748e 100644 --- a/test/ArrayUtilsTest.php +++ b/test/ArrayUtilsTest.php @@ -11,6 +11,7 @@ use Laminas\Stdlib\ArrayUtils\MergeReplaceKeyInterface; use Laminas\Stdlib\Exception\InvalidArgumentException; use Laminas\Stdlib\Parameters; +use LaminasTest\Stdlib\TestAsset\IteratorWithToArrayMethod; use PHPUnit\Framework\TestCase; use stdClass; use Traversable; @@ -579,4 +580,30 @@ public function testInvalidCallableRaiseInvalidArgumentException(): void $this->expectException(InvalidArgumentException::class); ArrayUtils::filter([], "INVALID"); } + + /** + * @link https://github.com/laminas/laminas-stdlib/issues/18 + */ + public function testIteratorToArrayWithIteratorHavingMethodToArrayAndRecursiveIsFalse(): void + { + $arrayB = [ + 'foo' => 'bar', + ]; + $iteratorB = new IteratorWithToArrayMethod($arrayB); + + $arrayA = [ + 'iteratorB' => $iteratorB, + ]; + $iterator = new IteratorWithToArrayMethod($arrayA); + + $result = ArrayUtils::iteratorToArray($iterator, true); + + $expectedResult = [ + 'iteratorB' => [ + 'foo' => 'bar', + ], + ]; + + $this->assertEquals($expectedResult, $result); + } } diff --git a/test/GlobTest.php b/test/GlobTest.php index 7e74754d..6a291524 100644 --- a/test/GlobTest.php +++ b/test/GlobTest.php @@ -11,6 +11,7 @@ use function count; use function defined; use function glob; +use function realpath; use function str_repeat; use const GLOB_BRACE; @@ -23,15 +24,29 @@ public function testFallback(): void $this->markTestSkipped('GLOB_BRACE not available'); } - self::assertEquals( - glob(__DIR__ . '/_files/{alph,bet}a', GLOB_BRACE), - Glob::glob(__DIR__ . '/_files/{alph,bet}a', Glob::GLOB_BRACE, true) + $expected = glob(__DIR__ . '/_files/{alph,bet}a', GLOB_BRACE); + $actual = Glob::glob( + __DIR__ . '/_files/{alph,bet}a', + Glob::GLOB_BRACE, + true + ); + + self::assertEquals($actual, $expected); + + $notExpectedPath = realpath(__DIR__ . '/_files/{alph,bet}a'); + + self::assertNotContains( + $notExpectedPath, + $actual ); } public function testNonMatchingGlobReturnsArray(): void { - $result = Glob::glob('/some/path/{,*.}{this,orthis}.php', Glob::GLOB_BRACE); + $result = Glob::glob( + '/some/path/{,*.}{this,orthis}.php', + Glob::GLOB_BRACE + ); self::assertIsArray($result); } @@ -80,4 +95,56 @@ public function patternsProvider(): array ], ]; } + + public function testGlobWithoutGlobBraceFlag(): void + { + $expected = [ + realpath(__DIR__ . '/_files/{alph,bet}a'), + ]; + + self::assertEquals( + glob(__DIR__ . '/_files/{alph,bet}a', 0), + $expected + ); + } + + /** + * @psalm-return array + */ + public function flagsIsEqualsToMethodDataProvider(): array + { + return [ + [ + Glob::GLOB_BRACE, + Glob::GLOB_BRACE, + true, + ], + [ + Glob::GLOB_BRACE, + Glob::GLOB_NOSORT, + false, + ], + ]; + } + + /** + * @dataProvider flagsIsEqualsToMethodDataProvider + */ + public function testFlagsIsEqualsToMethod( + int $flags, + int $otherFlags, + bool $expected + ): void { + /** + * @psalm-suppress InternalMethod this test is specifically testing the behavior of this method, + * to prevent regressions + */ + $actual = Glob::flagsIsEqualTo($flags, $otherFlags); + + $this->assertEquals($expected, $actual); + } } diff --git a/test/TestAsset/IteratorWithToArrayMethod.php b/test/TestAsset/IteratorWithToArrayMethod.php new file mode 100644 index 00000000..8857771b --- /dev/null +++ b/test/TestAsset/IteratorWithToArrayMethod.php @@ -0,0 +1,67 @@ +elements = $elements; + } + + /** @return void */ + #[ReturnTypeWillChange] + public function rewind() + { + reset($this->elements); + } + + /** @return mixed */ + #[ReturnTypeWillChange] + public function current() + { + return current($this->elements); + } + + /** @return int|string */ + #[ReturnTypeWillChange] + public function key() + { + return key($this->elements); + } + + /** @return mixed */ + #[ReturnTypeWillChange] + public function next() + { + return next($this->elements); + } + + /** @return bool */ + #[ReturnTypeWillChange] + public function valid() + { + $key = key($this->elements); + return $key !== null && $key !== false; + } + + public function toArray(): array + { + return [ + 'data from to array' => 'not good', + ]; + } +} diff --git a/test/_files/{alph,bet}a b/test/_files/{alph,bet}a new file mode 100644 index 00000000..e69de29b