Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Zend/tests/nullable_types/001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Nullable argument
--FILE--
<?php
function foo(?array $x) {
echo "ok\n";
}

foo([]);
foo(null);
foo(0);
?>
--EXPECTF--
ok
ok

Catchable fatal error: Argument 1 passed to foo() must be of the type array, integer given, called in %s001.php on line 8 and defined in %s001.php on line 2
17 changes: 17 additions & 0 deletions Zend/tests/nullable_types/002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Nullable argument with default NULL value
--FILE--
<?php
function foo(?array $x = null) {
echo "ok\n";
}

foo([]);
foo(null);
foo(0);
?>
--EXPECTF--
ok
ok

Catchable fatal error: Argument 1 passed to foo() must be of the type array, integer given, called in %s002.php on line 8 and defined in %s002.php on line 2
19 changes: 19 additions & 0 deletions Zend/tests/nullable_types/003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
Nullable return value
--FILE--
<?php
function foo($x) : ?array {
return $x;
}

foo([]);
echo "ok\n";
foo(null);
echo "ok\n";
foo(0);
?>
--EXPECTF--
ok
ok

Catchable fatal error: Return value of foo() must be of the type array, integer returned in %s003.php on line 3
15 changes: 15 additions & 0 deletions Zend/tests/nullable_types/inheritance001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Nullable parameter type inheritance rules (nullable and nullable)
--FILE--
<?php
class A {
function foo(?array $a) {}
}

class B extends A {
function foo(?array $a) {}
}
?>
DONE
--EXPECT--
DONE
16 changes: 16 additions & 0 deletions Zend/tests/nullable_types/inheritance002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Nullable parameter type inheritance rules (nullable and not-nullable)
--FILE--
<?php
class A {
function foo(?array $a) {}
}

class B extends A {
function foo(array $a) {}
}
?>
DONE
--EXPECTF--
Strict Standards: Declaration of B::foo() should be compatible with A::foo(?array $a) in %sinheritance002.php on line 8
DONE
15 changes: 15 additions & 0 deletions Zend/tests/nullable_types/inheritance003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Nullable parameter type inheritance rules (not-nullable and nullable)
--FILE--
<?php
class A {
function foo(array $a) {}
}

class B extends A {
function foo(?array $a) {}
}
?>
DONE
--EXPECT--
DONE
15 changes: 15 additions & 0 deletions Zend/tests/nullable_types/inheritance004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Nullable return type inheritance rules (nullable and nullable)
--FILE--
<?php
class A {
function foo() : ?array { return []; }
}

class B extends A {
function foo() : ?array { return []; }
}
?>
DONE
--EXPECT--
DONE
15 changes: 15 additions & 0 deletions Zend/tests/nullable_types/inheritance005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Nullable return type inheritance rules (nullable and not-nullable)
--FILE--
<?php
class A {
function foo() : ?array { return []; }
}

class B extends A {
function foo() : array { return []; }
}
?>
DONE
--EXPECT--
DONE
15 changes: 15 additions & 0 deletions Zend/tests/nullable_types/inheritance006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Nullable return type inheritance rules (not-nullable and nullable)
--FILE--
<?php
class A {
function foo() : array { return []; }
}

class B extends A {
function foo() : ?array { return []; }
}
?>
DONE
--EXPECTF--
Fatal error: Declaration of B::foo() must be compatible with A::foo(): array in %sinheritance006.php on line 8
18 changes: 9 additions & 9 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -621,12 +621,12 @@ zend_string *zend_resolve_non_class_name(
return zend_string_init(name->val + 1, name->len - 1, 0);
}

if (type == ZEND_NAME_FQ) {
if (type & ZEND_NAME_FQ) {
*is_fully_qualified = 1;
return zend_string_copy(name);
}

if (type == ZEND_NAME_RELATIVE) {
if (type & ZEND_NAME_RELATIVE) {
*is_fully_qualified = 1;
return zend_prefix_with_ns(name);
}
Expand Down Expand Up @@ -683,11 +683,11 @@ zend_string *zend_resolve_class_name(zend_string *name, uint32_t type) /* {{{ */
{
char *compound;

if (type == ZEND_NAME_RELATIVE) {
if (type & ZEND_NAME_RELATIVE) {
return zend_prefix_with_ns(name);
}

if (type == ZEND_NAME_FQ || name->val[0] == '\\') {
if ((type & ZEND_NAME_FQ) || name->val[0] == '\\') {
/* Remove \ prefix (only relevant if this is a string rather than a label) */
if (name->val[0] == '\\') {
name = zend_string_init(name->val + 1, name->len - 1, 0);
Expand Down Expand Up @@ -1902,7 +1902,7 @@ static inline zend_bool zend_is_const_default_class_ref(zend_ast *name_ast) /* {
}

/* Fully qualified names are always default refs */
if (!name_ast->attr) {
if (name_ast->attr & (ZEND_NAME_FQ | ZEND_NAME_RELATIVE)) {
return 1;
}

Expand Down Expand Up @@ -3824,11 +3824,11 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_
arg_infos->pass_by_reference = (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
arg_infos->is_variadic = 0;
arg_infos->type_hint = 0;
arg_infos->allow_null = 0;
arg_infos->allow_null = (return_type_ast->attr & ZEND_TYPE_NULLABLE) != 0;
arg_infos->class_name = NULL;

if (return_type_ast->kind == ZEND_AST_TYPE) {
arg_infos->type_hint = return_type_ast->attr;
arg_infos->type_hint = return_type_ast->attr & ZEND_TYPE_MASK;
} else {
zend_string *class_name = zend_ast_get_str(return_type_ast);

Expand Down Expand Up @@ -3929,10 +3929,10 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_
&& strcasecmp(Z_STRVAL(default_node.u.constant), "NULL") == 0));

op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
arg_info->allow_null = has_null_default;
arg_info->allow_null = (type_ast->attr & ZEND_TYPE_NULLABLE) || has_null_default;

if (type_ast->kind == ZEND_AST_TYPE) {
arg_info->type_hint = type_ast->attr;
arg_info->type_hint = type_ast->attr & ZEND_TYPE_MASK;
if (arg_info->type_hint == IS_ARRAY) {
if (default_ast && !has_null_default
&& Z_TYPE(default_node.u.constant) != IS_ARRAY
Expand Down
9 changes: 6 additions & 3 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -770,9 +770,12 @@ int zend_add_literal(zend_op_array *op_array, zval *zv);
#define ZEND_PARAM_REF (1<<0)
#define ZEND_PARAM_VARIADIC (1<<1)

#define ZEND_NAME_FQ 0
#define ZEND_NAME_NOT_FQ 1
#define ZEND_NAME_RELATIVE 2
#define ZEND_NAME_FQ (1<<0)
#define ZEND_NAME_NOT_FQ (1<<1)
#define ZEND_NAME_RELATIVE (1<<2)

#define ZEND_TYPE_MASK 0xff
#define ZEND_TYPE_NULLABLE (1<<8)

/* unset types */
#define ZEND_UNSET_REG 0
Expand Down
64 changes: 46 additions & 18 deletions Zend/zend_inheritance.c
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,10 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
if (fe_arg_info->pass_by_reference != proto_arg_info->pass_by_reference) {
return 0;
}

if (proto_arg_info->allow_null && !fe_arg_info->allow_null) {
return 0;
}
}

/* check return type compataibility */
Expand All @@ -382,6 +386,9 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
if (!zend_do_perform_type_hint_check(fe, fe->common.arg_info - 1, proto, proto->common.arg_info - 1)) {
return 0;
}
if (fe->common.arg_info[-1].allow_null && !proto->common.arg_info[-1].allow_null) {
return 0;
}
}
return 1;
}
Expand Down Expand Up @@ -423,6 +430,28 @@ static void zend_append_type_hint(smart_str *str, zend_function *fptr, zend_arg_
}
/* }}} */

static zval *zend_get_param_default_value(zend_op_array *op_array, int num) /* {{{ */
{
zend_op *op = op_array->opcodes;
zend_op *end = op + op_array->last;

while (op < end) {
if (op->opcode == ZEND_RECV) {
/* skip */
} else if (op->opcode == ZEND_RECV_INIT) {
if (op->op1.num == num + 1) {
return RT_CONSTANT(op_array, op->op2);
}
} else {
break;
}
op++;
}
return NULL;
}
/* }}} */


static zend_string *zend_get_function_declaration(zend_function *fptr) /* {{{ */
{
smart_str str = {0};
Expand All @@ -449,6 +478,18 @@ static zend_string *zend_get_function_declaration(zend_function *fptr) /* {{{ */
num_args++;
}
for (i = 0; i < num_args;) {
if (arg_info->allow_null && arg_info->type_hint) {
if (fptr->type == ZEND_USER_FUNCTION) {
zval *zv = zend_get_param_default_value(&fptr->op_array, i);

if (i < required || (zv && Z_TYPE_P(zv) != IS_NULL)) {
smart_str_appendc(&str, '?');
}
} else if (i < required) {
smart_str_appendc(&str, '?');
}
}

zend_append_type_hint(&str, fptr, arg_info, 0);

if (arg_info->pass_by_reference) {
Expand All @@ -475,25 +516,9 @@ static zend_string *zend_get_function_declaration(zend_function *fptr) /* {{{ */
if (i >= required && !arg_info->is_variadic) {
smart_str_appends(&str, " = ");
if (fptr->type == ZEND_USER_FUNCTION) {
zend_op *precv = NULL;
{
uint32_t idx = i;
zend_op *op = fptr->op_array.opcodes;
zend_op *end = op + fptr->op_array.last;

++idx;
while (op < end) {
if ((op->opcode == ZEND_RECV || op->opcode == ZEND_RECV_INIT)
&& op->op1.num == (zend_ulong)idx)
{
precv = op;
}
++op;
}
}
if (precv && precv->opcode == ZEND_RECV_INIT && precv->op2_type != IS_UNUSED) {
zval *zv = RT_CONSTANT(&fptr->op_array, precv->op2);
zval *zv = zend_get_param_default_value(&fptr->op_array, i);

if (zv) {
if (Z_TYPE_P(zv) == IS_CONSTANT) {
smart_str_append(&str, Z_STR_P(zv));
} else if (Z_TYPE_P(zv) == IS_FALSE) {
Expand Down Expand Up @@ -535,6 +560,9 @@ static zend_string *zend_get_function_declaration(zend_function *fptr) /* {{{ */

if (fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
smart_str_appends(&str, ": ");
if (fptr->common.arg_info[-1].allow_null) {
smart_str_appendc(&str, '?');
}
zend_append_type_hint(&str, fptr, fptr->common.arg_info - 1, 1);
}
smart_str_0(&str);
Expand Down
6 changes: 4 additions & 2 deletions Zend/zend_language_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,7 @@ parameter:
optional_type:
/* empty */ { $$ = NULL; }
| type { $$ = $1; }
| '?' type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; }
;

type:
Expand All @@ -581,8 +582,9 @@ type:
;

return_type:
/* empty */ { $$ = NULL; }
| ':' type { $$ = $2; }
/* empty */ { $$ = NULL; }
| ':' type { $$ = $2; }
| ':' '?' type { $$ = $3; $$->attr |= ZEND_TYPE_NULLABLE; }
;

argument_list:
Expand Down