Skip to content

Commit d7273c5

Browse files
committed
Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2: Prevents double call to internal iterator rewind handler adds failing test case for #12060
2 parents b4ce171 + c672a06 commit d7273c5

File tree

10 files changed

+235
-2
lines changed

10 files changed

+235
-2
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ PHP NEWS
88
(Girgias)
99
. Fixed bug GH-12073 (Segfault when freeing incompletely initialized
1010
closures). (ilutov)
11+
. Fixed bug GH-12060 (Internal iterator rewind handler is called twice).
12+
(ju1ius)
13+
1114

1215
- FPM:
1316
. Fixed GH-12077 (PHP 8.3.0RC1 borked socket-close-on-exec.phpt).

Zend/zend_interfaces.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,7 @@ ZEND_METHOD(InternalIterator, rewind) {
623623
RETURN_THROWS();
624624
}
625625

626+
intern->rewind_called = 1;
626627
if (!intern->iter->funcs->rewind) {
627628
/* Allow calling rewind() if no iteration has happened yet,
628629
* even if the iterator does not support rewinding. */

ext/zend_test/config.m4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ PHP_ARG_ENABLE([zend-test],
44
[Enable zend_test extension])])
55

66
if test "$PHP_ZEND_TEST" != "no"; then
7-
PHP_NEW_EXTENSION(zend_test, test.c observer.c fiber.c object_handlers.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
7+
PHP_NEW_EXTENSION(zend_test, test.c observer.c fiber.c iterators.c object_handlers.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
88
fi

ext/zend_test/config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
ARG_ENABLE("zend-test", "enable zend_test extension", "no");
44

55
if (PHP_ZEND_TEST != "no") {
6-
EXTENSION("zend_test", "test.c observer.c fiber.c object_handlers.c", PHP_ZEND_TEST_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
6+
EXTENSION("zend_test", "test.c observer.c fiber.c iterators.c object_handlers.c", PHP_ZEND_TEST_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
77
ADD_FLAG("CFLAGS_ZEND_TEST", "/D PHP_ZEND_TEST_EXPORTS ");
88
}

ext/zend_test/iterators.c

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| Copyright (c) The PHP Group |
4+
+----------------------------------------------------------------------+
5+
| This source file is subject to version 3.01 of the PHP license, |
6+
| that is bundled with this package in the file LICENSE, and is |
7+
| available through the world-wide-web at the following url: |
8+
| https://www.php.net/license/3_01.txt |
9+
| If you did not receive a copy of the PHP license and are unable to |
10+
| obtain it through the world-wide-web, please send a note to |
11+
| license@php.net so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
*/
14+
15+
#include "iterators.h"
16+
#include "zend_API.h"
17+
#include "iterators_arginfo.h"
18+
19+
#include <zend_interfaces.h>
20+
#include "php.h"
21+
22+
#define DUMP(s) php_output_write((s), sizeof((s)) - 1)
23+
24+
static zend_class_entry *traversable_test_ce;
25+
26+
// Dummy iterator that yields numbers from 0..4,
27+
// while printing operations to the output buffer
28+
typedef struct {
29+
zend_object_iterator intern;
30+
zval current;
31+
} test_traversable_it;
32+
33+
static test_traversable_it *test_traversable_it_fetch(zend_object_iterator *iter) {
34+
return (test_traversable_it *)iter;
35+
}
36+
37+
static void test_traversable_it_dtor(zend_object_iterator *iter) {
38+
DUMP("TraversableTest::drop\n");
39+
test_traversable_it *iterator = test_traversable_it_fetch(iter);
40+
zval_ptr_dtor(&iterator->intern.data);
41+
}
42+
43+
static void test_traversable_it_rewind(zend_object_iterator *iter) {
44+
DUMP("TraversableTest::rewind\n");
45+
test_traversable_it *iterator = test_traversable_it_fetch(iter);
46+
ZVAL_LONG(&iterator->current, 0);
47+
}
48+
49+
static void test_traversable_it_next(zend_object_iterator *iter) {
50+
DUMP("TraversableTest::next\n");
51+
test_traversable_it *iterator = test_traversable_it_fetch(iter);
52+
ZVAL_LONG(&iterator->current, Z_LVAL(iterator->current) + 1);
53+
}
54+
55+
static int test_traversable_it_valid(zend_object_iterator *iter) {
56+
DUMP("TraversableTest::valid\n");
57+
test_traversable_it *iterator = test_traversable_it_fetch(iter);
58+
if (Z_LVAL(iterator->current) < 4) {
59+
return SUCCESS;
60+
}
61+
return FAILURE;
62+
}
63+
64+
static void test_traversable_it_key(zend_object_iterator *iter, zval *return_value) {
65+
DUMP("TraversableTest::key\n");
66+
test_traversable_it *iterator = test_traversable_it_fetch(iter);
67+
ZVAL_LONG(return_value, Z_LVAL(iterator->current));
68+
}
69+
70+
static zval *test_traversable_it_current(zend_object_iterator *iter) {
71+
DUMP("TraversableTest::current\n");
72+
test_traversable_it *iterator = test_traversable_it_fetch(iter);
73+
return &iterator->current;
74+
}
75+
76+
static const zend_object_iterator_funcs test_traversable_it_vtable = {
77+
test_traversable_it_dtor,
78+
test_traversable_it_valid,
79+
test_traversable_it_current,
80+
test_traversable_it_key,
81+
test_traversable_it_next,
82+
test_traversable_it_rewind,
83+
NULL, // invalidate_current
84+
NULL, // get_gc
85+
};
86+
87+
static zend_object_iterator *test_traversable_get_iterator(
88+
zend_class_entry *ce,
89+
zval *object,
90+
int by_ref
91+
) {
92+
test_traversable_it *iterator;
93+
94+
if (by_ref) {
95+
zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
96+
return NULL;
97+
}
98+
99+
iterator = emalloc(sizeof(test_traversable_it));
100+
zend_iterator_init((zend_object_iterator*)iterator);
101+
102+
ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(object));
103+
iterator->intern.funcs = &test_traversable_it_vtable;
104+
ZVAL_LONG(&iterator->current, 0);
105+
106+
return (zend_object_iterator*)iterator;
107+
}
108+
109+
ZEND_METHOD(ZendTest_Iterators_TraversableTest, __construct) {
110+
ZEND_PARSE_PARAMETERS_NONE();
111+
}
112+
113+
ZEND_METHOD(ZendTest_Iterators_TraversableTest, getIterator) {
114+
ZEND_PARSE_PARAMETERS_NONE();
115+
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
116+
}
117+
118+
void zend_test_iterators_init(void) {
119+
traversable_test_ce = register_class_ZendTest_Iterators_TraversableTest(zend_ce_aggregate);
120+
traversable_test_ce->get_iterator = test_traversable_get_iterator;
121+
}

ext/zend_test/iterators.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| Copyright (c) The PHP Group |
4+
+----------------------------------------------------------------------+
5+
| This source file is subject to version 3.01 of the PHP license, |
6+
| that is bundled with this package in the file LICENSE, and is |
7+
| available through the world-wide-web at the following url: |
8+
| https://www.php.net/license/3_01.txt |
9+
| If you did not receive a copy of the PHP license and are unable to |
10+
| obtain it through the world-wide-web, please send a note to |
11+
| license@php.net so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
*/
14+
15+
#ifndef ZEND_TEST_ITERATORS_H
16+
#define ZEND_TEST_ITERATORS_H
17+
18+
void zend_test_iterators_init(void);
19+
20+
#endif
21+

ext/zend_test/iterators.stub.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
/**
4+
* @generate-class-entries static
5+
* @undocumentable
6+
*/
7+
8+
namespace ZendTest\Iterators;
9+
10+
final class TraversableTest implements \IteratorAggregate
11+
{
12+
public function __construct() {}
13+
public function getIterator(): \Iterator {}
14+
}

ext/zend_test/iterators_arginfo.h

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/zend_test/test.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "php_test.h"
2525
#include "observer.h"
2626
#include "fiber.h"
27+
#include "iterators.h"
2728
#include "object_handlers.h"
2829
#include "zend_attributes.h"
2930
#include "zend_enum.h"
@@ -1073,6 +1074,7 @@ PHP_MINIT_FUNCTION(zend_test)
10731074

10741075
zend_test_observer_init(INIT_FUNC_ARGS_PASSTHRU);
10751076
zend_test_fiber_init();
1077+
zend_test_iterators_init();
10761078
zend_test_object_handlers_init();
10771079

10781080
le_throwing_resource = zend_register_list_destructors_ex(le_throwing_resource_dtor, NULL, "throwing resource", module_number);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
Tests that internal iterator's rewind function is called once
3+
--EXTENSIONS--
4+
zend_test
5+
--FILE--
6+
<?php
7+
8+
$subject = new \ZendTest\Iterators\TraversableTest();
9+
$it = $subject->getIterator();
10+
var_dump($it);
11+
foreach ($it as $key => $value) {
12+
echo "{$key} => {$value}\n";
13+
}
14+
?>
15+
--EXPECT--
16+
object(InternalIterator)#3 (0) {
17+
}
18+
TraversableTest::rewind
19+
TraversableTest::valid
20+
TraversableTest::current
21+
TraversableTest::key
22+
0 => 0
23+
TraversableTest::next
24+
TraversableTest::valid
25+
TraversableTest::current
26+
TraversableTest::key
27+
1 => 1
28+
TraversableTest::next
29+
TraversableTest::valid
30+
TraversableTest::current
31+
TraversableTest::key
32+
2 => 2
33+
TraversableTest::next
34+
TraversableTest::valid
35+
TraversableTest::current
36+
TraversableTest::key
37+
3 => 3
38+
TraversableTest::next
39+
TraversableTest::valid
40+
TraversableTest::drop

0 commit comments

Comments
 (0)