summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Zend/tests/generators/generator_return_without_value.phpt43
-rw-r--r--Zend/tests/is_a.phpt8
-rw-r--r--Zend/tests/return_types/return_reference_separation.phpt34
-rw-r--r--Zend/tests/return_types/rfc002.phpt6
-rw-r--r--Zend/tests/typehints/default_boolean_hint_values.phpt16
-rw-r--r--Zend/tests/typehints/explicit_weak_include_strict.phpt14
-rw-r--r--Zend/tests/typehints/internal_function_strict_mode.phpt35
-rw-r--r--Zend/tests/typehints/scalar_basic.phpt277
-rw-r--r--Zend/tests/typehints/scalar_constant_defaults.phpt83
-rw-r--r--Zend/tests/typehints/scalar_constant_defaults_error.phpt16
-rw-r--r--Zend/tests/typehints/scalar_none.phpt54
-rw-r--r--Zend/tests/typehints/scalar_null.phpt56
-rw-r--r--Zend/tests/typehints/scalar_relative_typehint_disallowed.phpt14
-rw-r--r--Zend/tests/typehints/scalar_reserved2.phpt8
-rw-r--r--Zend/tests/typehints/scalar_reserved2_class_alias.phpt9
-rw-r--r--Zend/tests/typehints/scalar_reserved2_use.phpt8
-rw-r--r--Zend/tests/typehints/scalar_reserved3.phpt8
-rw-r--r--Zend/tests/typehints/scalar_reserved3_class_alias.phpt9
-rw-r--r--Zend/tests/typehints/scalar_reserved3_use.phpt8
-rw-r--r--Zend/tests/typehints/scalar_reserved4.phpt8
-rw-r--r--Zend/tests/typehints/scalar_reserved4_class_alias.phpt9
-rw-r--r--Zend/tests/typehints/scalar_reserved4_use.phpt8
-rw-r--r--Zend/tests/typehints/scalar_reserved6.phpt8
-rw-r--r--Zend/tests/typehints/scalar_reserved6_class_alias.phpt9
-rw-r--r--Zend/tests/typehints/scalar_reserved6_use.phpt8
-rw-r--r--Zend/tests/typehints/scalar_reserved7.phpt9
-rw-r--r--Zend/tests/typehints/scalar_return_basic.phpt216
-rw-r--r--Zend/tests/typehints/scalar_return_basic_64bit.phpt216
-rw-r--r--Zend/tests/typehints/scalar_strict.phpt279
-rw-r--r--Zend/tests/typehints/scalar_strict_64bit.phpt279
-rw-r--r--Zend/tests/typehints/scalar_strict_basic.phpt181
-rw-r--r--Zend/tests/typehints/scalar_strict_declaration_placement_001.phpt24
-rw-r--r--Zend/tests/typehints/scalar_strict_declaration_placement_002.phpt14
-rw-r--r--Zend/tests/typehints/scalar_strict_declaration_placement_003.phpt11
-rw-r--r--Zend/tests/typehints/scalar_strict_declaration_placement_004.phpt17
-rw-r--r--Zend/tests/typehints/scalar_strict_declaration_placement_005.phpt14
-rw-r--r--Zend/tests/typehints/scalar_strict_declaration_placement_006.phpt25
-rw-r--r--Zend/tests/typehints/scalar_strict_declaration_placement_007.phpt25
-rw-r--r--Zend/tests/typehints/scalar_strict_declaration_placement_008.phpt11
-rw-r--r--Zend/tests/typehints/scalar_weak_reference.phpt29
-rw-r--r--Zend/tests/typehints/strict_call_weak.phpt17
-rw-r--r--Zend/tests/typehints/strict_call_weak_2.inc7
-rw-r--r--Zend/tests/typehints/strict_call_weak_explicit.phpt17
-rw-r--r--Zend/tests/typehints/strict_call_weak_explicit_2.inc7
-rw-r--r--Zend/tests/typehints/strict_include_explicit_weak.phpt14
-rw-r--r--Zend/tests/typehints/strict_include_explicit_weak_2.inc9
-rw-r--r--Zend/tests/typehints/strict_include_weak.phpt14
-rw-r--r--Zend/tests/typehints/strict_include_weak_2.inc9
-rw-r--r--Zend/tests/typehints/strict_nested.phpt43
-rw-r--r--Zend/tests/typehints/weak_call_strict.phpt16
-rw-r--r--Zend/tests/typehints/weak_call_strict_2.inc7
-rw-r--r--Zend/tests/typehints/weak_explicit_call_strict.phpt16
-rw-r--r--Zend/tests/typehints/weak_include_strict.phpt14
-rw-r--r--Zend/tests/typehints/weak_include_strict_2.inc9
-rw-r--r--Zend/tests/variadic/typehint_suppressed_error.phpt2
-rw-r--r--Zend/zend.c29
-rw-r--r--Zend/zend.h2
-rw-r--r--Zend/zend_API.c344
-rw-r--r--Zend/zend_API.h124
-rw-r--r--Zend/zend_compile.c200
-rw-r--r--Zend/zend_compile.h19
-rw-r--r--Zend/zend_exceptions.c20
-rw-r--r--Zend/zend_exceptions.h1
-rw-r--r--Zend/zend_execute.c320
-rw-r--r--Zend/zend_execute.h5
-rw-r--r--Zend/zend_types.h5
-rw-r--r--Zend/zend_vm_def.h74
-rw-r--r--Zend/zend_vm_execute.h259
-rw-r--r--ext/standard/tests/array/krsort_object.phpt74
-rw-r--r--ext/standard/tests/array/ksort_object.phpt74
-rw-r--r--ext/standard/tests/strings/lcfirst.phptbin6878 -> 6906 bytes
-rw-r--r--ext/standard/tests/strings/strlen.phptbin7091 -> 7099 bytes
-rw-r--r--ext/standard/tests/strings/strpos.phptbin9991 -> 9999 bytes
-rw-r--r--ext/standard/tests/strings/strstr.phptbin10531 -> 10539 bytes
-rw-r--r--ext/standard/tests/strings/ucfirst.phptbin6106 -> 6130 bytes
-rw-r--r--tests/lang/catchable_error_002.phpt2
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
index e603f4bbf8..6cbd213fbe 100644
--- a/ext/standard/tests/strings/lcfirst.phpt
+++ b/ext/standard/tests/strings/lcfirst.phpt
Binary files differ
diff --git a/ext/standard/tests/strings/strlen.phpt b/ext/standard/tests/strings/strlen.phpt
index df39f2469c..ab54445943 100644
--- a/ext/standard/tests/strings/strlen.phpt
+++ b/ext/standard/tests/strings/strlen.phpt
Binary files differ
diff --git a/ext/standard/tests/strings/strpos.phpt b/ext/standard/tests/strings/strpos.phpt
index b2bfedeb87..36854d1b37 100644
--- a/ext/standard/tests/strings/strpos.phpt
+++ b/ext/standard/tests/strings/strpos.phpt
Binary files differ
diff --git a/ext/standard/tests/strings/strstr.phpt b/ext/standard/tests/strings/strstr.phpt
index bdedb7e9f6..fd7f58ef1a 100644
--- a/ext/standard/tests/strings/strstr.phpt
+++ b/ext/standard/tests/strings/strstr.phpt
Binary files differ
diff --git a/ext/standard/tests/strings/ucfirst.phpt b/ext/standard/tests/strings/ucfirst.phpt
index 468f7f034e..8fb1a156b4 100644
--- a/ext/standard/tests/strings/ucfirst.phpt
+++ b/ext/standard/tests/strings/ucfirst.phpt
Binary files differ
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!