mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-20 14:36:57 +00:00
bug #4713 Fix cycle() with non-countable ArrayAccess+Traversable objects (yoeunes)
This PR was merged into the 3.x branch.
Discussion
----------
Fix cycle() with non-countable ArrayAccess+Traversable objects
When using `cycle()` with an object that implements `\ArrayAccess` and `\Traversable` but is not `\Countable`, the function currently returns the object instance immediately after triggering the deprecation notice.
This prevents the value from being converted to an array, causing the cycle logic to fail or return the object itself.
This PR removes the early return to ensure `self::toArray()` is called, allowing these objects to be cycled correctly as expected.
**How to test**
```php
$seq = new class implements \ArrayAccess, \IteratorAggregate {
public function offsetExists($offset): bool { return true; }
public function offsetGet($offset): mixed { return 'val'; }
public function offsetSet($offset, $value): void {}
public function offsetUnset($offset): void {}
public function getIterator(): \Traversable { yield 'odd'; yield 'even'; }
};
// Should return 'odd', currently returns the $seq object
$result = CoreExtension::cycle($seq, 0);
```
Related to #4241
Commits
-------
473653d19b [Core] Fix cycle() with non-countable ArrayAccess+Traversable objects
This commit is contained in:
@@ -417,10 +417,8 @@ final class CoreExtension extends AbstractExtension
|
||||
|
||||
trigger_deprecation('twig/twig', '3.12', 'Passing a non-countable sequence of values to "%s()" is deprecated.', __METHOD__);
|
||||
|
||||
return $values;
|
||||
$values = self::toArray($values, false);
|
||||
}
|
||||
|
||||
$values = self::toArray($values, false);
|
||||
}
|
||||
|
||||
if (!$count = \count($values)) {
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Twig\Tests\Extension;
|
||||
*/
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
|
||||
use Twig\Environment;
|
||||
use Twig\Error\RuntimeError;
|
||||
use Twig\Extension\CoreExtension;
|
||||
@@ -31,6 +32,8 @@ use Twig\Sandbox\SecurityPolicy;
|
||||
|
||||
class CoreTest extends TestCase
|
||||
{
|
||||
use ExpectDeprecationTrait;
|
||||
|
||||
/**
|
||||
* @dataProvider provideCycleCases
|
||||
*/
|
||||
@@ -407,6 +410,44 @@ class CoreTest extends TestCase
|
||||
{
|
||||
$this->assertGreaterThan(1000000000, (new CoreExtension())->getLastModified());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
public function testCycleWithArrayAccessAndTraversableButNotCountable()
|
||||
{
|
||||
$this->expectDeprecation('Since twig/twig 3.12: Passing a non-countable sequence of values to "Twig\Extension\CoreExtension::cycle()" is deprecated.');
|
||||
|
||||
$seq = new class implements \ArrayAccess, \IteratorAggregate {
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function offsetGet($offset): mixed
|
||||
{
|
||||
return 'val';
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
}
|
||||
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
}
|
||||
|
||||
public function getIterator(): \Traversable
|
||||
{
|
||||
yield 'odd';
|
||||
yield 'even';
|
||||
}
|
||||
};
|
||||
|
||||
$result = CoreExtension::cycle($seq, 0);
|
||||
|
||||
$this->assertEquals('odd', $result, 'cycle should return the first item from the traversable sequence, not the sequence itself.');
|
||||
}
|
||||
}
|
||||
|
||||
final class CoreTestIteratorAggregate implements \IteratorAggregate
|
||||
|
||||
Reference in New Issue
Block a user