Skip to content

Commit 06030cf

Browse files
committed
Add tests to check ZPP handles intersection types for internal functions
1 parent 4457dba commit 06030cf

File tree

5 files changed

+142
-20
lines changed

5 files changed

+142
-20
lines changed

ext/zend_test/test.c

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,51 @@ static ZEND_FUNCTION(zend_string_or_stdclass)
206206
}
207207
}
208208

209+
/* Tests Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL */
210+
static ZEND_FUNCTION(zend_string_or_stdclass_or_null)
211+
{
212+
zend_string *str;
213+
zend_object *object;
214+
215+
ZEND_PARSE_PARAMETERS_START(1, 1)
216+
Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL(object, zend_standard_class_def, str)
217+
ZEND_PARSE_PARAMETERS_END();
218+
219+
if (str) {
220+
RETURN_STR_COPY(str);
221+
} else if (object) {
222+
RETURN_OBJ_COPY(object);
223+
} else {
224+
RETURN_NULL();
225+
}
226+
}
227+
228+
static ZEND_FUNCTION(zend_intersection_type)
229+
{
230+
zend_object *object;
231+
232+
ZEND_PARSE_PARAMETERS_START(1, 1)
233+
Z_PARAM_OBJ(object)
234+
ZEND_PARSE_PARAMETERS_END();
235+
236+
if (!(instanceof_function(object->ce, zend_ce_countable) && instanceof_function(object->ce, zend_ce_traversable))) {
237+
zend_argument_type_error(1, "must be of type Traversable&Countable, %s given", ZSTR_VAL(object->ce->name));
238+
}
239+
240+
RETURN_OBJ_COPY(object);
241+
}
242+
243+
static ZEND_FUNCTION(zend_intersection_type_return)
244+
{
245+
zend_object *object;
246+
247+
ZEND_PARSE_PARAMETERS_START(1, 1)
248+
Z_PARAM_OBJ(object)
249+
ZEND_PARSE_PARAMETERS_END();
250+
251+
RETURN_OBJ_COPY(object);
252+
}
253+
209254
static ZEND_FUNCTION(zend_test_compile_string)
210255
{
211256
zend_string *source_string = NULL;
@@ -241,25 +286,6 @@ static ZEND_FUNCTION(zend_test_compile_string)
241286
return;
242287
}
243288

244-
/* Tests Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL */
245-
static ZEND_FUNCTION(zend_string_or_stdclass_or_null)
246-
{
247-
zend_string *str;
248-
zend_object *object;
249-
250-
ZEND_PARSE_PARAMETERS_START(1, 1)
251-
Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL(object, zend_standard_class_def, str)
252-
ZEND_PARSE_PARAMETERS_END();
253-
254-
if (str) {
255-
RETURN_STR_COPY(str);
256-
} else if (object) {
257-
RETURN_OBJ_COPY(object);
258-
} else {
259-
RETURN_NULL();
260-
}
261-
}
262-
263289
static ZEND_FUNCTION(zend_weakmap_attach)
264290
{
265291
zval *value;

ext/zend_test/test.stub.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ function zend_string_or_stdclass($param): stdClass|string {}
119119
/** @param stdClass|string|null $param */
120120
function zend_string_or_stdclass_or_null($param): stdClass|string|null {}
121121

122+
function zend_intersection_type(Traversable&Countable $v): Traversable&Countable {}
123+
124+
function zend_intersection_type_return(object $v): Traversable&Countable {}
125+
122126
function zend_iterable(iterable $arg1, ?iterable $arg2 = null): void {}
123127

124128
function zend_weakmap_attach(object $object, mixed $value): bool {}

ext/zend_test/test_arginfo.h

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
Test that internal functions can accept intersection types via ZPP
3+
--EXTENSIONS--
4+
zend_test
5+
spl
6+
--FILE--
7+
<?php
8+
9+
class C implements Countable {
10+
public function count(): int {
11+
return 1;
12+
}
13+
}
14+
15+
class I extends EmptyIterator implements Countable {
16+
public function count(): int {
17+
return 1;
18+
}
19+
}
20+
21+
22+
try {
23+
zend_intersection_type(new EmptyIterator());
24+
} catch (TypeError $e) {
25+
echo $e->getMessage(), PHP_EOL;
26+
}
27+
try {
28+
zend_intersection_type(new C());
29+
} catch (TypeError $e) {
30+
echo $e->getMessage(), PHP_EOL;
31+
}
32+
33+
zend_intersection_type(new I());
34+
35+
?>
36+
==DONE==
37+
--EXPECT--
38+
zend_intersection_type(): Argument #1 ($v) must be of type Traversable&Countable, EmptyIterator given
39+
zend_intersection_type(): Argument #1 ($v) must be of type Traversable&Countable, C given
40+
==DONE==
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
Test that internal functions warn on improper intersection types return
3+
--EXTENSIONS--
4+
zend_test
5+
spl
6+
--FILE--
7+
<?php
8+
9+
class C implements Countable {
10+
public function count(): int {
11+
return 1;
12+
}
13+
}
14+
15+
class I extends EmptyIterator implements Countable {
16+
public function count(): int {
17+
return 1;
18+
}
19+
}
20+
21+
try {
22+
var_dump(zend_intersection_type(new EmptyIterator()));
23+
} catch (TypeError $e) {
24+
echo $e->getMessage(), PHP_EOL;
25+
}
26+
try {
27+
var_dump(zend_intersection_type(new C()));
28+
} catch (TypeError $e) {
29+
echo $e->getMessage(), PHP_EOL;
30+
}
31+
var_dump(zend_intersection_type(new I()));
32+
33+
?>
34+
==DONE==
35+
--EXPECT--
36+
zend_intersection_type(): Argument #1 ($v) must be of type Traversable&Countable, EmptyIterator given
37+
zend_intersection_type(): Argument #1 ($v) must be of type Traversable&Countable, C given
38+
object(I)#2 (0) {
39+
}
40+
==DONE==

0 commit comments

Comments
 (0)