mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
Move declare() to the top of the file only, and allow int->float generalization
This commit is contained in:
parent
d6bea5bb1e
commit
00b3e77ead
@ -1,13 +0,0 @@
|
||||
--TEST--
|
||||
RFC example: expected class int and returned integer
|
||||
|
||||
--FILE--
|
||||
<?php
|
||||
function answer(): int {
|
||||
return 42;
|
||||
}
|
||||
|
||||
answer();
|
||||
|
||||
--EXPECTF--
|
||||
Catchable fatal error: Return value of answer() must be an instance of int, integer returned in %s on line %d
|
16
Zend/tests/typehints/scalar_namespace_declaration_order.phpt
Normal file
16
Zend/tests/typehints/scalar_namespace_declaration_order.phpt
Normal file
@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
Scalar type with namespaces proper ordering
|
||||
--FILE--
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace Foo {
|
||||
function add1(int $arg): int {
|
||||
return $arg + 1;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
var_dump(Foo\add1(123));
|
||||
}
|
||||
--EXPECT--
|
||||
int(124)
|
268
Zend/tests/typehints/scalar_strict.phpt
Normal file
268
Zend/tests/typehints/scalar_strict.phpt
Normal file
@ -0,0 +1,268 @@
|
||||
--TEST--
|
||||
Scalar type hint strict mode
|
||||
--FILE--
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
$errnames = [
|
||||
E_NOTICE => 'E_NOTICE',
|
||||
E_WARNING => 'E_WARNING',
|
||||
E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR'
|
||||
];
|
||||
set_error_handler(function (int $errno, string $errmsg, string $file, int $line) use ($errnames) {
|
||||
echo "$errnames[$errno]: $errmsg on line $line\n";
|
||||
return true;
|
||||
});
|
||||
|
||||
$functions = [
|
||||
'int' => function (int $i) { return $i; },
|
||||
'float' => function (float $f) { return $f; },
|
||||
'string' => function (string $s) { return $s; },
|
||||
'bool' => function (bool $b) { return $b; }
|
||||
];
|
||||
|
||||
class Stringable {
|
||||
public function __toString() {
|
||||
return "foobar";
|
||||
}
|
||||
}
|
||||
|
||||
$values = [
|
||||
1,
|
||||
"1",
|
||||
1.0,
|
||||
1.5,
|
||||
"1a",
|
||||
"a",
|
||||
"",
|
||||
PHP_INT_MAX,
|
||||
NAN,
|
||||
TRUE,
|
||||
FALSE,
|
||||
NULL,
|
||||
[],
|
||||
new StdClass,
|
||||
new Stringable,
|
||||
fopen("data:text/plain,foobar", "r")
|
||||
];
|
||||
|
||||
foreach ($functions as $type => $function) {
|
||||
echo PHP_EOL, "Testing '$type' typehint:", PHP_EOL;
|
||||
foreach ($values as $value) {
|
||||
echo "*** Trying ";
|
||||
var_dump($value);
|
||||
var_dump($function($value));
|
||||
}
|
||||
}
|
||||
--EXPECTF--
|
||||
|
||||
Testing 'int' typehint:
|
||||
*** Trying int(1)
|
||||
int(1)
|
||||
*** Trying string(1) "1"
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d and defined on line %d
|
||||
string(1) "1"
|
||||
*** Trying float(1)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type integer, float given, called in %s on line %d and defined on line %d
|
||||
float(1)
|
||||
*** Trying float(1.5)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type integer, float given, called in %s on line %d and defined on line %d
|
||||
float(1.5)
|
||||
*** Trying string(2) "1a"
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d and defined on line %d
|
||||
string(2) "1a"
|
||||
*** Trying string(1) "a"
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d and defined on line %d
|
||||
string(1) "a"
|
||||
*** Trying string(0) ""
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d and defined on line %d
|
||||
string(0) ""
|
||||
*** Trying int(9223372036854775807)
|
||||
int(9223372036854775807)
|
||||
*** Trying float(NAN)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type integer, float given, called in %s on line %d and defined on line %d
|
||||
float(NAN)
|
||||
*** Trying bool(true)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type integer, boolean given, called in %s on line %d and defined on line %d
|
||||
bool(true)
|
||||
*** Trying bool(false)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type integer, boolean given, called in %s on line %d and defined on line %d
|
||||
bool(false)
|
||||
*** Trying NULL
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type integer, null given, called in %s on line %d and defined on line %d
|
||||
NULL
|
||||
*** Trying array(0) {
|
||||
}
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type integer, array given, called in %s on line %d and defined on line %d
|
||||
array(0) {
|
||||
}
|
||||
*** Trying object(stdClass)#6 (0) {
|
||||
}
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type integer, object given, called in %s on line %d and defined on line %d
|
||||
object(stdClass)#6 (0) {
|
||||
}
|
||||
*** Trying object(Stringable)#7 (0) {
|
||||
}
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type integer, object given, called in %s on line %d and defined on line %d
|
||||
object(Stringable)#7 (0) {
|
||||
}
|
||||
*** Trying resource(5) of type (stream)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type integer, resource given, called in %s on line %d and defined on line %d
|
||||
resource(5) of type (stream)
|
||||
|
||||
Testing 'float' typehint:
|
||||
*** Trying int(1)
|
||||
float(1)
|
||||
*** Trying string(1) "1"
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d and defined on line %d
|
||||
string(1) "1"
|
||||
*** Trying float(1)
|
||||
float(1)
|
||||
*** Trying float(1.5)
|
||||
float(1.5)
|
||||
*** Trying string(2) "1a"
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d and defined on line %d
|
||||
string(2) "1a"
|
||||
*** Trying string(1) "a"
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d and defined on line %d
|
||||
string(1) "a"
|
||||
*** Trying string(0) ""
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d and defined on line %d
|
||||
string(0) ""
|
||||
*** Trying int(9223372036854775807)
|
||||
float(9.2233720368548E+18)
|
||||
*** Trying float(NAN)
|
||||
float(NAN)
|
||||
*** Trying bool(true)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type float, boolean given, called in %s on line %d and defined on line %d
|
||||
bool(true)
|
||||
*** Trying bool(false)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type float, boolean given, called in %s on line %d and defined on line %d
|
||||
bool(false)
|
||||
*** Trying NULL
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type float, null given, called in %s on line %d and defined on line %d
|
||||
NULL
|
||||
*** Trying array(0) {
|
||||
}
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type float, array given, called in %s on line %d and defined on line %d
|
||||
array(0) {
|
||||
}
|
||||
*** Trying object(stdClass)#6 (0) {
|
||||
}
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type float, object given, called in %s on line %d and defined on line %d
|
||||
object(stdClass)#6 (0) {
|
||||
}
|
||||
*** Trying object(Stringable)#7 (0) {
|
||||
}
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type float, object given, called in %s on line %d and defined on line %d
|
||||
object(Stringable)#7 (0) {
|
||||
}
|
||||
*** Trying resource(5) of type (stream)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type float, resource given, called in %s on line %d and defined on line %d
|
||||
resource(5) of type (stream)
|
||||
|
||||
Testing 'string' typehint:
|
||||
*** Trying int(1)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type string, integer given, called in %s on line %d and defined on line %d
|
||||
int(1)
|
||||
*** Trying string(1) "1"
|
||||
string(1) "1"
|
||||
*** Trying float(1)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type string, float given, called in %s on line %d and defined on line %d
|
||||
float(1)
|
||||
*** Trying float(1.5)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type string, float given, called in %s on line %d and defined on line %d
|
||||
float(1.5)
|
||||
*** Trying string(2) "1a"
|
||||
string(2) "1a"
|
||||
*** Trying string(1) "a"
|
||||
string(1) "a"
|
||||
*** Trying string(0) ""
|
||||
string(0) ""
|
||||
*** Trying int(9223372036854775807)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type string, integer given, called in %s on line %d and defined on line %d
|
||||
int(9223372036854775807)
|
||||
*** Trying float(NAN)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type string, float given, called in %s on line %d and defined on line %d
|
||||
float(NAN)
|
||||
*** Trying bool(true)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type string, boolean given, called in %s on line %d and defined on line %d
|
||||
bool(true)
|
||||
*** Trying bool(false)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type string, boolean given, called in %s on line %d and defined on line %d
|
||||
bool(false)
|
||||
*** Trying NULL
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type string, null given, called in %s on line %d and defined on line %d
|
||||
NULL
|
||||
*** Trying array(0) {
|
||||
}
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type string, array given, called in %s on line %d and defined on line %d
|
||||
array(0) {
|
||||
}
|
||||
*** Trying object(stdClass)#6 (0) {
|
||||
}
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type string, object given, called in %s on line %d and defined on line %d
|
||||
object(stdClass)#6 (0) {
|
||||
}
|
||||
*** Trying object(Stringable)#7 (0) {
|
||||
}
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type string, object given, called in %s on line %d and defined on line %d
|
||||
object(Stringable)#7 (0) {
|
||||
}
|
||||
*** Trying resource(5) of type (stream)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type string, resource given, called in %s on line %d and defined on line %d
|
||||
resource(5) of type (stream)
|
||||
|
||||
Testing 'bool' typehint:
|
||||
*** Trying int(1)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type boolean, integer given, called in %s on line %d and defined on line %d
|
||||
int(1)
|
||||
*** Trying string(1) "1"
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type boolean, string given, called in %s on line %d and defined on line %d
|
||||
string(1) "1"
|
||||
*** Trying float(1)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type boolean, float given, called in %s on line %d and defined on line %d
|
||||
float(1)
|
||||
*** Trying float(1.5)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type boolean, float given, called in %s on line %d and defined on line %d
|
||||
float(1.5)
|
||||
*** Trying string(2) "1a"
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type boolean, string given, called in %s on line %d and defined on line %d
|
||||
string(2) "1a"
|
||||
*** Trying string(1) "a"
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type boolean, string given, called in %s on line %d and defined on line %d
|
||||
string(1) "a"
|
||||
*** Trying string(0) ""
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type boolean, string given, called in %s on line %d and defined on line %d
|
||||
string(0) ""
|
||||
*** Trying int(9223372036854775807)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type boolean, integer given, called in %s on line %d and defined on line %d
|
||||
int(9223372036854775807)
|
||||
*** Trying float(NAN)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type boolean, float given, called in %s on line %d and defined on line %d
|
||||
float(NAN)
|
||||
*** Trying bool(true)
|
||||
bool(true)
|
||||
*** Trying bool(false)
|
||||
bool(false)
|
||||
*** Trying NULL
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type boolean, null given, called in %s on line %d and defined on line %d
|
||||
NULL
|
||||
*** Trying array(0) {
|
||||
}
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type boolean, array given, called in %s on line %d and defined on line %d
|
||||
array(0) {
|
||||
}
|
||||
*** Trying object(stdClass)#6 (0) {
|
||||
}
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type boolean, object given, called in %s on line %d and defined on line %d
|
||||
object(stdClass)#6 (0) {
|
||||
}
|
||||
*** Trying object(Stringable)#7 (0) {
|
||||
}
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type boolean, object given, called in %s on line %d and defined on line %d
|
||||
object(Stringable)#7 (0) {
|
||||
}
|
||||
*** Trying resource(5) of type (stream)
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type boolean, resource given, called in %s on line %d and defined on line %d
|
||||
resource(5) of type (stream)
|
@ -83,7 +83,7 @@ E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type intege
|
||||
|
||||
Testing 'float' typehint:
|
||||
*** Trying integer value
|
||||
E_RECOVERABLE_ERROR: Argument 1 passed to {closure}() must be of the type float, integer given, called in %s on line 53 and defined on line 19
|
||||
float(1)
|
||||
*** Trying float value
|
||||
float(1)
|
||||
*** Trying string value
|
||||
|
@ -3,17 +3,6 @@ Test nested function calls in strict_types=0 and strict_types=1 modes
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$errored = FALSE;
|
||||
|
||||
set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) use (&$errored) {
|
||||
// skipping errors make testing more practical
|
||||
if ($errno !== E_RECOVERABLE_ERROR) die("Wrong errno");
|
||||
|
||||
echo "Catchable fatal error: $errstr in $errfile on line $errline\n";
|
||||
$errored = TRUE;
|
||||
return TRUE;
|
||||
});
|
||||
|
||||
function takes_int(int $x) {
|
||||
global $errored;
|
||||
if ($errored) {
|
||||
@ -48,68 +37,7 @@ declare(strict_types=0) {
|
||||
}
|
||||
}
|
||||
|
||||
// implicit weak mode code
|
||||
function weak_calls_takes_int() {
|
||||
takes_int(1.0); // should succeed, weak mode
|
||||
}
|
||||
|
||||
class WeakTakesIntCaller {
|
||||
public function call() {
|
||||
takes_int(1.0); // should succeed, weak mode
|
||||
}
|
||||
}
|
||||
|
||||
// Now for the calling!
|
||||
|
||||
// implicit weak mode code
|
||||
strict_calls_takes_int(); // should cause an error: our call to func is weak, but it was declared in strict mode so calls it makes are strict
|
||||
weak_calls_takes_int(); // should succeed
|
||||
explicit_weak_calls_takes_int(); // should succeed
|
||||
(new StrictTakesIntCaller)->call(); // should cause an error
|
||||
(new WeakTakesIntCaller)->call(); // should succeed
|
||||
(new ExplicitWeakTakesIntCaller)->call(); // should succeed
|
||||
|
||||
declare(strict_types=0) {
|
||||
strict_calls_takes_int(); // should cause an error: our call to func is weak, but it was declared in strict mode so calls it makes are strict
|
||||
weak_calls_takes_int(); // should succeed
|
||||
explicit_weak_calls_takes_int(); // should succeed
|
||||
(new StrictTakesIntCaller)->call(); // should cause an error
|
||||
(new WeakTakesIntCaller)->call(); // should succeed
|
||||
(new ExplicitWeakTakesIntCaller)->call(); // should succeed
|
||||
}
|
||||
|
||||
declare(strict_types=1) {
|
||||
strict_calls_takes_int(); // should cause an error
|
||||
weak_calls_takes_int(); // should succeed: our call to func is strict, but it was declared in weak mode so calls it makes are weak
|
||||
explicit_weak_calls_takes_int(); // should succeed
|
||||
(new StrictTakesIntCaller)->call(); // should cause an error
|
||||
(new WeakTakesIntCaller)->call(); // should succeed
|
||||
(new ExplicitWeakTakesIntCaller)->call(); // should succeed
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Catchable fatal error: Argument 1 passed to takes_int() must be of the type integer, float given, called in %sstrict_nested.php on line 26 and defined in %sstrict_nested.php on line 14
|
||||
Failure!
|
||||
Success!
|
||||
Success!
|
||||
Catchable fatal error: Argument 1 passed to takes_int() must be of the type integer, float given, called in %sstrict_nested.php on line 31 and defined in %sstrict_nested.php on line 14
|
||||
Failure!
|
||||
Success!
|
||||
Success!
|
||||
Catchable fatal error: Argument 1 passed to takes_int() must be of the type integer, float given, called in %sstrict_nested.php on line 26 and defined in %sstrict_nested.php on line 14
|
||||
Failure!
|
||||
Success!
|
||||
Success!
|
||||
Catchable fatal error: Argument 1 passed to takes_int() must be of the type integer, float given, called in %sstrict_nested.php on line 31 and defined in %sstrict_nested.php on line 14
|
||||
Failure!
|
||||
Success!
|
||||
Success!
|
||||
Catchable fatal error: Argument 1 passed to takes_int() must be of the type integer, float given, called in %sstrict_nested.php on line 26 and defined in %sstrict_nested.php on line 14
|
||||
Failure!
|
||||
Success!
|
||||
Success!
|
||||
Catchable fatal error: Argument 1 passed to takes_int() must be of the type integer, float given, called in %sstrict_nested.php on line 31 and defined in %sstrict_nested.php on line 14
|
||||
Failure!
|
||||
Success!
|
||||
Success!
|
||||
Fatal error: strict_types declaration must be the very first statement in the script in %s on line %d
|
||||
|
@ -1145,7 +1145,7 @@ static zend_always_inline int zend_parse_arg_double(zval *arg, double *dest, zen
|
||||
*is_null = 0;
|
||||
}
|
||||
|
||||
if (UNEXPECTED(strict && Z_TYPE_P(arg) != IS_DOUBLE && !(check_null && Z_TYPE_P(arg) == IS_NULL))) {
|
||||
if (UNEXPECTED(strict && Z_TYPE_P(arg) != IS_DOUBLE && Z_TYPE_P(arg) != IS_LONG && !(check_null && Z_TYPE_P(arg) == IS_NULL))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3870,7 +3870,22 @@ void zend_compile_declare(zend_ast *ast) /* {{{ */
|
||||
}
|
||||
} else if (zend_string_equals_literal_ci(name, "strict_types")) {
|
||||
zval value_zv;
|
||||
zend_const_expr_to_zval(&value_zv, value_ast);
|
||||
|
||||
/* Strict Hint declaration was already handled during parsing. Here we
|
||||
* only check that it is the first statement in the file. */
|
||||
uint32_t num = CG(active_op_array)->last;
|
||||
while (num > 0 &&
|
||||
(CG(active_op_array)->opcodes[num-1].opcode == ZEND_EXT_STMT ||
|
||||
CG(active_op_array)->opcodes[num-1].opcode == ZEND_TICKS)) {
|
||||
--num;
|
||||
}
|
||||
|
||||
if (num > 0) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "strict_types declaration must be "
|
||||
"the very first statement in the script");
|
||||
}
|
||||
|
||||
zend_const_expr_to_zval(&value_zv, value_ast);
|
||||
|
||||
if (Z_TYPE(value_zv) != IS_LONG || (Z_LVAL(value_zv) != 0 && Z_LVAL(value_zv) != 1)) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "strict_types declaration must have 0 or 1 as its value");
|
||||
|
Loading…
Reference in New Issue
Block a user