Skip to content

Commit 0cc9456

Browse files
Fix UTF-8 reverse and slicing
1 parent 173c0a1 commit 0cc9456

5 files changed

Lines changed: 42 additions & 3 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
## 2.9.0 - Upcoming
44

55
* Fixed to_number() to parse number strings using the JSON number grammar.
6+
* Fixed reverse() and string slicing to operate on UTF-8 characters rather than bytes.
7+
* Fixed slicing of array-like (ArrayAccess + Countable) values.
68
* Fixed the compiled runtime to apply JMESPath truthiness to || and &&.
79
* Fixed @(foo), foo[-] and oversized index literals to throw syntax errors.
810
* Fixed PHP warnings emitted while parsing certain invalid expressions.

‎src/FnDispatcher.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ private function fn_reverse(array $args)
162162
if (is_array($args[0])) {
163163
return array_reverse($args[0]);
164164
} elseif (is_string($args[0])) {
165-
return strrev($args[0]);
165+
return implode('', array_reverse(mb_str_split($args[0], 1, 'UTF-8')));
166166
} else {
167167
throw new \RuntimeException('Cannot reverse provided argument');
168168
}

‎src/Utils.php‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ public static function stableSort(array $data, callable $sortFn)
192192
*/
193193
public static function slice($value, $start = null, $stop = null, $step = 1)
194194
{
195-
if (!is_array($value) && !is_string($value)) {
195+
if (!is_string($value) && !self::isArray($value)) {
196196
throw new \InvalidArgumentException('Expects string or array');
197197
}
198198

@@ -239,7 +239,10 @@ private static function adjustSlice($length, $start, $stop, $step)
239239
private static function sliceIndices($subject, $start, $stop, $step)
240240
{
241241
$type = gettype($subject);
242-
$len = $type == 'string' ? mb_strlen($subject, 'UTF-8') : count($subject);
242+
if ($type == 'string') {
243+
$subject = mb_str_split($subject, 1, 'UTF-8');
244+
}
245+
$len = count($subject);
243246
list($start, $stop, $step) = self::adjustSlice($len, $start, $stop, $step);
244247

245248
$result = [];

‎tests/FnDispatcherTest.php‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ public function testMapRequiresArraySecondArgument(): void
3030
$fn('map', [function ($v) { return $v; }, null]);
3131
}
3232

33+
public function testReversesUtf8Strings(): void
34+
{
35+
$fn = new FnDispatcher();
36+
37+
$this->assertSame('aé', $fn('reverse', ['éa']));
38+
$this->assertSame('☃ba', $fn('reverse', ['ab☃']));
39+
}
40+
3341
/**
3442
* @dataProvider toNumberProvider
3543
*/

‎tests/UtilsTest.php‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22
namespace JmesPath\Tests;
33

4+
use JmesPath\AstRuntime;
45
use JmesPath\Utils;
56
use PHPUnit\Framework\TestCase;
67

@@ -111,6 +112,31 @@ public function testSlicesStrings(): void
111112
$this->assertEquals('ac', Utils::slice('abc', null, null, 2));
112113
$this->assertEquals('bc', Utils::slice('abc', 1));
113114
}
115+
116+
public function testSlicesUtf8Strings(): void
117+
{
118+
$this->assertSame('é', Utils::slice('éa', 0, 1));
119+
$this->assertSame('cbaé', Utils::slice('éabc', null, null, -1));
120+
}
121+
122+
public function testSlicesArrayAccessCountable(): void
123+
{
124+
$this->assertSame([1, 2], Utils::slice(new \ArrayObject([1, 2, 3]), 0, 2));
125+
}
126+
127+
public function testRejectsObjectLikeValuesInSlice(): void
128+
{
129+
$this->expectException(\InvalidArgumentException::class);
130+
131+
Utils::slice(new \ArrayObject(['foo' => 'bar']), 0, 1);
132+
}
133+
134+
public function testRuntimeSlicesArrayAccessCountable(): void
135+
{
136+
$runtime = new AstRuntime();
137+
138+
$this->assertSame([1, 2], $runtime('[0:2]', new \ArrayObject([1, 2, 3])));
139+
}
114140
}
115141

116142
class _TestClass implements \ArrayAccess

0 commit comments

Comments
 (0)