diff options
author | Dmitry Stogov <dmitry@zend.com> | 2015-03-21 01:00:00 +0300 |
---|---|---|
committer | Dmitry Stogov <dmitry@zend.com> | 2015-03-21 01:00:00 +0300 |
commit | 430266edfa93e04cf5199d289050ba14866645b3 (patch) | |
tree | 8417c4f5c30fb6da767376d3f6aa568cd3d7ad88 | |
parent | a43a9c91374b3cd9864eea5e7c0b0e2e9dafa2e2 (diff) | |
parent | 1f408af03cf5dcfbcabebb20f162d2ed787b5579 (diff) | |
download | php-git-430266edfa93e04cf5199d289050ba14866645b3.tar.gz |
Merge branch 'scalar_type_hints_v5'
* scalar_type_hints_v5: (65 commits)
Fixed in-place modification of IS_CONST operand
Changed SKIPIF messages
ZPP changed to lazely check for "strict/weak" only if it's really necessary. Cleanup.
cleanup
Fixed return type hint handling for constants
Fixed tests
Imroved ZPP rules (condititins reoredered to prevent duplicate checks)
Fixed comments
Fixed error messages
Improved type hinting:
Fixed white spaces
Add check for maintaining reference all the way through both type and return values
Reduce the number of times that the zval needs to be separated in return type checking to those that are necessary
Add test to ensure namespaced code can't use scalar types as class names
Disallow relative namespace type declarations
Add support and tests for null constant default values. Refactor complex conditionals into an extracted function for clarity and code-reuse
Refactor as to not use call info, but add the flag to the op_array.
Fix severity issues with callbacks, start work porting ZEND_STRLEN opcode to work with strict mode, more refactoring to come
Fix C89 compatibility by moving a misplaced if statement
Refactor gotos into more elaborate ifs to eliminate goto failure
...
76 files changed, 3333 insertions, 526 deletions
diff --git a/Zend/tests/generators/generator_return_without_value.phpt b/Zend/tests/generators/generator_return_without_value.phpt new file mode 100644 index 0000000000..dfea5aa78d --- /dev/null +++ b/Zend/tests/generators/generator_return_without_value.phpt @@ -0,0 +1,43 @@ +--TEST-- +Generators can return without values +--FILE-- +<?php + +function gen() { + yield; + return; +} + +function gen2() { + yield; + return null; +} + +function gen3() { + return; + yield; +} + +function gen4() { + return; + yield; +} + +var_dump(gen()); + +var_dump(gen2()); + +var_dump(gen3()); + +var_dump(gen4()); + +?> +--EXPECTF-- +object(Generator)#%d (0) { +} +object(Generator)#%d (0) { +} +object(Generator)#%d (0) { +} +object(Generator)#%d (0) { +}
\ No newline at end of file diff --git a/Zend/tests/is_a.phpt b/Zend/tests/is_a.phpt index 0a01eb756e..f4161f2390 100644 --- a/Zend/tests/is_a.phpt +++ b/Zend/tests/is_a.phpt @@ -12,21 +12,21 @@ function __autoload($name) { class BASE { } -interface INT { +interface I { } -class A extends BASE implements INT { +class A extends BASE implements I { } $a = new A; var_dump(is_a($a, "B1")); var_dump(is_a($a, "A")); var_dump(is_a($a, "BASE")); -var_dump(is_a($a, "INT")); +var_dump(is_a($a, "I")); var_dump(is_subclass_of($a, "B2")); var_dump(is_subclass_of($a, "A")); var_dump(is_subclass_of($a, "BASE")); -var_dump(is_subclass_of($a, "INT")); +var_dump(is_subclass_of($a, "I")); var_dump(is_subclass_of("X1", "X2")); ?> diff --git a/Zend/tests/return_types/return_reference_separation.phpt b/Zend/tests/return_types/return_reference_separation.phpt new file mode 100644 index 0000000000..2ec0939088 --- /dev/null +++ b/Zend/tests/return_types/return_reference_separation.phpt @@ -0,0 +1,34 @@ +--TEST-- +Return value separation + +--FILE-- +<?php +function test1(&$abc) : string { + return $abc; +} + +function &test2(int $abc) : string { + return $abc; +} + +function &test3(int &$abc) : string { + return $abc; +} + +$a = 123; + +var_dump(test1($a)); +var_dump($a); +var_dump(test2($a)); +var_dump($a); +var_dump(test3($a)); +var_dump($a); + +?> +--EXPECTF-- +string(3) "123" +int(123) +string(3) "123" +int(123) +string(3) "123" +string(3) "123"
\ No newline at end of file diff --git a/Zend/tests/return_types/rfc002.phpt b/Zend/tests/return_types/rfc002.phpt index 7ccef60d3d..2f3ff02b1c 100644 --- a/Zend/tests/return_types/rfc002.phpt +++ b/Zend/tests/return_types/rfc002.phpt @@ -1,5 +1,5 @@ --TEST-- -RFC example: expected class int and returned integer +RFC example: Scalar Types --FILE-- <?php @@ -7,7 +7,7 @@ function answer(): int { return 42; } -answer(); +var_dump(answer()); --EXPECTF-- -Fatal error: Return value of answer() must be an instance of int, integer returned in %s on line %d +int(42) diff --git a/Zend/tests/typehints/default_boolean_hint_values.phpt b/Zend/tests/typehints/default_boolean_hint_values.phpt new file mode 100644 index 0000000000..6ba262848d --- /dev/null +++ b/Zend/tests/typehints/default_boolean_hint_values.phpt @@ -0,0 +1,16 @@ +--TEST-- +Default values for boolean hints should work +--FILE-- +<?php + +function foo(bool $x = true, bool $y = false) { + var_dump($x, $y); +} + + + +foo(); +?> +--EXPECTF-- +bool(true) +bool(false) diff --git a/Zend/tests/typehints/explicit_weak_include_strict.phpt b/Zend/tests/typehints/explicit_weak_include_strict.phpt new file mode 100644 index 0000000000..a5f267cbef --- /dev/null +++ b/Zend/tests/typehints/explicit_weak_include_strict.phpt @@ -0,0 +1,14 @@ +--TEST-- +explicitly strict_types=0 code including strict_types=1 code +--FILE-- +<?php + +declare(strict_types=0); + +// file with strict_types=1 +require 'weak_include_strict_2.inc'; + +// calls within that file should stay strict, despite being included by weak file +?> +--EXPECTF-- +Fatal error: Argument 1 passed to takes_int() must be of the type integer, float given, called in %sweak_include_strict_2.inc on line 9 and defined in %sweak_include_strict_2.inc on line 5 diff --git a/Zend/tests/typehints/internal_function_strict_mode.phpt b/Zend/tests/typehints/internal_function_strict_mode.phpt new file mode 100644 index 0000000000..3e5629af95 --- /dev/null +++ b/Zend/tests/typehints/internal_function_strict_mode.phpt @@ -0,0 +1,35 @@ +--TEST-- +Scalar type hint - internal function strict mode +--FILE-- +<?php +declare(strict_types=1); + +echo "*** Trying Ord With Integer" . PHP_EOL; +try { + var_dump(ord(1)); +} catch (TypeException $e) { + echo "*** Caught " . $e->getMessage() . PHP_EOL; +} + +echo "*** Trying Array Map With Invalid Callback" . PHP_EOL; +try { + array_map([null, "bar"], []); +} catch (TypeException $e) { + echo "*** Caught " . $e->getMessage() . PHP_EOL; +} + +echo "*** Trying Strlen With Float" . PHP_EOL; +try { + var_dump(strlen(1.5)); +} catch (TypeException $e) { + echo "*** Caught " . $e->getMessage() . PHP_EOL; +} + +?> +--EXPECTF-- +*** Trying Ord With Integer +*** Caught ord() expects parameter 1 to be string, integer given +*** Trying Array Map With Invalid Callback +*** Caught array_map() expects parameter 1 to be a valid callback, first array member is not a valid class name or object +*** Trying Strlen With Float +*** Caught strlen() expects parameter 1 to be string, float given
\ No newline at end of file diff --git a/Zend/tests/typehints/scalar_basic.phpt b/Zend/tests/typehints/scalar_basic.phpt new file mode 100644 index 0000000000..08a0121bbe --- /dev/null +++ b/Zend/tests/typehints/scalar_basic.phpt @@ -0,0 +1,277 @@ +--TEST-- +Scalar type hint basics +--FILE-- +<?php + +$errnames = [ + E_NOTICE => 'E_NOTICE', + E_WARNING => 'E_WARNING', +]; +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 PHP_EOL . "*** Trying "; + var_dump($value); + try { + var_dump($function($value)); + } catch (\TypeException $e) { + echo "*** Caught " . $e->getMessage() . PHP_EOL; + } + } +} +echo PHP_EOL . "Done"; +?> +--EXPECTF-- + +Testing 'int' typehint: + +*** Trying int(1) +int(1) + +*** Trying string(1) "1" +int(1) + +*** Trying float(1) +int(1) + +*** Trying float(1.5) +int(1) + +*** Trying string(2) "1a" +E_NOTICE: A non well formed numeric value encountered on line %d +int(1) + +*** Trying string(1) "a" +*** Caught Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d + +*** Trying string(0) "" +*** Caught Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d + +*** Trying int(%d) +int(%d) + +*** Trying float(NAN) +*** Caught Argument 1 passed to {closure}() must be of the type integer, float given, called in %s on line %d + +*** Trying bool(true) +int(1) + +*** Trying bool(false) +int(0) + +*** Trying NULL +*** Caught Argument 1 passed to {closure}() must be of the type integer, null given, called in %s on line %d + +*** Trying array(0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type integer, array given, called in %s on line %d + +*** Trying object(stdClass)#%s (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type integer, object given, called in %s on line %d + +*** Trying object(Stringable)#%s (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type integer, object given, called in %s on line %d + +*** Trying resource(%d) of type (stream) +*** Caught Argument 1 passed to {closure}() must be of the type integer, resource given, called in %s on line %d + +Testing 'float' typehint: + +*** Trying int(1) +float(1) + +*** Trying string(1) "1" +float(1) + +*** Trying float(1) +float(1) + +*** Trying float(1.5) +float(1.5) + +*** Trying string(2) "1a" +E_NOTICE: A non well formed numeric value encountered on line %d +float(1) + +*** Trying string(1) "a" +*** Caught Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d + +*** Trying string(0) "" +*** Caught Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d + +*** Trying int(%d) +float(%s) + +*** Trying float(NAN) +float(NAN) + +*** Trying bool(true) +float(1) + +*** Trying bool(false) +float(0) + +*** Trying NULL +*** Caught Argument 1 passed to {closure}() must be of the type float, null given, called in %s on line %d + +*** Trying array(0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type float, array given, called in %s on line %d + +*** Trying object(stdClass)#%s (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type float, object given, called in %s on line %d + +*** Trying object(Stringable)#%s (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type float, object given, called in %s on line %d + +*** Trying resource(%d) of type (stream) +*** Caught Argument 1 passed to {closure}() must be of the type float, resource given, called in %s on line %d + +Testing 'string' typehint: + +*** Trying int(1) +string(1) "1" + +*** Trying string(1) "1" +string(1) "1" + +*** Trying float(1) +string(1) "1" + +*** Trying float(1.5) +string(3) "1.5" + +*** Trying string(2) "1a" +string(2) "1a" + +*** Trying string(1) "a" +string(1) "a" + +*** Trying string(0) "" +string(0) "" + +*** Trying int(%d) +string(%d) "%d" + +*** Trying float(NAN) +string(3) "NAN" + +*** Trying bool(true) +string(1) "1" + +*** Trying bool(false) +string(0) "" + +*** Trying NULL +*** Caught Argument 1 passed to {closure}() must be of the type string, null given, called in %s on line %d + +*** Trying array(0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type string, array given, called in %s on line %d + +*** Trying object(stdClass)#%s (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type string, object given, called in %s on line %d + +*** Trying object(Stringable)#%s (0) { +} +string(6) "foobar" + +*** Trying resource(%d) of type (stream) +*** Caught Argument 1 passed to {closure}() must be of the type string, resource given, called in %s on line %d + +Testing 'bool' typehint: + +*** Trying int(1) +bool(true) + +*** Trying string(1) "1" +bool(true) + +*** Trying float(1) +bool(true) + +*** Trying float(1.5) +bool(true) + +*** Trying string(2) "1a" +bool(true) + +*** Trying string(1) "a" +bool(true) + +*** Trying string(0) "" +bool(false) + +*** Trying int(%d) +bool(true) + +*** Trying float(NAN) +bool(true) + +*** Trying bool(true) +bool(true) + +*** Trying bool(false) +bool(false) + +*** Trying NULL +*** Caught Argument 1 passed to {closure}() must be of the type boolean, null given, called in %s on line %d + +*** Trying array(0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type boolean, array given, called in %s on line %d + +*** Trying object(stdClass)#%s (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type boolean, object given, called in %s on line %d + +*** Trying object(Stringable)#%s (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type boolean, object given, called in %s on line %d + +*** Trying resource(%d) of type (stream) +*** Caught Argument 1 passed to {closure}() must be of the type boolean, resource given, called in %s on line %d + +Done diff --git a/Zend/tests/typehints/scalar_constant_defaults.phpt b/Zend/tests/typehints/scalar_constant_defaults.phpt new file mode 100644 index 0000000000..4441022e16 --- /dev/null +++ b/Zend/tests/typehints/scalar_constant_defaults.phpt @@ -0,0 +1,83 @@ +--TEST-- +Scalar type hint - default via constants +--FILE-- +<?php + +const INT_VAL = 10; +const FLOAT_VAL = 10.5; +const STRING_VAL = "this is a test"; +const INT_ADD_VAL = 10 + 15; +const FLOAT_ADD_VAL = 10.5 + 0.2; +const STRING_ADD_VAL = "this" . " is a test"; +const NULL_VAL = null; + +function int_val(int $a = INT_VAL): int { + return $a; +} + +function float_val(float $a = FLOAT_VAL): float { + return $a; +} + +function string_val(string $a = STRING_VAL): string { + return $a; +} + +function int_add_val(int $a = INT_ADD_VAL): int { + return $a; +} + +function float_add_val(float $a = FLOAT_ADD_VAL): float { + return $a; +} + +function string_add_val(string $a = STRING_ADD_VAL): string { + return $a; +} + +function int_val_default_null(int $a = NULL_VAL) { + return $a; +} + +echo "Testing int val" . PHP_EOL; +var_dump(int_val()); + +echo "Testing float val" . PHP_EOL; +var_dump(float_val()); + +echo "Testing string val" . PHP_EOL; +var_dump(string_val()); + +echo "Testing int add val" . PHP_EOL; +var_dump(int_add_val()); + +echo "Testing float add val" . PHP_EOL; +var_dump(float_add_val()); + +echo "Testing string add val" . PHP_EOL; +var_dump(string_add_val()); + +echo "Testing int with default null constant" . PHP_EOL; +var_dump(int_val_default_null()); + +echo "Testing int with null null constant" . PHP_EOL; +var_dump(int_val_default_null(null)); + +?> +--EXPECTF-- +Testing int val +int(10) +Testing float val +float(10.5) +Testing string val +string(14) "this is a test" +Testing int add val +int(25) +Testing float add val +float(10.7) +Testing string add val +string(14) "this is a test" +Testing int with default null constant +NULL +Testing int with null null constant +NULL
\ No newline at end of file diff --git a/Zend/tests/typehints/scalar_constant_defaults_error.phpt b/Zend/tests/typehints/scalar_constant_defaults_error.phpt new file mode 100644 index 0000000000..53938d724f --- /dev/null +++ b/Zend/tests/typehints/scalar_constant_defaults_error.phpt @@ -0,0 +1,16 @@ +--TEST-- +Scalar type hint - default via constants - error condition +--FILE-- +<?php + +const STRING_VAL = "test"; + +function int_val(int $a = STRING_VAL): int { + return $a; +} + +var_dump(int_val()); + +?> +--EXPECTF-- +Fatal error: Argument 1 passed to int_val() must be of the type integer, string given, called in %s on line %d and defined in %s on line %d
\ No newline at end of file diff --git a/Zend/tests/typehints/scalar_none.phpt b/Zend/tests/typehints/scalar_none.phpt new file mode 100644 index 0000000000..af6d8c8dfa --- /dev/null +++ b/Zend/tests/typehints/scalar_none.phpt @@ -0,0 +1,54 @@ +--TEST-- +Scalar type hint missing parameters +--FILE-- +<?php + +$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; }, + 'int nullable' => function (int $i = NULL) { return $i; }, + 'float nullable' => function (float $f = NULL) { return $f; }, + 'string nullable' => function (string $s = NULL) { return $s; }, + 'bool nullable' => function (bool $b = NULL) { return $b; } +]; + +foreach ($functions as $type => $function) { + echo "Testing $type:", PHP_EOL; + try { + var_dump($function()); + } catch (TypeException $e) { + echo "*** Caught " . $e->getMessage() . PHP_EOL; + } +} +echo PHP_EOL . "Done"; +--EXPECTF-- +Testing int: +*** Caught Argument 1 passed to {closure}() must be of the type integer, none given, called in %s on line %d +Testing float: +*** Caught Argument 1 passed to {closure}() must be of the type float, none given, called in %s on line %d +Testing string: +*** Caught Argument 1 passed to {closure}() must be of the type string, none given, called in %s on line %d +Testing bool: +*** Caught Argument 1 passed to {closure}() must be of the type boolean, none given, called in %s on line %d +Testing int nullable: +NULL +Testing float nullable: +NULL +Testing string nullable: +NULL +Testing bool nullable: +NULL + +Done diff --git a/Zend/tests/typehints/scalar_null.phpt b/Zend/tests/typehints/scalar_null.phpt new file mode 100644 index 0000000000..e149388ef0 --- /dev/null +++ b/Zend/tests/typehints/scalar_null.phpt @@ -0,0 +1,56 @@ +--TEST-- +Scalar type hint nullability +--FILE-- +<?php + +$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; }, + 'int nullable' => function (int $i = NULL) { return $i; }, + 'float nullable' => function (float $f = NULL) { return $f; }, + 'string nullable' => function (string $s = NULL) { return $s; }, + 'bool nullable' => function (bool $b = NULL) { return $b; } +]; + +foreach ($functions as $type => $function) { + echo "Testing $type:", PHP_EOL; + try { + var_dump($function(null)); + } catch (TypeException $e) { + echo "*** Caught " . $e->getMessage() . PHP_EOL; + } +} + +echo PHP_EOL . "Done"; +?> +--EXPECTF-- +Testing int: +*** Caught Argument 1 passed to {closure}() must be of the type integer, null given, called in %s on line %d +Testing float: +*** Caught Argument 1 passed to {closure}() must be of the type float, null given, called in %s on line %d +Testing string: +*** Caught Argument 1 passed to {closure}() must be of the type string, null given, called in %s on line %d +Testing bool: +*** Caught Argument 1 passed to {closure}() must be of the type boolean, null given, called in %s on line %d +Testing int nullable: +NULL +Testing float nullable: +NULL +Testing string nullable: +NULL +Testing bool nullable: +NULL + +Done diff --git a/Zend/tests/typehints/scalar_relative_typehint_disallowed.phpt b/Zend/tests/typehints/scalar_relative_typehint_disallowed.phpt new file mode 100644 index 0000000000..30d2bd8b74 --- /dev/null +++ b/Zend/tests/typehints/scalar_relative_typehint_disallowed.phpt @@ -0,0 +1,14 @@ +--TEST-- +Scalar type hint - disallow relative typehints +--FILE-- +<?php + +function foo(bar\int $a): int { + return $a; +} + +foo(10); + +?> +--EXPECTF-- +Fatal error: "bar\int" cannot be used as a type declaration in %s on line %d
\ No newline at end of file diff --git a/Zend/tests/typehints/scalar_reserved2.phpt b/Zend/tests/typehints/scalar_reserved2.phpt new file mode 100644 index 0000000000..8ed4102497 --- /dev/null +++ b/Zend/tests/typehints/scalar_reserved2.phpt @@ -0,0 +1,8 @@ +--TEST-- +Scalar type hint names cannot be used as class, trait or interface names (2) +--FILE-- +<?php + +class int {} +--EXPECTF-- +Fatal error: "int" cannot be used as a class name in %s on line %d diff --git a/Zend/tests/typehints/scalar_reserved2_class_alias.phpt b/Zend/tests/typehints/scalar_reserved2_class_alias.phpt new file mode 100644 index 0000000000..2348f76df8 --- /dev/null +++ b/Zend/tests/typehints/scalar_reserved2_class_alias.phpt @@ -0,0 +1,9 @@ +--TEST-- +Scalar type hint names cannot be used as class, trait or interface names (2) - class_alias +--FILE-- +<?php + +class foobar {} +class_alias("foobar", "int"); +--EXPECTF-- +Fatal error: "int" cannot be used as a class name in %s on line %d diff --git a/Zend/tests/typehints/scalar_reserved2_use.phpt b/Zend/tests/typehints/scalar_reserved2_use.phpt new file mode 100644 index 0000000000..b7a1baed36 --- /dev/null +++ b/Zend/tests/typehints/scalar_reserved2_use.phpt @@ -0,0 +1,8 @@ +--TEST-- +Scalar type hint names cannot be used as class, trait or interface names (2) - use +--FILE-- +<?php + +use foobar as int; +--EXPECTF-- +Fatal error: "int" cannot be used as a class name in %s on line %d diff --git a/Zend/tests/typehints/scalar_reserved3.phpt b/Zend/tests/typehints/scalar_reserved3.phpt new file mode 100644 index 0000000000..00750955b7 --- /dev/null +++ b/Zend/tests/typehints/scalar_reserved3.phpt @@ -0,0 +1,8 @@ +--TEST-- +Scalar type hint names cannot be used as class, trait or interface names (3) +--FILE-- +<?php + +class float {} +--EXPECTF-- +Fatal error: "float" cannot be used as a class name in %s on line %d diff --git a/Zend/tests/typehints/scalar_reserved3_class_alias.phpt b/Zend/tests/typehints/scalar_reserved3_class_alias.phpt new file mode 100644 index 0000000000..05438b579c --- /dev/null +++ b/Zend/tests/typehints/scalar_reserved3_class_alias.phpt @@ -0,0 +1,9 @@ +--TEST-- +Scalar type hint names cannot be used as class, trait or interface names (3) - class_alias +--FILE-- +<?php + +class foobar {} +class_alias("foobar", "float"); +--EXPECTF-- +Fatal error: "float" cannot be used as a class name in %s on line %d diff --git a/Zend/tests/typehints/scalar_reserved3_use.phpt b/Zend/tests/typehints/scalar_reserved3_use.phpt new file mode 100644 index 0000000000..6f52e060ff --- /dev/null +++ b/Zend/tests/typehints/scalar_reserved3_use.phpt @@ -0,0 +1,8 @@ +--TEST-- +Scalar type hint names cannot be used as class, trait or interface names (3) - use +--FILE-- +<?php + +use foobar as float; +--EXPECTF-- +Fatal error: "float" cannot be used as a class name in %s on line %d diff --git a/Zend/tests/typehints/scalar_reserved4.phpt b/Zend/tests/typehints/scalar_reserved4.phpt new file mode 100644 index 0000000000..2e92514b8b --- /dev/null +++ b/Zend/tests/typehints/scalar_reserved4.phpt @@ -0,0 +1,8 @@ +--TEST-- +Scalar type hint names cannot be used as class, trait or interface names (4) +--FILE-- +<?php + +class string {} +--EXPECTF-- +Fatal error: "string" cannot be used as a class name in %s on line %d diff --git a/Zend/tests/typehints/scalar_reserved4_class_alias.phpt b/Zend/tests/typehints/scalar_reserved4_class_alias.phpt new file mode 100644 index 0000000000..ff7c6adc79 --- /dev/null +++ b/Zend/tests/typehints/scalar_reserved4_class_alias.phpt @@ -0,0 +1,9 @@ +--TEST-- +Scalar type hint names cannot be used as class, trait or interface names (4) - class_alias +--FILE-- +<?php + +class foobar {} +class_alias("foobar", "string"); +--EXPECTF-- +Fatal error: "string" cannot be used as a class name in %s on line %d diff --git a/Zend/tests/typehints/scalar_reserved4_use.phpt b/Zend/tests/typehints/scalar_reserved4_use.phpt new file mode 100644 index 0000000000..1d7e4039d8 --- /dev/null +++ b/Zend/tests/typehints/scalar_reserved4_use.phpt @@ -0,0 +1,8 @@ +--TEST-- +Scalar type hint names cannot be used as class, trait or interface names (4) - use +--FILE-- +<?php + +use foobar as string; +--EXPECTF-- +Fatal error: "string" cannot be used as a class name in %s on line %d diff --git a/Zend/tests/typehints/scalar_reserved6.phpt b/Zend/tests/typehints/scalar_reserved6.phpt new file mode 100644 index 0000000000..46b8c730e9 --- /dev/null +++ b/Zend/tests/typehints/scalar_reserved6.phpt @@ -0,0 +1,8 @@ +--TEST-- +Scalar type hint names cannot be used as class, trait or interface names (6) +--FILE-- +<?php + +class bool {} +--EXPECTF-- +Fatal error: "bool" cannot be used as a class name in %s on line %d diff --git a/Zend/tests/typehints/scalar_reserved6_class_alias.phpt b/Zend/tests/typehints/scalar_reserved6_class_alias.phpt new file mode 100644 index 0000000000..4a4c824eb6 --- /dev/null +++ b/Zend/tests/typehints/scalar_reserved6_class_alias.phpt @@ -0,0 +1,9 @@ +--TEST-- +Scalar type hint names cannot be used as class, trait or interface names (6) - class_alias +--FILE-- +<?php + +class foobar {} +class_alias("foobar", "bool"); +--EXPECTF-- +Fatal error: "bool" cannot be used as a class name in %s on line %d diff --git a/Zend/tests/typehints/scalar_reserved6_use.phpt b/Zend/tests/typehints/scalar_reserved6_use.phpt new file mode 100644 index 0000000000..6a6cb7a34c --- /dev/null +++ b/Zend/tests/typehints/scalar_reserved6_use.phpt @@ -0,0 +1,8 @@ +--TEST-- +Scalar type hint names cannot be used as class, trait or interface names (6) - use +--FILE-- +<?php + +use foobar as bool; +--EXPECTF-- +Fatal error: "bool" cannot be used as a class name in %s on line %d diff --git a/Zend/tests/typehints/scalar_reserved7.phpt b/Zend/tests/typehints/scalar_reserved7.phpt new file mode 100644 index 0000000000..330235d9db --- /dev/null +++ b/Zend/tests/typehints/scalar_reserved7.phpt @@ -0,0 +1,9 @@ +--TEST-- +Scalar type hint names cannot be used as class, trait or interface names (7) +--FILE-- +<?php +namespace foo; + +class int {} +--EXPECTF-- +Fatal error: "int" cannot be used as a class name in %s on line %d diff --git a/Zend/tests/typehints/scalar_return_basic.phpt b/Zend/tests/typehints/scalar_return_basic.phpt new file mode 100644 index 0000000000..76b01a8c89 --- /dev/null +++ b/Zend/tests/typehints/scalar_return_basic.phpt @@ -0,0 +1,216 @@ +--TEST-- +Return scalar type hint basics +--SKIPIF-- +<?php if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platform only"); ?> +--FILE-- +<?php + +$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 ($i): int { return $i; }, + 'float' => function ($f): float { return $f; }, + 'string' => function ($s): string { return $s; }, + 'bool' => function ($b): bool { 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); + try { + var_dump($function($value)); + } catch (TypeException $e) { + echo "*** Caught " . $e->getMessage() . PHP_EOL; + } + } +} + +echo PHP_EOL . "Done"; +?> +--EXPECTF-- +Testing 'int' typehint: +*** Trying int(1) +int(1) +*** Trying string(1) "1" +int(1) +*** Trying float(1) +int(1) +*** Trying float(1.5) +int(1) +*** Trying string(2) "1a" +E_NOTICE: A non well formed numeric value encountered on line %d +int(1) +*** Trying string(1) "a" +*** Caught Return value of {closure}() must be of the type integer, string returned in %s on line %d +*** Trying string(0) "" +*** Caught Return value of {closure}() must be of the type integer, string returned in %s on line %d +*** Trying int(2147483647) +int(2147483647) +*** Trying float(NAN) +*** Caught Return value of {closure}() must be of the type integer, float returned in %s on line %d +*** Trying bool(true) +int(1) +*** Trying bool(false) +int(0) +*** Trying NULL +*** Caught Return value of {closure}() must be of the type integer, null returned in %s on line %d +*** Trying array(0) { +} +*** Caught Return value of {closure}() must be of the type integer, array returned in %s on line %d +*** Trying object(stdClass)#6 (0) { +} +*** Caught Return value of {closure}() must be of the type integer, object returned in %s on line %d +*** Trying object(Stringable)#7 (0) { +} +*** Caught Return value of {closure}() must be of the type integer, object returned in %s on line %d +*** Trying resource(5) of type (stream) +*** Caught Return value of {closure}() must be of the type integer, resource returned in %s on line %d + +Testing 'float' typehint: +*** Trying int(1) +float(1) +*** Trying string(1) "1" +float(1) +*** Trying float(1) +float(1) +*** Trying float(1.5) +float(1.5) +*** Trying string(2) "1a" +E_NOTICE: A non well formed numeric value encountered on line %d +float(1) +*** Trying string(1) "a" +*** Caught Return value of {closure}() must be of the type float, string returned in %s on line %d +*** Trying string(0) "" +*** Caught Return value of {closure}() must be of the type float, string returned in %s on line %d +*** Trying int(2147483647) +float(2147483647) +*** Trying float(NAN) +float(NAN) +*** Trying bool(true) +float(1) +*** Trying bool(false) +float(0) +*** Trying NULL +*** Caught Return value of {closure}() must be of the type float, null returned in %s on line %d +*** Trying array(0) { +} +*** Caught Return value of {closure}() must be of the type float, array returned in %s on line %d +*** Trying object(stdClass)#6 (0) { +} +*** Caught Return value of {closure}() must be of the type float, object returned in %s on line %d +*** Trying object(Stringable)#7 (0) { +} +*** Caught Return value of {closure}() must be of the type float, object returned in %s on line %d +*** Trying resource(5) of type (stream) +*** Caught Return value of {closure}() must be of the type float, resource returned in %s on line %d + +Testing 'string' typehint: +*** Trying int(1) +string(1) "1" +*** Trying string(1) "1" +string(1) "1" +*** Trying float(1) +string(1) "1" +*** Trying float(1.5) +string(3) "1.5" +*** Trying string(2) "1a" +string(2) "1a" +*** Trying string(1) "a" +string(1) "a" +*** Trying string(0) "" +string(0) "" +*** Trying int(2147483647) +string(10) "2147483647" +*** Trying float(NAN) +string(3) "NAN" +*** Trying bool(true) +string(1) "1" +*** Trying bool(false) +string(0) "" +*** Trying NULL +*** Caught Return value of {closure}() must be of the type string, null returned in %s on line %d +*** Trying array(0) { +} +*** Caught Return value of {closure}() must be of the type string, array returned in %s on line %d +*** Trying object(stdClass)#6 (0) { +} +*** Caught Return value of {closure}() must be of the type string, object returned in %s on line %d +*** Trying object(Stringable)#7 (0) { +} +string(6) "foobar" +*** Trying resource(5) of type (stream) +*** Caught Return value of {closure}() must be of the type string, resource returned in %s on line %d + +Testing 'bool' typehint: +*** Trying int(1) +bool(true) +*** Trying string(1) "1" +bool(true) +*** Trying float(1) +bool(true) +*** Trying float(1.5) +bool(true) +*** Trying string(2) "1a" +bool(true) +*** Trying string(1) "a" +bool(true) +*** Trying string(0) "" +bool(false) +*** Trying int(2147483647) +bool(true) +*** Trying float(NAN) +bool(true) +*** Trying bool(true) +bool(true) +*** Trying bool(false) +bool(false) +*** Trying NULL +*** Caught Return value of {closure}() must be of the type boolean, null returned in %s on line %d +*** Trying array(0) { +} +*** Caught Return value of {closure}() must be of the type boolean, array returned in %s on line %d +*** Trying object(stdClass)#6 (0) { +} +*** Caught Return value of {closure}() must be of the type boolean, object returned in %s on line %d +*** Trying object(Stringable)#7 (0) { +} +*** Caught Return value of {closure}() must be of the type boolean, object returned in %s on line %d +*** Trying resource(5) of type (stream) +*** Caught Return value of {closure}() must be of the type boolean, resource returned in %s on line %d + +Done diff --git a/Zend/tests/typehints/scalar_return_basic_64bit.phpt b/Zend/tests/typehints/scalar_return_basic_64bit.phpt new file mode 100644 index 0000000000..edb5231dc0 --- /dev/null +++ b/Zend/tests/typehints/scalar_return_basic_64bit.phpt @@ -0,0 +1,216 @@ +--TEST-- +Return scalar type hint basics +--SKIPIF-- +<?php if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only"); ?> +--FILE-- +<?php + +$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 ($i): int { return $i; }, + 'float' => function ($f): float { return $f; }, + 'string' => function ($s): string { return $s; }, + 'bool' => function ($b): bool { 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); + try { + var_dump($function($value)); + } catch (TypeException $e) { + echo "*** Caught " . $e->getMessage() . PHP_EOL; + } + } +} + +echo PHP_EOL . "Done"; +?> +--EXPECTF-- +Testing 'int' typehint: +*** Trying int(1) +int(1) +*** Trying string(1) "1" +int(1) +*** Trying float(1) +int(1) +*** Trying float(1.5) +int(1) +*** Trying string(2) "1a" +E_NOTICE: A non well formed numeric value encountered on line %d +int(1) +*** Trying string(1) "a" +*** Caught Return value of {closure}() must be of the type integer, string returned in %s on line %d +*** Trying string(0) "" +*** Caught Return value of {closure}() must be of the type integer, string returned in %s on line %d +*** Trying int(9223372036854775807) +int(9223372036854775807) +*** Trying float(NAN) +*** Caught Return value of {closure}() must be of the type integer, float returned in %s on line %d +*** Trying bool(true) +int(1) +*** Trying bool(false) +int(0) +*** Trying NULL +*** Caught Return value of {closure}() must be of the type integer, null returned in %s on line %d +*** Trying array(0) { +} +*** Caught Return value of {closure}() must be of the type integer, array returned in %s on line %d +*** Trying object(stdClass)#6 (0) { +} +*** Caught Return value of {closure}() must be of the type integer, object returned in %s on line %d +*** Trying object(Stringable)#7 (0) { +} +*** Caught Return value of {closure}() must be of the type integer, object returned in %s on line %d +*** Trying resource(5) of type (stream) +*** Caught Return value of {closure}() must be of the type integer, resource returned in %s on line %d + +Testing 'float' typehint: +*** Trying int(1) +float(1) +*** Trying string(1) "1" +float(1) +*** Trying float(1) +float(1) +*** Trying float(1.5) +float(1.5) +*** Trying string(2) "1a" +E_NOTICE: A non well formed numeric value encountered on line %d +float(1) +*** Trying string(1) "a" +*** Caught Return value of {closure}() must be of the type float, string returned in %s on line %d +*** Trying string(0) "" +*** Caught Return value of {closure}() must be of the type float, string returned in %s on line %d +*** Trying int(9223372036854775807) +float(9.2233720368548E+18) +*** Trying float(NAN) +float(NAN) +*** Trying bool(true) +float(1) +*** Trying bool(false) +float(0) +*** Trying NULL +*** Caught Return value of {closure}() must be of the type float, null returned in %s on line %d +*** Trying array(0) { +} +*** Caught Return value of {closure}() must be of the type float, array returned in %s on line %d +*** Trying object(stdClass)#6 (0) { +} +*** Caught Return value of {closure}() must be of the type float, object returned in %s on line %d +*** Trying object(Stringable)#7 (0) { +} +*** Caught Return value of {closure}() must be of the type float, object returned in %s on line %d +*** Trying resource(5) of type (stream) +*** Caught Return value of {closure}() must be of the type float, resource returned in %s on line %d + +Testing 'string' typehint: +*** Trying int(1) +string(1) "1" +*** Trying string(1) "1" +string(1) "1" +*** Trying float(1) +string(1) "1" +*** Trying float(1.5) +string(3) "1.5" +*** Trying string(2) "1a" +string(2) "1a" +*** Trying string(1) "a" +string(1) "a" +*** Trying string(0) "" +string(0) "" +*** Trying int(9223372036854775807) +string(19) "9223372036854775807" +*** Trying float(NAN) +string(3) "NAN" +*** Trying bool(true) +string(1) "1" +*** Trying bool(false) +string(0) "" +*** Trying NULL +*** Caught Return value of {closure}() must be of the type string, null returned in %s on line %d +*** Trying array(0) { +} +*** Caught Return value of {closure}() must be of the type string, array returned in %s on line %d +*** Trying object(stdClass)#6 (0) { +} +*** Caught Return value of {closure}() must be of the type string, object returned in %s on line %d +*** Trying object(Stringable)#7 (0) { +} +string(6) "foobar" +*** Trying resource(5) of type (stream) +*** Caught Return value of {closure}() must be of the type string, resource returned in %s on line %d + +Testing 'bool' typehint: +*** Trying int(1) +bool(true) +*** Trying string(1) "1" +bool(true) +*** Trying float(1) +bool(true) +*** Trying float(1.5) +bool(true) +*** Trying string(2) "1a" +bool(true) +*** Trying string(1) "a" +bool(true) +*** Trying string(0) "" +bool(false) +*** Trying int(9223372036854775807) +bool(true) +*** Trying float(NAN) +bool(true) +*** Trying bool(true) +bool(true) +*** Trying bool(false) +bool(false) +*** Trying NULL +*** Caught Return value of {closure}() must be of the type boolean, null returned in %s on line %d +*** Trying array(0) { +} +*** Caught Return value of {closure}() must be of the type boolean, array returned in %s on line %d +*** Trying object(stdClass)#6 (0) { +} +*** Caught Return value of {closure}() must be of the type boolean, object returned in %s on line %d +*** Trying object(Stringable)#7 (0) { +} +*** Caught Return value of {closure}() must be of the type boolean, object returned in %s on line %d +*** Trying resource(5) of type (stream) +*** Caught Return value of {closure}() must be of the type boolean, resource returned in %s on line %d + +Done diff --git a/Zend/tests/typehints/scalar_strict.phpt b/Zend/tests/typehints/scalar_strict.phpt new file mode 100644 index 0000000000..e0f89f95b1 --- /dev/null +++ b/Zend/tests/typehints/scalar_strict.phpt @@ -0,0 +1,279 @@ +--TEST-- +Scalar type hint strict mode +--SKIPIF-- +<?php if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platform only"); ?> +--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 PHP_EOL . "*** Trying "; + var_dump($value); + try { + var_dump($function($value)); + } catch (TypeException $e) { + echo "*** Caught " . $e->getMessage() . PHP_EOL; + } + } +} + +echo PHP_EOL . "Done"; +?> +--EXPECTF-- +Testing 'int' typehint: + +*** Trying int(1) +int(1) + +*** Trying string(1) "1" +*** Caught Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d + +*** Trying float(1) +*** Caught Argument 1 passed to {closure}() must be of the type integer, float given, called in %s on line %d + +*** Trying float(1.5) +*** Caught Argument 1 passed to {closure}() must be of the type integer, float given, called in %s on line %d + +*** Trying string(2) "1a" +*** Caught Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d + +*** Trying string(1) "a" +*** Caught Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d + +*** Trying string(0) "" +*** Caught Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d + +*** Trying int(2147483647) +int(2147483647) + +*** Trying float(NAN) +*** Caught Argument 1 passed to {closure}() must be of the type integer, float given, called in %s on line %d + +*** Trying bool(true) +*** Caught Argument 1 passed to {closure}() must be of the type integer, boolean given, called in %s on line %d + +*** Trying bool(false) +*** Caught Argument 1 passed to {closure}() must be of the type integer, boolean given, called in %s on line %d + +*** Trying NULL +*** Caught Argument 1 passed to {closure}() must be of the type integer, null given, called in %s on line %d + +*** Trying array(0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type integer, array given, called in %s on line %d + +*** Trying object(stdClass)#6 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type integer, object given, called in %s on line %d + +*** Trying object(Stringable)#7 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type integer, object given, called in %s on line %d + +*** Trying resource(5) of type (stream) +*** Caught Argument 1 passed to {closure}() must be of the type integer, resource given, called in %s on line %d + +Testing 'float' typehint: + +*** Trying int(1) +float(1) + +*** Trying string(1) "1" +*** Caught Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d + +*** Trying float(1) +float(1) + +*** Trying float(1.5) +float(1.5) + +*** Trying string(2) "1a" +*** Caught Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d + +*** Trying string(1) "a" +*** Caught Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d + +*** Trying string(0) "" +*** Caught Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d + +*** Trying int(2147483647) +float(2147483647) + +*** Trying float(NAN) +float(NAN) + +*** Trying bool(true) +*** Caught Argument 1 passed to {closure}() must be of the type float, boolean given, called in %s on line %d + +*** Trying bool(false) +*** Caught Argument 1 passed to {closure}() must be of the type float, boolean given, called in %s on line %d + +*** Trying NULL +*** Caught Argument 1 passed to {closure}() must be of the type float, null given, called in %s on line %d + +*** Trying array(0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type float, array given, called in %s on line %d + +*** Trying object(stdClass)#6 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type float, object given, called in %s on line %d + +*** Trying object(Stringable)#7 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type float, object given, called in %s on line %d + +*** Trying resource(5) of type (stream) +*** Caught Argument 1 passed to {closure}() must be of the type float, resource given, called in %s on line %d + +Testing 'string' typehint: + +*** Trying int(1) +*** Caught Argument 1 passed to {closure}() must be of the type string, integer given, called in %s on line %d + +*** Trying string(1) "1" +string(1) "1" + +*** Trying float(1) +*** Caught Argument 1 passed to {closure}() must be of the type string, float given, called in %s on line %d + +*** Trying float(1.5) +*** Caught Argument 1 passed to {closure}() must be of the type string, float given, called in %s on line %d + +*** Trying string(2) "1a" +string(2) "1a" + +*** Trying string(1) "a" +string(1) "a" + +*** Trying string(0) "" +string(0) "" + +*** Trying int(2147483647) +*** Caught Argument 1 passed to {closure}() must be of the type string, integer given, called in %s on line %d + +*** Trying float(NAN) +*** Caught Argument 1 passed to {closure}() must be of the type string, float given, called in %s on line %d + +*** Trying bool(true) +*** Caught Argument 1 passed to {closure}() must be of the type string, boolean given, called in %s on line %d + +*** Trying bool(false) +*** Caught Argument 1 passed to {closure}() must be of the type string, boolean given, called in %s on line %d + +*** Trying NULL +*** Caught Argument 1 passed to {closure}() must be of the type string, null given, called in %s on line %d + +*** Trying array(0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type string, array given, called in %s on line %d + +*** Trying object(stdClass)#6 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type string, object given, called in %s on line %d + +*** Trying object(Stringable)#7 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type string, object given, called in %s on line %d + +*** Trying resource(5) of type (stream) +*** Caught Argument 1 passed to {closure}() must be of the type string, resource given, called in %s on line %d + +Testing 'bool' typehint: + +*** Trying int(1) +*** Caught Argument 1 passed to {closure}() must be of the type boolean, integer given, called in %s on line %d + +*** Trying string(1) "1" +*** Caught Argument 1 passed to {closure}() must be of the type boolean, string given, called in %s on line %d + +*** Trying float(1) +*** Caught Argument 1 passed to {closure}() must be of the type boolean, float given, called in %s on line %d + +*** Trying float(1.5) +*** Caught Argument 1 passed to {closure}() must be of the type boolean, float given, called in %s on line %d + +*** Trying string(2) "1a" +*** Caught Argument 1 passed to {closure}() must be of the type boolean, string given, called in %s on line %d + +*** Trying string(1) "a" +*** Caught Argument 1 passed to {closure}() must be of the type boolean, string given, called in %s on line %d + +*** Trying string(0) "" +*** Caught Argument 1 passed to {closure}() must be of the type boolean, string given, called in %s on line %d + +*** Trying int(2147483647) +*** Caught Argument 1 passed to {closure}() must be of the type boolean, integer given, called in %s on line %d + +*** Trying float(NAN) +*** Caught Argument 1 passed to {closure}() must be of the type boolean, float given, called in %s on line %d + +*** Trying bool(true) +bool(true) + +*** Trying bool(false) +bool(false) + +*** Trying NULL +*** Caught Argument 1 passed to {closure}() must be of the type boolean, null given, called in %s on line %d + +*** Trying array(0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type boolean, array given, called in %s on line %d + +*** Trying object(stdClass)#6 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type boolean, object given, called in %s on line %d + +*** Trying object(Stringable)#7 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type boolean, object given, called in %s on line %d + +*** Trying resource(5) of type (stream) +*** Caught Argument 1 passed to {closure}() must be of the type boolean, resource given, called in %s on line %d + +Done diff --git a/Zend/tests/typehints/scalar_strict_64bit.phpt b/Zend/tests/typehints/scalar_strict_64bit.phpt new file mode 100644 index 0000000000..da3e1e4546 --- /dev/null +++ b/Zend/tests/typehints/scalar_strict_64bit.phpt @@ -0,0 +1,279 @@ +--TEST-- +Scalar type hint strict mode +--SKIPIF-- +<?php if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only"); ?> +--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 PHP_EOL . "*** Trying "; + var_dump($value); + try { + var_dump($function($value)); + } catch (TypeException $e) { + echo "*** Caught " . $e->getMessage() . PHP_EOL; + } + } +} + +echo PHP_EOL . "Done"; +?> +--EXPECTF-- +Testing 'int' typehint: + +*** Trying int(1) +int(1) + +*** Trying string(1) "1" +*** Caught Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d + +*** Trying float(1) +*** Caught Argument 1 passed to {closure}() must be of the type integer, float given, called in %s on line %d + +*** Trying float(1.5) +*** Caught Argument 1 passed to {closure}() must be of the type integer, float given, called in %s on line %d + +*** Trying string(2) "1a" +*** Caught Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d + +*** Trying string(1) "a" +*** Caught Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d + +*** Trying string(0) "" +*** Caught Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d + +*** Trying int(9223372036854775807) +int(9223372036854775807) + +*** Trying float(NAN) +*** Caught Argument 1 passed to {closure}() must be of the type integer, float given, called in %s on line %d + +*** Trying bool(true) +*** Caught Argument 1 passed to {closure}() must be of the type integer, boolean given, called in %s on line %d + +*** Trying bool(false) +*** Caught Argument 1 passed to {closure}() must be of the type integer, boolean given, called in %s on line %d + +*** Trying NULL +*** Caught Argument 1 passed to {closure}() must be of the type integer, null given, called in %s on line %d + +*** Trying array(0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type integer, array given, called in %s on line %d + +*** Trying object(stdClass)#6 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type integer, object given, called in %s on line %d + +*** Trying object(Stringable)#7 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type integer, object given, called in %s on line %d + +*** Trying resource(5) of type (stream) +*** Caught Argument 1 passed to {closure}() must be of the type integer, resource given, called in %s on line %d + +Testing 'float' typehint: + +*** Trying int(1) +float(1) + +*** Trying string(1) "1" +*** Caught Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d + +*** Trying float(1) +float(1) + +*** Trying float(1.5) +float(1.5) + +*** Trying string(2) "1a" +*** Caught Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d + +*** Trying string(1) "a" +*** Caught Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d + +*** Trying string(0) "" +*** Caught Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d + +*** Trying int(9223372036854775807) +float(9.2233720368548E+18) + +*** Trying float(NAN) +float(NAN) + +*** Trying bool(true) +*** Caught Argument 1 passed to {closure}() must be of the type float, boolean given, called in %s on line %d + +*** Trying bool(false) +*** Caught Argument 1 passed to {closure}() must be of the type float, boolean given, called in %s on line %d + +*** Trying NULL +*** Caught Argument 1 passed to {closure}() must be of the type float, null given, called in %s on line %d + +*** Trying array(0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type float, array given, called in %s on line %d + +*** Trying object(stdClass)#6 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type float, object given, called in %s on line %d + +*** Trying object(Stringable)#7 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type float, object given, called in %s on line %d + +*** Trying resource(5) of type (stream) +*** Caught Argument 1 passed to {closure}() must be of the type float, resource given, called in %s on line %d + +Testing 'string' typehint: + +*** Trying int(1) +*** Caught Argument 1 passed to {closure}() must be of the type string, integer given, called in %s on line %d + +*** Trying string(1) "1" +string(1) "1" + +*** Trying float(1) +*** Caught Argument 1 passed to {closure}() must be of the type string, float given, called in %s on line %d + +*** Trying float(1.5) +*** Caught Argument 1 passed to {closure}() must be of the type string, float given, called in %s on line %d + +*** Trying string(2) "1a" +string(2) "1a" + +*** Trying string(1) "a" +string(1) "a" + +*** Trying string(0) "" +string(0) "" + +*** Trying int(9223372036854775807) +*** Caught Argument 1 passed to {closure}() must be of the type string, integer given, called in %s on line %d + +*** Trying float(NAN) +*** Caught Argument 1 passed to {closure}() must be of the type string, float given, called in %s on line %d + +*** Trying bool(true) +*** Caught Argument 1 passed to {closure}() must be of the type string, boolean given, called in %s on line %d + +*** Trying bool(false) +*** Caught Argument 1 passed to {closure}() must be of the type string, boolean given, called in %s on line %d + +*** Trying NULL +*** Caught Argument 1 passed to {closure}() must be of the type string, null given, called in %s on line %d + +*** Trying array(0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type string, array given, called in %s on line %d + +*** Trying object(stdClass)#6 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type string, object given, called in %s on line %d + +*** Trying object(Stringable)#7 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type string, object given, called in %s on line %d + +*** Trying resource(5) of type (stream) +*** Caught Argument 1 passed to {closure}() must be of the type string, resource given, called in %s on line %d + +Testing 'bool' typehint: + +*** Trying int(1) +*** Caught Argument 1 passed to {closure}() must be of the type boolean, integer given, called in %s on line %d + +*** Trying string(1) "1" +*** Caught Argument 1 passed to {closure}() must be of the type boolean, string given, called in %s on line %d + +*** Trying float(1) +*** Caught Argument 1 passed to {closure}() must be of the type boolean, float given, called in %s on line %d + +*** Trying float(1.5) +*** Caught Argument 1 passed to {closure}() must be of the type boolean, float given, called in %s on line %d + +*** Trying string(2) "1a" +*** Caught Argument 1 passed to {closure}() must be of the type boolean, string given, called in %s on line %d + +*** Trying string(1) "a" +*** Caught Argument 1 passed to {closure}() must be of the type boolean, string given, called in %s on line %d + +*** Trying string(0) "" +*** Caught Argument 1 passed to {closure}() must be of the type boolean, string given, called in %s on line %d + +*** Trying int(9223372036854775807) +*** Caught Argument 1 passed to {closure}() must be of the type boolean, integer given, called in %s on line %d + +*** Trying float(NAN) +*** Caught Argument 1 passed to {closure}() must be of the type boolean, float given, called in %s on line %d + +*** Trying bool(true) +bool(true) + +*** Trying bool(false) +bool(false) + +*** Trying NULL +*** Caught Argument 1 passed to {closure}() must be of the type boolean, null given, called in %s on line %d + +*** Trying array(0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type boolean, array given, called in %s on line %d + +*** Trying object(stdClass)#6 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type boolean, object given, called in %s on line %d + +*** Trying object(Stringable)#7 (0) { +} +*** Caught Argument 1 passed to {closure}() must be of the type boolean, object given, called in %s on line %d + +*** Trying resource(5) of type (stream) +*** Caught Argument 1 passed to {closure}() must be of the type boolean, resource given, called in %s on line %d + +Done diff --git a/Zend/tests/typehints/scalar_strict_basic.phpt b/Zend/tests/typehints/scalar_strict_basic.phpt new file mode 100644 index 0000000000..d56af159d7 --- /dev/null +++ b/Zend/tests/typehints/scalar_strict_basic.phpt @@ -0,0 +1,181 @@ +--TEST-- +Strict scalar type hint basics +--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; } +]; + +$values = [ + 1, + 1.0, + "1", + TRUE, + FALSE, + NULL, + [], + new StdClass, + fopen("data:text/plain,foobar", "r") +]; + +function type($value) { + if (is_float($value)) { + return "float"; + } else if (is_bool($value)) { + return $value ? "true" : "false"; + } else if (is_null($value)) { + return "null"; + } else { + return gettype($value); + } +} + +foreach ($functions as $type => $function) { + echo PHP_EOL, "Testing '$type' typehint:", PHP_EOL; + foreach ($values as $value) { + $errored = false; + echo PHP_EOL . "*** Trying ", type($value), " value", PHP_EOL; + try { + var_dump($function($value)); + } catch (TypeException $e) { + echo "*** Caught " . $e->getMessage() . PHP_EOL; + } + } +} +echo PHP_EOL . "Done"; +?> +--EXPECTF-- +Testing 'int' typehint: + +*** Trying integer value +int(1) + +*** Trying float value +*** Caught Argument 1 passed to {closure}() must be of the type integer, float given, called in %s on line %d + +*** Trying string value +*** Caught Argument 1 passed to {closure}() must be of the type integer, string given, called in %s on line %d + +*** Trying true value +*** Caught Argument 1 passed to {closure}() must be of the type integer, boolean given, called in %s on line %d + +*** Trying false value +*** Caught Argument 1 passed to {closure}() must be of the type integer, boolean given, called in %s on line %d + +*** Trying null value +*** Caught Argument 1 passed to {closure}() must be of the type integer, null given, called in %s on line %d + +*** Trying array value +*** Caught Argument 1 passed to {closure}() must be of the type integer, array given, called in %s on line %d + +*** Trying object value +*** Caught Argument 1 passed to {closure}() must be of the type integer, object given, called in %s on line %d + +*** Trying resource value +*** Caught Argument 1 passed to {closure}() must be of the type integer, resource given, called in %s on line %d + +Testing 'float' typehint: + +*** Trying integer value +float(1) + +*** Trying float value +float(1) + +*** Trying string value +*** Caught Argument 1 passed to {closure}() must be of the type float, string given, called in %s on line %d + +*** Trying true value +*** Caught Argument 1 passed to {closure}() must be of the type float, boolean given, called in %s on line %d + +*** Trying false value +*** Caught Argument 1 passed to {closure}() must be of the type float, boolean given, called in %s on line %d + +*** Trying null value +*** Caught Argument 1 passed to {closure}() must be of the type float, null given, called in %s on line %d + +*** Trying array value +*** Caught Argument 1 passed to {closure}() must be of the type float, array given, called in %s on line %d + +*** Trying object value +*** Caught Argument 1 passed to {closure}() must be of the type float, object given, called in %s on line %d + +*** Trying resource value +*** Caught Argument 1 passed to {closure}() must be of the type float, resource given, called in %s on line %d + +Testing 'string' typehint: + +*** Trying integer value +*** Caught Argument 1 passed to {closure}() must be of the type string, integer given, called in %s on line %d + +*** Trying float value +*** Caught Argument 1 passed to {closure}() must be of the type string, float given, called in %s on line %d + +*** Trying string value +string(1) "1" + +*** Trying true value +*** Caught Argument 1 passed to {closure}() must be of the type string, boolean given, called in %s on line %d + +*** Trying false value +*** Caught Argument 1 passed to {closure}() must be of the type string, boolean given, called in %s on line %d + +*** Trying null value +*** Caught Argument 1 passed to {closure}() must be of the type string, null given, called in %s on line %d + +*** Trying array value +*** Caught Argument 1 passed to {closure}() must be of the type string, array given, called in %s on line %d + +*** Trying object value +*** Caught Argument 1 passed to {closure}() must be of the type string, object given, called in %s on line %d + +*** Trying resource value +*** Caught Argument 1 passed to {closure}() must be of the type string, resource given, called in %s on line %d + +Testing 'bool' typehint: + +*** Trying integer value +*** Caught Argument 1 passed to {closure}() must be of the type boolean, integer given, called in %s on line %d + +*** Trying float value +*** Caught Argument 1 passed to {closure}() must be of the type boolean, float given, called in %s on line %d + +*** Trying string value +*** Caught Argument 1 passed to {closure}() must be of the type boolean, string given, called in %s on line %d + +*** Trying true value +bool(true) + +*** Trying false value +bool(false) + +*** Trying null value +*** Caught Argument 1 passed to {closure}() must be of the type boolean, null given, called in %s on line %d + +*** Trying array value +*** Caught Argument 1 passed to {closure}() must be of the type boolean, array given, called in %s on line %d + +*** Trying object value +*** Caught Argument 1 passed to {closure}() must be of the type boolean, object given, called in %s on line %d + +*** Trying resource value +*** Caught Argument 1 passed to {closure}() must be of the type boolean, resource given, called in %s on line %d + +Done diff --git a/Zend/tests/typehints/scalar_strict_declaration_placement_001.phpt b/Zend/tests/typehints/scalar_strict_declaration_placement_001.phpt new file mode 100644 index 0000000000..2a2b3e6fd3 --- /dev/null +++ b/Zend/tests/typehints/scalar_strict_declaration_placement_001.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test strict declaration being first operation only +--FILE-- +<?php + +function takes_int(int $x) { + global $errored; + if ($errored) { + echo "Failure!", PHP_EOL; + $errored = FALSE; + } else { + echo "Success!", PHP_EOL; + } +} + +?> +<?php + +declare(strict_types=1); +var_dump(takes_int(32)); + +?> +--EXPECTF-- +Fatal error: strict_types declaration must be the very first statement in the script in %s on line %d diff --git a/Zend/tests/typehints/scalar_strict_declaration_placement_002.phpt b/Zend/tests/typehints/scalar_strict_declaration_placement_002.phpt new file mode 100644 index 0000000000..09ac1119ab --- /dev/null +++ b/Zend/tests/typehints/scalar_strict_declaration_placement_002.phpt @@ -0,0 +1,14 @@ +--TEST-- +Test strict declaration being first operation only 002 +--FILE-- +<?php + +namespace Foo; + +declare(strict_types=1); + +var_dump(strlen("abc")); + +?> +--EXPECTF-- +Fatal error: strict_types declaration must be the very first statement in the script in %s on line %d diff --git a/Zend/tests/typehints/scalar_strict_declaration_placement_003.phpt b/Zend/tests/typehints/scalar_strict_declaration_placement_003.phpt new file mode 100644 index 0000000000..6ec5b59b98 --- /dev/null +++ b/Zend/tests/typehints/scalar_strict_declaration_placement_003.phpt @@ -0,0 +1,11 @@ +--TEST-- +Test strict declaration being first operation only 003 +--FILE-- +hi<?php + +declare(strict_types=1); +var_dump(strlen(32)); + +?> +--EXPECTF-- +Fatal error: strict_types declaration must be the very first statement in the script in %s on line %d diff --git a/Zend/tests/typehints/scalar_strict_declaration_placement_004.phpt b/Zend/tests/typehints/scalar_strict_declaration_placement_004.phpt new file mode 100644 index 0000000000..60003241cf --- /dev/null +++ b/Zend/tests/typehints/scalar_strict_declaration_placement_004.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test strict declaration being first operation only 004 +--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) diff --git a/Zend/tests/typehints/scalar_strict_declaration_placement_005.phpt b/Zend/tests/typehints/scalar_strict_declaration_placement_005.phpt new file mode 100644 index 0000000000..8611a205cf --- /dev/null +++ b/Zend/tests/typehints/scalar_strict_declaration_placement_005.phpt @@ -0,0 +1,14 @@ +--TEST-- +Test strict declaration being first operation only 005 +--FILE-- +<?php + +declare(strict_types=1); + +namespace Foo; + +var_dump(strlen("abc")); + +?> +--EXPECTF-- +int(3)
\ No newline at end of file diff --git a/Zend/tests/typehints/scalar_strict_declaration_placement_006.phpt b/Zend/tests/typehints/scalar_strict_declaration_placement_006.phpt new file mode 100644 index 0000000000..2af05b6c65 --- /dev/null +++ b/Zend/tests/typehints/scalar_strict_declaration_placement_006.phpt @@ -0,0 +1,25 @@ +--TEST-- +Test strict declaration being first operation only 006 +--SKIPIF-- +<?php +if (!in_array("zend.detect_unicode", array_keys(ini_get_all()))) { + die("skip Requires configure --enable-zend-multibyte option"); +} +if (!extension_loaded("mbstring")) { + die("skip Requires mbstring extension"); +} +?> +--INI-- +zend.multibyte=1 +--FILE-- +<?php +declare(encoding="UTF-8"); +declare(strict_types=1); + +namespace Foo; + +var_dump(strlen("abc")); + +?> +--EXPECTF-- +int(3)
\ No newline at end of file diff --git a/Zend/tests/typehints/scalar_strict_declaration_placement_007.phpt b/Zend/tests/typehints/scalar_strict_declaration_placement_007.phpt new file mode 100644 index 0000000000..3e4c693afb --- /dev/null +++ b/Zend/tests/typehints/scalar_strict_declaration_placement_007.phpt @@ -0,0 +1,25 @@ +--TEST-- +Test strict declaration being first operation only 007 +--SKIPIF-- +<?php +if (!in_array("zend.detect_unicode", array_keys(ini_get_all()))) { + die("skip Requires configure --enable-zend-multibyte option"); +} +if (!extension_loaded("mbstring")) { + die("skip Requires mbstring extension"); +} +?> +--INI-- +zend.multibyte=1 +--FILE-- +<?php +declare(strict_types=1); +declare(encoding="ISO-8859-1"); + +namespace Foo; + +var_dump(strlen("abc")); + +?> +--EXPECTF-- +int(3)
\ No newline at end of file diff --git a/Zend/tests/typehints/scalar_strict_declaration_placement_008.phpt b/Zend/tests/typehints/scalar_strict_declaration_placement_008.phpt new file mode 100644 index 0000000000..87a09af96c --- /dev/null +++ b/Zend/tests/typehints/scalar_strict_declaration_placement_008.phpt @@ -0,0 +1,11 @@ +--TEST-- +Test strict declaration block declaration 008 +--FILE-- +<?php +declare(strict_types=1) { + var_dump(strlen("abc")); +} + +?> +--EXPECTF-- +Fatal error: strict_types declaration must not use block mode in %s on line %d
\ No newline at end of file diff --git a/Zend/tests/typehints/scalar_weak_reference.phpt b/Zend/tests/typehints/scalar_weak_reference.phpt new file mode 100644 index 0000000000..f397ceaa8d --- /dev/null +++ b/Zend/tests/typehints/scalar_weak_reference.phpt @@ -0,0 +1,29 @@ +--TEST-- +Weak scalar type hints, with references +--FILE-- +<?php + +// implicitly weak mode code + +function to_int(int &$x) {} +function to_float(float &$x) {} +function to_string(string &$x) {} +function to_bool(bool &$x) {} + +$x = 1.0; +var_dump($x); +to_int($x); // because $x is by-reference, the weak type hint converts it +var_dump($x); +to_float($x); +var_dump($x); +to_string($x); +var_dump($x); +to_bool($x); +var_dump($x); +?> +--EXPECT-- +float(1) +int(1) +float(1) +string(1) "1" +bool(true) diff --git a/Zend/tests/typehints/strict_call_weak.phpt b/Zend/tests/typehints/strict_call_weak.phpt new file mode 100644 index 0000000000..94d5953f2b --- /dev/null +++ b/Zend/tests/typehints/strict_call_weak.phpt @@ -0,0 +1,17 @@ +--TEST-- +strict_types=1 code calling strict_types=0 code +--FILE-- +<?php + +declare(strict_types=1); + +// file that's implicitly weak +require 'strict_call_weak_2.inc'; + +// Will succeed: Function was declared in weak mode, but that does not matter +// This file uses strict mode, so the call is strict, and float denied for int +function_declared_in_weak_mode(1.0); +?> +--EXPECTF-- +Fatal error: Argument 1 passed to function_declared_in_weak_mode() must be of the type integer, float given, called in %sstrict_call_weak.php on line 10 and defined in %sstrict_call_weak_2.inc on line 5 + diff --git a/Zend/tests/typehints/strict_call_weak_2.inc b/Zend/tests/typehints/strict_call_weak_2.inc new file mode 100644 index 0000000000..cba5512897 --- /dev/null +++ b/Zend/tests/typehints/strict_call_weak_2.inc @@ -0,0 +1,7 @@ +<?php + +// implicitly weak code + +function function_declared_in_weak_mode(int $x) { + echo "Success!"; +} diff --git a/Zend/tests/typehints/strict_call_weak_explicit.phpt b/Zend/tests/typehints/strict_call_weak_explicit.phpt new file mode 100644 index 0000000000..2d573e85f2 --- /dev/null +++ b/Zend/tests/typehints/strict_call_weak_explicit.phpt @@ -0,0 +1,17 @@ +--TEST-- +strict_types=1 code calling explicitly strict_types=0 code +--FILE-- +<?php + +declare(strict_types=1); + +// file that's explicitly weak +require 'strict_call_weak_explicit_2.inc'; + +// Will succeed: Function was declared in weak mode, but that does not matter +// This file uses strict mode, so the call is strict, and float denied for int +function_declared_in_weak_mode(1.0); +?> +--EXPECTF-- +Fatal error: Argument 1 passed to function_declared_in_weak_mode() must be of the type integer, float given, called in %sstrict_call_weak_explicit.php on line 10 and defined in %sstrict_call_weak_explicit_2.inc on line 5 + diff --git a/Zend/tests/typehints/strict_call_weak_explicit_2.inc b/Zend/tests/typehints/strict_call_weak_explicit_2.inc new file mode 100644 index 0000000000..d26c84761c --- /dev/null +++ b/Zend/tests/typehints/strict_call_weak_explicit_2.inc @@ -0,0 +1,7 @@ +<?php + +declare(strict_types=0); + +function function_declared_in_weak_mode(int $x) { + echo "Success!"; +} diff --git a/Zend/tests/typehints/strict_include_explicit_weak.phpt b/Zend/tests/typehints/strict_include_explicit_weak.phpt new file mode 100644 index 0000000000..a42d633f47 --- /dev/null +++ b/Zend/tests/typehints/strict_include_explicit_weak.phpt @@ -0,0 +1,14 @@ +--TEST-- +strict_types=1 code including explicitly strict_types=0 code +--FILE-- +<?php + +declare(strict_types=1); + +// file that's explicitly weak +require 'strict_include_explicit_weak_2.inc'; + +// calls within that file should stay weak, despite being included by strict fille +?> +--EXPECTF-- +Success! diff --git a/Zend/tests/typehints/strict_include_explicit_weak_2.inc b/Zend/tests/typehints/strict_include_explicit_weak_2.inc new file mode 100644 index 0000000000..3da32e1738 --- /dev/null +++ b/Zend/tests/typehints/strict_include_explicit_weak_2.inc @@ -0,0 +1,9 @@ +<?php + +declare(strict_types=0); + +function takes_int(int $x) { + echo "Success!"; +} + +takes_int(1.0); // succeeds in weak mode diff --git a/Zend/tests/typehints/strict_include_weak.phpt b/Zend/tests/typehints/strict_include_weak.phpt new file mode 100644 index 0000000000..ce29db7e75 --- /dev/null +++ b/Zend/tests/typehints/strict_include_weak.phpt @@ -0,0 +1,14 @@ +--TEST-- +strict_types=1 code including strict_types=0 code +--FILE-- +<?php + +declare(strict_types=1); + +// file that's implicitly weak +require 'strict_include_weak_2.inc'; + +// calls within that file should stay weak, despite being included by strict file +?> +--EXPECTF-- +Success! diff --git a/Zend/tests/typehints/strict_include_weak_2.inc b/Zend/tests/typehints/strict_include_weak_2.inc new file mode 100644 index 0000000000..d4ee8a836b --- /dev/null +++ b/Zend/tests/typehints/strict_include_weak_2.inc @@ -0,0 +1,9 @@ +<?php + +// implicitly weak code + +function takes_int(int $x) { + echo "Success!"; +} + +takes_int(1.0); // succeeds in weak mode diff --git a/Zend/tests/typehints/strict_nested.phpt b/Zend/tests/typehints/strict_nested.phpt new file mode 100644 index 0000000000..2001f8b1f4 --- /dev/null +++ b/Zend/tests/typehints/strict_nested.phpt @@ -0,0 +1,43 @@ +--TEST-- +Test nested function calls in strict_types=0 and strict_types=1 modes +--FILE-- +<?php + +function takes_int(int $x) { + global $errored; + if ($errored) { + echo "Failure!", PHP_EOL; + $errored = FALSE; + } else { + echo "Success!", PHP_EOL; + } +} + +declare(strict_types=1) { + function strict_calls_takes_int() { + takes_int(1.0); // should fail, strict mode + } + + class StrictTakesIntCaller { + public function call() { + takes_int(1.0); // should fail, strict mode + } + } +} + +declare(strict_types=0) { + function explicit_weak_calls_takes_int() { + takes_int(1.0); // should succeed, weak mode + } + + class ExplicitWeakTakesIntCaller { + public function call() { + takes_int(1.0); // should succeed, weak mode + } + } +} + + +?> +--EXPECTF-- +Fatal error: strict_types declaration must be the very first statement in the script in %s on line %d diff --git a/Zend/tests/typehints/weak_call_strict.phpt b/Zend/tests/typehints/weak_call_strict.phpt new file mode 100644 index 0000000000..b0ea78ce67 --- /dev/null +++ b/Zend/tests/typehints/weak_call_strict.phpt @@ -0,0 +1,16 @@ +--TEST-- +strict_types=0 code calling strict_types=1 code +--FILE-- +<?php + +// implicitly strict_types=0 + +// file with strict_types=1 +require 'weak_call_strict_2.inc'; + +// Will succeed: Function was declared in strict mode, but that does not matter +// This file uses weak mode, so the call is weak, and float accepted for int +function_declared_in_strict_mode(1.0); +?> +--EXPECT-- +Success! diff --git a/Zend/tests/typehints/weak_call_strict_2.inc b/Zend/tests/typehints/weak_call_strict_2.inc new file mode 100644 index 0000000000..fe2a17f682 --- /dev/null +++ b/Zend/tests/typehints/weak_call_strict_2.inc @@ -0,0 +1,7 @@ +<?php + +declare(strict_types=1); + +function function_declared_in_strict_mode(int $x) { + echo "Success!"; +} diff --git a/Zend/tests/typehints/weak_explicit_call_strict.phpt b/Zend/tests/typehints/weak_explicit_call_strict.phpt new file mode 100644 index 0000000000..b07cef5ece --- /dev/null +++ b/Zend/tests/typehints/weak_explicit_call_strict.phpt @@ -0,0 +1,16 @@ +--TEST-- +Explicitly strict_types=0 code calling strict_types=1 code +--FILE-- +<?php + +declare(strict_types=0); + +// file with strict_types=1 +require 'weak_call_strict_2.inc'; + +// Will succeed: Function was declared in strict mode, but that does not matter +// This file uses weak mode, so the call is weak, and float accepted for int +function_declared_in_strict_mode(1.0); +?> +--EXPECT-- +Success! diff --git a/Zend/tests/typehints/weak_include_strict.phpt b/Zend/tests/typehints/weak_include_strict.phpt new file mode 100644 index 0000000000..c3a0820657 --- /dev/null +++ b/Zend/tests/typehints/weak_include_strict.phpt @@ -0,0 +1,14 @@ +--TEST-- +strict_types=0 code including strict_types=1 code +--FILE-- +<?php + +// implicitly weak code + +// file with strict_types=1 +require 'weak_include_strict_2.inc'; + +// calls within that file should stay strict, despite being included by weak file +?> +--EXPECTF-- +Fatal error: Argument 1 passed to takes_int() must be of the type integer, float given, called in %sweak_include_strict_2.inc on line 9 and defined in %sweak_include_strict_2.inc on line 5 diff --git a/Zend/tests/typehints/weak_include_strict_2.inc b/Zend/tests/typehints/weak_include_strict_2.inc new file mode 100644 index 0000000000..8acfc039e8 --- /dev/null +++ b/Zend/tests/typehints/weak_include_strict_2.inc @@ -0,0 +1,9 @@ +<?php + +declare(strict_types=1); + +function takes_int(int $x) { + echo "Success!"; +} + +takes_int(1.0); // fails in strict mode diff --git a/Zend/tests/variadic/typehint_suppressed_error.phpt b/Zend/tests/variadic/typehint_suppressed_error.phpt index f03ec43d0a..5879de6d95 100644 --- a/Zend/tests/variadic/typehint_suppressed_error.phpt +++ b/Zend/tests/variadic/typehint_suppressed_error.phpt @@ -15,4 +15,4 @@ try { ?> --EXPECTF-- -string(%d) "Argument 3 passed to test() must be of the type array, integer given, called in %s on line %d and defined" +string(%d) "Argument 3 passed to test() must be of the type array, integer given, called in %s on line %d" diff --git a/Zend/zend.c b/Zend/zend.c index fe507d7154..6bb614fab5 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1277,6 +1277,35 @@ ZEND_API ZEND_NORETURN void zend_error_noreturn(int type, const char *format, .. # endif #endif +ZEND_API void zend_type_error(const char *format, ...) /* {{{ */ +{ + va_list va; + char *message = NULL; + + va_start(va, format); + zend_vspprintf(&message, 0, format, va); + zend_throw_exception(zend_get_type_exception(), message, E_ERROR); + efree(message); + va_end(va); +} /* }}} */ + +ZEND_API void zend_internal_type_error(zend_bool strict, const char *format, ...) /* {{{ */ +{ + va_list va; + char *message = NULL; + + va_start(va, format); + zend_vspprintf(&message, 0, format, va); + if (strict) { + zend_throw_exception(zend_get_type_exception(), message, E_ERROR); + } else { + zend_error(E_WARNING, message); + } + efree(message); + + va_end(va); +} /* }}} */ + ZEND_API void zend_output_debug_string(zend_bool trigger_break, const char *format, ...) /* {{{ */ { #if ZEND_DEBUG diff --git a/Zend/zend.h b/Zend/zend.h index 94cab74af1..db132a583a 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -285,6 +285,8 @@ extern ZEND_API char *(*zend_getenv)(char *name, size_t name_len); extern ZEND_API zend_string *(*zend_resolve_path)(const char *filename, int filename_len); ZEND_API void zend_error(int type, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3); +ZEND_API void zend_type_error(const char *format, ...); +ZEND_API void zend_internal_type_error(zend_bool strict, const char *format, ...); void zenderror(const char *error); diff --git a/Zend/zend_API.c b/Zend/zend_API.c index c513d5db5e..91247bbecc 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -154,7 +154,7 @@ ZEND_API void zend_wrong_param_count(void) /* {{{ */ const char *space; const char *class_name = get_active_class_name(&space); - zend_error(E_WARNING, "Wrong parameter count for %s%s%s()", class_name, space, get_active_function_name()); + zend_internal_type_error(ZEND_ARG_USES_STRICT_TYPES(), "Wrong parameter count for %s%s%s()", class_name, space, get_active_function_name()); } /* }}} */ @@ -164,6 +164,7 @@ ZEND_API char *zend_get_type_by_const(int type) /* {{{ */ switch(type) { case IS_FALSE: case IS_TRUE: + case _IS_BOOL: return "boolean"; case IS_LONG: return "integer"; @@ -194,51 +195,13 @@ ZEND_API char *zend_zval_type_name(const zval *arg) /* {{{ */ } /* }}} */ -ZEND_API int parse_arg_object_to_str(zval *arg, zend_string **str, int type) /* {{{ */ -{ - if (Z_OBJ_HANDLER_P(arg, cast_object)) { - zval obj; - if (Z_OBJ_HANDLER_P(arg, cast_object)(arg, &obj, type) == SUCCESS) { - zval_ptr_dtor(arg); - ZVAL_COPY_VALUE(arg, &obj); - *str = Z_STR_P(arg); - return SUCCESS; - } - } - /* Standard PHP objects */ - if (Z_OBJ_HT_P(arg) == &std_object_handlers || !Z_OBJ_HANDLER_P(arg, cast_object)) { - SEPARATE_ZVAL_NOREF(arg); - if (zend_std_cast_object_tostring(arg, arg, type) == SUCCESS) { - *str = Z_STR_P(arg); - return SUCCESS; - } - } - if (!Z_OBJ_HANDLER_P(arg, cast_object) && Z_OBJ_HANDLER_P(arg, get)) { - zval rv; - zval *z = Z_OBJ_HANDLER_P(arg, get)(arg, &rv); - Z_ADDREF_P(z); - if(Z_TYPE_P(z) != IS_OBJECT) { - zval_dtor(arg); - ZVAL_NULL(arg); - if (!zend_make_printable_zval(z, arg)) { - ZVAL_ZVAL(arg, z, 1, 1); - } - *str = Z_STR_P(arg); - return SUCCESS; - } - zval_ptr_dtor(z); - } - return FAILURE; -} -/* }}} */ - #ifdef FAST_ZPP -ZEND_API void zend_wrong_paramers_count_error(int num_args, int min_num_args, int max_num_args) /* {{{ */ +ZEND_API void ZEND_FASTCALL zend_wrong_paramers_count_error(int num_args, int min_num_args, int max_num_args) /* {{{ */ { zend_function *active_function = EG(current_execute_data)->func; const char *class_name = active_function->common.scope ? active_function->common.scope->name->val : ""; - zend_error(E_WARNING, "%s%s%s() expects %s %d parameter%s, %d given", + zend_internal_type_error(ZEND_ARG_USES_STRICT_TYPES(), "%s%s%s() expects %s %d parameter%s, %d given", class_name, \ class_name[0] ? "::" : "", \ active_function->common.function_name->val, @@ -249,7 +212,7 @@ ZEND_API void zend_wrong_paramers_count_error(int num_args, int min_num_args, in } /* }}} */ -ZEND_API void zend_wrong_paramer_type_error(int num, zend_expected_type expected_type, zval *arg) /* {{{ */ +ZEND_API void ZEND_FASTCALL zend_wrong_paramer_type_error(int num, zend_expected_type expected_type, zval *arg) /* {{{ */ { const char *space; const char *class_name = get_active_class_name(&space); @@ -258,33 +221,38 @@ ZEND_API void zend_wrong_paramer_type_error(int num, zend_expected_type expected NULL }; - zend_error(E_WARNING, "%s%s%s() expects parameter %d to be %s, %s given", + zend_internal_type_error(ZEND_ARG_USES_STRICT_TYPES(), "%s%s%s() expects parameter %d to be %s, %s given", class_name, space, get_active_function_name(), num, expected_error[expected_type], zend_zval_type_name(arg)); } /* }}} */ -ZEND_API void zend_wrong_paramer_class_error(int num, char *name, zval *arg) /* {{{ */ +ZEND_API void ZEND_FASTCALL zend_wrong_paramer_class_error(int num, char *name, zval *arg) /* {{{ */ { const char *space; const char *class_name = get_active_class_name(&space); - zend_error(E_WARNING, "%s%s%s() expects parameter %d to be %s, %s given", + zend_internal_type_error(ZEND_ARG_USES_STRICT_TYPES(), "%s%s%s() expects parameter %d to be %s, %s given", class_name, space, get_active_function_name(), num, name, zend_zval_type_name(arg)); } /* }}} */ -ZEND_API void zend_wrong_callback_error(int severity, int num, char *error) /* {{{ */ +ZEND_API void ZEND_FASTCALL zend_wrong_callback_error(int severity, int num, char *error) /* {{{ */ { const char *space; const char *class_name = get_active_class_name(&space); - zend_error(severity, "%s%s%s() expects parameter %d to be a valid callback, %s", - class_name, space, get_active_function_name(), num, error); + if (severity == E_WARNING) { + zend_internal_type_error(ZEND_ARG_USES_STRICT_TYPES(), "%s%s%s() expects parameter %d to be a valid callback, %s", + class_name, space, get_active_function_name(), num, error); + } else { + zend_error(severity, "%s%s%s() expects parameter %d to be a valid callback, %s", + class_name, space, get_active_function_name(), num, error); + } efree(error); } /* }}} */ -ZEND_API int zend_parse_arg_class(zval *arg, zend_class_entry **pce, int num, int check_null) /* {{{ */ +ZEND_API int ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **pce, int num, int check_null) /* {{{ */ { zend_class_entry *ce_base = *pce; @@ -299,7 +267,7 @@ ZEND_API int zend_parse_arg_class(zval *arg, zend_class_entry **pce, int num, in const char *space; const char *class_name = get_active_class_name(&space); - zend_error(E_WARNING, "%s%s%s() expects parameter %d to be a class name derived from %s, '%s' given", + zend_internal_type_error(ZEND_ARG_USES_STRICT_TYPES(), "%s%s%s() expects parameter %d to be a class name derived from %s, '%s' given", class_name, space, get_active_function_name(), num, ce_base->name->val, Z_STRVAL_P(arg)); *pce = NULL; @@ -310,7 +278,7 @@ ZEND_API int zend_parse_arg_class(zval *arg, zend_class_entry **pce, int num, in const char *space; const char *class_name = get_active_class_name(&space); - zend_error(E_WARNING, "%s%s%s() expects parameter %d to be a valid class name, '%s' given", + zend_internal_type_error(ZEND_ARG_USES_STRICT_TYPES(), "%s%s%s() expects parameter %d to be a valid class name, '%s' given", class_name, space, get_active_function_name(), num, Z_STRVAL_P(arg)); return 0; @@ -320,6 +288,218 @@ ZEND_API int zend_parse_arg_class(zval *arg, zend_class_entry **pce, int num, in /* }}} */ #endif +ZEND_API int ZEND_FASTCALL zend_parse_arg_bool_weak(zval *arg, zend_bool *dest) /* {{{ */ +{ + if (EXPECTED(Z_TYPE_P(arg) <= IS_STRING)) { + *dest = zend_is_true(arg); + } else { + return 0; + } + return 1; +} +/* }}} */ + +ZEND_API int ZEND_FASTCALL zend_parse_arg_bool_slow(zval *arg, zend_bool *dest) /* {{{ */ +{ + if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { + return 0; + } + return zend_parse_arg_bool_weak(arg, dest); +} +/* }}} */ + +ZEND_API int ZEND_FASTCALL zend_parse_arg_long_weak(zval *arg, zend_long *dest) /* {{{ */ +{ + if (EXPECTED(Z_TYPE_P(arg) == IS_DOUBLE)) { + if (UNEXPECTED(zend_isnan(Z_DVAL_P(arg)))) { + return 0; + } + if (UNEXPECTED(!ZEND_DOUBLE_FITS_LONG(Z_DVAL_P(arg)))) { + return 0; + } else { + *dest = zend_dval_to_lval(Z_DVAL_P(arg)); + } + } else if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) { + double d; + int type; + + if (UNEXPECTED((type = is_numeric_str_function(Z_STR_P(arg), dest, &d)) != IS_LONG)) { + if (EXPECTED(type != 0)) { + if (UNEXPECTED(zend_isnan(d))) { + return 0; + } + if (UNEXPECTED(!ZEND_DOUBLE_FITS_LONG(d))) { + return 0; + } else { + *dest = zend_dval_to_lval(d); + } + } else { + return 0; + } + } + } else if (EXPECTED(Z_TYPE_P(arg) < IS_TRUE)) { + *dest = 0; + } else if (EXPECTED(Z_TYPE_P(arg) == IS_TRUE)) { + *dest = 1; + } else { + return 0; + } + return 1; +} +/* }}} */ + +ZEND_API int ZEND_FASTCALL zend_parse_arg_long_slow(zval *arg, zend_long *dest) /* {{{ */ +{ + if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { + return 0; + } + return zend_parse_arg_long_weak(arg, dest); +} +/* }}} */ + +ZEND_API int ZEND_FASTCALL zend_parse_arg_long_cap_weak(zval *arg, zend_long *dest) /* {{{ */ +{ + if (EXPECTED(Z_TYPE_P(arg) == IS_DOUBLE)) { + if (UNEXPECTED(zend_isnan(Z_DVAL_P(arg)))) { + return 0; + } + if (UNEXPECTED(!ZEND_DOUBLE_FITS_LONG(Z_DVAL_P(arg)))) { + *dest = (Z_DVAL_P(arg) > 0) ? ZEND_LONG_MAX : ZEND_LONG_MIN; + } else { + *dest = zend_dval_to_lval(Z_DVAL_P(arg)); + } + } else if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) { + double d; + int type; + + if (UNEXPECTED((type = is_numeric_str_function(Z_STR_P(arg), dest, &d)) != IS_LONG)) { + if (EXPECTED(type != 0)) { + if (UNEXPECTED(zend_isnan(d))) { + return 0; + } + if (UNEXPECTED(!ZEND_DOUBLE_FITS_LONG(d))) { + *dest = (d > 0) ? ZEND_LONG_MAX : ZEND_LONG_MIN; + } else { + *dest = zend_dval_to_lval(d); + } + } else { + return 0; + } + } + } else if (EXPECTED(Z_TYPE_P(arg) < IS_TRUE)) { + *dest = 0; + } else if (EXPECTED(Z_TYPE_P(arg) == IS_TRUE)) { + *dest = 1; + } else { + return 0; + } + return 1; +} +/* }}} */ + +ZEND_API int ZEND_FASTCALL zend_parse_arg_long_cap_slow(zval *arg, zend_long *dest) /* {{{ */ +{ + if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { + return 0; + } + return zend_parse_arg_long_cap_weak(arg, dest); +} +/* }}} */ + +ZEND_API int ZEND_FASTCALL zend_parse_arg_double_weak(zval *arg, double *dest) /* {{{ */ +{ + if (EXPECTED(Z_TYPE_P(arg) == IS_LONG)) { + *dest = (double)Z_LVAL_P(arg); + } else if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) { + zend_long l; + int type; + + if (UNEXPECTED((type = is_numeric_str_function(Z_STR_P(arg), &l, dest)) != IS_DOUBLE)) { + if (EXPECTED(type != 0)) { + *dest = (double)(l); + } else { + return 0; + } + } + } else if (EXPECTED(Z_TYPE_P(arg) < IS_TRUE)) { + *dest = 0.0; + } else if (EXPECTED(Z_TYPE_P(arg) == IS_TRUE)) { + *dest = 1.0; + } else { + return 0; + } + return 1; +} +/* }}} */ + +ZEND_API int ZEND_FASTCALL zend_parse_arg_double_slow(zval *arg, double *dest) /* {{{ */ +{ + if (EXPECTED(Z_TYPE_P(arg) == IS_LONG)) { + /* SSTH Exception: IS_LONG may be accepted instead as IS_DOUBLE */ + *dest = (double)Z_LVAL_P(arg); + } else if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { + return 0; + } + return zend_parse_arg_double_weak(arg, dest); +} +/* }}} */ + +ZEND_API int ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest) /* {{{ */ +{ + if (EXPECTED(Z_TYPE_P(arg) < IS_STRING)) { + convert_to_string(arg); + *dest = Z_STR_P(arg); + } else if (UNEXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { + if (Z_OBJ_HANDLER_P(arg, cast_object)) { + zval obj; + if (Z_OBJ_HANDLER_P(arg, cast_object)(arg, &obj, IS_STRING) == SUCCESS) { + zval_ptr_dtor(arg); + ZVAL_COPY_VALUE(arg, &obj); + *dest = Z_STR_P(arg); + return 1; + } + } + /* Standard PHP objects */ + if (Z_OBJ_HT_P(arg) == &std_object_handlers || !Z_OBJ_HANDLER_P(arg, cast_object)) { + SEPARATE_ZVAL_NOREF(arg); + if (zend_std_cast_object_tostring(arg, arg, IS_STRING) == SUCCESS) { + *dest = Z_STR_P(arg); + return 1; + } + } + if (!Z_OBJ_HANDLER_P(arg, cast_object) && Z_OBJ_HANDLER_P(arg, get)) { + zval rv; + zval *z = Z_OBJ_HANDLER_P(arg, get)(arg, &rv); + + Z_ADDREF_P(z); + if (Z_TYPE_P(z) != IS_OBJECT) { + zval_dtor(arg); + ZVAL_NULL(arg); + if (!zend_make_printable_zval(z, arg)) { + ZVAL_ZVAL(arg, z, 1, 1); + } + *dest = Z_STR_P(arg); + return 1; + } + zval_ptr_dtor(z); + } + return 0; + } else { + return 0; + } + return 1; +} +/* }}} */ + +ZEND_API int ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest) /* {{{ */ +{ + if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { + return 0; + } + return zend_parse_arg_str_weak(arg, dest); +} +/* }}} */ + static const char *zend_parse_arg_impl(int arg_num, zval *arg, va_list *va, const char **spec, char **error, int *severity) /* {{{ */ { const char *spec_walk = *spec; @@ -539,7 +719,7 @@ static const char *zend_parse_arg_impl(int arg_num, zval *arg, va_list *va, cons break; } else { if (is_callable_error) { - *severity = E_WARNING; + *severity = E_EXCEPTION | E_ERROR; zend_spprintf(error, 0, "to be a valid callback, %s", is_callable_error); efree(is_callable_error); return ""; @@ -570,24 +750,24 @@ static const char *zend_parse_arg_impl(int arg_num, zval *arg, va_list *va, cons } /* }}} */ -static int zend_parse_arg(int arg_num, zval *arg, va_list *va, const char **spec, int quiet) /* {{{ */ +static int zend_parse_arg(int arg_num, zval *arg, va_list *va, const char **spec, int flags) /* {{{ */ { const char *expected_type = NULL; char *error = NULL; - int severity = E_WARNING; + int severity; expected_type = zend_parse_arg_impl(arg_num, arg, va, spec, &error, &severity); if (expected_type) { - if (!quiet && (*expected_type || error)) { + if (!(flags & ZEND_PARSE_PARAMS_QUIET) && (*expected_type || error)) { const char *space; const char *class_name = get_active_class_name(&space); if (error) { - zend_error(severity, "%s%s%s() expects parameter %d %s", - class_name, space, get_active_function_name(), arg_num, error); + zend_internal_type_error(ZEND_ARG_USES_STRICT_TYPES(), "%s%s%s() expects parameter %d %s", + class_name, space, get_active_function_name(), arg_num, error); efree(error); } else { - zend_error(severity, "%s%s%s() expects parameter %d to be %s, %s given", + zend_internal_type_error(ZEND_ARG_USES_STRICT_TYPES(), "%s%s%s() expects parameter %d to be %s, %s given", class_name, space, get_active_function_name(), arg_num, expected_type, zend_zval_type_name(arg)); } @@ -605,10 +785,9 @@ ZEND_API int zend_parse_parameter(int flags, int arg_num, zval *arg, const char { va_list va; int ret; - int quiet = flags & ZEND_PARSE_PARAMS_QUIET; va_start(va, spec); - ret = zend_parse_arg(arg_num, arg, &va, &spec, quiet); + ret = zend_parse_arg(arg_num, arg, &va, &spec, flags); va_end(va); return ret; @@ -623,7 +802,6 @@ static int zend_parse_va_args(int num_args, const char *type_spec, va_list *va, int post_varargs = 0; zval *arg; int arg_count; - int quiet = flags & ZEND_PARSE_PARAMS_QUIET; zend_bool have_varargs = 0; zval **varargs = NULL; int *n_varargs = NULL; @@ -656,9 +834,10 @@ static int zend_parse_va_args(int num_args, const char *type_spec, va_list *va, case '*': case '+': if (have_varargs) { - if (!quiet) { + if (!(flags & ZEND_PARSE_PARAMS_QUIET)) { zend_function *active_function = EG(current_execute_data)->func; const char *class_name = active_function->common.scope ? active_function->common.scope->name->val : ""; + zend_error(E_WARNING, "%s%s%s(): only one varargs specifier (* or +) is permitted", class_name, class_name[0] ? "::" : "", @@ -676,7 +855,7 @@ static int zend_parse_va_args(int num_args, const char *type_spec, va_list *va, break; default: - if (!quiet) { + if (!(flags & ZEND_PARSE_PARAMS_QUIET)) { zend_function *active_function = EG(current_execute_data)->func; const char *class_name = active_function->common.scope ? active_function->common.scope->name->val : ""; zend_error(E_WARNING, "%s%s%s(): bad type specifier while parsing parameters", @@ -699,10 +878,10 @@ static int zend_parse_va_args(int num_args, const char *type_spec, va_list *va, } if (num_args < min_num_args || (num_args > max_num_args && max_num_args > 0)) { - if (!quiet) { + if (!(flags & ZEND_PARSE_PARAMS_QUIET)) { zend_function *active_function = EG(current_execute_data)->func; const char *class_name = active_function->common.scope ? active_function->common.scope->name->val : ""; - zend_error(E_WARNING, "%s%s%s() expects %s %d parameter%s, %d given", + zend_internal_type_error(ZEND_ARG_USES_STRICT_TYPES(), "%s%s%s() expects %s %d parameter%s, %d given", class_name, class_name[0] ? "::" : "", active_function->common.function_name->val, @@ -751,7 +930,7 @@ static int zend_parse_va_args(int num_args, const char *type_spec, va_list *va, arg = ZEND_CALL_ARG(EG(current_execute_data), i + 1); - if (zend_parse_arg(i+1, arg, va, &type_spec, quiet) == FAILURE) { + if (zend_parse_arg(i+1, arg, va, &type_spec, flags) == FAILURE) { /* clean up varargs array if it was used */ if (varargs && *varargs) { *varargs = NULL; @@ -765,13 +944,13 @@ static int zend_parse_va_args(int num_args, const char *type_spec, va_list *va, } /* }}} */ -#define RETURN_IF_ZERO_ARGS(num_args, type_spec, quiet) { \ +#define RETURN_IF_ZERO_ARGS(num_args, type_spec, flags) { \ int __num_args = (num_args); \ \ - if (0 == (type_spec)[0] && 0 != __num_args && !(quiet)) { \ + if (0 == (type_spec)[0] && 0 != __num_args && !(flags & ZEND_PARSE_PARAMS_QUIET)) { \ const char *__space; \ const char * __class_name = get_active_class_name(&__space); \ - zend_error(E_WARNING, "%s%s%s() expects exactly 0 parameters, %d given", \ + zend_internal_type_error(ZEND_ARG_USES_STRICT_TYPES(), "%s%s%s() expects exactly 0 parameters, %d given", \ __class_name, __space, \ get_active_function_name(), __num_args); \ return FAILURE; \ @@ -783,7 +962,7 @@ ZEND_API int zend_parse_parameters_ex(int flags, int num_args, const char *type_ va_list va; int retval; - RETURN_IF_ZERO_ARGS(num_args, type_spec, flags & ZEND_PARSE_PARAMS_QUIET); + RETURN_IF_ZERO_ARGS(num_args, type_spec, flags); va_start(va, type_spec); retval = zend_parse_va_args(num_args, type_spec, &va, flags); @@ -797,11 +976,12 @@ ZEND_API int zend_parse_parameters(int num_args, const char *type_spec, ...) /* { va_list va; int retval; + int flags = 0; - RETURN_IF_ZERO_ARGS(num_args, type_spec, 0); + RETURN_IF_ZERO_ARGS(num_args, type_spec, flags); va_start(va, type_spec); - retval = zend_parse_va_args(num_args, type_spec, &va, 0); + retval = zend_parse_va_args(num_args, type_spec, &va, flags); va_end(va); return retval; @@ -812,6 +992,7 @@ ZEND_API int zend_parse_method_parameters(int num_args, zval *this_ptr, const ch { va_list va; int retval; + int flags = 0; const char *p = type_spec; zval **object; zend_class_entry *ce; @@ -821,15 +1002,16 @@ ZEND_API int zend_parse_method_parameters(int num_args, zval *this_ptr, const ch * In that case EG(This) would still be the $this from the calling code and we'd take the * wrong branch here. */ zend_bool is_method = EG(current_execute_data)->func->common.scope != NULL; + if (!is_method || !this_ptr || Z_TYPE_P(this_ptr) != IS_OBJECT) { - RETURN_IF_ZERO_ARGS(num_args, p, 0); + RETURN_IF_ZERO_ARGS(num_args, p, flags); va_start(va, type_spec); - retval = zend_parse_va_args(num_args, type_spec, &va, 0); + retval = zend_parse_va_args(num_args, type_spec, &va, flags); va_end(va); } else { p++; - RETURN_IF_ZERO_ARGS(num_args, p, 0); + RETURN_IF_ZERO_ARGS(num_args, p, flags); va_start(va, type_spec); @@ -842,7 +1024,7 @@ ZEND_API int zend_parse_method_parameters(int num_args, zval *this_ptr, const ch Z_OBJCE_P(this_ptr)->name->val, get_active_function_name(), ce->name->val, get_active_function_name()); } - retval = zend_parse_va_args(num_args, p, &va, 0); + retval = zend_parse_va_args(num_args, p, &va, flags); va_end(va); } return retval; @@ -856,17 +1038,16 @@ ZEND_API int zend_parse_method_parameters_ex(int flags, int num_args, zval *this const char *p = type_spec; zval **object; zend_class_entry *ce; - int quiet = flags & ZEND_PARSE_PARAMS_QUIET; if (!this_ptr) { - RETURN_IF_ZERO_ARGS(num_args, p, quiet); + RETURN_IF_ZERO_ARGS(num_args, p, flags); va_start(va, type_spec); retval = zend_parse_va_args(num_args, type_spec, &va, flags); va_end(va); } else { p++; - RETURN_IF_ZERO_ARGS(num_args, p, quiet); + RETURN_IF_ZERO_ARGS(num_args, p, flags); va_start(va, type_spec); @@ -875,7 +1056,7 @@ ZEND_API int zend_parse_method_parameters_ex(int flags, int num_args, zval *this *object = this_ptr; if (ce && !instanceof_function(Z_OBJCE_P(this_ptr), ce)) { - if (!quiet) { + if (!(flags & ZEND_PARSE_PARAMS_QUIET)) { zend_error(E_CORE_ERROR, "%s::%s() must be derived from %s::%s", ce->name->val, get_active_function_name(), Z_OBJCE_P(this_ptr)->name->val, get_active_function_name()); } @@ -2535,6 +2716,9 @@ ZEND_API int zend_register_class_alias_ex(const char *name, size_t name_len, zen lcname = zend_string_alloc(name_len, 1); zend_str_tolower_copy(lcname->val, name, name_len); } + + zend_assert_valid_class_name(lcname); + ce = zend_hash_add_ptr(CG(class_table), lcname, ce); zend_string_release(lcname); if (ce) { diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 41ef47e4ff..bd90e953fe 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -704,10 +704,10 @@ typedef enum _zend_expected_type { Z_EXPECTED_LAST } zend_expected_type; -ZEND_API void zend_wrong_paramers_count_error(int num_args, int min_num_args, int max_num_args); -ZEND_API void zend_wrong_paramer_type_error(int num, zend_expected_type expected_type, zval *arg); -ZEND_API void zend_wrong_paramer_class_error(int num, char *name, zval *arg); -ZEND_API void zend_wrong_callback_error(int severity, int num, char *error); +ZEND_API void ZEND_FASTCALL zend_wrong_paramers_count_error(int num_args, int min_num_args, int max_num_args); +ZEND_API void ZEND_FASTCALL zend_wrong_paramer_type_error(int num, zend_expected_type expected_type, zval *arg); +ZEND_API void ZEND_FASTCALL zend_wrong_paramer_class_error(int num, char *name, zval *arg); +ZEND_API void ZEND_FASTCALL zend_wrong_callback_error(int severity, int num, char *error); #define ZPP_ERROR_OK 0 #define ZPP_ERROR_FAILURE 1 @@ -1048,8 +1048,17 @@ ZEND_API void zend_wrong_callback_error(int severity, int num, char *error); /* Inlined implementations shared by new and old parameter parsing APIs */ -ZEND_API int parse_arg_object_to_str(zval *arg, zend_string **str, int type); -ZEND_API int zend_parse_arg_class(zval *arg, zend_class_entry **pce, int num, int check_null); +ZEND_API int ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **pce, int num, int check_null); +ZEND_API int ZEND_FASTCALL zend_parse_arg_bool_slow(zval *arg, zend_bool *dest); +ZEND_API int ZEND_FASTCALL zend_parse_arg_bool_weak(zval *arg, zend_bool *dest); +ZEND_API int ZEND_FASTCALL zend_parse_arg_long_slow(zval *arg, zend_long *dest); +ZEND_API int ZEND_FASTCALL zend_parse_arg_long_weak(zval *arg, zend_long *dest); +ZEND_API int ZEND_FASTCALL zend_parse_arg_long_cap_slow(zval *arg, zend_long *dest); +ZEND_API int ZEND_FASTCALL zend_parse_arg_long_cap_weak(zval *arg, zend_long *dest); +ZEND_API int ZEND_FASTCALL zend_parse_arg_double_slow(zval *arg, double *dest); +ZEND_API int ZEND_FASTCALL zend_parse_arg_double_weak(zval *arg, double *dest); +ZEND_API int ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest); +ZEND_API int ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest); static zend_always_inline int zend_parse_arg_bool(zval *arg, zend_bool *dest, zend_bool *is_null, int check_null) { @@ -1058,71 +1067,31 @@ static zend_always_inline int zend_parse_arg_bool(zval *arg, zend_bool *dest, ze } if (EXPECTED(Z_TYPE_P(arg) == IS_TRUE)) { *dest = 1; - } else if (EXPECTED(Z_TYPE_P(arg) < IS_TRUE)) { - if (check_null) { - *is_null = (Z_TYPE_P(arg) == IS_NULL); - } + } else if (EXPECTED(Z_TYPE_P(arg) == IS_FALSE)) { + *dest = 0; + } else if (check_null && Z_TYPE_P(arg) == IS_NULL) { + *is_null = 1; *dest = 0; - } else if (EXPECTED(Z_TYPE_P(arg) <= IS_STRING)) { - *dest = zend_is_true(arg); } else { - return 0; + return zend_parse_arg_bool_slow(arg, dest); } return 1; } -static zend_always_inline int zend_parse_arg_long(zval *arg, zend_long *dest, zend_bool *is_null, int check_null, int strict) +static zend_always_inline int zend_parse_arg_long(zval *arg, zend_long *dest, zend_bool *is_null, int check_null, int cap) { if (check_null) { *is_null = 0; } if (EXPECTED(Z_TYPE_P(arg) == IS_LONG)) { *dest = Z_LVAL_P(arg); - } else if (EXPECTED(Z_TYPE_P(arg) == IS_DOUBLE)) { - if (UNEXPECTED(zend_isnan(Z_DVAL_P(arg)))) { - return 0; - } - if (UNEXPECTED(!ZEND_DOUBLE_FITS_LONG(Z_DVAL_P(arg)))) { - /* Ironically, the strict parameter makes zpp *non*-strict here */ - if (strict) { - *dest = (Z_DVAL_P(arg) > 0) ? ZEND_LONG_MAX : ZEND_LONG_MIN; - } else { - return 0; - } - } else { - *dest = zend_dval_to_lval(Z_DVAL_P(arg)); - } - } else if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) { - double d; - int type; - - if (UNEXPECTED((type = is_numeric_str_function(Z_STR_P(arg), dest, &d)) != IS_LONG)) { - if (EXPECTED(type != 0)) { - if (UNEXPECTED(zend_isnan(d))) { - return 0; - } - if (UNEXPECTED(!ZEND_DOUBLE_FITS_LONG(d))) { - if (strict) { - *dest = (d > 0) ? ZEND_LONG_MAX : ZEND_LONG_MIN; - } else { - return 0; - } - } else { - *dest = zend_dval_to_lval(d); - } - } else { - return 0; - } - } - } else if (EXPECTED(Z_TYPE_P(arg) < IS_TRUE)) { - if (check_null) { - *is_null = (Z_TYPE_P(arg) == IS_NULL); - } + } else if (check_null && Z_TYPE_P(arg) == IS_NULL) { + *is_null = 1; *dest = 0; - } else if (EXPECTED(Z_TYPE_P(arg) == IS_TRUE)) { - *dest = 1; + } else if (cap) { + return zend_parse_arg_long_cap_slow(arg, dest); } else { - return 0; + return zend_parse_arg_long_slow(arg, dest); } return 1; } @@ -1134,28 +1103,11 @@ static zend_always_inline int zend_parse_arg_double(zval *arg, double *dest, zen } if (EXPECTED(Z_TYPE_P(arg) == IS_DOUBLE)) { *dest = Z_DVAL_P(arg); - } else if (EXPECTED(Z_TYPE_P(arg) == IS_LONG)) { - *dest = (double)Z_LVAL_P(arg); - } else if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) { - zend_long l; - int type; - - if (UNEXPECTED((type = is_numeric_str_function(Z_STR_P(arg), &l, dest)) != IS_DOUBLE)) { - if (EXPECTED(type != 0)) { - *dest = (double)(l); - } else { - return 0; - } - } - } else if (EXPECTED(Z_TYPE_P(arg) < IS_TRUE)) { - if (check_null) { - *is_null = (Z_TYPE_P(arg) == IS_NULL); - } + } else if (check_null && Z_TYPE_P(arg) == IS_NULL) { + *is_null = 1; *dest = 0.0; - } else if (EXPECTED(Z_TYPE_P(arg) == IS_TRUE)) { - *dest = 1.0; } else { - return 0; + return zend_parse_arg_double_slow(arg, dest); } return 1; } @@ -1164,20 +1116,10 @@ static zend_always_inline int zend_parse_arg_str(zval *arg, zend_string **dest, { if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) { *dest = Z_STR_P(arg); - } else if (EXPECTED(Z_TYPE_P(arg) < IS_STRING)) { - if (check_null && UNEXPECTED(Z_TYPE_P(arg) == IS_NULL)) { - *dest = NULL; - } else { - if (Z_COPYABLE_P(arg) && Z_REFCOUNT_P(arg) > 1) { - Z_DELREF_P(arg); - zval_copy_ctor_func(arg); - } - convert_to_string(arg); - *dest = Z_STR_P(arg); - } - } else if (UNEXPECTED(Z_TYPE_P(arg) != IS_OBJECT) || - UNEXPECTED(parse_arg_object_to_str(arg, dest, IS_STRING) != SUCCESS)) { - return 0; + } else if (check_null && Z_TYPE_P(arg) == IS_NULL) { + *dest = NULL; + } else { + return zend_parse_arg_str_slow(arg, dest); } return 1; } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index b7c25eea8d..64d96e452d 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -129,6 +129,72 @@ static zend_bool zend_get_unqualified_name(const zend_string *name, const char * } /* }}} */ +typedef struct _scalar_typehint_info { + const char* name; + const size_t name_len; + const zend_uchar type; +} scalar_typehint_info; + +static const scalar_typehint_info scalar_typehints[] = { + {"int", sizeof("int") - 1, IS_LONG}, + {"float", sizeof("float") - 1, IS_DOUBLE}, + {"string", sizeof("string") - 1, IS_STRING}, + {"bool", sizeof("bool") - 1, _IS_BOOL}, + {NULL, 0, IS_UNDEF} +}; + +static zend_always_inline const scalar_typehint_info* zend_find_scalar_typehint(const zend_string *const_name) /* {{{ */ +{ + const scalar_typehint_info *info = &scalar_typehints[0]; + const char *uqname; + size_t uqname_len; + + if (!zend_get_unqualified_name(const_name, &uqname, &uqname_len)) { + uqname = const_name->val; + uqname_len = const_name->len; + } + + while (info->name) { + if (uqname_len == info->name_len && zend_binary_strcasecmp(uqname, uqname_len, info->name, info->name_len) == 0) { + break; + } + info++; + } + + if (info->name) { + return info; + } else { + return NULL; + } +} +/* }}} */ + +ZEND_API void zend_assert_valid_class_name(const zend_string *const_name) /* {{{ */ +{ + const scalar_typehint_info *info = zend_find_scalar_typehint(const_name); + + if (info) { + zend_error_noreturn(E_COMPILE_ERROR, "\"%s\" cannot be used as a class name", info->name); + } +} +/* }}} */ + +static zend_always_inline zend_uchar zend_lookup_scalar_typehint_by_name(const zend_string *const_name) /* {{{ */ +{ + const scalar_typehint_info *info = zend_find_scalar_typehint(const_name); + + if (info) { + if (const_name->len != info->name_len) { + zend_error_noreturn(E_COMPILE_ERROR, "\"%s\" cannot be used as a type declaration", const_name->val); + } + return info->type; + } else { + return 0; + } +} +/* }}} */ + + static void init_compiler_declarables(void) /* {{{ */ { ZVAL_LONG(&CG(declarables).ticks, 0); @@ -1864,7 +1930,11 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */ static void zend_emit_return_type_check(znode *expr, zend_arg_info *return_info) /* {{{ */ { if (return_info->type_hint != IS_UNDEF) { - zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL); + zend_op *opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL); + if (expr && expr->op_type == IS_CONST) { + opline->result_type = expr->op_type = IS_TMP_VAR; + opline->result.var = expr->u.op.var = get_temporary_variable(CG(active_op_array)); + } } } /* }}} */ @@ -3334,11 +3404,9 @@ void zend_compile_return(zend_ast *ast) /* {{{ */ opline->op1.var = CG(context).fast_call_var; } - if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - zend_emit_return_type_check(&expr_node, CG(active_op_array)->arg_info - 1); - if (expr_node.op_type == IS_CONST) { - zval_copy_ctor(&expr_node.u.constant); - } + /* Generator return types are handled separately */ + if (!(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) && CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { + zend_emit_return_type_check(expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1); } opline = zend_emit_op(NULL, by_ref ? ZEND_RETURN_BY_REF : ZEND_RETURN, &expr_node, NULL); @@ -3908,6 +3976,28 @@ void zend_handle_encoding_declaration(zend_ast *ast) /* {{{ */ } /* }}} */ +static int zend_declare_is_first_statement(zend_ast *ast) /* {{{ */ +{ + uint32_t i = 0; + zend_ast_list *file_ast = zend_ast_get_list(CG(ast)); + + /* Check to see if this declare is preceeded only by declare statements */ + while (i < file_ast->children) { + if (file_ast->child[i] == ast) { + return SUCCESS; + } else if (file_ast->child[i] == NULL) { + /* Empty statements are not allowed prior to a declare */ + return FAILURE; + } else if (file_ast->child[i]->kind != ZEND_AST_DECLARE) { + /* declares can only be preceeded by other declares */ + return FAILURE; + } + i++; + } + return FAILURE; +} +/* }}} */ + void zend_compile_declare(zend_ast *ast) /* {{{ */ { zend_ast_list *declares = zend_ast_get_list(ast->child[0]); @@ -3928,29 +4018,34 @@ void zend_compile_declare(zend_ast *ast) /* {{{ */ ZVAL_COPY_VALUE(&CG(declarables).ticks, &value_zv); zval_dtor(&value_zv); } else if (zend_string_equals_literal_ci(name, "encoding")) { - uint32_t i = 0; - zend_bool valid = 0; - /* Encoding declaration was already handled during parsing. Here we - * only check that it is the first statement in the file. */ - zend_ast_list *file_ast = zend_ast_get_list(CG(ast)); - - /* Check to see if this declare is preceeded only by declare statements */ - while (valid == 0 && i < file_ast->children) { - if (file_ast->child[i] == ast) { - valid = 1; - } else if (file_ast->child[i] == NULL) { - /* Empty statements are not allowed prior to a declare */ - break; - } else if (file_ast->child[i]->kind != ZEND_AST_DECLARE) { - /* declares can only be preceeded by other declares */ - break; - } - i++; - } - if (valid != 1) { + + if (FAILURE == zend_declare_is_first_statement(ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Encoding declaration pragma must be " "the very first statement in the script"); } + } else if (zend_string_equals_literal_ci(name, "strict_types")) { + zval value_zv; + + if (FAILURE == zend_declare_is_first_statement(ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "strict_types declaration must be " + "the very first statement in the script"); + } + + if (ast->child[1] != NULL) { + zend_error_noreturn(E_COMPILE_ERROR, "strict_types declaration must not " + "use block mode"); + } + + 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"); + } + + if (Z_LVAL(value_zv) == 1) { + CG(active_op_array)->fn_flags |= ZEND_ACC_STRICT_TYPES; + } + } else { zend_error(E_COMPILE_WARNING, "Unsupported declare '%s'", name->val); } @@ -3995,19 +4090,24 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_ arg_infos->type_hint = return_type_ast->attr; } else { zend_string *class_name = zend_ast_get_str(return_type_ast); + zend_uchar type = zend_lookup_scalar_typehint_by_name(class_name); - if (zend_is_const_default_class_ref(return_type_ast)) { - class_name = zend_resolve_class_name_ast(return_type_ast); + if (type != 0) { + arg_infos->type_hint = type; } else { - zend_string_addref(class_name); - if (!is_method) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare a return type of %s outside of a class scope", class_name->val); - return; + if (zend_is_const_default_class_ref(return_type_ast)) { + class_name = zend_resolve_class_name_ast(return_type_ast); + } else { + zend_string_addref(class_name); + if (!is_method) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare a return type of %s outside of a class scope", class_name->val); + return; + } } - } - arg_infos->type_hint = IS_OBJECT; - arg_infos->class_name = class_name; + arg_infos->type_hint = IS_OBJECT; + arg_infos->class_name = class_name; + } } arg_infos++; @@ -4117,19 +4217,30 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_ } } else { zend_string *class_name = zend_ast_get_str(type_ast); + zend_uchar type; - if (zend_is_const_default_class_ref(type_ast)) { - class_name = zend_resolve_class_name_ast(type_ast); + type = zend_lookup_scalar_typehint_by_name(class_name); + if (type != 0) { + arg_info->type_hint = type; } else { - zend_string_addref(class_name); - } - arg_info->type_hint = IS_OBJECT; - arg_info->class_name = class_name; + if (zend_is_const_default_class_ref(type_ast)) { + class_name = zend_resolve_class_name_ast(type_ast); + } else { + zend_string_addref(class_name); + } + arg_info->type_hint = IS_OBJECT; + arg_info->class_name = class_name; + } if (default_ast && !has_null_default && !Z_CONSTANT(default_node.u.constant)) { + if (arg_info->class_name) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " "with a class type hint can only be NULL"); + } else if (!ZEND_SAME_FAKE_TYPE(arg_info->type_hint, Z_TYPE(default_node.u.constant))) { + zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " + "with a %s type hint can only be %s or NULL", class_name->val, class_name->val); + } } } } @@ -4409,6 +4520,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */ init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE); + op_array->fn_flags |= (orig_op_array->fn_flags & ZEND_ACC_STRICT_TYPES); op_array->fn_flags |= decl->flags; op_array->line_start = decl->start_lineno; op_array->line_end = decl->end_lineno; @@ -4743,6 +4855,8 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ import_name = zend_hash_find_ptr(CG(current_import), lcname); } + zend_assert_valid_class_name(name); + if (CG(current_namespace)) { name = zend_prefix_with_ns(name); @@ -4978,6 +5092,10 @@ void zend_compile_use(zend_ast *ast) /* {{{ */ } } + if (type == T_CLASS) { + zend_assert_valid_class_name(new_name); + } + if (case_sensitive) { lookup_name = zend_string_copy(new_name); } else { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 45441e2108..c53833f149 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -252,6 +252,9 @@ typedef struct _zend_try_catch_element { /* Function has a return type hint (or class has such non-private function) */ #define ZEND_ACC_HAS_RETURN_TYPE 0x40000000 +/* op_array uses strict mode types */ +#define ZEND_ACC_STRICT_TYPES 0x80000000 + char *zend_visibility_string(uint32_t fn_flags); typedef struct _zend_property_info { @@ -459,6 +462,20 @@ struct _zend_execute_data { #define EX_CALL_KIND() ZEND_CALL_KIND(execute_data) #define EX_NUM_ARGS() ZEND_CALL_NUM_ARGS(execute_data) +#define ZEND_CALL_USES_STRICT_TYPES(call) \ + (((call)->func->common.fn_flags & ZEND_ACC_STRICT_TYPES) != 0) + +#define EX_USES_STRICT_TYPES() \ + ZEND_CALL_USES_STRICT_TYPES(execute_data) + +#define ZEND_ARG_USES_STRICT_TYPES() \ + (EG(current_execute_data)->prev_execute_data && \ + EG(current_execute_data)->prev_execute_data->func && \ + ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)->prev_execute_data)) + +#define ZEND_RET_USES_STRICT_TYPES() \ + ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)) + #define EX_VAR(n) ZEND_CALL_VAR(execute_data, n) #define EX_VAR_NUM(n) ZEND_CALL_VAR_NUM(execute_data, n) @@ -732,6 +749,8 @@ int zendlex(zend_parser_stack_elem *elem); int zend_add_literal(zend_op_array *op_array, zval *zv); +ZEND_API void zend_assert_valid_class_name(const zend_string *const_name); + /* BEGIN: OPCODES */ #include "zend_vm_opcodes.h" diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 7070934426..b88c13e6d0 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -35,6 +35,7 @@ static zend_class_entry *default_exception_ce; static zend_class_entry *error_exception_ce; static zend_class_entry *engine_exception_ce; static zend_class_entry *parse_exception_ce; +static zend_class_entry *type_exception_ce; static zend_object_handlers default_exception_handlers; ZEND_API void (*zend_throw_exception_hook)(zval *ex); @@ -778,6 +779,10 @@ void zend_register_default_exception(void) /* {{{ */ INIT_CLASS_ENTRY(ce, "ParseException", NULL); parse_exception_ce = zend_register_internal_class_ex(&ce, base_exception_ce); parse_exception_ce->create_object = zend_default_exception_new; + + INIT_CLASS_ENTRY(ce, "TypeException", NULL); + type_exception_ce = zend_register_internal_class_ex(&ce, engine_exception_ce); + type_exception_ce->create_object = zend_default_exception_new; } /* }}} */ @@ -811,6 +816,12 @@ ZEND_API zend_class_entry *zend_get_parse_exception(void) /* {{{ */ } /* }}} */ +ZEND_API zend_class_entry *zend_get_type_exception(void) /* {{{ */ +{ + return type_exception_ce; +} +/* }}} */ + ZEND_API zend_object *zend_throw_exception(zend_class_entry *exception_ce, const char *message, zend_long code) /* {{{ */ { @@ -892,13 +903,18 @@ ZEND_API void zend_exception_error(zend_object *ex, int severity) /* {{{ */ ZVAL_OBJ(&exception, ex); ce_exception = Z_OBJCE(exception); EG(exception) = NULL; - if (ce_exception == parse_exception_ce || ce_exception == engine_exception_ce) { + if (ce_exception == parse_exception_ce || ce_exception == engine_exception_ce || ce_exception == type_exception_ce) { zend_string *message = zval_get_string(GET_PROPERTY(&exception, "message")); zend_string *file = zval_get_string(GET_PROPERTY_SILENT(&exception, "file")); zend_long line = zval_get_long(GET_PROPERTY_SILENT(&exception, "line")); zend_long code = zval_get_long(GET_PROPERTY_SILENT(&exception, "code")); - zend_error_helper(code, file->val, line, "%s", message->val); + if (ce_exception == type_exception_ce && strstr(message->val, ", called in ")) { + zend_error_helper(code, file->val, line, "%s and defined", message->val); + } else { + zend_error_helper(code, file->val, line, "%s", message->val); + } + zend_string_release(file); zend_string_release(message); OBJ_RELEASE(ex); diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h index 10609fecb6..e2c1f1fac7 100644 --- a/Zend/zend_exceptions.h +++ b/Zend/zend_exceptions.h @@ -39,6 +39,7 @@ ZEND_API zend_class_entry *zend_exception_get_default(void); ZEND_API zend_class_entry *zend_get_error_exception(void); ZEND_API zend_class_entry *zend_get_engine_exception(void); ZEND_API zend_class_entry *zend_get_parse_exception(void); +ZEND_API zend_class_entry *zend_get_type_exception(void); ZEND_API void zend_register_default_classes(void); /* exception_ce NULL or zend_exception_get_default() or a derived class diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index caaeb6a5fd..0fe2a26e97 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -598,7 +598,7 @@ ZEND_API char * zend_verify_arg_class_kind(const zend_arg_info *cur_arg_info, ch } } -ZEND_API void zend_verify_arg_error(int error_type, const zend_function *zf, uint32_t arg_num, const char *need_msg, const char *need_kind, const char *given_msg, const char *given_kind, zval *arg) +ZEND_API void zend_verify_arg_error(const zend_function *zf, uint32_t arg_num, const char *need_msg, const char *need_kind, const char *given_msg, const char *given_kind, zval *arg) { zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data; const char *fname = zf->common.function_name->val; @@ -621,16 +621,18 @@ ZEND_API void zend_verify_arg_error(int error_type, const zend_function *zf, uin } if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) { - zend_error(error_type, "Argument %d passed to %s%s%s() must %s%s, %s%s given, called in %s on line %d and defined", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind, ptr->func->op_array.filename->val, ptr->opline->lineno); + zend_type_error("Argument %d passed to %s%s%s() must %s%s, %s%s given, called in %s on line %d", + arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind, + ptr->func->op_array.filename->val, ptr->opline->lineno); } else { - zend_error(error_type, "Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); + zend_type_error("Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); } if (arg) { ZVAL_COPY_VALUE(arg, &old_arg); } } else { - zend_error(error_type, "Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); + zend_type_error("Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); } } @@ -649,10 +651,71 @@ static int is_null_constant(zval *default_value) return 0; } +static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *arg) +{ + switch (type_hint) { + case _IS_BOOL: { + zend_bool dest; + + if (!zend_parse_arg_bool_weak(arg, &dest)) { + return 0; + } + zval_ptr_dtor(arg); + ZVAL_BOOL(arg, dest); + return 1; + } + case IS_LONG: { + zend_long dest; + + if (!zend_parse_arg_long_weak(arg, &dest)) { + return 0; + } + zval_ptr_dtor(arg); + ZVAL_LONG(arg, dest); + return 1; + } + case IS_DOUBLE: { + double dest; + + if (!zend_parse_arg_double_weak(arg, &dest)) { + return 0; + } + zval_ptr_dtor(arg); + ZVAL_DOUBLE(arg, dest); + return 1; + } + case IS_STRING: { + zend_string *dest; + + /* on success "arg" is converted to IS_STRING */ + if (!zend_parse_arg_str_weak(arg, &dest)) { + return 0; + } + return 1; + } + default: + return 0; + } +} + +static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, zend_bool strict) +{ + if (UNEXPECTED(strict)) { + /* SSTH Exception: IS_LONG may be accepted as IS_DOUBLE (converted) */ + if (type_hint != IS_DOUBLE || Z_TYPE_P(arg) != IS_LONG) { + return 0; + } + } else if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL)) { + /* NULL may be accepted only by nullable hints (this is already checked) */ + return 0; + } + return zend_verify_weak_scalar_type_hint(type_hint, arg); +} + static void zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zval *arg) { zend_internal_arg_info *cur_arg_info; - char *need_msg; + char *need_msg, *class_name; zend_class_entry *ce; if (EXPECTED(arg_num <= zf->internal_function.num_args)) { @@ -663,33 +726,29 @@ static void zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, z return; } - if (cur_arg_info->class_name) { - char *class_name; - + if (cur_arg_info->type_hint) { ZVAL_DEREF(arg); - if (Z_TYPE_P(arg) == IS_OBJECT) { - need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce); - if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce)) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name->val, arg); + if (EXPECTED(cur_arg_info->type_hint == Z_TYPE_P(arg))) { + if (cur_arg_info->class_name) { + need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce); + if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce)) { + zend_verify_arg_error(zf, arg_num, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name->val, arg); + } } } else if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) { - need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce); - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "", arg); - } - } else if (cur_arg_info->type_hint) { - if (cur_arg_info->type_hint == IS_ARRAY) { - ZVAL_DEREF(arg); - if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null)) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, "be of the type array", "", zend_zval_type_name(arg), "", arg); - } - } else if (cur_arg_info->type_hint == IS_CALLABLE) { - if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL) && (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null)) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, "be callable", "", zend_zval_type_name(arg), "", arg); + if (cur_arg_info->class_name) { + need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce); + zend_verify_arg_error(zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "", arg); + } else if (cur_arg_info->type_hint == IS_CALLABLE) { + if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL)) { + zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), "", arg); + } + } else if (cur_arg_info->type_hint == _IS_BOOL && + EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { + /* pass */ + } else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) { + zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), "", arg); } -#if ZEND_DEBUG - } else { - zend_error(E_ERROR, "Unknown typehint"); -#endif } } } @@ -697,7 +756,7 @@ static void zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, z static void zend_verify_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value) { zend_arg_info *cur_arg_info; - char *need_msg; + char *need_msg, *class_name; zend_class_entry *ce; if (EXPECTED(arg_num <= zf->common.num_args)) { @@ -708,33 +767,29 @@ static void zend_verify_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, return; } - if (cur_arg_info->class_name) { - char *class_name; - + if (cur_arg_info->type_hint) { ZVAL_DEREF(arg); - if (Z_TYPE_P(arg) == IS_OBJECT) { - need_msg = zend_verify_arg_class_kind(cur_arg_info, &class_name, &ce); - if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce)) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name->val, arg); + if (EXPECTED(cur_arg_info->type_hint == Z_TYPE_P(arg))) { + if (cur_arg_info->class_name) { + need_msg = zend_verify_arg_class_kind(cur_arg_info, &class_name, &ce); + if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce)) { + zend_verify_arg_error(zf, arg_num, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name->val, arg); + } } } else if (Z_TYPE_P(arg) != IS_NULL || !(cur_arg_info->allow_null || (default_value && is_null_constant(default_value)))) { - need_msg = zend_verify_arg_class_kind(cur_arg_info, &class_name, &ce); - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "", arg); - } - } else if (cur_arg_info->type_hint) { - if (cur_arg_info->type_hint == IS_ARRAY) { - ZVAL_DEREF(arg); - if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != IS_NULL || !(cur_arg_info->allow_null || (default_value && is_null_constant(default_value))))) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, "be of the type array", "", zend_zval_type_name(arg), "", arg); - } - } else if (cur_arg_info->type_hint == IS_CALLABLE) { - if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL) && (Z_TYPE_P(arg) != IS_NULL || !(cur_arg_info->allow_null || (default_value && is_null_constant(default_value))))) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, "be callable", "", zend_zval_type_name(arg), "", arg); + if (cur_arg_info->class_name) { + need_msg = zend_verify_arg_class_kind(cur_arg_info, &class_name, &ce); + zend_verify_arg_error(zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "", arg); + } else if (cur_arg_info->type_hint == IS_CALLABLE) { + if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL)) { + zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), "", arg); + } + } else if (cur_arg_info->type_hint == _IS_BOOL && + EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { + /* pass */ + } else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, ZEND_ARG_USES_STRICT_TYPES()))) { + zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), "", arg); } -#if ZEND_DEBUG - } else { - zend_error(E_ERROR, "Unknown typehint"); -#endif } } } @@ -753,21 +808,17 @@ static inline int zend_verify_missing_arg_type(zend_function *zf, uint32_t arg_n return 1; } - if (cur_arg_info->class_name) { - char *class_name; + if (cur_arg_info->type_hint) { + if (cur_arg_info->class_name) { + char *class_name; - need_msg = zend_verify_arg_class_kind(cur_arg_info, &class_name, &ce); - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, need_msg, class_name, "none", "", NULL); - return 0; - } else if (cur_arg_info->type_hint) { - if (cur_arg_info->type_hint == IS_ARRAY) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, "be of the type array", "", "none", "", NULL); + need_msg = zend_verify_arg_class_kind(cur_arg_info, &class_name, &ce); + zend_verify_arg_error(zf, arg_num, need_msg, class_name, "none", "", NULL); + return 0; } else if (cur_arg_info->type_hint == IS_CALLABLE) { - zend_verify_arg_error(E_EXCEPTION | E_ERROR, zf, arg_num, "be callable", "", "none", "", NULL); -#if ZEND_DEBUG + zend_verify_arg_error(zf, arg_num, "be callable", "", "none", "", NULL); } else { - zend_error(E_ERROR, "Unknown typehint"); -#endif + zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), "none", "", NULL); } return 0; } @@ -791,7 +842,7 @@ static void zend_verify_missing_arg(zend_execute_data *execute_data, uint32_t ar } } -ZEND_API void zend_verify_return_error(int error_type, const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind) +ZEND_API void zend_verify_return_error(const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind) { const char *fname = zf->common.function_name->val; const char *fsep; @@ -805,8 +856,31 @@ ZEND_API void zend_verify_return_error(int error_type, const zend_function *zf, fclass = ""; } - zend_error(error_type, - "Return value of %s%s%s() must %s%s, %s%s returned", + if (zf->common.type == ZEND_USER_FUNCTION) { + zend_type_error("Return value of %s%s%s() must %s%s, %s%s returned in %s on line %d", + fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind, + zf->op_array.filename->val, EG(current_execute_data)->opline->lineno); + } else { + zend_type_error("Return value of %s%s%s() must %s%s, %s%s returned", + fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind); + } +} + +ZEND_API void zend_verify_internal_return_error(const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind) +{ + const char *fname = zf->common.function_name->val; + const char *fsep; + const char *fclass; + + if (zf->common.scope) { + fsep = "::"; + fclass = zf->common.scope->name->val; + } else { + fsep = ""; + fclass = ""; + } + + zend_error(E_CORE_ERROR, "Return value of %s%s%s() must %s%s, %s%s returned", fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind); } @@ -814,39 +888,36 @@ ZEND_API void zend_verify_return_error(int error_type, const zend_function *zf, static int zend_verify_internal_return_type(zend_function *zf, zval *ret) { zend_arg_info *ret_info = zf->common.arg_info - 1; - char *need_msg; + char *need_msg, *class_name; zend_class_entry *ce; - if (ret_info->class_name) { - char *class_name; - if (Z_TYPE_P(ret) == IS_OBJECT) { - need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info *)ret_info, &class_name, &ce); - if (!ce || !instanceof_function(Z_OBJCE_P(ret), ce)) { - zend_verify_return_error(E_CORE_ERROR, zf, need_msg, class_name, "instance of ", Z_OBJCE_P(ret)->name->val); - return 0; + if (ret_info->type_hint) { + if (EXPECTED(ret_info->type_hint == Z_TYPE_P(ret))) { + if (ret_info->class_name) { + need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info *)ret_info, &class_name, &ce); + if (!ce || !instanceof_function(Z_OBJCE_P(ret), ce)) { + zend_verify_internal_return_error(zf, need_msg, class_name, "instance of ", Z_OBJCE_P(ret)->name->val); + return 0; + } } } else if (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null) { - need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info *)ret_info, &class_name, &ce); - zend_verify_return_error(E_CORE_ERROR, zf, need_msg, class_name, zend_zval_type_name(ret), ""); - return 0; - } - } else if (ret_info->type_hint) { - if (ret_info->type_hint == IS_ARRAY) { - if (Z_TYPE_P(ret) != IS_ARRAY && (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null)) { - zend_verify_return_error(E_CORE_ERROR, zf, "be of the type array", "", zend_zval_type_name(ret), ""); - return 0; - } - } else if (ret_info->type_hint == IS_CALLABLE) { - if (!zend_is_callable(ret, IS_CALLABLE_CHECK_SILENT, NULL) && (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null)) { - zend_verify_return_error(E_CORE_ERROR, zf, "be callable", "", zend_zval_type_name(ret), ""); + if (ret_info->class_name) { + need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info *)ret_info, &class_name, &ce); + zend_verify_internal_return_error(zf, need_msg, class_name, zend_zval_type_name(ret), ""); + } else if (ret_info->type_hint == IS_CALLABLE) { + if (!zend_is_callable(ret, IS_CALLABLE_CHECK_SILENT, NULL) && (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null)) { + zend_verify_internal_return_error(zf, "be callable", "", zend_zval_type_name(ret), ""); + return 0; + } + } else if (ret_info->type_hint == _IS_BOOL && + EXPECTED(Z_TYPE_P(ret) == IS_FALSE || Z_TYPE_P(ret) == IS_TRUE)) { + /* pass */ + } else { + /* Use strict check to verify return value of internal function */ + zend_verify_internal_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), zend_zval_type_name(ret), ""); return 0; } -#if ZEND_DEBUG - } else { - zend_error(E_CORE_ERROR, "Unknown typehint"); - return 0; -#endif } } return 1; @@ -856,34 +927,31 @@ static int zend_verify_internal_return_type(zend_function *zf, zval *ret) static void zend_verify_return_type(zend_function *zf, zval *ret) { zend_arg_info *ret_info = zf->common.arg_info - 1; - char *need_msg; + char *need_msg, *class_name; zend_class_entry *ce; - if (ret_info->class_name) { - char *class_name; - - if (Z_TYPE_P(ret) == IS_OBJECT) { - need_msg = zend_verify_arg_class_kind(ret_info, &class_name, &ce); - if (!ce || !instanceof_function(Z_OBJCE_P(ret), ce)) { - zend_verify_return_error(E_EXCEPTION | E_ERROR, zf, need_msg, class_name, "instance of ", Z_OBJCE_P(ret)->name->val); + if (ret_info->type_hint) { + if (EXPECTED(ret_info->type_hint == Z_TYPE_P(ret))) { + if (ret_info->class_name) { + need_msg = zend_verify_arg_class_kind(ret_info, &class_name, &ce); + if (!ce || !instanceof_function(Z_OBJCE_P(ret), ce)) { + zend_verify_return_error(zf, need_msg, class_name, "instance of ", Z_OBJCE_P(ret)->name->val); + } } } else if (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null) { - need_msg = zend_verify_arg_class_kind(ret_info, &class_name, &ce); - zend_verify_return_error(E_EXCEPTION | E_ERROR, zf, need_msg, class_name, zend_zval_type_name(ret), ""); - } - } else if (ret_info->type_hint) { - if (ret_info->type_hint == IS_ARRAY) { - if (Z_TYPE_P(ret) != IS_ARRAY && (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null)) { - zend_verify_return_error(E_EXCEPTION | E_ERROR, zf, "be of the type array", "", zend_zval_type_name(ret), ""); - } - } else if (ret_info->type_hint == IS_CALLABLE) { - if (!zend_is_callable(ret, IS_CALLABLE_CHECK_SILENT, NULL) && (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null)) { - zend_verify_return_error(E_EXCEPTION | E_ERROR, zf, "be callable", "", zend_zval_type_name(ret), ""); + if (ret_info->class_name) { + need_msg = zend_verify_arg_class_kind(ret_info, &class_name, &ce); + zend_verify_return_error(zf, need_msg, class_name, zend_zval_type_name(ret), ""); + } else if (ret_info->type_hint == IS_CALLABLE) { + if (!zend_is_callable(ret, IS_CALLABLE_CHECK_SILENT, NULL)) { + zend_verify_return_error(zf, "be callable", "", zend_zval_type_name(ret), ""); + } + } else if (ret_info->type_hint == _IS_BOOL && + EXPECTED(Z_TYPE_P(ret) == IS_FALSE || Z_TYPE_P(ret) == IS_TRUE)) { + /* pass */ + } else if (UNEXPECTED(!zend_verify_scalar_type_hint(ret_info->type_hint, ret, ZEND_RET_USES_STRICT_TYPES()))) { + zend_verify_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), zend_zval_type_name(ret), ""); } -#if ZEND_DEBUG - } else { - zend_error(E_ERROR, "Unknown typehint"); -#endif } } } @@ -894,21 +962,17 @@ static inline int zend_verify_missing_return_type(zend_function *zf) char *need_msg; zend_class_entry *ce; - if (ret_info->class_name) { - char *class_name; + if (ret_info->type_hint) { + if (ret_info->class_name) { + char *class_name; - need_msg = zend_verify_arg_class_kind(ret_info, &class_name, &ce); - zend_verify_return_error(E_EXCEPTION | E_ERROR, zf, need_msg, class_name, "none", ""); - return 0; - } else if (ret_info->type_hint) { - if (ret_info->type_hint == IS_ARRAY) { - zend_verify_return_error(E_EXCEPTION | E_ERROR, zf, "be of the type array", "", "none", ""); + need_msg = zend_verify_arg_class_kind(ret_info, &class_name, &ce); + zend_verify_return_error(zf, need_msg, class_name, "none", ""); + return 0; } else if (ret_info->type_hint == IS_CALLABLE) { - zend_verify_return_error(E_EXCEPTION | E_ERROR, zf, "be callable", "", "none", ""); -#if ZEND_DEBUG + zend_verify_return_error(zf, "be callable", "", "none", ""); } else { - zend_error(E_ERROR, "Unknown typehint"); -#endif + zend_verify_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), "none", ""); } return 0; } diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 0149da6929..239185ccb7 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -49,8 +49,9 @@ ZEND_API int zend_eval_stringl_ex(char *str, size_t str_len, zval *retval_ptr, c ZEND_API char * zend_verify_internal_arg_class_kind(const zend_internal_arg_info *cur_arg_info, char **class_name, zend_class_entry **pce); ZEND_API char * zend_verify_arg_class_kind(const zend_arg_info *cur_arg_info, char **class_name, zend_class_entry **pce); -ZEND_API void zend_verify_arg_error(int error_type, const zend_function *zf, uint32_t arg_num, const char *need_msg, const char *need_kind, const char *given_msg, const char *given_kind, zval *arg); -ZEND_API void zend_verify_return_error(int error_type, const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind); +ZEND_API void zend_verify_arg_error(const zend_function *zf, uint32_t arg_num, const char *need_msg, const char *need_kind, const char *given_msg, const char *given_kind, zval *arg); +ZEND_API void zend_verify_return_error(const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind); +ZEND_API void zend_verify_internal_return_error(const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind); static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval *value, zend_uchar value_type) { diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 0cfd8fca90..39464df282 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -313,6 +313,11 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) { return pz->u1.v.type; } +#define ZEND_SAME_FAKE_TYPE(faketype, realtype) ( \ + (faketype) == (realtype) \ + || ((faketype) == _IS_BOOL && ((realtype) == IS_TRUE || (realtype) == IS_FALSE)) \ +) + /* we should never set just Z_TYPE, we should set Z_TYPE_INFO */ #define Z_TYPE(zval) zval_get_type(&(zval)) #define Z_TYPE_P(zval_p) Z_TYPE(*(zval_p)) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index ab2c7b89a3..95408ebdd4 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3133,7 +3133,7 @@ ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV) } } } else { - zend_error(E_WARNING, "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error); + zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error); efree(error); func = (zend_function*)&zend_pass_function; called_scope = NULL; @@ -3589,15 +3589,39 @@ ZEND_VM_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED) #if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED) USE_OPLINE #endif + SAVE_OPLINE(); if (OP1_TYPE == IS_UNUSED) { zend_verify_missing_return_type(EX(func)); } else { +/* prevents "undefined variable opline" errors */ +#if !defined(ZEND_VM_SPEC) || (OP1_TYPE != IS_UNUSED) zval *retval_ptr; zend_free_op free_op1; + zend_arg_info *ret_info = EX(func)->common.arg_info - 1; + + retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); + + if (OP1_TYPE == IS_CONST) { + ZVAL_COPY(EX_VAR(opline->result.var), retval_ptr); + retval_ptr = EX_VAR(opline->result.var); + } - retval_ptr = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R); + if (UNEXPECTED(!ret_info->class_name + && ret_info->type_hint != IS_CALLABLE + && !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(retval_ptr)))) { + /* A cast or an error will happen, so separate the zval to prevent overwriting it */ + + if (EXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) == 0)) { + /* Does not return by reference */ + SEPARATE_ZVAL(retval_ptr); + } else { + ZVAL_DEREF(retval_ptr); + SEPARATE_ZVAL_NOREF(retval_ptr); + } + } zend_verify_return_type(EX(func), retval_ptr); +#endif } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -4169,7 +4193,7 @@ ZEND_VM_HANDLER(119, ZEND_SEND_ARRAY, ANY, ANY) ZEND_VM_C_GOTO(send_array); } } - zend_error(E_WARNING, "call_user_func_array() expects parameter 2 to be array, %s given", zend_get_type_by_const(Z_TYPE_P(args))); + zend_internal_type_error(EX_USES_STRICT_TYPES(), "call_user_func_array() expects parameter 2 to be array, %s given", zend_get_type_by_const(Z_TYPE_P(args))); if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) { OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype); } @@ -7250,32 +7274,29 @@ ZEND_VM_C_LABEL(try_strlen): if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) { ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value)); } else { + zend_bool strict; + if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) { value = Z_REFVAL_P(value); ZEND_VM_C_GOTO(try_strlen); - } else if (Z_TYPE_P(value) < IS_TRUE) { - ZVAL_LONG(EX_VAR(opline->result.var), 0); - } else if (Z_TYPE_P(value) == IS_TRUE) { - ZVAL_LONG(EX_VAR(opline->result.var), 1); - } else if (Z_TYPE_P(value) <= IS_DOUBLE) { - zend_string *str = zval_get_string(value); - ZVAL_LONG(EX_VAR(opline->result.var), str->len); - zend_string_release(str); - } else if (Z_TYPE_P(value) == IS_OBJECT) { - zend_string *str; - zval tmp; - - ZVAL_COPY(&tmp, value); - if (parse_arg_object_to_str(&tmp, &str, IS_STRING) == FAILURE) { - ZEND_VM_C_GOTO(strlen_error); - } - ZVAL_LONG(EX_VAR(opline->result.var), str->len); - zval_dtor(&tmp); - } else { -ZEND_VM_C_LABEL(strlen_error): - zend_error(E_WARNING, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); - ZVAL_NULL(EX_VAR(opline->result.var)); } + strict = EX_USES_STRICT_TYPES(); + do { + if (EXPECTED(!strict)) { + zend_string *str; + zval tmp; + + ZVAL_COPY(&tmp, value); + if (zend_parse_arg_str_weak(&tmp, &str)) { + ZVAL_LONG(EX_VAR(opline->result.var), str->len); + zval_ptr_dtor(&tmp); + break; + } + zval_ptr_dtor(&tmp); + } + zend_internal_type_error(strict, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + ZVAL_NULL(EX_VAR(opline->result.var)); + } while (0); } FREE_OP1(); CHECK_EXCEPTION(); @@ -7373,4 +7394,5 @@ ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, ANY, ANY) ZVAL_EMPTY_STRING(EX_VAR(opline->result.var)); } ZEND_VM_NEXT_OPCODE(); -}
\ No newline at end of file +} + diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index e5547f3e1e..eb551ee15f 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1098,7 +1098,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O goto send_array; } } - zend_error(E_WARNING, "call_user_func_array() expects parameter 2 to be array, %s given", zend_get_type_by_const(Z_TYPE_P(args))); + zend_internal_type_error(EX_USES_STRICT_TYPES(), "call_user_func_array() expects parameter 2 to be array, %s given", zend_get_type_by_const(Z_TYPE_P(args))); if (EX(call)->func->common.fn_flags & ZEND_ACC_CLOSURE) { OBJ_RELEASE((zend_object*)EX(call)->func->common.prototype); } @@ -1789,7 +1789,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_HANDLER( ZVAL_EMPTY_STRING(EX_VAR(opline->result.var)); } ZEND_VM_NEXT_OPCODE(); -}static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +} + +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -3808,32 +3810,29 @@ try_strlen: if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) { ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value)); } else { + zend_bool strict; + if ((IS_CONST & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) { value = Z_REFVAL_P(value); goto try_strlen; - } else if (Z_TYPE_P(value) < IS_TRUE) { - ZVAL_LONG(EX_VAR(opline->result.var), 0); - } else if (Z_TYPE_P(value) == IS_TRUE) { - ZVAL_LONG(EX_VAR(opline->result.var), 1); - } else if (Z_TYPE_P(value) <= IS_DOUBLE) { - zend_string *str = zval_get_string(value); - ZVAL_LONG(EX_VAR(opline->result.var), str->len); - zend_string_release(str); - } else if (Z_TYPE_P(value) == IS_OBJECT) { - zend_string *str; - zval tmp; - - ZVAL_COPY(&tmp, value); - if (parse_arg_object_to_str(&tmp, &str, IS_STRING) == FAILURE) { - goto strlen_error; - } - ZVAL_LONG(EX_VAR(opline->result.var), str->len); - zval_dtor(&tmp); - } else { -strlen_error: - zend_error(E_WARNING, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); - ZVAL_NULL(EX_VAR(opline->result.var)); } + strict = EX_USES_STRICT_TYPES(); + do { + if (EXPECTED(!strict)) { + zend_string *str; + zval tmp; + + ZVAL_COPY(&tmp, value); + if (zend_parse_arg_str_weak(&tmp, &str)) { + ZVAL_LONG(EX_VAR(opline->result.var), str->len); + zval_ptr_dtor(&tmp); + break; + } + zval_ptr_dtor(&tmp); + } + zend_internal_type_error(strict, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + ZVAL_NULL(EX_VAR(opline->result.var)); + } while (0); } CHECK_EXCEPTION(); @@ -5097,7 +5096,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CONS } } } else { - zend_error(E_WARNING, "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error); + zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error); efree(error); func = (zend_function*)&zend_pass_function; called_scope = NULL; @@ -6958,15 +6957,39 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CONST_ #if 0 || (IS_CONST != IS_UNUSED) USE_OPLINE #endif + SAVE_OPLINE(); if (IS_CONST == IS_UNUSED) { zend_verify_missing_return_type(EX(func)); } else { +/* prevents "undefined variable opline" errors */ +#if 0 || (IS_CONST != IS_UNUSED) zval *retval_ptr; + zend_arg_info *ret_info = EX(func)->common.arg_info - 1; retval_ptr = EX_CONSTANT(opline->op1); + + if (IS_CONST == IS_CONST) { + ZVAL_COPY(EX_VAR(opline->result.var), retval_ptr); + retval_ptr = EX_VAR(opline->result.var); + } + + if (UNEXPECTED(!ret_info->class_name + && ret_info->type_hint != IS_CALLABLE + && !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(retval_ptr)))) { + /* A cast or an error will happen, so separate the zval to prevent overwriting it */ + + if (EXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) == 0)) { + /* Does not return by reference */ + SEPARATE_ZVAL(retval_ptr); + } else { + ZVAL_DEREF(retval_ptr); + SEPARATE_ZVAL_NOREF(retval_ptr); + } + } zend_verify_return_type(EX(func), retval_ptr); +#endif } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -8415,7 +8438,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CV_H } } } else { - zend_error(E_WARNING, "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error); + zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error); efree(error); func = (zend_function*)&zend_pass_function; called_scope = NULL; @@ -9928,7 +9951,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_TMPV } } } else { - zend_error(E_WARNING, "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error); + zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error); efree(error); func = (zend_function*)&zend_pass_function; called_scope = NULL; @@ -11900,15 +11923,39 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN #if 0 || (IS_TMP_VAR != IS_UNUSED) USE_OPLINE #endif + SAVE_OPLINE(); if (IS_TMP_VAR == IS_UNUSED) { zend_verify_missing_return_type(EX(func)); } else { +/* prevents "undefined variable opline" errors */ +#if 0 || (IS_TMP_VAR != IS_UNUSED) zval *retval_ptr; zend_free_op free_op1; + zend_arg_info *ret_info = EX(func)->common.arg_info - 1; retval_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + + if (IS_TMP_VAR == IS_CONST) { + ZVAL_COPY(EX_VAR(opline->result.var), retval_ptr); + retval_ptr = EX_VAR(opline->result.var); + } + + if (UNEXPECTED(!ret_info->class_name + && ret_info->type_hint != IS_CALLABLE + && !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(retval_ptr)))) { + /* A cast or an error will happen, so separate the zval to prevent overwriting it */ + + if (EXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) == 0)) { + /* Does not return by reference */ + SEPARATE_ZVAL(retval_ptr); + } else { + ZVAL_DEREF(retval_ptr); + SEPARATE_ZVAL_NOREF(retval_ptr); + } + } zend_verify_return_type(EX(func), retval_ptr); +#endif } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -17242,15 +17289,39 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN #if 0 || (IS_VAR != IS_UNUSED) USE_OPLINE #endif + SAVE_OPLINE(); if (IS_VAR == IS_UNUSED) { zend_verify_missing_return_type(EX(func)); } else { +/* prevents "undefined variable opline" errors */ +#if 0 || (IS_VAR != IS_UNUSED) zval *retval_ptr; zend_free_op free_op1; + zend_arg_info *ret_info = EX(func)->common.arg_info - 1; + + retval_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + + if (IS_VAR == IS_CONST) { + ZVAL_COPY(EX_VAR(opline->result.var), retval_ptr); + retval_ptr = EX_VAR(opline->result.var); + } + + if (UNEXPECTED(!ret_info->class_name + && ret_info->type_hint != IS_CALLABLE + && !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(retval_ptr)))) { + /* A cast or an error will happen, so separate the zval to prevent overwriting it */ - retval_ptr = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1); + if (EXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) == 0)) { + /* Does not return by reference */ + SEPARATE_ZVAL(retval_ptr); + } else { + ZVAL_DEREF(retval_ptr); + SEPARATE_ZVAL_NOREF(retval_ptr); + } + } zend_verify_return_type(EX(func), retval_ptr); +#endif } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -22841,15 +22912,39 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED #if 0 || (IS_UNUSED != IS_UNUSED) USE_OPLINE #endif + SAVE_OPLINE(); if (IS_UNUSED == IS_UNUSED) { zend_verify_missing_return_type(EX(func)); } else { +/* prevents "undefined variable opline" errors */ +#if 0 || (IS_UNUSED != IS_UNUSED) zval *retval_ptr; + zend_arg_info *ret_info = EX(func)->common.arg_info - 1; retval_ptr = NULL; + + if (IS_UNUSED == IS_CONST) { + ZVAL_COPY(EX_VAR(opline->result.var), retval_ptr); + retval_ptr = EX_VAR(opline->result.var); + } + + if (UNEXPECTED(!ret_info->class_name + && ret_info->type_hint != IS_CALLABLE + && !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(retval_ptr)))) { + /* A cast or an error will happen, so separate the zval to prevent overwriting it */ + + if (EXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) == 0)) { + /* Does not return by reference */ + SEPARATE_ZVAL(retval_ptr); + } else { + ZVAL_DEREF(retval_ptr); + SEPARATE_ZVAL_NOREF(retval_ptr); + } + } zend_verify_return_type(EX(func), retval_ptr); +#endif } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -27245,32 +27340,29 @@ try_strlen: if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) { ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value)); } else { + zend_bool strict; + if ((IS_CV & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) { value = Z_REFVAL_P(value); goto try_strlen; - } else if (Z_TYPE_P(value) < IS_TRUE) { - ZVAL_LONG(EX_VAR(opline->result.var), 0); - } else if (Z_TYPE_P(value) == IS_TRUE) { - ZVAL_LONG(EX_VAR(opline->result.var), 1); - } else if (Z_TYPE_P(value) <= IS_DOUBLE) { - zend_string *str = zval_get_string(value); - ZVAL_LONG(EX_VAR(opline->result.var), str->len); - zend_string_release(str); - } else if (Z_TYPE_P(value) == IS_OBJECT) { - zend_string *str; - zval tmp; - - ZVAL_COPY(&tmp, value); - if (parse_arg_object_to_str(&tmp, &str, IS_STRING) == FAILURE) { - goto strlen_error; - } - ZVAL_LONG(EX_VAR(opline->result.var), str->len); - zval_dtor(&tmp); - } else { -strlen_error: - zend_error(E_WARNING, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); - ZVAL_NULL(EX_VAR(opline->result.var)); } + strict = EX_USES_STRICT_TYPES(); + do { + if (EXPECTED(!strict)) { + zend_string *str; + zval tmp; + + ZVAL_COPY(&tmp, value); + if (zend_parse_arg_str_weak(&tmp, &str)) { + ZVAL_LONG(EX_VAR(opline->result.var), str->len); + zval_ptr_dtor(&tmp); + break; + } + zval_ptr_dtor(&tmp); + } + zend_internal_type_error(strict, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + ZVAL_NULL(EX_VAR(opline->result.var)); + } while (0); } CHECK_EXCEPTION(); @@ -31767,15 +31859,39 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU #if 0 || (IS_CV != IS_UNUSED) USE_OPLINE #endif + SAVE_OPLINE(); if (IS_CV == IS_UNUSED) { zend_verify_missing_return_type(EX(func)); } else { +/* prevents "undefined variable opline" errors */ +#if 0 || (IS_CV != IS_UNUSED) zval *retval_ptr; + zend_arg_info *ret_info = EX(func)->common.arg_info - 1; + + retval_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + + if (IS_CV == IS_CONST) { + ZVAL_COPY(EX_VAR(opline->result.var), retval_ptr); + retval_ptr = EX_VAR(opline->result.var); + } + + if (UNEXPECTED(!ret_info->class_name + && ret_info->type_hint != IS_CALLABLE + && !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(retval_ptr)))) { + /* A cast or an error will happen, so separate the zval to prevent overwriting it */ - retval_ptr = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var); + if (EXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) == 0)) { + /* Does not return by reference */ + SEPARATE_ZVAL(retval_ptr); + } else { + ZVAL_DEREF(retval_ptr); + SEPARATE_ZVAL_NOREF(retval_ptr); + } + } zend_verify_return_type(EX(func), retval_ptr); +#endif } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -37492,32 +37608,29 @@ try_strlen: if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) { ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value)); } else { + zend_bool strict; + if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) { value = Z_REFVAL_P(value); goto try_strlen; - } else if (Z_TYPE_P(value) < IS_TRUE) { - ZVAL_LONG(EX_VAR(opline->result.var), 0); - } else if (Z_TYPE_P(value) == IS_TRUE) { - ZVAL_LONG(EX_VAR(opline->result.var), 1); - } else if (Z_TYPE_P(value) <= IS_DOUBLE) { - zend_string *str = zval_get_string(value); - ZVAL_LONG(EX_VAR(opline->result.var), str->len); - zend_string_release(str); - } else if (Z_TYPE_P(value) == IS_OBJECT) { - zend_string *str; - zval tmp; - - ZVAL_COPY(&tmp, value); - if (parse_arg_object_to_str(&tmp, &str, IS_STRING) == FAILURE) { - goto strlen_error; - } - ZVAL_LONG(EX_VAR(opline->result.var), str->len); - zval_dtor(&tmp); - } else { -strlen_error: - zend_error(E_WARNING, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); - ZVAL_NULL(EX_VAR(opline->result.var)); } + strict = EX_USES_STRICT_TYPES(); + do { + if (EXPECTED(!strict)) { + zend_string *str; + zval tmp; + + ZVAL_COPY(&tmp, value); + if (zend_parse_arg_str_weak(&tmp, &str)) { + ZVAL_LONG(EX_VAR(opline->result.var), str->len); + zval_ptr_dtor(&tmp); + break; + } + zval_ptr_dtor(&tmp); + } + zend_internal_type_error(strict, "strlen() expects parameter 1 to be string, %s given", zend_get_type_by_const(Z_TYPE_P(value))); + ZVAL_NULL(EX_VAR(opline->result.var)); + } while (0); } zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); diff --git a/ext/standard/tests/array/krsort_object.phpt b/ext/standard/tests/array/krsort_object.phpt index 36d8589a82..6236595796 100644 --- a/ext/standard/tests/array/krsort_object.phpt +++ b/ext/standard/tests/array/krsort_object.phpt @@ -15,7 +15,7 @@ Test krsort() function : object functionality - sort objects echo "*** Testing krsort() : object functionality ***\n"; // class declaration for integer objects -class Integer +class IntegerObject { public $class_value; // initializing object member value @@ -25,7 +25,7 @@ class Integer } // class declaration for string objects -class String +class StringObject { public $class_value; // initializing object member value @@ -42,17 +42,17 @@ class String // array of integer objects with different key values $unsorted_int_obj = array ( - 10 => new Integer(11), 20 => new Integer(66), - 3 => new Integer(23), 4 => new Integer(-5), - 50 => new Integer(0.001), 6 => new Integer(0) + 10 => new IntegerObject(11), 20 => new IntegerObject(66), + 3 => new IntegerObject(23), 4 => new IntegerObject(-5), + 50 => new IntegerObject(0.001), 6 => new IntegerObject(0) ); // array of string objects with different key values $unsorted_str_obj = array ( - "axx" => new String("axx"), "t" => new String("t"), - "w" => new String("w"), "py" => new String("py"), - "apple" => new String("apple"), "Orange" => new String("Orange"), - "Lemon" => new String("Lemon"), "aPPle" => new String("aPPle") + "axx" => new StringObject("axx"), "t" => new StringObject("t"), + "w" => new StringObject("w"), "py" => new StringObject("py"), + "apple" => new StringObject("apple"), "Orange" => new StringObject("Orange"), + "Lemon" => new StringObject("Lemon"), "aPPle" => new StringObject("aPPle") ); @@ -88,32 +88,32 @@ echo "Done\n"; bool(true) array(6) { [50]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> float(0.001) } [20]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(66) } [10]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(11) } [6]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(0) } [4]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(-5) } [3]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(23) } @@ -121,42 +121,42 @@ array(6) { bool(true) array(8) { ["w"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(1) "w" } ["t"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(1) "t" } ["py"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(2) "py" } ["axx"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(3) "axx" } ["apple"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(5) "apple" } ["aPPle"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(5) "aPPle" } ["Orange"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(6) "Orange" } ["Lemon"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(5) "Lemon" } @@ -166,32 +166,32 @@ array(8) { bool(true) array(6) { [50]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> float(0.001) } [20]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(66) } [10]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(11) } [6]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(0) } [4]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(-5) } [3]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(23) } @@ -199,42 +199,42 @@ array(6) { bool(true) array(8) { ["w"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(1) "w" } ["t"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(1) "t" } ["py"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(2) "py" } ["axx"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(3) "axx" } ["apple"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(5) "apple" } ["aPPle"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(5) "aPPle" } ["Orange"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(6) "Orange" } ["Lemon"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(5) "Lemon" } diff --git a/ext/standard/tests/array/ksort_object.phpt b/ext/standard/tests/array/ksort_object.phpt index 20e8ba26eb..2d76026aa4 100644 --- a/ext/standard/tests/array/ksort_object.phpt +++ b/ext/standard/tests/array/ksort_object.phpt @@ -15,7 +15,7 @@ Test ksort() function : object functionality - sort objects echo "*** Testing ksort() : object functionality ***\n"; // class declaration for integer objects -class Integer +class IntegerObject { public $class_value; // initializing object member value @@ -26,7 +26,7 @@ class Integer } // class declaration for string objects -class String +class StringObject { public $class_value; // initializing object member value @@ -43,17 +43,17 @@ class String // array of integer objects $unsorted_int_obj = array ( - 11 => new Integer(11), 66 => new Integer(66), - 23 => new Integer(23), -5 => new Integer(-5), - 1 => new Integer(0.001), 0 => new Integer(0) + 11 => new IntegerObject(11), 66 => new IntegerObject(66), + 23 => new IntegerObject(23), -5 => new IntegerObject(-5), + 1 => new IntegerObject(0.001), 0 => new IntegerObject(0) ); // array of string objects $unsorted_str_obj = array ( - "axx" => new String("axx"), "t" => new String("t"), - "w" => new String("w"), "py" => new String("py"), - "apple" => new String("apple"), "Orange" => new String("Orange"), - "Lemon" => new String("Lemon"), "aPPle" => new String("aPPle") + "axx" => new StringObject("axx"), "t" => new StringObject("t"), + "w" => new StringObject("w"), "py" => new StringObject("py"), + "apple" => new StringObject("apple"), "Orange" => new StringObject("Orange"), + "Lemon" => new StringObject("Lemon"), "aPPle" => new StringObject("aPPle") ); echo "\n-- Testing ksort() by supplying various object arrays, 'flag' value is defualt --\n"; @@ -87,32 +87,32 @@ echo "Done\n"; bool(true) array(6) { [-5]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(-5) } [0]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(0) } [1]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> float(0.001) } [11]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(11) } [23]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(23) } [66]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(66) } @@ -120,42 +120,42 @@ array(6) { bool(true) array(8) { ["Lemon"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(5) "Lemon" } ["Orange"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(6) "Orange" } ["aPPle"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(5) "aPPle" } ["apple"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(5) "apple" } ["axx"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(3) "axx" } ["py"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(2) "py" } ["t"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(1) "t" } ["w"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(1) "w" } @@ -165,32 +165,32 @@ array(8) { bool(true) array(6) { [-5]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(-5) } [0]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(0) } [1]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> float(0.001) } [11]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(11) } [23]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(23) } [66]=> - object(Integer)#%d (1) { + object(IntegerObject)#%d (1) { ["class_value"]=> int(66) } @@ -198,42 +198,42 @@ array(6) { bool(true) array(8) { ["Lemon"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(5) "Lemon" } ["Orange"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(6) "Orange" } ["aPPle"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(5) "aPPle" } ["apple"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(5) "apple" } ["axx"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(3) "axx" } ["py"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(2) "py" } ["t"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(1) "t" } ["w"]=> - object(String)#%d (1) { + object(StringObject)#%d (1) { ["class_value"]=> string(1) "w" } diff --git a/ext/standard/tests/strings/lcfirst.phpt b/ext/standard/tests/strings/lcfirst.phpt Binary files differindex e603f4bbf8..6cbd213fbe 100644 --- a/ext/standard/tests/strings/lcfirst.phpt +++ b/ext/standard/tests/strings/lcfirst.phpt diff --git a/ext/standard/tests/strings/strlen.phpt b/ext/standard/tests/strings/strlen.phpt Binary files differindex df39f2469c..ab54445943 100644 --- a/ext/standard/tests/strings/strlen.phpt +++ b/ext/standard/tests/strings/strlen.phpt diff --git a/ext/standard/tests/strings/strpos.phpt b/ext/standard/tests/strings/strpos.phpt Binary files differindex b2bfedeb87..36854d1b37 100644 --- a/ext/standard/tests/strings/strpos.phpt +++ b/ext/standard/tests/strings/strpos.phpt diff --git a/ext/standard/tests/strings/strstr.phpt b/ext/standard/tests/strings/strstr.phpt Binary files differindex bdedb7e9f6..fd7f58ef1a 100644 --- a/ext/standard/tests/strings/strstr.phpt +++ b/ext/standard/tests/strings/strstr.phpt diff --git a/ext/standard/tests/strings/ucfirst.phpt b/ext/standard/tests/strings/ucfirst.phpt Binary files differindex 468f7f034e..8fb1a156b4 100644 --- a/ext/standard/tests/strings/ucfirst.phpt +++ b/ext/standard/tests/strings/ucfirst.phpt diff --git a/tests/lang/catchable_error_002.phpt b/tests/lang/catchable_error_002.phpt index a80928523b..bc585f7b10 100644 --- a/tests/lang/catchable_error_002.phpt +++ b/tests/lang/catchable_error_002.phpt @@ -25,5 +25,5 @@ Catchable fatal error [2] echo "ALIVE!\n"; ?> --EXPECTF-- -Argument 1 passed to blah() must be an instance of Foo, instance of stdClass given, called in %scatchable_error_002.php on line 18 and defined +Argument 1 passed to blah() must be an instance of Foo, instance of stdClass given, called in %scatchable_error_002.php on line %d ALIVE! |