Skip to content

Commit ca8e332

Browse files
committed
Add tests to check ZPP handles intersection types for internal functions
1 parent 5024259 commit ca8e332

File tree

5 files changed

+142
-20
lines changed

5 files changed

+142
-20
lines changed

ext/zend_test/test.c

+45-19
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,51 @@ static ZEND_FUNCTION(zend_string_or_stdclass)
207207
}
208208
}
209209

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

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

ext/zend_test/test.stub.php

+4
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ function zend_string_or_stdclass($param): stdClass|string {}
125125
/** @param stdClass|string|null $param */
126126
function zend_string_or_stdclass_or_null($param): stdClass|string|null {}
127127

128+
function zend_intersection_type(Traversable&Countable $v): Traversable&Countable {}
129+
130+
function zend_intersection_type_return(object $v): Traversable&Countable {}
131+
128132
function zend_iterable(iterable $arg1, ?iterable $arg2 = null): void {}
129133

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

ext/zend_test/test_arginfo.h

+13-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
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==
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)