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
This commit is contained in:
George Peter Banyard 2023-09-05 10:36:16 +01:00
commit d7273c5963
10 changed files with 235 additions and 2 deletions

3
NEWS
View File

@ -8,6 +8,9 @@ PHP NEWS
(Girgias)
. Fixed bug GH-12073 (Segfault when freeing incompletely initialized
closures). (ilutov)
. Fixed bug GH-12060 (Internal iterator rewind handler is called twice).
(ju1ius)
- FPM:
. Fixed GH-12077 (PHP 8.3.0RC1 borked socket-close-on-exec.phpt).

View File

@ -623,6 +623,7 @@ ZEND_METHOD(InternalIterator, rewind) {
RETURN_THROWS();
}
intern->rewind_called = 1;
if (!intern->iter->funcs->rewind) {
/* Allow calling rewind() if no iteration has happened yet,
* even if the iterator does not support rewinding. */

View File

@ -4,5 +4,5 @@ PHP_ARG_ENABLE([zend-test],
[Enable zend_test extension])])
if test "$PHP_ZEND_TEST" != "no"; then
PHP_NEW_EXTENSION(zend_test, test.c observer.c fiber.c object_handlers.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
PHP_NEW_EXTENSION(zend_test, test.c observer.c fiber.c iterators.c object_handlers.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
fi

View File

@ -3,6 +3,6 @@
ARG_ENABLE("zend-test", "enable zend_test extension", "no");
if (PHP_ZEND_TEST != "no") {
EXTENSION("zend_test", "test.c observer.c fiber.c object_handlers.c", PHP_ZEND_TEST_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
EXTENSION("zend_test", "test.c observer.c fiber.c iterators.c object_handlers.c", PHP_ZEND_TEST_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
ADD_FLAG("CFLAGS_ZEND_TEST", "/D PHP_ZEND_TEST_EXPORTS ");
}

121
ext/zend_test/iterators.c Normal file
View File

@ -0,0 +1,121 @@
/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "iterators.h"
#include "zend_API.h"
#include "iterators_arginfo.h"
#include <zend_interfaces.h>
#include "php.h"
#define DUMP(s) php_output_write((s), sizeof((s)) - 1)
static zend_class_entry *traversable_test_ce;
// Dummy iterator that yields numbers from 0..4,
// while printing operations to the output buffer
typedef struct {
zend_object_iterator intern;
zval current;
} test_traversable_it;
static test_traversable_it *test_traversable_it_fetch(zend_object_iterator *iter) {
return (test_traversable_it *)iter;
}
static void test_traversable_it_dtor(zend_object_iterator *iter) {
DUMP("TraversableTest::drop\n");
test_traversable_it *iterator = test_traversable_it_fetch(iter);
zval_ptr_dtor(&iterator->intern.data);
}
static void test_traversable_it_rewind(zend_object_iterator *iter) {
DUMP("TraversableTest::rewind\n");
test_traversable_it *iterator = test_traversable_it_fetch(iter);
ZVAL_LONG(&iterator->current, 0);
}
static void test_traversable_it_next(zend_object_iterator *iter) {
DUMP("TraversableTest::next\n");
test_traversable_it *iterator = test_traversable_it_fetch(iter);
ZVAL_LONG(&iterator->current, Z_LVAL(iterator->current) + 1);
}
static int test_traversable_it_valid(zend_object_iterator *iter) {
DUMP("TraversableTest::valid\n");
test_traversable_it *iterator = test_traversable_it_fetch(iter);
if (Z_LVAL(iterator->current) < 4) {
return SUCCESS;
}
return FAILURE;
}
static void test_traversable_it_key(zend_object_iterator *iter, zval *return_value) {
DUMP("TraversableTest::key\n");
test_traversable_it *iterator = test_traversable_it_fetch(iter);
ZVAL_LONG(return_value, Z_LVAL(iterator->current));
}
static zval *test_traversable_it_current(zend_object_iterator *iter) {
DUMP("TraversableTest::current\n");
test_traversable_it *iterator = test_traversable_it_fetch(iter);
return &iterator->current;
}
static const zend_object_iterator_funcs test_traversable_it_vtable = {
test_traversable_it_dtor,
test_traversable_it_valid,
test_traversable_it_current,
test_traversable_it_key,
test_traversable_it_next,
test_traversable_it_rewind,
NULL, // invalidate_current
NULL, // get_gc
};
static zend_object_iterator *test_traversable_get_iterator(
zend_class_entry *ce,
zval *object,
int by_ref
) {
test_traversable_it *iterator;
if (by_ref) {
zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
return NULL;
}
iterator = emalloc(sizeof(test_traversable_it));
zend_iterator_init((zend_object_iterator*)iterator);
ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(object));
iterator->intern.funcs = &test_traversable_it_vtable;
ZVAL_LONG(&iterator->current, 0);
return (zend_object_iterator*)iterator;
}
ZEND_METHOD(ZendTest_Iterators_TraversableTest, __construct) {
ZEND_PARSE_PARAMETERS_NONE();
}
ZEND_METHOD(ZendTest_Iterators_TraversableTest, getIterator) {
ZEND_PARSE_PARAMETERS_NONE();
zend_create_internal_iterator_zval(return_value, ZEND_THIS);
}
void zend_test_iterators_init(void) {
traversable_test_ce = register_class_ZendTest_Iterators_TraversableTest(zend_ce_aggregate);
traversable_test_ce->get_iterator = test_traversable_get_iterator;
}

21
ext/zend_test/iterators.h Normal file
View File

@ -0,0 +1,21 @@
/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_TEST_ITERATORS_H
#define ZEND_TEST_ITERATORS_H
void zend_test_iterators_init(void);
#endif

View File

@ -0,0 +1,14 @@
<?php
/**
* @generate-class-entries static
* @undocumentable
*/
namespace ZendTest\Iterators;
final class TraversableTest implements \IteratorAggregate
{
public function __construct() {}
public function getIterator(): \Iterator {}
}

31
ext/zend_test/iterators_arginfo.h generated Normal file
View File

@ -0,0 +1,31 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: f9558686a7393ddd4ba3302e811f70d4496317ee */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZendTest_Iterators_TraversableTest___construct, 0, 0, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ZendTest_Iterators_TraversableTest_getIterator, 0, 0, Iterator, 0)
ZEND_END_ARG_INFO()
static ZEND_METHOD(ZendTest_Iterators_TraversableTest, __construct);
static ZEND_METHOD(ZendTest_Iterators_TraversableTest, getIterator);
static const zend_function_entry class_ZendTest_Iterators_TraversableTest_methods[] = {
ZEND_ME(ZendTest_Iterators_TraversableTest, __construct, arginfo_class_ZendTest_Iterators_TraversableTest___construct, ZEND_ACC_PUBLIC)
ZEND_ME(ZendTest_Iterators_TraversableTest, getIterator, arginfo_class_ZendTest_Iterators_TraversableTest_getIterator, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
static zend_class_entry *register_class_ZendTest_Iterators_TraversableTest(zend_class_entry *class_entry_IteratorAggregate)
{
zend_class_entry ce, *class_entry;
INIT_NS_CLASS_ENTRY(ce, "ZendTest\\Iterators", "TraversableTest", class_ZendTest_Iterators_TraversableTest_methods);
class_entry = zend_register_internal_class_ex(&ce, NULL);
class_entry->ce_flags |= ZEND_ACC_FINAL;
zend_class_implements(class_entry, 1, class_entry_IteratorAggregate);
return class_entry;
}

View File

@ -24,6 +24,7 @@
#include "php_test.h"
#include "observer.h"
#include "fiber.h"
#include "iterators.h"
#include "object_handlers.h"
#include "zend_attributes.h"
#include "zend_enum.h"
@ -1073,6 +1074,7 @@ PHP_MINIT_FUNCTION(zend_test)
zend_test_observer_init(INIT_FUNC_ARGS_PASSTHRU);
zend_test_fiber_init();
zend_test_iterators_init();
zend_test_object_handlers_init();
le_throwing_resource = zend_register_list_destructors_ex(le_throwing_resource_dtor, NULL, "throwing resource", module_number);

View File

@ -0,0 +1,40 @@
--TEST--
Tests that internal iterator's rewind function is called once
--EXTENSIONS--
zend_test
--FILE--
<?php
$subject = new \ZendTest\Iterators\TraversableTest();
$it = $subject->getIterator();
var_dump($it);
foreach ($it as $key => $value) {
echo "{$key} => {$value}\n";
}
?>
--EXPECT--
object(InternalIterator)#3 (0) {
}
TraversableTest::rewind
TraversableTest::valid
TraversableTest::current
TraversableTest::key
0 => 0
TraversableTest::next
TraversableTest::valid
TraversableTest::current
TraversableTest::key
1 => 1
TraversableTest::next
TraversableTest::valid
TraversableTest::current
TraversableTest::key
2 => 2
TraversableTest::next
TraversableTest::valid
TraversableTest::current
TraversableTest::key
3 => 3
TraversableTest::next
TraversableTest::valid
TraversableTest::drop