mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
[RFC] Implement dereferencable for new exprs with constructor args
https://wiki.php.net/rfc/new_without_parentheses Closes GH-13029
This commit is contained in:
committed by
Ilija Tovilo
parent
cc477ff3bb
commit
b6b16a1758
@@ -192,6 +192,10 @@ PHP 8.4 UPGRADE NOTES
|
||||
it references, or null if the reference is no longer valid.
|
||||
. The output of Closure::__debugInfo() now includes the name, file, and line
|
||||
of the Closure.
|
||||
. new expressions with constructor arguments are now dereferencable, meaning
|
||||
they allow chaining method calls, property accesses, etc. without enclosing
|
||||
the expression in parentheses.
|
||||
RFC: https://wiki.php.net/rfc/new_without_parentheses
|
||||
|
||||
- Curl:
|
||||
. curl_version() returns an additional feature_list value, which is an
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
--TEST--
|
||||
Immediate access on new anonymous class
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
echo new class {
|
||||
const C = 'constant' . PHP_EOL;
|
||||
}::C;
|
||||
|
||||
echo new class {
|
||||
const C = 'constant' . PHP_EOL;
|
||||
}::{'C'};
|
||||
|
||||
echo new class {
|
||||
public $property = 'property' . PHP_EOL;
|
||||
}->property;
|
||||
|
||||
echo new class {
|
||||
public static $property = 'static property' . PHP_EOL;
|
||||
}::$property;
|
||||
|
||||
new class {
|
||||
public function method() { echo 'method' . PHP_EOL; }
|
||||
}->method();
|
||||
|
||||
new class {
|
||||
public static function method() { echo 'static method' . PHP_EOL; }
|
||||
}::method();
|
||||
|
||||
new class {
|
||||
public function __invoke() { echo '__invoke' . PHP_EOL; }
|
||||
}();
|
||||
|
||||
new class () implements ArrayAccess {
|
||||
public function offsetExists(mixed $offset): bool { return true; }
|
||||
|
||||
public function offsetGet(mixed $offset): mixed { echo 'offsetGet' . PHP_EOL; return null; }
|
||||
|
||||
public function offsetSet(mixed $offset, mixed $value): void {}
|
||||
|
||||
public function offsetUnset(mixed $offset): void {}
|
||||
}['key'];
|
||||
|
||||
isset(new class () implements ArrayAccess {
|
||||
public function offsetExists(mixed $offset): bool { echo 'offsetExists' . PHP_EOL; return true; }
|
||||
|
||||
public function offsetGet(mixed $offset): mixed { return null; }
|
||||
|
||||
public function offsetSet(mixed $offset, mixed $value): void {}
|
||||
|
||||
public function offsetUnset(mixed $offset): void {}
|
||||
}['key']);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
constant
|
||||
constant
|
||||
property
|
||||
static property
|
||||
method
|
||||
static method
|
||||
__invoke
|
||||
offsetGet
|
||||
offsetExists
|
||||
10
Zend/tests/new_without_parentheses/assign_to_new.phpt
Normal file
10
Zend/tests/new_without_parentheses/assign_to_new.phpt
Normal file
@@ -0,0 +1,10 @@
|
||||
--TEST--
|
||||
Cannot assign to new expression
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
new ArrayObject() = 1;
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Parse error: syntax error, unexpected token "=" in %s on line %d
|
||||
26
Zend/tests/new_without_parentheses/garbage_collection.phpt
Normal file
26
Zend/tests/new_without_parentheses/garbage_collection.phpt
Normal file
@@ -0,0 +1,26 @@
|
||||
--TEST--
|
||||
Object instantiated without parentheses is collected
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
echo 'called' . PHP_EOL;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
echo 'collected' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
new A()->test();
|
||||
echo 'code after' . PHP_EOL;
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
called
|
||||
collected
|
||||
code after
|
||||
@@ -0,0 +1,25 @@
|
||||
--TEST--
|
||||
A function and a class with the same name work as expected
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function Something(): string
|
||||
{
|
||||
return 'Another';
|
||||
}
|
||||
|
||||
class Something {}
|
||||
|
||||
class Another {}
|
||||
|
||||
echo Something() . PHP_EOL;
|
||||
var_dump(new Something());
|
||||
var_dump(new (Something()));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Another
|
||||
object(Something)#1 (0) {
|
||||
}
|
||||
object(Another)#1 (0) {
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
--TEST--
|
||||
Immediate access on new object with constructor arguments' parentheses
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A implements ArrayAccess
|
||||
{
|
||||
const C = 'constant' . PHP_EOL;
|
||||
public $property = 'property' . PHP_EOL;
|
||||
public static $staticProperty = 'static property' . PHP_EOL;
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
echo '__invoke' . PHP_EOL;
|
||||
}
|
||||
|
||||
public function method(): void
|
||||
{
|
||||
echo 'method' . PHP_EOL;
|
||||
}
|
||||
|
||||
public static function staticMethod(): void
|
||||
{
|
||||
echo 'static method' . PHP_EOL;
|
||||
}
|
||||
|
||||
public function offsetExists(mixed $offset): bool
|
||||
{
|
||||
echo 'offsetExists' . PHP_EOL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function offsetGet(mixed $offset): mixed
|
||||
{
|
||||
echo 'offsetGet' . PHP_EOL;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function offsetSet(mixed $offset, mixed $value): void {}
|
||||
|
||||
public function offsetUnset(mixed $offset): void {}
|
||||
}
|
||||
|
||||
$class = A::class;
|
||||
|
||||
echo new A()::C;
|
||||
echo new A()::{'C'};
|
||||
echo new $class()::C;
|
||||
echo new $class()::{'C'};
|
||||
echo new (trim(' A '))()::C;
|
||||
echo new (trim(' A '))()::{'C'};
|
||||
|
||||
echo new A()->property;
|
||||
echo new $class()->property;
|
||||
echo new (trim(' A '))()->property;
|
||||
|
||||
echo new A()::$staticProperty;
|
||||
echo new $class()::$staticProperty;
|
||||
echo new (trim(' A '))()::$staticProperty;
|
||||
|
||||
new A()();
|
||||
new $class()();
|
||||
new (trim(' A '))()();
|
||||
|
||||
new A()->method();
|
||||
new $class()->method();
|
||||
new (trim(' A '))()->method();
|
||||
|
||||
new A()::staticMethod();
|
||||
new $class()::staticMethod();
|
||||
new (trim(' A '))()::staticMethod();
|
||||
|
||||
new A()['key'];
|
||||
new $class()['key'];
|
||||
new (trim(' A '))()['key'];
|
||||
|
||||
isset(new A()['key']);
|
||||
isset(new $class()['key']);
|
||||
isset(new (trim(' A '))()['key']);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
constant
|
||||
constant
|
||||
constant
|
||||
constant
|
||||
constant
|
||||
constant
|
||||
property
|
||||
property
|
||||
property
|
||||
static property
|
||||
static property
|
||||
static property
|
||||
__invoke
|
||||
__invoke
|
||||
__invoke
|
||||
method
|
||||
method
|
||||
method
|
||||
static method
|
||||
static method
|
||||
static method
|
||||
offsetGet
|
||||
offsetGet
|
||||
offsetGet
|
||||
offsetExists
|
||||
offsetExists
|
||||
offsetExists
|
||||
@@ -0,0 +1,14 @@
|
||||
--TEST--
|
||||
Immediate array access on new object without constructor parentheses
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A extends ArrayObject
|
||||
{
|
||||
}
|
||||
|
||||
echo new A['test'];
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Parse error: syntax error, unexpected token "[", expecting "," or ";" in %s on line %d
|
||||
@@ -0,0 +1,15 @@
|
||||
--TEST--
|
||||
Immediate constant access on new object without constructor parentheses
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A
|
||||
{
|
||||
const C = 'constant';
|
||||
}
|
||||
|
||||
echo new A::C;
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Parse error: syntax error, unexpected identifier "C", expecting variable or "$" in %s on line %d
|
||||
@@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
Immediate method call on new object without constructor parentheses
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
echo 'called';
|
||||
}
|
||||
}
|
||||
|
||||
new A->test();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Parse error: syntax error, unexpected token "->" in %s on line %d
|
||||
@@ -0,0 +1,15 @@
|
||||
--TEST--
|
||||
Immediate property access on new object without constructor parentheses
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A
|
||||
{
|
||||
public $prop = 'property';
|
||||
}
|
||||
|
||||
echo new A->prop;
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Parse error: syntax error, unexpected token "->", expecting "," or ";" in %s on line %d
|
||||
@@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
Immediate static method call on new object without constructor parentheses
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class A
|
||||
{
|
||||
public static function test(): void
|
||||
{
|
||||
echo 'called';
|
||||
}
|
||||
}
|
||||
|
||||
new A::test();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Parse error: syntax error, unexpected identifier "test", expecting variable or "$" in %s on line %d
|
||||
@@ -0,0 +1,20 @@
|
||||
--TEST--
|
||||
Immediate static property access on new object without constructor parentheses
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class B
|
||||
{
|
||||
}
|
||||
|
||||
class A
|
||||
{
|
||||
public static $prop = B::class;
|
||||
}
|
||||
|
||||
var_dump(new A::$prop);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
object(B)#1 (0) {
|
||||
}
|
||||
10
Zend/tests/new_without_parentheses/unset_new.phpt
Normal file
10
Zend/tests/new_without_parentheses/unset_new.phpt
Normal file
@@ -0,0 +1,10 @@
|
||||
--TEST--
|
||||
Cannot unset new expression
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
unset(new ArrayObject());
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Parse error: syntax error, unexpected token ")", expecting "->" or "?->" or "{" or "[" in %s on line %d
|
||||
@@ -255,7 +255,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
|
||||
%type <ast> extends_from parameter optional_type_without_static argument global_var
|
||||
%type <ast> static_var class_statement trait_adaptation trait_precedence trait_alias
|
||||
%type <ast> absolute_trait_method_reference trait_method_reference property echo_expr
|
||||
%type <ast> new_expr anonymous_class class_name class_name_reference simple_variable
|
||||
%type <ast> new_dereferenceable new_non_dereferenceable anonymous_class class_name class_name_reference simple_variable
|
||||
%type <ast> internal_functions_in_yacc
|
||||
%type <ast> exit_expr scalar backticks_expr lexical_var function_call member_name property_name
|
||||
%type <ast> variable_class_name dereferenceable_scalar constant class_constant
|
||||
@@ -1123,8 +1123,8 @@ anonymous_class:
|
||||
}
|
||||
;
|
||||
|
||||
new_expr:
|
||||
T_NEW class_name_reference ctor_arguments
|
||||
new_dereferenceable:
|
||||
T_NEW class_name_reference argument_list
|
||||
{ $$ = zend_ast_create(ZEND_AST_NEW, $2, $3); }
|
||||
| T_NEW anonymous_class
|
||||
{ $$ = $2; }
|
||||
@@ -1132,6 +1132,11 @@ new_expr:
|
||||
{ zend_ast_with_attributes($3->child[0], $2); $$ = $3; }
|
||||
;
|
||||
|
||||
new_non_dereferenceable:
|
||||
T_NEW class_name_reference
|
||||
{ $$ = zend_ast_create(ZEND_AST_NEW, $2, zend_ast_create_list(0, ZEND_AST_ARG_LIST)); }
|
||||
;
|
||||
|
||||
expr:
|
||||
variable
|
||||
{ $$ = $1; }
|
||||
@@ -1225,7 +1230,8 @@ expr:
|
||||
$$ = $2;
|
||||
if ($$->kind == ZEND_AST_CONDITIONAL) $$->attr = ZEND_PARENTHESIZED_CONDITIONAL;
|
||||
}
|
||||
| new_expr { $$ = $1; }
|
||||
| new_dereferenceable { $$ = $1; }
|
||||
| new_non_dereferenceable { $$ = $1; }
|
||||
| expr '?' expr ':' expr
|
||||
{ $$ = zend_ast_create(ZEND_AST_CONDITIONAL, $1, $3, $5); }
|
||||
| expr '?' ':' expr
|
||||
@@ -1418,6 +1424,7 @@ fully_dereferenceable:
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
| dereferenceable_scalar { $$ = $1; }
|
||||
| class_constant { $$ = $1; }
|
||||
| new_dereferenceable { $$ = $1; }
|
||||
;
|
||||
|
||||
array_object_dereferenceable:
|
||||
@@ -1429,6 +1436,7 @@ callable_expr:
|
||||
callable_variable { $$ = $1; }
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
| dereferenceable_scalar { $$ = $1; }
|
||||
| new_dereferenceable { $$ = $1; }
|
||||
;
|
||||
|
||||
callable_variable:
|
||||
|
||||
Reference in New Issue
Block a user