mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Typed class constants (#10444)
RFC: https://wiki.php.net/rfc/typed_class_constants Co-Authored-By: Ben <7127204+moliata@users.noreply.github.com> Co-Authored-By: Bob Weinand <3154871+bwoebi@users.noreply.github.com> Co-Authored-By: Ilija Tovilo <ilija.tovilo@me.com>
This commit is contained in:
parent
4dad419ae6
commit
414f71a902
53 changed files with 1227 additions and 92 deletions
|
@ -52,6 +52,8 @@ PHP 8.3 UPGRADE NOTES
|
||||||
. Anonymous classes may now be marked as readonly.
|
. Anonymous classes may now be marked as readonly.
|
||||||
. Readonly properties can now be reinitialized during cloning.
|
. Readonly properties can now be reinitialized during cloning.
|
||||||
RFC: https://wiki.php.net/rfc/readonly_amendments
|
RFC: https://wiki.php.net/rfc/readonly_amendments
|
||||||
|
. Class, interface, trait, and enum constants now support type
|
||||||
|
declarations. RFC: https://wiki.php.net/rfc/typed_class_constants
|
||||||
|
|
||||||
- Posix
|
- Posix
|
||||||
. posix_getrlimit() now takes an optional $res parameter to allow fetching a
|
. posix_getrlimit() now takes an optional $res parameter to allow fetching a
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (diamond error with self)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const self CONST1 = C;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
define("C", new A());
|
||||||
|
} catch (Error $exception) {
|
||||||
|
echo $exception->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Undefined constant "C"
|
|
@ -0,0 +1,14 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (incompatible inheritance; simple)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const int CONST1 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class B extends A {
|
||||||
|
public const string CONST1 = 'a';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Type of B::CONST1 must be compatible with A::CONST1 of type int in %s on line %d
|
|
@ -0,0 +1,14 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (incompatible inheritance; missing type in child)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const int CONST1 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class B extends A {
|
||||||
|
public const CONST1 = 0;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Type of B::CONST1 must be compatible with A::CONST1 of type int in %s on line %d
|
|
@ -0,0 +1,18 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (incompatible composition; traits)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
trait T {
|
||||||
|
public const ?array CONST1 = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class C {
|
||||||
|
use T;
|
||||||
|
|
||||||
|
public const CONST1 = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: C and T define the same constant (CONST1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d
|
|
@ -0,0 +1,18 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (incompatible covariant composition; traits)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
trait T {
|
||||||
|
public const ?array CONST1 = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class C {
|
||||||
|
use T;
|
||||||
|
|
||||||
|
public const array CONST1 = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: C and T define the same constant (CONST1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d
|
|
@ -0,0 +1,18 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (incompatible contravariant composition; traits)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
trait T {
|
||||||
|
public const array CONST1 = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class C {
|
||||||
|
use T;
|
||||||
|
|
||||||
|
public const ?array CONST1 = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: C and T define the same constant (CONST1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d
|
|
@ -0,0 +1,29 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (inheritance success)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const CONST1 = 1;
|
||||||
|
public const CONST2 = 1;
|
||||||
|
public const mixed CONST3 = 1;
|
||||||
|
public const iterable CONST4 = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class B extends A {
|
||||||
|
public const int CONST1 = 0;
|
||||||
|
public const mixed CONST2 = 0;
|
||||||
|
public const mixed CONST3 = 0;
|
||||||
|
public const array CONST4 = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(B::CONST1);
|
||||||
|
var_dump(B::CONST2);
|
||||||
|
var_dump(B::CONST3);
|
||||||
|
var_dump(B::CONST4);
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(0)
|
||||||
|
int(0)
|
||||||
|
int(0)
|
||||||
|
array(0) {
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (inheritance success - object types)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class S implements Stringable {
|
||||||
|
public function __toString() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Z extends S {}
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public const object CONST1 = S;
|
||||||
|
public const S CONST2 = S;
|
||||||
|
public const S|Stringable CONST3 = S;
|
||||||
|
public const S CONST4 = S;
|
||||||
|
public const ?S CONST5 = S;
|
||||||
|
}
|
||||||
|
|
||||||
|
class B extends A {
|
||||||
|
public const S CONST1 = Z;
|
||||||
|
public const Z CONST2 = Z;
|
||||||
|
public const S CONST3 = Z;
|
||||||
|
public const S&Stringable CONST4 = Z;
|
||||||
|
public const (S&Stringable)|null CONST5 = Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
define("S", new S());
|
||||||
|
define("Z", new Z());
|
||||||
|
|
||||||
|
var_dump(B::CONST1);
|
||||||
|
var_dump(B::CONST2);
|
||||||
|
var_dump(B::CONST3);
|
||||||
|
var_dump(B::CONST4);
|
||||||
|
var_dump(B::CONST5);
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
object(Z)#%d (%d) {
|
||||||
|
}
|
||||||
|
object(Z)#%d (%d) {
|
||||||
|
}
|
||||||
|
object(Z)#%d (%d) {
|
||||||
|
}
|
||||||
|
object(Z)#%d (%d) {
|
||||||
|
}
|
||||||
|
object(Z)#%d (%d) {
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (inheritance; private constants)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
private const int CONST1 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class B extends A {
|
||||||
|
public const string CONST1 = 'a';
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(B::CONST1);
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
string(1) "a"
|
|
@ -0,0 +1,39 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (composition; traits)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
const G = new stdClass();
|
||||||
|
|
||||||
|
enum E {
|
||||||
|
case Case1;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait T {
|
||||||
|
public const int CONST1 = 1;
|
||||||
|
public const ?array CONST2 = [];
|
||||||
|
public const E CONST3 = E::Case1;
|
||||||
|
public const stdClass CONST4 = G;
|
||||||
|
}
|
||||||
|
|
||||||
|
class C {
|
||||||
|
use T;
|
||||||
|
|
||||||
|
public const int CONST1 = 1;
|
||||||
|
public const ?array CONST2 = [];
|
||||||
|
public const E CONST3 = E::Case1;
|
||||||
|
public const stdClass CONST4 = G;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(C::CONST1);
|
||||||
|
var_dump(C::CONST2);
|
||||||
|
var_dump(C::CONST3);
|
||||||
|
var_dump(C::CONST4);
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(1)
|
||||||
|
array(0) {
|
||||||
|
}
|
||||||
|
enum(E::Case1)
|
||||||
|
object(stdClass)#1 (0) {
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (redefinition; interfaces and traits)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
enum E {
|
||||||
|
case Case1;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface I {
|
||||||
|
public const E CONST1 = E::Case1;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait T {
|
||||||
|
public const E CONST1 = E::Case1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class C implements I {
|
||||||
|
use T;
|
||||||
|
|
||||||
|
public const E CONST1 = E::Case1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(C::CONST1);
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
enum(E::Case1)
|
|
@ -0,0 +1,10 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (type mismatch; simple)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const string CONST1 = 1;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Cannot use int as value for class constant A::CONST1 of type string in %s on line %d
|
|
@ -0,0 +1,15 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (type error)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A1 {
|
||||||
|
const ?B1 C = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class A2 extends A1 {
|
||||||
|
const ?B2 C = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Type of A2::C must be compatible with A1::C of type ?B1 in %s on line %d
|
|
@ -0,0 +1,21 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (static type error)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
enum E1 {
|
||||||
|
const static C = E2::Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum E2 {
|
||||||
|
case Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(E1::C);
|
||||||
|
} catch (TypeError $e) {
|
||||||
|
echo $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot assign E2 to class constant E1::C of type static
|
|
@ -0,0 +1,15 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants with static in union
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const static|B CONST1 = B;
|
||||||
|
public const static|stdClass|B CONST2 = B;
|
||||||
|
}
|
||||||
|
class B {}
|
||||||
|
define("B", new B());
|
||||||
|
var_dump(new A());
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
object(A)#2 (0) {
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (type mismatch; runtime simple)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const int CONST1 = C;
|
||||||
|
}
|
||||||
|
|
||||||
|
define("C", "c");
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(A::CONST1);
|
||||||
|
} catch (TypeError $exception) {
|
||||||
|
echo $exception->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(A::CONST1);
|
||||||
|
} catch (TypeError $exception) {
|
||||||
|
echo $exception->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot assign string to class constant A::CONST1 of type int
|
||||||
|
Cannot assign string to class constant A::CONST1 of type int
|
|
@ -0,0 +1,25 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (type mismatch; runtime object)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const string CONST1 = C;
|
||||||
|
}
|
||||||
|
|
||||||
|
define('C', new stdClass);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(A::CONST1);
|
||||||
|
} catch (TypeError $exception) {
|
||||||
|
echo $exception->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(A::CONST1);
|
||||||
|
} catch (TypeError $exception) {
|
||||||
|
echo $exception->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot assign stdClass to class constant A::CONST1 of type string
|
||||||
|
Cannot assign stdClass to class constant A::CONST1 of type string
|
|
@ -0,0 +1,10 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (type not allowed; callable)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const callable CONST1 = 1;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Class constant A::CONST1 cannot have type callable in %s on line %d
|
|
@ -0,0 +1,10 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (type not allowed; void)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const void CONST1 = 1;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Class constant A::CONST1 cannot have type void in %s on line %d
|
|
@ -0,0 +1,10 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (type not allowed; never)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const never CONST1 = 1;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Class constant A::CONST1 cannot have type never in %s on line %d
|
|
@ -0,0 +1,25 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (type mismatch; runtime)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const stdClass&Stringable CONST1 = C;
|
||||||
|
}
|
||||||
|
|
||||||
|
define("C", new stdClass);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(A::CONST1);
|
||||||
|
} catch (TypeError $exception) {
|
||||||
|
echo $exception->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(A::CONST1);
|
||||||
|
} catch (TypeError $exception) {
|
||||||
|
echo $exception->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable
|
||||||
|
Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable
|
|
@ -0,0 +1,40 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (type mismatch; runtime)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const stdClass&Stringable CONST1 = C;
|
||||||
|
public const stdClass&Stringable CONST2 = A::CONST1;
|
||||||
|
}
|
||||||
|
|
||||||
|
define("C", new stdClass);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(A::CONST2);
|
||||||
|
} catch (TypeError $exception) {
|
||||||
|
echo $exception->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(A::CONST2);
|
||||||
|
} catch (TypeError $exception) {
|
||||||
|
echo $exception->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(A::CONST1);
|
||||||
|
} catch (TypeError $exception) {
|
||||||
|
echo $exception->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(A::CONST1);
|
||||||
|
} catch (TypeError $exception) {
|
||||||
|
echo $exception->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable
|
||||||
|
Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable
|
||||||
|
Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable
|
||||||
|
Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable
|
|
@ -0,0 +1,25 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (type coercion is unsupported)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class S {
|
||||||
|
public function __toString() {
|
||||||
|
echo "Side effect!\n";
|
||||||
|
return 'S';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public const string S = S;
|
||||||
|
}
|
||||||
|
|
||||||
|
define("S", new S());
|
||||||
|
|
||||||
|
try {
|
||||||
|
var_dump(A::S);
|
||||||
|
} catch (TypeError $e) {
|
||||||
|
echo $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot assign S to class constant A::S of type string
|
|
@ -0,0 +1,66 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (declaration; compile-type simple)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const null CONST1 = null;
|
||||||
|
public const false CONST2 = false;
|
||||||
|
public const true CONST3 = true;
|
||||||
|
public const bool CONST4 = true;
|
||||||
|
public const int CONST5 = 0;
|
||||||
|
public const float CONST6 = 3.14;
|
||||||
|
public const float CONST7 = 3;
|
||||||
|
public const string CONST8 = "";
|
||||||
|
public const array CONST9 = [];
|
||||||
|
public const array|string CONST10 = "";
|
||||||
|
public const array|string|null CONST11 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(A::CONST1);
|
||||||
|
var_dump(A::CONST1);
|
||||||
|
var_dump(A::CONST2);
|
||||||
|
var_dump(A::CONST2);
|
||||||
|
var_dump(A::CONST3);
|
||||||
|
var_dump(A::CONST3);
|
||||||
|
var_dump(A::CONST4);
|
||||||
|
var_dump(A::CONST4);
|
||||||
|
var_dump(A::CONST5);
|
||||||
|
var_dump(A::CONST5);
|
||||||
|
var_dump(A::CONST6);
|
||||||
|
var_dump(A::CONST6);
|
||||||
|
var_dump(A::CONST7);
|
||||||
|
var_dump(A::CONST7);
|
||||||
|
var_dump(A::CONST8);
|
||||||
|
var_dump(A::CONST8);
|
||||||
|
var_dump(A::CONST9);
|
||||||
|
var_dump(A::CONST9);
|
||||||
|
var_dump(A::CONST10);
|
||||||
|
var_dump(A::CONST10);
|
||||||
|
var_dump(A::CONST11);
|
||||||
|
var_dump(A::CONST11);
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
NULL
|
||||||
|
NULL
|
||||||
|
bool(false)
|
||||||
|
bool(false)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
bool(true)
|
||||||
|
int(0)
|
||||||
|
int(0)
|
||||||
|
float(3.14)
|
||||||
|
float(3.14)
|
||||||
|
float(3)
|
||||||
|
float(3)
|
||||||
|
string(0) ""
|
||||||
|
string(0) ""
|
||||||
|
array(0) {
|
||||||
|
}
|
||||||
|
array(0) {
|
||||||
|
}
|
||||||
|
string(0) ""
|
||||||
|
string(0) ""
|
||||||
|
NULL
|
||||||
|
NULL
|
|
@ -0,0 +1,52 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constants (declaration; runtime)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class A {
|
||||||
|
public const object CONST1 = C;
|
||||||
|
public const ?object CONST2 = C;
|
||||||
|
public const B|stdClass CONST3 = C;
|
||||||
|
public const B&Stringable CONST4 = C;
|
||||||
|
public const (B&Stringable)|null CONST5 = C;
|
||||||
|
}
|
||||||
|
|
||||||
|
class B implements Stringable {
|
||||||
|
public function __toString() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const C = new B();
|
||||||
|
|
||||||
|
var_dump(A::CONST1);
|
||||||
|
var_dump(A::CONST1);
|
||||||
|
var_dump(A::CONST2);
|
||||||
|
var_dump(A::CONST2);
|
||||||
|
var_dump(A::CONST3);
|
||||||
|
var_dump(A::CONST3);
|
||||||
|
var_dump(A::CONST4);
|
||||||
|
var_dump(A::CONST4);
|
||||||
|
var_dump(A::CONST5);
|
||||||
|
var_dump(A::CONST5);
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
object(B)#%d (%d) {
|
||||||
|
}
|
||||||
|
object(B)#%d (%d) {
|
||||||
|
}
|
||||||
|
object(B)#%d (%d) {
|
||||||
|
}
|
||||||
|
object(B)#%d (%d) {
|
||||||
|
}
|
||||||
|
object(B)#%d (%d) {
|
||||||
|
}
|
||||||
|
object(B)#%d (%d) {
|
||||||
|
}
|
||||||
|
object(B)#%d (%d) {
|
||||||
|
}
|
||||||
|
object(B)#%d (%d) {
|
||||||
|
}
|
||||||
|
object(B)#%d (%d) {
|
||||||
|
}
|
||||||
|
object(B)#%d (%d) {
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
--TEST--
|
||||||
|
Typed enum constants (self/static)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
enum E {
|
||||||
|
public const E CONST1 = E::Foo;
|
||||||
|
public const self CONST2 = E::Foo;
|
||||||
|
public const static CONST3 = E::Foo;
|
||||||
|
public const static|stdClass CONST4 = E::Foo;
|
||||||
|
|
||||||
|
case Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public const E ENUM_CONST = E::Foo;
|
||||||
|
public const E CONST1 = E::CONST1;
|
||||||
|
public const E CONST2 = E::CONST2;
|
||||||
|
public const E CONST3 = E::CONST3;
|
||||||
|
public const E CONST4 = E::CONST4;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_dump(A::ENUM_CONST);
|
||||||
|
var_dump(A::ENUM_CONST);
|
||||||
|
var_dump(A::CONST1);
|
||||||
|
var_dump(A::CONST1);
|
||||||
|
var_dump(A::CONST2);
|
||||||
|
var_dump(A::CONST2);
|
||||||
|
var_dump(E::CONST3);
|
||||||
|
var_dump(E::CONST3);
|
||||||
|
var_dump(A::CONST3);
|
||||||
|
var_dump(A::CONST3);
|
||||||
|
var_dump(E::CONST4);
|
||||||
|
var_dump(E::CONST4);
|
||||||
|
var_dump(A::CONST4);
|
||||||
|
var_dump(A::CONST4);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
enum(E::Foo)
|
||||||
|
enum(E::Foo)
|
||||||
|
enum(E::Foo)
|
||||||
|
enum(E::Foo)
|
||||||
|
enum(E::Foo)
|
||||||
|
enum(E::Foo)
|
||||||
|
enum(E::Foo)
|
||||||
|
enum(E::Foo)
|
||||||
|
enum(E::Foo)
|
||||||
|
enum(E::Foo)
|
||||||
|
enum(E::Foo)
|
||||||
|
enum(E::Foo)
|
||||||
|
enum(E::Foo)
|
||||||
|
enum(E::Foo)
|
|
@ -1412,6 +1412,34 @@ static zend_result update_property(zval *val, zend_property_info *prop_info) {
|
||||||
return zval_update_constant_ex(val, prop_info->ce);
|
return zval_update_constant_ex(val, prop_info->ce);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZEND_API zend_result zend_update_class_constant(zend_class_constant *c, const zend_string *name, zend_class_entry *scope)
|
||||||
|
{
|
||||||
|
ZEND_ASSERT(Z_TYPE(c->value) == IS_CONSTANT_AST);
|
||||||
|
|
||||||
|
if (EXPECTED(!ZEND_TYPE_IS_SET(c->type) || ZEND_TYPE_PURE_MASK(c->type) == MAY_BE_ANY)) {
|
||||||
|
return zval_update_constant_ex(&c->value, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
zval tmp;
|
||||||
|
|
||||||
|
ZVAL_COPY(&tmp, &c->value);
|
||||||
|
zend_result result = zval_update_constant_ex(&tmp, scope);
|
||||||
|
if (result == FAILURE) {
|
||||||
|
zval_ptr_dtor(&tmp);
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UNEXPECTED(!zend_verify_class_constant_type(c, name, &tmp))) {
|
||||||
|
zval_ptr_dtor(&tmp);
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
zval_ptr_dtor(&c->value);
|
||||||
|
ZVAL_COPY_VALUE(&c->value, &tmp);
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /* {{{ */
|
ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /* {{{ */
|
||||||
{
|
{
|
||||||
zend_class_mutable_data *mutable_data = NULL;
|
zend_class_mutable_data *mutable_data = NULL;
|
||||||
|
@ -1470,7 +1498,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
|
||||||
}
|
}
|
||||||
|
|
||||||
val = &c->value;
|
val = &c->value;
|
||||||
if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) {
|
if (UNEXPECTED(zend_update_class_constant(c, name, c->ce) != SUCCESS)) {
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4532,7 +4560,7 @@ ZEND_API void zend_declare_property_stringl(zend_class_entry *ce, const char *na
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int flags, zend_string *doc_comment) /* {{{ */
|
ZEND_API zend_class_constant *zend_declare_typed_class_constant(zend_class_entry *ce, zend_string *name, zval *value, int flags, zend_string *doc_comment, zend_type type) /* {{{ */
|
||||||
{
|
{
|
||||||
zend_class_constant *c;
|
zend_class_constant *c;
|
||||||
|
|
||||||
|
@ -4561,6 +4589,8 @@ ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *c
|
||||||
c->doc_comment = doc_comment;
|
c->doc_comment = doc_comment;
|
||||||
c->attributes = NULL;
|
c->attributes = NULL;
|
||||||
c->ce = ce;
|
c->ce = ce;
|
||||||
|
c->type = type;
|
||||||
|
|
||||||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||||
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
|
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
|
||||||
ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
|
ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
|
||||||
|
@ -4576,7 +4606,11 @@ ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *c
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
/* }}} */
|
|
||||||
|
ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int flags, zend_string *doc_comment)
|
||||||
|
{
|
||||||
|
return zend_declare_typed_class_constant(ce, name, value, flags, doc_comment, (zend_type) ZEND_TYPE_INIT_NONE(0));
|
||||||
|
}
|
||||||
|
|
||||||
ZEND_API void zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value) /* {{{ */
|
ZEND_API void zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value) /* {{{ */
|
||||||
{
|
{
|
||||||
|
|
|
@ -429,6 +429,7 @@ ZEND_API void zend_declare_property_double(zend_class_entry *ce, const char *nam
|
||||||
ZEND_API void zend_declare_property_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value, int access_type);
|
ZEND_API void zend_declare_property_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value, int access_type);
|
||||||
ZEND_API void zend_declare_property_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_len, int access_type);
|
ZEND_API void zend_declare_property_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_len, int access_type);
|
||||||
|
|
||||||
|
ZEND_API zend_class_constant *zend_declare_typed_class_constant(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment, zend_type type);
|
||||||
ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment);
|
ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment);
|
||||||
ZEND_API void zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value);
|
ZEND_API void zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value);
|
||||||
ZEND_API void zend_declare_class_constant_null(zend_class_entry *ce, const char *name, size_t name_length);
|
ZEND_API void zend_declare_class_constant_null(zend_class_entry *ce, const char *name, size_t name_length);
|
||||||
|
@ -438,6 +439,7 @@ ZEND_API void zend_declare_class_constant_double(zend_class_entry *ce, const cha
|
||||||
ZEND_API void zend_declare_class_constant_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_length);
|
ZEND_API void zend_declare_class_constant_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_length);
|
||||||
ZEND_API void zend_declare_class_constant_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value);
|
ZEND_API void zend_declare_class_constant_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value);
|
||||||
|
|
||||||
|
ZEND_API zend_result zend_update_class_constant(zend_class_constant *c, const zend_string *name, zend_class_entry *scope);
|
||||||
ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type);
|
ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type);
|
||||||
ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type);
|
ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type);
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,6 @@ enum _zend_ast_kind {
|
||||||
ZEND_AST_CATCH,
|
ZEND_AST_CATCH,
|
||||||
ZEND_AST_PROP_GROUP,
|
ZEND_AST_PROP_GROUP,
|
||||||
ZEND_AST_PROP_ELEM,
|
ZEND_AST_PROP_ELEM,
|
||||||
ZEND_AST_CONST_ELEM,
|
|
||||||
|
|
||||||
// Pseudo node for initializing enums
|
// Pseudo node for initializing enums
|
||||||
ZEND_AST_CONST_ENUM_INIT,
|
ZEND_AST_CONST_ENUM_INIT,
|
||||||
|
@ -170,6 +169,7 @@ enum _zend_ast_kind {
|
||||||
ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT,
|
ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT,
|
||||||
ZEND_AST_FOREACH,
|
ZEND_AST_FOREACH,
|
||||||
ZEND_AST_ENUM_CASE,
|
ZEND_AST_ENUM_CASE,
|
||||||
|
ZEND_AST_CONST_ELEM,
|
||||||
|
|
||||||
/* 5 child nodes */
|
/* 5 child nodes */
|
||||||
ZEND_AST_PARAM = 5 << ZEND_AST_NUM_CHILDREN_SHIFT,
|
ZEND_AST_PARAM = 5 << ZEND_AST_NUM_CHILDREN_SHIFT,
|
||||||
|
|
|
@ -7693,7 +7693,7 @@ static void zend_check_trait_alias_modifiers(uint32_t attr) /* {{{ */
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_ast *attr_ast) /* {{{ */
|
static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_ast *attr_ast)
|
||||||
{
|
{
|
||||||
zend_ast_list *list = zend_ast_get_list(ast);
|
zend_ast_list *list = zend_ast_get_list(ast);
|
||||||
zend_class_entry *ce = CG(active_class_entry);
|
zend_class_entry *ce = CG(active_class_entry);
|
||||||
|
@ -7705,9 +7705,24 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
|
||||||
zend_ast *name_ast = const_ast->child[0];
|
zend_ast *name_ast = const_ast->child[0];
|
||||||
zend_ast **value_ast_ptr = &const_ast->child[1];
|
zend_ast **value_ast_ptr = &const_ast->child[1];
|
||||||
zend_ast *doc_comment_ast = const_ast->child[2];
|
zend_ast *doc_comment_ast = const_ast->child[2];
|
||||||
|
zend_ast *type_ast = const_ast->child[3];
|
||||||
zend_string *name = zval_make_interned_string(zend_ast_get_zval(name_ast));
|
zend_string *name = zval_make_interned_string(zend_ast_get_zval(name_ast));
|
||||||
zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL;
|
zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL;
|
||||||
zval value_zv;
|
zval value_zv;
|
||||||
|
zend_type type = ZEND_TYPE_INIT_NONE(0);
|
||||||
|
|
||||||
|
if (type_ast) {
|
||||||
|
type = zend_compile_typename(type_ast, /* force_allow_null */ 0);
|
||||||
|
|
||||||
|
uint32_t type_mask = ZEND_TYPE_PURE_MASK(type);
|
||||||
|
|
||||||
|
if (type_mask != MAY_BE_ANY && (type_mask & (MAY_BE_CALLABLE|MAY_BE_VOID|MAY_BE_NEVER))) {
|
||||||
|
zend_string *type_str = zend_type_to_string(type);
|
||||||
|
|
||||||
|
zend_error_noreturn(E_COMPILE_ERROR, "Class constant %s::%s cannot have type %s",
|
||||||
|
ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(type_str));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (UNEXPECTED((flags & ZEND_ACC_PRIVATE) && (flags & ZEND_ACC_FINAL))) {
|
if (UNEXPECTED((flags & ZEND_ACC_PRIVATE) && (flags & ZEND_ACC_FINAL))) {
|
||||||
zend_error_noreturn(
|
zend_error_noreturn(
|
||||||
|
@ -7717,14 +7732,21 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
|
||||||
}
|
}
|
||||||
|
|
||||||
zend_const_expr_to_zval(&value_zv, value_ast_ptr, /* allow_dynamic */ false);
|
zend_const_expr_to_zval(&value_zv, value_ast_ptr, /* allow_dynamic */ false);
|
||||||
c = zend_declare_class_constant_ex(ce, name, &value_zv, flags, doc_comment);
|
|
||||||
|
if (!Z_CONSTANT(value_zv) && ZEND_TYPE_IS_SET(type) && !zend_is_valid_default_value(type, &value_zv)) {
|
||||||
|
zend_string *type_str = zend_type_to_string(type);
|
||||||
|
|
||||||
|
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use %s as value for class constant %s::%s of type %s",
|
||||||
|
zend_zval_type_name(&value_zv), ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(type_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
c = zend_declare_typed_class_constant(ce, name, &value_zv, flags, doc_comment, type);
|
||||||
|
|
||||||
if (attr_ast) {
|
if (attr_ast) {
|
||||||
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0);
|
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* }}} */
|
|
||||||
|
|
||||||
static void zend_compile_class_const_group(zend_ast *ast) /* {{{ */
|
static void zend_compile_class_const_group(zend_ast *ast) /* {{{ */
|
||||||
{
|
{
|
||||||
|
|
|
@ -407,6 +407,7 @@ typedef struct _zend_class_constant {
|
||||||
zend_string *doc_comment;
|
zend_string *doc_comment;
|
||||||
HashTable *attributes;
|
HashTable *attributes;
|
||||||
zend_class_entry *ce;
|
zend_class_entry *ce;
|
||||||
|
zend_type type;
|
||||||
} zend_class_constant;
|
} zend_class_constant;
|
||||||
|
|
||||||
#define ZEND_CLASS_CONST_FLAGS(c) Z_CONSTANT_FLAGS((c)->value)
|
#define ZEND_CLASS_CONST_FLAGS(c) Z_CONSTANT_FLAGS((c)->value)
|
||||||
|
|
|
@ -361,7 +361,7 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *
|
||||||
}
|
}
|
||||||
|
|
||||||
MARK_CONSTANT_VISITED(ret_constant);
|
MARK_CONSTANT_VISITED(ret_constant);
|
||||||
ret = zval_update_constant_ex(ret_constant, c->ce);
|
ret = zend_update_class_constant(c, constant_name, c->ce);
|
||||||
RESET_CONSTANT_VISITED(ret_constant);
|
RESET_CONSTANT_VISITED(ret_constant);
|
||||||
|
|
||||||
if (UNEXPECTED(ret != SUCCESS)) {
|
if (UNEXPECTED(ret != SUCCESS)) {
|
||||||
|
|
|
@ -820,6 +820,16 @@ ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool s
|
||||||
return zend_verify_weak_scalar_type_hint(type_mask, arg);
|
return zend_verify_weak_scalar_type_hint(type_mask, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZEND_COLD zend_never_inline void zend_verify_class_constant_type_error(const zend_class_constant *c, const zend_string *name, const zval *constant)
|
||||||
|
{
|
||||||
|
zend_string *type_str = zend_type_to_string(c->type);
|
||||||
|
|
||||||
|
zend_type_error("Cannot assign %s to class constant %s::%s of type %s",
|
||||||
|
zend_zval_type_name(constant), ZSTR_VAL(c->ce->name), ZSTR_VAL(name), ZSTR_VAL(type_str));
|
||||||
|
|
||||||
|
zend_string_release(type_str);
|
||||||
|
}
|
||||||
|
|
||||||
ZEND_COLD zend_never_inline void zend_verify_property_type_error(const zend_property_info *info, const zval *property)
|
ZEND_COLD zend_never_inline void zend_verify_property_type_error(const zend_property_info *info, const zval *property)
|
||||||
{
|
{
|
||||||
zend_string *type_str;
|
zend_string *type_str;
|
||||||
|
@ -908,7 +918,7 @@ static const zend_class_entry *resolve_single_class_type(zend_string *name, cons
|
||||||
}
|
}
|
||||||
|
|
||||||
static zend_always_inline const zend_class_entry *zend_ce_from_type(
|
static zend_always_inline const zend_class_entry *zend_ce_from_type(
|
||||||
const zend_property_info *info, const zend_type *type) {
|
const zend_class_entry *scope, const zend_type *type) {
|
||||||
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*type));
|
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*type));
|
||||||
zend_string *name = ZEND_TYPE_NAME(*type);
|
zend_string *name = ZEND_TYPE_NAME(*type);
|
||||||
if (ZSTR_HAS_CE_CACHE(name)) {
|
if (ZSTR_HAS_CE_CACHE(name)) {
|
||||||
|
@ -918,52 +928,61 @@ static zend_always_inline const zend_class_entry *zend_ce_from_type(
|
||||||
}
|
}
|
||||||
return ce;
|
return ce;
|
||||||
}
|
}
|
||||||
return resolve_single_class_type(name, info->ce);
|
return resolve_single_class_type(name, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool zend_check_intersection_for_property_class_type(zend_type_list *intersection_type_list,
|
static bool zend_check_intersection_for_property_or_class_constant_class_type(
|
||||||
const zend_property_info *info, const zend_class_entry *object_ce)
|
const zend_class_entry *scope, zend_type_list *intersection_type_list, const zend_class_entry *value_ce)
|
||||||
{
|
{
|
||||||
zend_type *list_type;
|
zend_type *list_type;
|
||||||
|
|
||||||
ZEND_TYPE_LIST_FOREACH(intersection_type_list, list_type) {
|
ZEND_TYPE_LIST_FOREACH(intersection_type_list, list_type) {
|
||||||
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
|
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
|
||||||
const zend_class_entry *ce = zend_ce_from_type(info, list_type);
|
const zend_class_entry *ce = zend_ce_from_type(scope, list_type);
|
||||||
if (!ce || !instanceof_function(object_ce, ce)) {
|
if (!ce || !instanceof_function(value_ce, ce)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} ZEND_TYPE_LIST_FOREACH_END();
|
} ZEND_TYPE_LIST_FOREACH_END();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool zend_check_and_resolve_property_class_type(
|
static bool zend_check_and_resolve_property_or_class_constant_class_type(
|
||||||
const zend_property_info *info, const zend_class_entry *object_ce) {
|
const zend_class_entry *scope, zend_type member_type, const zend_class_entry *value_ce) {
|
||||||
if (ZEND_TYPE_HAS_LIST(info->type)) {
|
if (ZEND_TYPE_HAS_LIST(member_type)) {
|
||||||
zend_type *list_type;
|
zend_type *list_type;
|
||||||
if (ZEND_TYPE_IS_INTERSECTION(info->type)) {
|
if (ZEND_TYPE_IS_INTERSECTION(member_type)) {
|
||||||
return zend_check_intersection_for_property_class_type(
|
return zend_check_intersection_for_property_or_class_constant_class_type(
|
||||||
ZEND_TYPE_LIST(info->type), info, object_ce);
|
scope, ZEND_TYPE_LIST(member_type), value_ce);
|
||||||
} else {
|
} else {
|
||||||
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(info->type), list_type) {
|
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(member_type), list_type) {
|
||||||
if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
|
if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
|
||||||
if (zend_check_intersection_for_property_class_type(
|
if (zend_check_intersection_for_property_or_class_constant_class_type(
|
||||||
ZEND_TYPE_LIST(*list_type), info, object_ce)) {
|
scope, ZEND_TYPE_LIST(*list_type), value_ce)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
|
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
|
||||||
const zend_class_entry *ce = zend_ce_from_type(info, list_type);
|
const zend_class_entry *ce = zend_ce_from_type(scope, list_type);
|
||||||
if (ce && instanceof_function(object_ce, ce)) {
|
if (ce && instanceof_function(value_ce, ce)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} ZEND_TYPE_LIST_FOREACH_END();
|
} ZEND_TYPE_LIST_FOREACH_END();
|
||||||
|
|
||||||
|
if ((ZEND_TYPE_PURE_MASK(member_type) & MAY_BE_STATIC)) {
|
||||||
|
return value_ce == scope;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else if ((ZEND_TYPE_PURE_MASK(member_type) & MAY_BE_STATIC) && value_ce == scope) {
|
||||||
const zend_class_entry *ce = zend_ce_from_type(info, &info->type);
|
return true;
|
||||||
return ce && instanceof_function(object_ce, ce);
|
} else if (ZEND_TYPE_HAS_NAME(member_type)) {
|
||||||
|
const zend_class_entry *ce = zend_ce_from_type(scope, &member_type);
|
||||||
|
return ce && instanceof_function(value_ce, ce);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static zend_always_inline bool i_zend_check_property_type(const zend_property_info *info, zval *property, bool strict)
|
static zend_always_inline bool i_zend_check_property_type(const zend_property_info *info, zval *property, bool strict)
|
||||||
|
@ -974,7 +993,7 @@ static zend_always_inline bool i_zend_check_property_type(const zend_property_in
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ZEND_TYPE_IS_COMPLEX(info->type) && Z_TYPE_P(property) == IS_OBJECT
|
if (ZEND_TYPE_IS_COMPLEX(info->type) && Z_TYPE_P(property) == IS_OBJECT
|
||||||
&& zend_check_and_resolve_property_class_type(info, Z_OBJCE_P(property))) {
|
&& zend_check_and_resolve_property_or_class_constant_class_type(info->ce, info->type, Z_OBJCE_P(property))) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1455,6 +1474,33 @@ static ZEND_COLD void zend_verify_missing_return_type(const zend_function *zf)
|
||||||
zend_verify_return_error(zf, NULL);
|
zend_verify_return_error(zf, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static zend_always_inline bool zend_check_class_constant_type(zend_class_constant *c, zval *constant)
|
||||||
|
{
|
||||||
|
ZEND_ASSERT(!Z_ISREF_P(constant));
|
||||||
|
if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(c->type, Z_TYPE_P(constant)))) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((ZEND_TYPE_PURE_MASK(c->type) & MAY_BE_STATIC) || ZEND_TYPE_IS_COMPLEX(c->type)) && Z_TYPE_P(constant) == IS_OBJECT
|
||||||
|
&& zend_check_and_resolve_property_or_class_constant_class_type(c->ce, c->type, Z_OBJCE_P(constant))) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t type_mask = ZEND_TYPE_FULL_MASK(c->type);
|
||||||
|
ZEND_ASSERT(!(type_mask & (MAY_BE_CALLABLE|MAY_BE_NEVER|MAY_BE_VOID)));
|
||||||
|
return zend_verify_scalar_type_hint(type_mask, constant, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZEND_API bool zend_never_inline zend_verify_class_constant_type(zend_class_constant *c, const zend_string *name, zval *constant)
|
||||||
|
{
|
||||||
|
if (!zend_check_class_constant_type(c, constant)) {
|
||||||
|
zend_verify_class_constant_type_error(c, name, constant);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_object_as_array(void)
|
static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_object_as_array(void)
|
||||||
{
|
{
|
||||||
zend_throw_error(NULL, "Cannot use object as array");
|
zend_throw_error(NULL, "Cannot use object as array");
|
||||||
|
@ -3473,7 +3519,7 @@ static zend_always_inline int i_zend_verify_type_assignable_zval(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ZEND_TYPE_IS_COMPLEX(type) && zv_type == IS_OBJECT
|
if (ZEND_TYPE_IS_COMPLEX(type) && zv_type == IS_OBJECT
|
||||||
&& zend_check_and_resolve_property_class_type(info, Z_OBJCE_P(zv))) {
|
&& zend_check_and_resolve_property_or_class_constant_class_type(info->ce, info->type, Z_OBJCE_P(zv))) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -484,6 +484,10 @@ ZEND_API int ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *call);
|
||||||
#define ZEND_CLASS_HAS_TYPE_HINTS(ce) ((ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) == ZEND_ACC_HAS_TYPE_HINTS)
|
#define ZEND_CLASS_HAS_TYPE_HINTS(ce) ((ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) == ZEND_ACC_HAS_TYPE_HINTS)
|
||||||
#define ZEND_CLASS_HAS_READONLY_PROPS(ce) ((ce->ce_flags & ZEND_ACC_HAS_READONLY_PROPS) == ZEND_ACC_HAS_READONLY_PROPS)
|
#define ZEND_CLASS_HAS_READONLY_PROPS(ce) ((ce->ce_flags & ZEND_ACC_HAS_READONLY_PROPS) == ZEND_ACC_HAS_READONLY_PROPS)
|
||||||
|
|
||||||
|
|
||||||
|
ZEND_API bool zend_verify_class_constant_type(zend_class_constant *c, const zend_string *name, zval *constant);
|
||||||
|
ZEND_COLD void zend_verify_class_constant_type_error(const zend_class_constant *c, const zend_string *name, const zval *constant);
|
||||||
|
|
||||||
ZEND_API bool zend_verify_property_type(const zend_property_info *info, zval *property, bool strict);
|
ZEND_API bool zend_verify_property_type(const zend_property_info *info, zval *property, bool strict);
|
||||||
ZEND_COLD void zend_verify_property_type_error(const zend_property_info *info, const zval *property);
|
ZEND_COLD void zend_verify_property_type_error(const zend_property_info *info, const zval *property);
|
||||||
ZEND_COLD void zend_magic_get_property_type_inconsistency_error(const zend_property_info *info, const zval *property);
|
ZEND_COLD void zend_magic_get_property_type_inconsistency_error(const zend_property_info *info, const zval *property);
|
||||||
|
|
|
@ -51,6 +51,9 @@ static void add_compatibility_obligation(
|
||||||
static void add_property_compatibility_obligation(
|
static void add_property_compatibility_obligation(
|
||||||
zend_class_entry *ce, const zend_property_info *child_prop,
|
zend_class_entry *ce, const zend_property_info *child_prop,
|
||||||
const zend_property_info *parent_prop);
|
const zend_property_info *parent_prop);
|
||||||
|
static void add_class_constant_compatibility_obligation(
|
||||||
|
zend_class_entry *ce, const zend_class_constant *child_const,
|
||||||
|
const zend_class_constant *parent_const, const zend_string *const_name);
|
||||||
|
|
||||||
static void ZEND_COLD emit_incompatible_method_error(
|
static void ZEND_COLD emit_incompatible_method_error(
|
||||||
const zend_function *child, zend_class_entry *child_scope,
|
const zend_function *child, zend_class_entry *child_scope,
|
||||||
|
@ -1359,6 +1362,29 @@ static void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_en
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
static void emit_incompatible_class_constant_error(
|
||||||
|
const zend_class_constant *child, const zend_class_constant *parent, const zend_string *const_name) {
|
||||||
|
zend_string *type_str = zend_type_to_string_resolved(parent->type, parent->ce);
|
||||||
|
zend_error_noreturn(E_COMPILE_ERROR,
|
||||||
|
"Type of %s::%s must be compatible with %s::%s of type %s",
|
||||||
|
ZSTR_VAL(child->ce->name),
|
||||||
|
ZSTR_VAL(const_name),
|
||||||
|
ZSTR_VAL(parent->ce->name),
|
||||||
|
ZSTR_VAL(const_name),
|
||||||
|
ZSTR_VAL(type_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inheritance_status class_constant_types_compatible(const zend_class_constant *parent, const zend_class_constant *child)
|
||||||
|
{
|
||||||
|
ZEND_ASSERT(ZEND_TYPE_IS_SET(parent->type));
|
||||||
|
|
||||||
|
if (!ZEND_TYPE_IS_SET(child->type)) {
|
||||||
|
return INHERITANCE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return zend_perform_covariant_type_check(child->ce, child->type, parent->ce, parent->type);
|
||||||
|
}
|
||||||
|
|
||||||
static void do_inherit_class_constant(zend_string *name, zend_class_constant *parent_const, zend_class_entry *ce) /* {{{ */
|
static void do_inherit_class_constant(zend_string *name, zend_class_constant *parent_const, zend_class_entry *ce) /* {{{ */
|
||||||
{
|
{
|
||||||
zval *zv = zend_hash_find_known_hash(&ce->constants_table, name);
|
zval *zv = zend_hash_find_known_hash(&ce->constants_table, name);
|
||||||
|
@ -1366,9 +1392,14 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa
|
||||||
|
|
||||||
if (zv != NULL) {
|
if (zv != NULL) {
|
||||||
c = (zend_class_constant*)Z_PTR_P(zv);
|
c = (zend_class_constant*)Z_PTR_P(zv);
|
||||||
|
|
||||||
if (UNEXPECTED((ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_PPP_MASK) > (ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PPP_MASK))) {
|
if (UNEXPECTED((ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_PPP_MASK) > (ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PPP_MASK))) {
|
||||||
zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s must be %s (as in class %s)%s",
|
zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s must be %s (as in class %s)%s",
|
||||||
ZSTR_VAL(ce->name), ZSTR_VAL(name), zend_visibility_string(ZEND_CLASS_CONST_FLAGS(parent_const)), ZSTR_VAL(parent_const->ce->name), (ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PUBLIC) ? "" : " or weaker");
|
ZSTR_VAL(ce->name), ZSTR_VAL(name),
|
||||||
|
zend_visibility_string(ZEND_CLASS_CONST_FLAGS(parent_const)),
|
||||||
|
ZSTR_VAL(parent_const->ce->name),
|
||||||
|
(ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PUBLIC) ? "" : " or weaker"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UNEXPECTED((ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_FINAL))) {
|
if (UNEXPECTED((ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_FINAL))) {
|
||||||
|
@ -1377,6 +1408,15 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa
|
||||||
ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(parent_const->ce->name), ZSTR_VAL(name)
|
ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(parent_const->ce->name), ZSTR_VAL(name)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PRIVATE) && UNEXPECTED(ZEND_TYPE_IS_SET(parent_const->type))) {
|
||||||
|
inheritance_status status = class_constant_types_compatible(parent_const, c);
|
||||||
|
if (status == INHERITANCE_ERROR) {
|
||||||
|
emit_incompatible_class_constant_error(c, parent_const, name);
|
||||||
|
} else if (status == INHERITANCE_UNRESOLVED) {
|
||||||
|
add_class_constant_compatibility_obligation(ce, c, parent_const, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (!(ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PRIVATE)) {
|
} else if (!(ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PRIVATE)) {
|
||||||
if (Z_TYPE(parent_const->value) == IS_CONSTANT_AST) {
|
if (Z_TYPE(parent_const->value) == IS_CONSTANT_AST) {
|
||||||
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
|
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
|
||||||
|
@ -1641,12 +1681,18 @@ static zend_always_inline bool check_trait_property_or_constant_value_compatibil
|
||||||
/* if any of the values is a constant, we try to resolve it */
|
/* if any of the values is a constant, we try to resolve it */
|
||||||
if (UNEXPECTED(Z_TYPE_P(op1) == IS_CONSTANT_AST)) {
|
if (UNEXPECTED(Z_TYPE_P(op1) == IS_CONSTANT_AST)) {
|
||||||
ZVAL_COPY_OR_DUP(&op1_tmp, op1);
|
ZVAL_COPY_OR_DUP(&op1_tmp, op1);
|
||||||
zval_update_constant_ex(&op1_tmp, ce);
|
if (UNEXPECTED(zval_update_constant_ex(&op1_tmp, ce) != SUCCESS)) {
|
||||||
|
zval_ptr_dtor(&op1_tmp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
op1 = &op1_tmp;
|
op1 = &op1_tmp;
|
||||||
}
|
}
|
||||||
if (UNEXPECTED(Z_TYPE_P(op2) == IS_CONSTANT_AST)) {
|
if (UNEXPECTED(Z_TYPE_P(op2) == IS_CONSTANT_AST)) {
|
||||||
ZVAL_COPY_OR_DUP(&op2_tmp, op2);
|
ZVAL_COPY_OR_DUP(&op2_tmp, op2);
|
||||||
zval_update_constant_ex(&op2_tmp, ce);
|
if (UNEXPECTED(zval_update_constant_ex(&op2_tmp, ce) != SUCCESS)) {
|
||||||
|
zval_ptr_dtor(&op2_tmp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
op2 = &op2_tmp;
|
op2 = &op2_tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2231,8 +2277,22 @@ static zend_class_entry* find_first_constant_definition(zend_class_entry *ce, ze
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
static bool do_trait_constant_check(zend_class_entry *ce, zend_class_constant *trait_constant, zend_string *name, zend_class_entry **traits, size_t current_trait) /* {{{ */
|
static void emit_incompatible_trait_constant_error(
|
||||||
{
|
zend_class_entry *ce, zend_class_constant *existing_constant, zend_class_constant *trait_constant, zend_string *name,
|
||||||
|
zend_class_entry **traits, size_t current_trait
|
||||||
|
) {
|
||||||
|
zend_error_noreturn(E_COMPILE_ERROR,
|
||||||
|
"%s and %s define the same constant (%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed",
|
||||||
|
ZSTR_VAL(find_first_constant_definition(ce, traits, current_trait, name, existing_constant->ce)->name),
|
||||||
|
ZSTR_VAL(trait_constant->ce->name),
|
||||||
|
ZSTR_VAL(name),
|
||||||
|
ZSTR_VAL(ce->name)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool do_trait_constant_check(
|
||||||
|
zend_class_entry *ce, zend_class_constant *trait_constant, zend_string *name, zend_class_entry **traits, size_t current_trait
|
||||||
|
) {
|
||||||
uint32_t flags_mask = ZEND_ACC_PPP_MASK | ZEND_ACC_FINAL;
|
uint32_t flags_mask = ZEND_ACC_PPP_MASK | ZEND_ACC_FINAL;
|
||||||
|
|
||||||
zval *zv = zend_hash_find_known_hash(&ce->constants_table, name);
|
zval *zv = zend_hash_find_known_hash(&ce->constants_table, name);
|
||||||
|
@ -2243,21 +2303,32 @@ static bool do_trait_constant_check(zend_class_entry *ce, zend_class_constant *t
|
||||||
|
|
||||||
zend_class_constant *existing_constant = Z_PTR_P(zv);
|
zend_class_constant *existing_constant = Z_PTR_P(zv);
|
||||||
|
|
||||||
if ((ZEND_CLASS_CONST_FLAGS(trait_constant) & flags_mask) != (ZEND_CLASS_CONST_FLAGS(existing_constant) & flags_mask) ||
|
if ((ZEND_CLASS_CONST_FLAGS(trait_constant) & flags_mask) != (ZEND_CLASS_CONST_FLAGS(existing_constant) & flags_mask)) {
|
||||||
!check_trait_property_or_constant_value_compatibility(ce, &trait_constant->value, &existing_constant->value)) {
|
emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ZEND_TYPE_IS_SET(trait_constant->type) != ZEND_TYPE_IS_SET(existing_constant->type)) {
|
||||||
|
emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait);
|
||||||
|
return false;
|
||||||
|
} else if (ZEND_TYPE_IS_SET(trait_constant->type)) {
|
||||||
|
inheritance_status status1 = zend_perform_covariant_type_check(ce, existing_constant->type, traits[current_trait], trait_constant->type);
|
||||||
|
inheritance_status status2 = zend_perform_covariant_type_check(traits[current_trait], trait_constant->type, ce, existing_constant->type);
|
||||||
|
if (status1 == INHERITANCE_ERROR || status2 == INHERITANCE_ERROR) {
|
||||||
|
emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!check_trait_property_or_constant_value_compatibility(ce, &trait_constant->value, &existing_constant->value)) {
|
||||||
/* There is an existing constant of the same name, and it conflicts with the new one, so let's throw a fatal error */
|
/* There is an existing constant of the same name, and it conflicts with the new one, so let's throw a fatal error */
|
||||||
zend_error_noreturn(E_COMPILE_ERROR,
|
emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait);
|
||||||
"%s and %s define the same constant (%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed",
|
return false;
|
||||||
ZSTR_VAL(find_first_constant_definition(ce, traits, current_trait, name, existing_constant->ce)->name),
|
|
||||||
ZSTR_VAL(trait_constant->ce->name),
|
|
||||||
ZSTR_VAL(name),
|
|
||||||
ZSTR_VAL(ce->name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There is an existing constant which is compatible with the new one, so no need to add it */
|
/* There is an existing constant which is compatible with the new one, so no need to add it */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/* }}} */
|
|
||||||
|
|
||||||
static void zend_do_traits_constant_binding(zend_class_entry *ce, zend_class_entry **traits) /* {{{ */
|
static void zend_do_traits_constant_binding(zend_class_entry *ce, zend_class_entry **traits) /* {{{ */
|
||||||
{
|
{
|
||||||
|
@ -2508,7 +2579,8 @@ typedef struct {
|
||||||
enum {
|
enum {
|
||||||
OBLIGATION_DEPENDENCY,
|
OBLIGATION_DEPENDENCY,
|
||||||
OBLIGATION_COMPATIBILITY,
|
OBLIGATION_COMPATIBILITY,
|
||||||
OBLIGATION_PROPERTY_COMPATIBILITY
|
OBLIGATION_PROPERTY_COMPATIBILITY,
|
||||||
|
OBLIGATION_CLASS_CONSTANT_COMPATIBILITY
|
||||||
} type;
|
} type;
|
||||||
union {
|
union {
|
||||||
zend_class_entry *dependency_ce;
|
zend_class_entry *dependency_ce;
|
||||||
|
@ -2524,6 +2596,11 @@ typedef struct {
|
||||||
const zend_property_info *parent_prop;
|
const zend_property_info *parent_prop;
|
||||||
const zend_property_info *child_prop;
|
const zend_property_info *child_prop;
|
||||||
};
|
};
|
||||||
|
struct {
|
||||||
|
const zend_string *const_name;
|
||||||
|
const zend_class_constant *parent_const;
|
||||||
|
const zend_class_constant *child_const;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
} variance_obligation;
|
} variance_obligation;
|
||||||
|
|
||||||
|
@ -2599,6 +2676,18 @@ static void add_property_compatibility_obligation(
|
||||||
zend_hash_next_index_insert_ptr(obligations, obligation);
|
zend_hash_next_index_insert_ptr(obligations, obligation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void add_class_constant_compatibility_obligation(
|
||||||
|
zend_class_entry *ce, const zend_class_constant *child_const,
|
||||||
|
const zend_class_constant *parent_const, const zend_string *const_name) {
|
||||||
|
HashTable *obligations = get_or_init_obligations_for_class(ce);
|
||||||
|
variance_obligation *obligation = emalloc(sizeof(variance_obligation));
|
||||||
|
obligation->type = OBLIGATION_CLASS_CONSTANT_COMPATIBILITY;
|
||||||
|
obligation->const_name = const_name;
|
||||||
|
obligation->child_const = child_const;
|
||||||
|
obligation->parent_const = parent_const;
|
||||||
|
zend_hash_next_index_insert_ptr(obligations, obligation);
|
||||||
|
}
|
||||||
|
|
||||||
static void resolve_delayed_variance_obligations(zend_class_entry *ce);
|
static void resolve_delayed_variance_obligations(zend_class_entry *ce);
|
||||||
|
|
||||||
static void check_variance_obligation(variance_obligation *obligation) {
|
static void check_variance_obligation(variance_obligation *obligation) {
|
||||||
|
@ -2622,13 +2711,19 @@ static void check_variance_obligation(variance_obligation *obligation) {
|
||||||
&obligation->parent_fn, obligation->parent_scope, status);
|
&obligation->parent_fn, obligation->parent_scope, status);
|
||||||
}
|
}
|
||||||
/* Either the compatibility check was successful or only threw a warning. */
|
/* Either the compatibility check was successful or only threw a warning. */
|
||||||
} else {
|
} else if (obligation->type == OBLIGATION_PROPERTY_COMPATIBILITY) {
|
||||||
ZEND_ASSERT(obligation->type == OBLIGATION_PROPERTY_COMPATIBILITY);
|
|
||||||
inheritance_status status =
|
inheritance_status status =
|
||||||
property_types_compatible(obligation->parent_prop, obligation->child_prop);
|
property_types_compatible(obligation->parent_prop, obligation->child_prop);
|
||||||
if (status != INHERITANCE_SUCCESS) {
|
if (status != INHERITANCE_SUCCESS) {
|
||||||
emit_incompatible_property_error(obligation->child_prop, obligation->parent_prop);
|
emit_incompatible_property_error(obligation->child_prop, obligation->parent_prop);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ZEND_ASSERT(obligation->type == OBLIGATION_CLASS_CONSTANT_COMPATIBILITY);
|
||||||
|
inheritance_status status =
|
||||||
|
class_constant_types_compatible(obligation->parent_const, obligation->child_const);
|
||||||
|
if (status != INHERITANCE_SUCCESS) {
|
||||||
|
emit_incompatible_class_constant_error(obligation->child_const, obligation->parent_const, obligation->const_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3092,6 +3187,7 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e
|
||||||
zend_string *key;
|
zend_string *key;
|
||||||
zend_function *parent_func;
|
zend_function *parent_func;
|
||||||
zend_property_info *parent_info;
|
zend_property_info *parent_info;
|
||||||
|
zend_class_constant *parent_const;
|
||||||
inheritance_status overall_status = INHERITANCE_SUCCESS;
|
inheritance_status overall_status = INHERITANCE_SUCCESS;
|
||||||
|
|
||||||
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, parent_func) {
|
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, parent_func) {
|
||||||
|
@ -3130,6 +3226,25 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e
|
||||||
}
|
}
|
||||||
} ZEND_HASH_FOREACH_END();
|
} ZEND_HASH_FOREACH_END();
|
||||||
|
|
||||||
|
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&parent_ce->constants_table, key, parent_const) {
|
||||||
|
zval *zv;
|
||||||
|
if ((ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PRIVATE) || !ZEND_TYPE_IS_SET(parent_const->type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
zv = zend_hash_find_known_hash(&ce->constants_table, key);
|
||||||
|
if (zv) {
|
||||||
|
zend_class_constant *child_const = Z_PTR_P(zv);
|
||||||
|
if (ZEND_TYPE_IS_SET(child_const->type)) {
|
||||||
|
inheritance_status status = class_constant_types_compatible(parent_const, child_const);
|
||||||
|
ZEND_ASSERT(status != INHERITANCE_WARNING);
|
||||||
|
if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ZEND_HASH_FOREACH_END();
|
||||||
|
|
||||||
return overall_status;
|
return overall_status;
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
|
@ -267,7 +267,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
|
||||||
%type <ast> echo_expr_list unset_variables catch_name_list catch_list optional_variable parameter_list class_statement_list
|
%type <ast> echo_expr_list unset_variables catch_name_list catch_list optional_variable parameter_list class_statement_list
|
||||||
%type <ast> implements_list case_list if_stmt_without_else
|
%type <ast> implements_list case_list if_stmt_without_else
|
||||||
%type <ast> non_empty_parameter_list argument_list non_empty_argument_list property_list
|
%type <ast> non_empty_parameter_list argument_list non_empty_argument_list property_list
|
||||||
%type <ast> class_const_list class_const_decl class_name_list trait_adaptations method_body non_empty_for_exprs
|
%type <ast> class_const_list first_class_const_decl class_const_decl class_name_list trait_adaptations method_body non_empty_for_exprs
|
||||||
%type <ast> ctor_arguments alt_if_stmt_without_else trait_adaptation_list lexical_vars
|
%type <ast> ctor_arguments alt_if_stmt_without_else trait_adaptation_list lexical_vars
|
||||||
%type <ast> lexical_var_list encaps_list
|
%type <ast> lexical_var_list encaps_list
|
||||||
%type <ast> array_pair non_empty_array_pair_list array_pair_list possible_array_pair
|
%type <ast> array_pair non_empty_array_pair_list array_pair_list possible_array_pair
|
||||||
|
@ -812,7 +812,6 @@ parameter:
|
||||||
NULL, $6 ? zend_ast_create_zval_from_str($6) : NULL); }
|
NULL, $6 ? zend_ast_create_zval_from_str($6) : NULL); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
optional_type_without_static:
|
optional_type_without_static:
|
||||||
%empty { $$ = NULL; }
|
%empty { $$ = NULL; }
|
||||||
| type_expr_without_static { $$ = $1; }
|
| type_expr_without_static { $$ = $1; }
|
||||||
|
@ -1077,15 +1076,21 @@ property:
|
||||||
|
|
||||||
class_const_list:
|
class_const_list:
|
||||||
class_const_list ',' class_const_decl { $$ = zend_ast_list_add($1, $3); }
|
class_const_list ',' class_const_decl { $$ = zend_ast_list_add($1, $3); }
|
||||||
| class_const_decl { $$ = zend_ast_create_list(1, ZEND_AST_CLASS_CONST_DECL, $1); }
|
| first_class_const_decl { $$ = zend_ast_create_list(1, ZEND_AST_CLASS_CONST_DECL, $1); }
|
||||||
|
;
|
||||||
|
|
||||||
|
first_class_const_decl:
|
||||||
|
T_STRING '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL), NULL); }
|
||||||
|
| semi_reserved '=' expr backup_doc_comment { zval zv; if (zend_lex_tstring(&zv, $1) == FAILURE) { YYABORT; } $$ = zend_ast_create(ZEND_AST_CONST_ELEM, zend_ast_create_zval(&zv), $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL), NULL); }
|
||||||
|
| type_expr identifier '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $2, $4, ($5 ? zend_ast_create_zval_from_str($5) : NULL), $1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
class_const_decl:
|
class_const_decl:
|
||||||
identifier '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); }
|
identifier '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL), NULL); }
|
||||||
;
|
;
|
||||||
|
|
||||||
const_decl:
|
const_decl:
|
||||||
T_STRING '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); }
|
T_STRING '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL), NULL); }
|
||||||
;
|
;
|
||||||
|
|
||||||
echo_expr_list:
|
echo_expr_list:
|
||||||
|
@ -1317,8 +1322,8 @@ function_call:
|
||||||
{ $$ = zend_ast_create(ZEND_AST_STATIC_CALL, $1, $3, $4); }
|
{ $$ = zend_ast_create(ZEND_AST_STATIC_CALL, $1, $3, $4); }
|
||||||
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM member_name argument_list
|
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM member_name argument_list
|
||||||
{ $$ = zend_ast_create(ZEND_AST_STATIC_CALL, $1, $3, $4); }
|
{ $$ = zend_ast_create(ZEND_AST_STATIC_CALL, $1, $3, $4); }
|
||||||
| callable_expr { $<num>$ = CG(zend_lineno); } argument_list {
|
| callable_expr { $<num>$ = CG(zend_lineno); } argument_list {
|
||||||
$$ = zend_ast_create(ZEND_AST_CALL, $1, $3);
|
$$ = zend_ast_create(ZEND_AST_CALL, $1, $3);
|
||||||
$$->lineno = $<num>2;
|
$$->lineno = $<num>2;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
|
@ -5991,8 +5991,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||||
zval_update_constant_ex(value, c->ce);
|
if (UNEXPECTED(zend_update_class_constant(c, constant_name, c->ce) != SUCCESS)) {
|
||||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
|
||||||
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||||
FREE_OP2();
|
FREE_OP2();
|
||||||
HANDLE_EXCEPTION();
|
HANDLE_EXCEPTION();
|
||||||
|
|
18
Zend/zend_vm_execute.h
generated
18
Zend/zend_vm_execute.h
generated
|
@ -7265,8 +7265,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||||
zval_update_constant_ex(value, c->ce);
|
if (UNEXPECTED(zend_update_class_constant(c, constant_name, c->ce) != SUCCESS)) {
|
||||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
|
||||||
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||||
|
|
||||||
HANDLE_EXCEPTION();
|
HANDLE_EXCEPTION();
|
||||||
|
@ -8419,8 +8418,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||||
zval_update_constant_ex(value, c->ce);
|
if (UNEXPECTED(zend_update_class_constant(c, constant_name, c->ce) != SUCCESS)) {
|
||||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
|
||||||
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||||
FREE_OP(opline->op2_type, opline->op2.var);
|
FREE_OP(opline->op2_type, opline->op2.var);
|
||||||
HANDLE_EXCEPTION();
|
HANDLE_EXCEPTION();
|
||||||
|
@ -25084,8 +25082,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||||
zval_update_constant_ex(value, c->ce);
|
if (UNEXPECTED(zend_update_class_constant(c, constant_name, c->ce) != SUCCESS)) {
|
||||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
|
||||||
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||||
|
|
||||||
HANDLE_EXCEPTION();
|
HANDLE_EXCEPTION();
|
||||||
|
@ -25647,8 +25644,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||||
zval_update_constant_ex(value, c->ce);
|
if (UNEXPECTED(zend_update_class_constant(c, constant_name, c->ce) != SUCCESS)) {
|
||||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
|
||||||
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||||
FREE_OP(opline->op2_type, opline->op2.var);
|
FREE_OP(opline->op2_type, opline->op2.var);
|
||||||
HANDLE_EXCEPTION();
|
HANDLE_EXCEPTION();
|
||||||
|
@ -34232,8 +34228,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||||
zval_update_constant_ex(value, c->ce);
|
if (UNEXPECTED(zend_update_class_constant(c, constant_name, c->ce) != SUCCESS)) {
|
||||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
|
||||||
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||||
|
|
||||||
HANDLE_EXCEPTION();
|
HANDLE_EXCEPTION();
|
||||||
|
@ -34585,8 +34580,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||||
zval_update_constant_ex(value, c->ce);
|
if (UNEXPECTED(zend_update_class_constant(c, constant_name, c->ce) != SUCCESS)) {
|
||||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
|
||||||
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
ZVAL_UNDEF(EX_VAR(opline->result.var));
|
||||||
FREE_OP(opline->op2_type, opline->op2.var);
|
FREE_OP(opline->op2_type, opline->op2.var);
|
||||||
HANDLE_EXCEPTION();
|
HANDLE_EXCEPTION();
|
||||||
|
|
|
@ -3749,15 +3749,16 @@ static bool preload_try_resolve_constants(zend_class_entry *ce)
|
||||||
bool ok, changed, was_changed = false;
|
bool ok, changed, was_changed = false;
|
||||||
zend_class_constant *c;
|
zend_class_constant *c;
|
||||||
zval *val;
|
zval *val;
|
||||||
|
zend_string *key;
|
||||||
|
|
||||||
EG(exception) = (void*)(uintptr_t)-1; /* prevent error reporting */
|
EG(exception) = (void*)(uintptr_t)-1; /* prevent error reporting */
|
||||||
do {
|
do {
|
||||||
ok = true;
|
ok = true;
|
||||||
changed = false;
|
changed = false;
|
||||||
ZEND_HASH_MAP_FOREACH_PTR(&ce->constants_table, c) {
|
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
|
||||||
val = &c->value;
|
val = &c->value;
|
||||||
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
|
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
|
||||||
if (EXPECTED(zval_update_constant_ex(val, c->ce) == SUCCESS)) {
|
if (EXPECTED(zend_update_class_constant(c, key, c->ce) == SUCCESS)) {
|
||||||
was_changed = changed = true;
|
was_changed = changed = true;
|
||||||
} else {
|
} else {
|
||||||
ok = false;
|
ok = false;
|
||||||
|
|
|
@ -692,12 +692,12 @@ static void zend_file_cache_serialize_class_constant(zval *z
|
||||||
SERIALIZE_PTR(c->ce);
|
SERIALIZE_PTR(c->ce);
|
||||||
|
|
||||||
zend_file_cache_serialize_zval(&c->value, script, info, buf);
|
zend_file_cache_serialize_zval(&c->value, script, info, buf);
|
||||||
|
|
||||||
if (c->doc_comment) {
|
if (c->doc_comment) {
|
||||||
SERIALIZE_STR(c->doc_comment);
|
SERIALIZE_STR(c->doc_comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
SERIALIZE_ATTRIBUTES(c->attributes);
|
SERIALIZE_ATTRIBUTES(c->attributes);
|
||||||
|
zend_file_cache_serialize_type(&c->type, script, info, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1531,6 +1531,7 @@ static void zend_file_cache_unserialize_class_constant(zval *
|
||||||
UNSERIALIZE_STR(c->doc_comment);
|
UNSERIALIZE_STR(c->doc_comment);
|
||||||
}
|
}
|
||||||
UNSERIALIZE_ATTRIBUTES(c->attributes);
|
UNSERIALIZE_ATTRIBUTES(c->attributes);
|
||||||
|
zend_file_cache_unserialize_type(&c->type, c->ce, script, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -840,6 +840,7 @@ static void zend_persist_class_constant(zval *zv)
|
||||||
if (c->attributes) {
|
if (c->attributes) {
|
||||||
c->attributes = zend_persist_attributes(c->attributes);
|
c->attributes = zend_persist_attributes(c->attributes);
|
||||||
}
|
}
|
||||||
|
zend_persist_type(&c->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce)
|
zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce)
|
||||||
|
|
|
@ -399,6 +399,7 @@ static void zend_persist_class_constant_calc(zval *zv)
|
||||||
if (c->attributes) {
|
if (c->attributes) {
|
||||||
zend_persist_attributes_calc(c->attributes);
|
zend_persist_attributes_calc(c->attributes);
|
||||||
}
|
}
|
||||||
|
zend_persist_type_calc(&c->type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -297,7 +297,7 @@ static zval *reflection_instantiate(zend_class_entry *pce, zval *object) /* {{{
|
||||||
static void _const_string(smart_str *str, char *name, zval *value, char *indent);
|
static void _const_string(smart_str *str, char *name, zval *value, char *indent);
|
||||||
static void _function_string(smart_str *str, zend_function *fptr, zend_class_entry *scope, char* indent);
|
static void _function_string(smart_str *str, zend_function *fptr, zend_class_entry *scope, char* indent);
|
||||||
static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, char* indent);
|
static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, char* indent);
|
||||||
static void _class_const_string(smart_str *str, char *name, zend_class_constant *c, char* indent);
|
static void _class_const_string(smart_str *str, zend_string *name, zend_class_constant *c, char* indent);
|
||||||
static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char *indent);
|
static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char *indent);
|
||||||
static void _extension_string(smart_str *str, zend_module_entry *module, char *indent);
|
static void _extension_string(smart_str *str, zend_module_entry *module, char *indent);
|
||||||
static void _zend_extension_string(smart_str *str, zend_extension *extension, char *indent);
|
static void _zend_extension_string(smart_str *str, zend_extension *extension, char *indent);
|
||||||
|
@ -384,7 +384,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char
|
||||||
zend_class_constant *c;
|
zend_class_constant *c;
|
||||||
|
|
||||||
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, c) {
|
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, c) {
|
||||||
_class_const_string(str, ZSTR_VAL(key), c, ZSTR_VAL(sub_indent));
|
_class_const_string(str, key, c, ZSTR_VAL(sub_indent));
|
||||||
if (UNEXPECTED(EG(exception))) {
|
if (UNEXPECTED(EG(exception))) {
|
||||||
zend_string_release(sub_indent);
|
zend_string_release(sub_indent);
|
||||||
return;
|
return;
|
||||||
|
@ -556,17 +556,18 @@ static void _const_string(smart_str *str, char *name, zval *value, char *indent)
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
/* {{{ _class_const_string */
|
/* {{{ _class_const_string */
|
||||||
static void _class_const_string(smart_str *str, char *name, zend_class_constant *c, char *indent)
|
static void _class_const_string(smart_str *str, zend_string *name, zend_class_constant *c, char *indent)
|
||||||
{
|
{
|
||||||
if (zval_update_constant_ex(&c->value, c->ce) == FAILURE) {
|
if (Z_TYPE(c->value) == IS_CONSTANT_AST && zend_update_class_constant(c, name, c->ce) == FAILURE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *visibility = zend_visibility_string(ZEND_CLASS_CONST_FLAGS(c));
|
const char *visibility = zend_visibility_string(ZEND_CLASS_CONST_FLAGS(c));
|
||||||
const char *final = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_FINAL ? "final " : "";
|
const char *final = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_FINAL ? "final " : "";
|
||||||
const char *type = zend_zval_type_name(&c->value);
|
zend_string *type_str = ZEND_TYPE_IS_SET(c->type) ? zend_type_to_string(c->type) : NULL;
|
||||||
|
const char *type = type_str ? ZSTR_VAL(type_str) : zend_zval_type_name(&c->value);
|
||||||
smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ",
|
smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ",
|
||||||
indent, final, visibility, type, name);
|
indent, final, visibility, type, ZSTR_VAL(name));
|
||||||
if (Z_TYPE(c->value) == IS_ARRAY) {
|
if (Z_TYPE(c->value) == IS_ARRAY) {
|
||||||
smart_str_appends(str, "Array");
|
smart_str_appends(str, "Array");
|
||||||
} else if (Z_TYPE(c->value) == IS_OBJECT) {
|
} else if (Z_TYPE(c->value) == IS_OBJECT) {
|
||||||
|
@ -578,6 +579,10 @@ static void _class_const_string(smart_str *str, char *name, zend_class_constant
|
||||||
zend_tmp_string_release(tmp_value_str);
|
zend_tmp_string_release(tmp_value_str);
|
||||||
}
|
}
|
||||||
smart_str_appends(str, " }\n");
|
smart_str_appends(str, " }\n");
|
||||||
|
|
||||||
|
if (type_str) {
|
||||||
|
zend_string_release(type_str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
@ -3796,7 +3801,7 @@ ZEND_METHOD(ReflectionClassConstant, __toString)
|
||||||
ZVAL_DEREF(name);
|
ZVAL_DEREF(name);
|
||||||
ZEND_ASSERT(Z_TYPE_P(name) == IS_STRING);
|
ZEND_ASSERT(Z_TYPE_P(name) == IS_STRING);
|
||||||
|
|
||||||
_class_const_string(&str, Z_STRVAL_P(name), ref, "");
|
_class_const_string(&str, Z_STR_P(name), ref, "");
|
||||||
RETURN_STR(smart_str_extract(&str));
|
RETURN_STR(smart_str_extract(&str));
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
@ -3820,6 +3825,39 @@ ZEND_METHOD(ReflectionClassConstant, getName)
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
/* Returns the type associated with the class constant */
|
||||||
|
ZEND_METHOD(ReflectionClassConstant, getType)
|
||||||
|
{
|
||||||
|
reflection_object *intern;
|
||||||
|
zend_class_constant *ref;
|
||||||
|
|
||||||
|
if (zend_parse_parameters_none() == FAILURE) {
|
||||||
|
RETURN_THROWS();
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_REFLECTION_OBJECT_PTR(ref);
|
||||||
|
|
||||||
|
if (!ZEND_TYPE_IS_SET(ref->type)) {
|
||||||
|
RETURN_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
reflection_type_factory(ref->type, return_value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns whether class constant has a type */
|
||||||
|
ZEND_METHOD(ReflectionClassConstant, hasType)
|
||||||
|
{
|
||||||
|
reflection_object *intern;
|
||||||
|
zend_class_constant *ref;
|
||||||
|
|
||||||
|
if (zend_parse_parameters_none() == FAILURE) {
|
||||||
|
RETURN_THROWS();
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_REFLECTION_OBJECT_PTR(ref);
|
||||||
|
RETVAL_BOOL(ZEND_TYPE_IS_SET(ref->type));
|
||||||
|
}
|
||||||
|
|
||||||
static void _class_constant_check_flag(INTERNAL_FUNCTION_PARAMETERS, int mask) /* {{{ */
|
static void _class_constant_check_flag(INTERNAL_FUNCTION_PARAMETERS, int mask) /* {{{ */
|
||||||
{
|
{
|
||||||
reflection_object *intern;
|
reflection_object *intern;
|
||||||
|
@ -3887,8 +3925,19 @@ ZEND_METHOD(ReflectionClassConstant, getValue)
|
||||||
}
|
}
|
||||||
GET_REFLECTION_OBJECT_PTR(ref);
|
GET_REFLECTION_OBJECT_PTR(ref);
|
||||||
|
|
||||||
|
zval *name = reflection_prop_name(ZEND_THIS);
|
||||||
|
if (Z_ISUNDEF_P(name)) {
|
||||||
|
zend_throw_error(NULL,
|
||||||
|
"Typed property ReflectionClassConstant::$name "
|
||||||
|
"must not be accessed before initialization");
|
||||||
|
RETURN_THROWS();
|
||||||
|
}
|
||||||
|
|
||||||
if (Z_TYPE(ref->value) == IS_CONSTANT_AST) {
|
if (Z_TYPE(ref->value) == IS_CONSTANT_AST) {
|
||||||
zval_update_constant_ex(&ref->value, ref->ce);
|
zend_result result = zend_update_class_constant(ref, Z_STR_P(name), ref->ce);
|
||||||
|
if (result == FAILURE) {
|
||||||
|
RETURN_THROWS();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ZVAL_COPY_OR_DUP(return_value, &ref->value);
|
ZVAL_COPY_OR_DUP(return_value, &ref->value);
|
||||||
}
|
}
|
||||||
|
@ -4694,7 +4743,7 @@ ZEND_METHOD(ReflectionClass, getConstants)
|
||||||
|
|
||||||
array_init(return_value);
|
array_init(return_value);
|
||||||
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, constant) {
|
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, constant) {
|
||||||
if (UNEXPECTED(zval_update_constant_ex(&constant->value, constant->ce) != SUCCESS)) {
|
if (UNEXPECTED(Z_TYPE(constant->value) == IS_CONSTANT_AST && zend_update_class_constant(constant, key, constant->ce) != SUCCESS)) {
|
||||||
RETURN_THROWS();
|
RETURN_THROWS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4744,7 +4793,7 @@ ZEND_METHOD(ReflectionClass, getConstant)
|
||||||
zend_class_entry *ce;
|
zend_class_entry *ce;
|
||||||
HashTable *constants_table;
|
HashTable *constants_table;
|
||||||
zend_class_constant *c;
|
zend_class_constant *c;
|
||||||
zend_string *name;
|
zend_string *name, *key;
|
||||||
|
|
||||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) {
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) {
|
||||||
RETURN_THROWS();
|
RETURN_THROWS();
|
||||||
|
@ -4752,8 +4801,8 @@ ZEND_METHOD(ReflectionClass, getConstant)
|
||||||
|
|
||||||
GET_REFLECTION_OBJECT_PTR(ce);
|
GET_REFLECTION_OBJECT_PTR(ce);
|
||||||
constants_table = CE_CONSTANTS_TABLE(ce);
|
constants_table = CE_CONSTANTS_TABLE(ce);
|
||||||
ZEND_HASH_MAP_FOREACH_PTR(constants_table, c) {
|
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(constants_table, key, c) {
|
||||||
if (UNEXPECTED(zval_update_constant_ex(&c->value, c->ce) != SUCCESS)) {
|
if (UNEXPECTED(Z_TYPE(c->value) == IS_CONSTANT_AST && zend_update_class_constant(c, key, c->ce) != SUCCESS)) {
|
||||||
RETURN_THROWS();
|
RETURN_THROWS();
|
||||||
}
|
}
|
||||||
} ZEND_HASH_FOREACH_END();
|
} ZEND_HASH_FOREACH_END();
|
||||||
|
@ -6954,7 +7003,7 @@ ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue)
|
||||||
if (Z_TYPE(ref->value) == IS_CONSTANT_AST) {
|
if (Z_TYPE(ref->value) == IS_CONSTANT_AST) {
|
||||||
zval_update_constant_ex(&ref->value, ref->ce);
|
zval_update_constant_ex(&ref->value, ref->ce);
|
||||||
if (EG(exception)) {
|
if (EG(exception)) {
|
||||||
return;
|
RETURN_THROWS();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -346,7 +346,7 @@ class ReflectionClass implements Reflector
|
||||||
public function getReflectionConstants(?int $filter = null): array {}
|
public function getReflectionConstants(?int $filter = null): array {}
|
||||||
|
|
||||||
/** @tentative-return-type */
|
/** @tentative-return-type */
|
||||||
public function getConstant(string $name): mixed {}
|
public function getConstant(string $name): mixed {} // TODO throw exception when the constant doesn't exist
|
||||||
|
|
||||||
/** @tentative-return-type */
|
/** @tentative-return-type */
|
||||||
public function getReflectionConstant(string $name): ReflectionClassConstant|false {}
|
public function getReflectionConstant(string $name): ReflectionClassConstant|false {}
|
||||||
|
@ -609,6 +609,10 @@ class ReflectionClassConstant implements Reflector
|
||||||
public function getAttributes(?string $name = null, int $flags = 0): array {}
|
public function getAttributes(?string $name = null, int $flags = 0): array {}
|
||||||
|
|
||||||
public function isEnumCase(): bool {}
|
public function isEnumCase(): bool {}
|
||||||
|
|
||||||
|
public function hasType(): bool {}
|
||||||
|
|
||||||
|
public function getType(): ?ReflectionType {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @not-serializable */
|
/** @not-serializable */
|
||||||
|
|
10
ext/reflection/php_reflection_arginfo.h
generated
10
ext/reflection/php_reflection_arginfo.h
generated
|
@ -1,5 +1,5 @@
|
||||||
/* This is a generated file, edit the .stub.php file instead.
|
/* This is a generated file, edit the .stub.php file instead.
|
||||||
* Stub hash: f640c1b592a7e9e7a8e92195df579bfaaa3da6dc */
|
* Stub hash: 75d10a475cce503d94bd8471764adf495f0ddd34 */
|
||||||
|
|
||||||
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0)
|
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0)
|
||||||
ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0)
|
ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0)
|
||||||
|
@ -414,6 +414,10 @@ ZEND_END_ARG_INFO()
|
||||||
|
|
||||||
#define arginfo_class_ReflectionClassConstant_isEnumCase arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
|
#define arginfo_class_ReflectionClassConstant_isEnumCase arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
|
||||||
|
|
||||||
|
#define arginfo_class_ReflectionClassConstant_hasType arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
|
||||||
|
|
||||||
|
#define arginfo_class_ReflectionClassConstant_getType arginfo_class_ReflectionFunctionAbstract_getTentativeReturnType
|
||||||
|
|
||||||
#define arginfo_class_ReflectionParameter___clone arginfo_class_ReflectionFunctionAbstract___clone
|
#define arginfo_class_ReflectionParameter___clone arginfo_class_ReflectionFunctionAbstract___clone
|
||||||
|
|
||||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionParameter___construct, 0, 0, 2)
|
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionParameter___construct, 0, 0, 2)
|
||||||
|
@ -760,6 +764,8 @@ ZEND_METHOD(ReflectionClassConstant, getDeclaringClass);
|
||||||
ZEND_METHOD(ReflectionClassConstant, getDocComment);
|
ZEND_METHOD(ReflectionClassConstant, getDocComment);
|
||||||
ZEND_METHOD(ReflectionClassConstant, getAttributes);
|
ZEND_METHOD(ReflectionClassConstant, getAttributes);
|
||||||
ZEND_METHOD(ReflectionClassConstant, isEnumCase);
|
ZEND_METHOD(ReflectionClassConstant, isEnumCase);
|
||||||
|
ZEND_METHOD(ReflectionClassConstant, hasType);
|
||||||
|
ZEND_METHOD(ReflectionClassConstant, getType);
|
||||||
ZEND_METHOD(ReflectionParameter, __construct);
|
ZEND_METHOD(ReflectionParameter, __construct);
|
||||||
ZEND_METHOD(ReflectionParameter, __toString);
|
ZEND_METHOD(ReflectionParameter, __toString);
|
||||||
ZEND_METHOD(ReflectionParameter, getName);
|
ZEND_METHOD(ReflectionParameter, getName);
|
||||||
|
@ -1046,6 +1052,8 @@ static const zend_function_entry class_ReflectionClassConstant_methods[] = {
|
||||||
ZEND_ME(ReflectionClassConstant, getDocComment, arginfo_class_ReflectionClassConstant_getDocComment, ZEND_ACC_PUBLIC)
|
ZEND_ME(ReflectionClassConstant, getDocComment, arginfo_class_ReflectionClassConstant_getDocComment, ZEND_ACC_PUBLIC)
|
||||||
ZEND_ME(ReflectionClassConstant, getAttributes, arginfo_class_ReflectionClassConstant_getAttributes, ZEND_ACC_PUBLIC)
|
ZEND_ME(ReflectionClassConstant, getAttributes, arginfo_class_ReflectionClassConstant_getAttributes, ZEND_ACC_PUBLIC)
|
||||||
ZEND_ME(ReflectionClassConstant, isEnumCase, arginfo_class_ReflectionClassConstant_isEnumCase, ZEND_ACC_PUBLIC)
|
ZEND_ME(ReflectionClassConstant, isEnumCase, arginfo_class_ReflectionClassConstant_isEnumCase, ZEND_ACC_PUBLIC)
|
||||||
|
ZEND_ME(ReflectionClassConstant, hasType, arginfo_class_ReflectionClassConstant_hasType, ZEND_ACC_PUBLIC)
|
||||||
|
ZEND_ME(ReflectionClassConstant, getType, arginfo_class_ReflectionClassConstant_getType, ZEND_ACC_PUBLIC)
|
||||||
ZEND_FE_END
|
ZEND_FE_END
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,11 +28,15 @@ function reflectClassConstant($base, $constant) {
|
||||||
var_dump($constInfo->getDeclaringClass());
|
var_dump($constInfo->getDeclaringClass());
|
||||||
echo "getDocComment():\n";
|
echo "getDocComment():\n";
|
||||||
var_dump($constInfo->getDocComment());
|
var_dump($constInfo->getDocComment());
|
||||||
|
echo "hasType():\n";
|
||||||
|
var_dump($constInfo->hasType());
|
||||||
|
echo "getType():\n";
|
||||||
|
echo $constInfo->getType() ?? "NULL";
|
||||||
echo "\n**********************************\n";
|
echo "\n**********************************\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestClass {
|
class TestClass {
|
||||||
public const /** My Doc comment */ PUB = true;
|
public const bool /** My Doc comment */ PUB = true;
|
||||||
/** Another doc comment */
|
/** Another doc comment */
|
||||||
protected const PROT = 4;
|
protected const PROT = 4;
|
||||||
private const PRIV = "keepOut";
|
private const PRIV = "keepOut";
|
||||||
|
@ -76,7 +80,10 @@ object(ReflectionClass)#3 (1) {
|
||||||
}
|
}
|
||||||
getDocComment():
|
getDocComment():
|
||||||
string(21) "/** My Doc comment */"
|
string(21) "/** My Doc comment */"
|
||||||
|
hasType():
|
||||||
|
bool(true)
|
||||||
|
getType():
|
||||||
|
bool
|
||||||
**********************************
|
**********************************
|
||||||
**********************************
|
**********************************
|
||||||
Reflecting on class constant TestClass::PROT
|
Reflecting on class constant TestClass::PROT
|
||||||
|
@ -105,7 +112,10 @@ object(ReflectionClass)#3 (1) {
|
||||||
}
|
}
|
||||||
getDocComment():
|
getDocComment():
|
||||||
string(26) "/** Another doc comment */"
|
string(26) "/** Another doc comment */"
|
||||||
|
hasType():
|
||||||
|
bool(false)
|
||||||
|
getType():
|
||||||
|
NULL
|
||||||
**********************************
|
**********************************
|
||||||
**********************************
|
**********************************
|
||||||
Reflecting on class constant TestClass::PRIV
|
Reflecting on class constant TestClass::PRIV
|
||||||
|
@ -134,7 +144,10 @@ object(ReflectionClass)#3 (1) {
|
||||||
}
|
}
|
||||||
getDocComment():
|
getDocComment():
|
||||||
bool(false)
|
bool(false)
|
||||||
|
hasType():
|
||||||
|
bool(false)
|
||||||
|
getType():
|
||||||
|
NULL
|
||||||
**********************************
|
**********************************
|
||||||
**********************************
|
**********************************
|
||||||
Reflecting on class constant TestClass::FINAL
|
Reflecting on class constant TestClass::FINAL
|
||||||
|
@ -163,7 +176,10 @@ object(ReflectionClass)#3 (1) {
|
||||||
}
|
}
|
||||||
getDocComment():
|
getDocComment():
|
||||||
bool(false)
|
bool(false)
|
||||||
|
hasType():
|
||||||
|
bool(false)
|
||||||
|
getType():
|
||||||
|
NULL
|
||||||
**********************************
|
**********************************
|
||||||
**********************************
|
**********************************
|
||||||
Reflecting on class constant TestClass::PRIV
|
Reflecting on class constant TestClass::PRIV
|
||||||
|
@ -192,7 +208,10 @@ object(ReflectionClass)#3 (1) {
|
||||||
}
|
}
|
||||||
getDocComment():
|
getDocComment():
|
||||||
bool(false)
|
bool(false)
|
||||||
|
hasType():
|
||||||
|
bool(false)
|
||||||
|
getType():
|
||||||
|
NULL
|
||||||
**********************************
|
**********************************
|
||||||
|
|
||||||
Fatal error: Uncaught ReflectionException: Constant TestClass::BAD_CONST does not exist in %s:%d
|
Fatal error: Uncaught ReflectionException: Constant TestClass::BAD_CONST does not exist in %s:%d
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
--TEST--
|
||||||
|
Test variations of getting typed class constant values
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/* Use separate classes to make sure that in-place constant updates don't interfere */
|
||||||
|
class A {
|
||||||
|
const object CONST1 = C;
|
||||||
|
}
|
||||||
|
class B {
|
||||||
|
const array CONST1 = C;
|
||||||
|
}
|
||||||
|
|
||||||
|
define("C", new stdClass());
|
||||||
|
|
||||||
|
$rc = new ReflectionClassConstant(A::class, 'CONST1');
|
||||||
|
var_dump($rc->getValue());
|
||||||
|
echo $rc;
|
||||||
|
|
||||||
|
$rc = new ReflectionClassConstant(B::class, 'CONST1');
|
||||||
|
try {
|
||||||
|
$rc->getValue();
|
||||||
|
} catch (TypeError $e) {
|
||||||
|
echo $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
echo $rc;
|
||||||
|
} catch (TypeError $e) {
|
||||||
|
echo $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
object(stdClass)#1 (0) {
|
||||||
|
}
|
||||||
|
Constant [ public object CONST1 ] { Object }
|
||||||
|
Cannot assign stdClass to class constant B::CONST1 of type array
|
||||||
|
Cannot assign stdClass to class constant B::CONST1 of type array
|
29
ext/reflection/tests/ReflectionClass_getConstant_typed.phpt
Normal file
29
ext/reflection/tests/ReflectionClass_getConstant_typed.phpt
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
--TEST--
|
||||||
|
ReflectionClass::getConstant() with typed class constants
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
class C {
|
||||||
|
const object CONST1 = C;
|
||||||
|
}
|
||||||
|
|
||||||
|
class D {
|
||||||
|
const array CONST1 = C;
|
||||||
|
}
|
||||||
|
|
||||||
|
const C = new stdClass();
|
||||||
|
|
||||||
|
$rc = new ReflectionClass(C::class);
|
||||||
|
var_dump($rc->getConstant("CONST1"));
|
||||||
|
|
||||||
|
$rc = new ReflectionClass(D::class);
|
||||||
|
try {
|
||||||
|
$rc->getConstant("CONST1");
|
||||||
|
} catch (TypeError $e) {
|
||||||
|
echo $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
object(stdClass)#1 (0) {
|
||||||
|
}
|
||||||
|
Cannot assign stdClass to class constant D::CONST1 of type array
|
32
ext/reflection/tests/ReflectionClass_toString_006.phpt
Normal file
32
ext/reflection/tests/ReflectionClass_toString_006.phpt
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
--TEST--
|
||||||
|
Using ReflectionClass::__toString() with typed class constants
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
const ?int CONST1 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo new ReflectionClass(Foo::class);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Class [ <user> class Foo ] {
|
||||||
|
@@ %s 3-5
|
||||||
|
|
||||||
|
- Constants [1] {
|
||||||
|
Constant [ public ?int CONST1 ] { 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
- Static properties [0] {
|
||||||
|
}
|
||||||
|
|
||||||
|
- Static methods [0] {
|
||||||
|
}
|
||||||
|
|
||||||
|
- Properties [0] {
|
||||||
|
}
|
||||||
|
|
||||||
|
- Methods [0] {
|
||||||
|
}
|
||||||
|
}
|
20
ext/reflection/tests/ReflectionClass_toString_007.phpt
Normal file
20
ext/reflection/tests/ReflectionClass_toString_007.phpt
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
--TEST--
|
||||||
|
Using ReflectionClass::__toString() with typed class constants when there is a type mismatch
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
const array CONST1 = C;
|
||||||
|
}
|
||||||
|
|
||||||
|
define("C", new stdClass());
|
||||||
|
|
||||||
|
try {
|
||||||
|
(string) new ReflectionClass(Foo::class);
|
||||||
|
} catch (TypeError $e) {
|
||||||
|
echo $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot assign stdClass to class constant Foo::CONST1 of type array
|
|
@ -0,0 +1,24 @@
|
||||||
|
--TEST--
|
||||||
|
Typed class constant reflection
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
const int CONST1 = C;
|
||||||
|
|
||||||
|
public int $prop1 = Foo::CONST1;
|
||||||
|
}
|
||||||
|
|
||||||
|
define("C", "bar");
|
||||||
|
|
||||||
|
$foo = new ReflectionClass(Foo::class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$foo->getStaticProperties();
|
||||||
|
} catch (TypeError $e) {
|
||||||
|
echo $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot assign string to class constant Foo::CONST1 of type int
|
25
ext/standard/tests/constant_with_typed_class_constant.phpt
Normal file
25
ext/standard/tests/constant_with_typed_class_constant.phpt
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
--TEST--
|
||||||
|
Calling constant() with a typed class constant
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
const object CONST1 = C;
|
||||||
|
const array CONST2 = C;
|
||||||
|
}
|
||||||
|
|
||||||
|
define("C", new stdClass());
|
||||||
|
|
||||||
|
var_dump(constant("FOO::CONST1"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
constant("FOO::CONST2");
|
||||||
|
} catch (TypeError $e) {
|
||||||
|
echo $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
object(stdClass)#1 (0) {
|
||||||
|
}
|
||||||
|
Cannot assign stdClass to class constant Foo::CONST2 of type array
|
Loading…
Add table
Add a link
Reference in a new issue