1
0
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:
Valentin Udaltsov
2023-12-26 15:08:00 +03:00
committed by Ilija Tovilo
parent cc477ff3bb
commit b6b16a1758
14 changed files with 362 additions and 4 deletions

View File

@@ -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

View File

@@ -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

View 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

View 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

View File

@@ -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) {
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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) {
}

View 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

View File

@@ -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: