Skip to content

add nameof operator #11172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -1994,6 +1994,8 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
FUNC_OP("empty");
case ZEND_AST_ISSET:
FUNC_OP("isset");
case ZEND_AST_NAMEOF:
FUNC_OP("nameof");
case ZEND_AST_SILENCE:
PREFIX_OP("@", 240, 241);
case ZEND_AST_SHELL_EXEC:
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ enum _zend_ast_kind {
ZEND_AST_POST_DEC,
ZEND_AST_YIELD_FROM,
ZEND_AST_CLASS_NAME,
ZEND_AST_NAMEOF,

ZEND_AST_GLOBAL,
ZEND_AST_UNSET,
Expand Down
32 changes: 32 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -9443,6 +9443,35 @@ static void zend_compile_include_or_eval(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */

static void zend_compile_nameof(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *var_ast = ast->child[0];
zend_ast *name_ast;

// todo: proper error handling and better error messages

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are your ideas for proper error handling? Happy to thinker with you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MelvinRook, I'm actually still trying to come up with something in the RFC that makes sense (and will probably come up in discussions). For example, should this be an error and what might the error message be:

$x = null;
unset($x);
echo nameof($x);

In this specific implementation, no error/warning message is shown if you use a non-existent variable/method/member/etc, nor is any autoloading triggered. This might be desired for performance/optimization reasons, but the entire point is to make refactoring of messages/strings easier, thus inconsistencies could be missed.

If a message is emitted, what should be emitted? Notices, warnings, errors, exceptions? I personally, would favor a normal message, so the above would emit a warning ("Undefined variable $x in ...") but I can see how other people might want something special to happen.

Lots to think about and I haven't particularly settled on any particular implementation. I'd love to hear any arguments for any of them.


switch (var_ast->kind) {
case ZEND_AST_VAR:
case ZEND_AST_CALL:
name_ast = var_ast->child[0];
ZVAL_COPY(&result->u.constant, zend_ast_get_zval(name_ast));
result->op_type = IS_CONST;
break;
case ZEND_AST_PROP:
case ZEND_AST_NULLSAFE_PROP:
case ZEND_AST_STATIC_PROP:
case ZEND_AST_CLASS_CONST:
name_ast = var_ast->child[1];
ZVAL_COPY(&result->u.constant, zend_ast_get_zval(name_ast));
result->op_type = IS_CONST;
break;
default:
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot use nameof() on the result of an expression or something with an ambiguous name");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can "something" be more specific in this error, or is it as specific as it can be?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes and yes; that's actually a good question. I imagine that given more examples, more helpful error messages can be created, but there are "somethings" that are ambiguous or don't make sense:

echo nameof($a['b']);
echo nameof(empty($x));
echo nameof($x === $y);
echo nameof('literal');

In the grammar, I chose an expression as what can go in the brackets/parenthesis, but it doesn't make sense to accept literally any expression. Perhaps there is something better to use (or construct), but I didn't want to spend too much time on it in case there was a name for these more ambiguous things that I don't have the imagination for.

}
}
/* }}} */

static void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *var_ast = ast->child[0];
Expand Down Expand Up @@ -10423,6 +10452,9 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
case ZEND_AST_EMPTY:
zend_compile_isset_or_empty(result, ast);
return;
case ZEND_AST_NAMEOF:
zend_compile_nameof(result, ast);
return;
case ZEND_AST_SILENCE:
zend_compile_silence(result, ast);
return;
Expand Down
4 changes: 3 additions & 1 deletion Zend/zend_language_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%token <ident> T_INSTEADOF "'insteadof'"
%token <ident> T_GLOBAL "'global'"
%token <ident> T_STATIC "'static'"
%token <ident> T_NAMEOF "'nameof'"
%token <ident> T_ABSTRACT "'abstract'"
%token <ident> T_FINAL "'final'"
%token <ident> T_PRIVATE "'private'"
Expand Down Expand Up @@ -299,7 +300,7 @@ reserved_non_modifiers:
T_INCLUDE | T_INCLUDE_ONCE | T_EVAL | T_REQUIRE | T_REQUIRE_ONCE | T_LOGICAL_OR | T_LOGICAL_XOR | T_LOGICAL_AND
| T_INSTANCEOF | T_NEW | T_CLONE | T_EXIT | T_IF | T_ELSEIF | T_ELSE | T_ENDIF | T_ECHO | T_DO | T_WHILE | T_ENDWHILE
| T_FOR | T_ENDFOR | T_FOREACH | T_ENDFOREACH | T_DECLARE | T_ENDDECLARE | T_AS | T_TRY | T_CATCH | T_FINALLY
| T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO
| T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_NAMEOF | T_EMPTY | T_CONTINUE | T_GOTO
| T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT | T_BREAK
| T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_FN | T_MATCH | T_ENUM
Expand Down Expand Up @@ -1575,6 +1576,7 @@ encaps_var_offset:

internal_functions_in_yacc:
T_ISSET '(' isset_variables possible_comma ')' { $$ = $3; }
| T_NAMEOF '(' expr ')' { $$ = zend_ast_create(ZEND_AST_NAMEOF, $3); }
| T_EMPTY '(' expr ')' { $$ = zend_ast_create(ZEND_AST_EMPTY, $3); }
| T_INCLUDE expr
{ $$ = zend_ast_create_ex(ZEND_AST_INCLUDE_OR_EVAL, ZEND_INCLUDE, $2); }
Expand Down
4 changes: 4 additions & 0 deletions Zend/zend_language_scanner.l
Original file line number Diff line number Diff line change
Expand Up @@ -1703,6 +1703,10 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_
RETURN_TOKEN_WITH_IDENT(T_ISSET);
}

<ST_IN_SCRIPTING>"nameof" {
RETURN_TOKEN_WITH_IDENT(T_NAMEOF);
}

<ST_IN_SCRIPTING>"empty" {
RETURN_TOKEN_WITH_IDENT(T_EMPTY);
}
Expand Down
2 changes: 2 additions & 0 deletions ext/tokenizer/tests/001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ echo token_name(T_THROW), "\n";
echo token_name(T_TRY), "\n";
echo token_name(T_CLONE), "\n";
echo token_name(T_HALT_COMPILER), "\n";
echo token_name(T_NAMEOF), "\n";

echo token_name(-1), "\n";
echo token_name(0x8000000F), "\n";
Expand Down Expand Up @@ -249,6 +250,7 @@ T_THROW
T_TRY
T_CLONE
T_HALT_COMPILER
T_NAMEOF
UNKNOWN
UNKNOWN
Done
1 change: 1 addition & 0 deletions ext/tokenizer/tokenizer_data.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions ext/tokenizer/tokenizer_data.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,11 @@
* @cvalue T_STATIC
*/
const T_STATIC = UNKNOWN;
/**
* @var int
* @cvalue T_NAMEOF
*/
const T_NAMEOF = UNKNOWN;
/**
* @var int
* @cvalue T_ABSTRACT
Expand Down
3 changes: 2 additions & 1 deletion ext/tokenizer/tokenizer_data_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tests/basic/020.post
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a[a[]]=1&a[b[]]=3
9 changes: 9 additions & 0 deletions tests/basic/nameof.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
nameof operator
--FILE--
<?php
$a = 'test';
echo 'name: ' . nameof($a);
?>
--EXPECT--
name: a