Skip to content

Commit 0ec39dd

Browse files
committed
Fix IntlPartsIterator key off-by-one error
Closes GH-7734
1 parent 925a309 commit 0ec39dd

File tree

4 files changed

+64
-10
lines changed

4 files changed

+64
-10
lines changed

ext/intl/breakiterator/breakiterator_iterators.cpp

+13-2
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ typedef struct zoi_break_iter_parts {
127127
zoi_with_current zoi_cur;
128128
parts_iter_key_type key_type;
129129
BreakIterator_object *bio; /* so we don't have to fetch it all the time */
130+
zend_ulong index_right;
130131
} zoi_break_iter_parts;
131132

132133
static void _breakiterator_parts_destroy_it(zend_object_iterator *iter)
@@ -136,8 +137,16 @@ static void _breakiterator_parts_destroy_it(zend_object_iterator *iter)
136137

137138
static void _breakiterator_parts_get_current_key(zend_object_iterator *iter, zval *key)
138139
{
139-
/* the actual work is done in move_forward and rewind */
140-
ZVAL_LONG(key, iter->index);
140+
// The engine resets the iterator index to -1 after rewinding. When using
141+
// PARTS_ITERATOR_KEY_RIGHT we store it in zoi_break_iter_parts.index_right
142+
// so it doesn't get lost.
143+
zoi_break_iter_parts *zoi_bit = (zoi_break_iter_parts*)iter;
144+
145+
if (zoi_bit->key_type == PARTS_ITERATOR_KEY_RIGHT && iter->index == 0) {
146+
ZVAL_LONG(key, zoi_bit->index_right);
147+
} else {
148+
ZVAL_LONG(key, iter->index);
149+
}
141150
}
142151

143152
static void _breakiterator_parts_move_forward(zend_object_iterator *iter)
@@ -163,6 +172,7 @@ static void _breakiterator_parts_move_forward(zend_object_iterator *iter)
163172
iter->index = cur;
164173
} else if (zoi_bit->key_type == PARTS_ITERATOR_KEY_RIGHT) {
165174
iter->index = next;
175+
zoi_bit->index_right = next;
166176
}
167177
/* else zoi_bit->key_type == PARTS_ITERATOR_KEY_SEQUENTIAL
168178
* No need to do anything, the engine increments ->index */
@@ -229,6 +239,7 @@ void IntlIterator_from_BreakIterator_parts(zval *break_iter_zv,
229239
assert(((zoi_break_iter_parts*)ii->iterator)->bio->biter != NULL);
230240

231241
((zoi_break_iter_parts*)ii->iterator)->key_type = key_type;
242+
((zoi_break_iter_parts*)ii->iterator)->index_right = 0;
232243
}
233244

234245
U_CFUNC PHP_METHOD(IntlPartsIterator, getBreakIterator)

ext/intl/common/common_enum.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ U_CFUNC void intl_register_IntlIterator_class(void)
291291
IntlIterator_ce_ptr = register_class_IntlIterator(zend_ce_iterator);
292292
IntlIterator_ce_ptr->create_object = IntlIterator_object_create;
293293
IntlIterator_ce_ptr->get_iterator = IntlIterator_get_iterator;
294+
IntlIterator_ce_ptr->ce_flags |= ZEND_ACC_REUSE_GET_ITERATOR;
294295

295296
memcpy(&IntlIterator_handlers, &std_object_handlers,
296297
sizeof IntlIterator_handlers);

ext/intl/tests/breakiter_getPartsIterator_var1.phpt

+8-8
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,24 @@ array(5) {
3333
array(5) {
3434
[0]=>
3535
string(3) "foo"
36-
[4]=>
36+
[3]=>
3737
string(1) " "
38-
[5]=>
38+
[4]=>
3939
string(3) "bar"
40-
[8]=>
40+
[7]=>
4141
string(1) " "
42-
[9]=>
42+
[8]=>
4343
string(3) "tao"
4444
}
4545
array(5) {
4646
[3]=>
4747
string(3) "foo"
48-
[5]=>
48+
[4]=>
4949
string(1) " "
50-
[8]=>
50+
[7]=>
5151
string(3) "bar"
52-
[9]=>
52+
[8]=>
5353
string(1) " "
54-
[12]=>
54+
[11]=>
5555
string(3) "tao"
5656
}

ext/intl/tests/gh7734.phpt

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
GH-7734 (IntlPartsIterator key is wrong for KEY_LEFT/KEY_RIGHT)
3+
--EXTENSIONS--
4+
intl
5+
--FILE--
6+
<?php
7+
8+
$iter = \IntlBreakIterator::createCodePointInstance();
9+
$iter->setText('ABC');
10+
11+
foreach ($iter->getPartsIterator(\IntlPartsIterator::KEY_SEQUENTIAL) as $key => $value) {
12+
var_dump($key, $value);
13+
}
14+
15+
foreach ($iter->getPartsIterator(\IntlPartsIterator::KEY_LEFT) as $key => $value) {
16+
var_dump($key, $value);
17+
}
18+
19+
foreach ($iter->getPartsIterator(\IntlPartsIterator::KEY_RIGHT) as $key => $value) {
20+
var_dump($key, $value);
21+
}
22+
23+
?>
24+
--EXPECT--
25+
int(0)
26+
string(1) "A"
27+
int(1)
28+
string(1) "B"
29+
int(2)
30+
string(1) "C"
31+
int(0)
32+
string(1) "A"
33+
int(1)
34+
string(1) "B"
35+
int(2)
36+
string(1) "C"
37+
int(1)
38+
string(1) "A"
39+
int(2)
40+
string(1) "B"
41+
int(3)
42+
string(1) "C"

0 commit comments

Comments
 (0)