Skip to content

Added support for keywords for class/method/label/declare/namespace/class const names #438

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

Closed
wants to merge 9 commits into from
Closed
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: 1 addition & 1 deletion Zend/tests/bug43343.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ $foo = 'bar';
var_dump(new namespace::$foo);
?>
--EXPECTF--
Parse error: %s error%sexpecting%sT_NS_SEPARATOR%sin %sbug43343.php on line 5
Fatal error: Class 'Foo\namespace' not found in %s on line %d
59 changes: 59 additions & 0 deletions Zend/tests/identifier_or_keyword.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
--TEST--
Basic keyword support
--FILE--
<?php

class return {
const finally = 1;

public $catch = 1;
}

class yield {
public function __toString() {
return 1;
}

public static function default () {
}

public function namespace () {
}
}

yield::default();

switch (return::finally) {
case (new return)->catch:
case new yield:
echo "O";
default:
goto try;
}
echo "goto T_TRY failed";
try:

(new yield)->namespace();

interface global {}

class abstract extends namespace\yield implements \global {
public function __construct (array $array, callable $call) {
}
}

if (new abstract(["array_arg"], function () {})) {
echo "k";

// Test if it doesn't throw a parse error. fatal error is normal.
// inner_keyword
interface function extends const, break, continue, goto, echo, throw, if, do, for, foreach, declare, instanceof, as, switch, print, class, interface, while, trait, extends, implements, new, clone, var, eval, include, require, use, insteadof, isset, empty, abstract, final, private, protected, public, unset, list, array, callable, or, and, xor {}

// keyword
$obj->elseif->endif->else->endwhile->endfor->endforeach->enddeclare->endswitch->case->exit;
}

?>
--EXPECTF--
Ok
Fatal error: %s in %s on line %d
2 changes: 1 addition & 1 deletion Zend/tests/lsb_006.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ class Foo extends static {
?>
==DONE==
--EXPECTF--
Parse error: %s error,%sexpecting %s in %s on line %d
Fatal error: Cannot use 'static' as class name as it is reserved in %s on line %d

2 changes: 1 addition & 1 deletion Zend/tests/lsb_007.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ class Foo implements static {
?>
==DONE==
--EXPECTF--
Parse error: %s error,%sexpecting %s in %s on line %d
Fatal error: Cannot use 'static' as interface name as it is reserved in %s on line %d

3 changes: 2 additions & 1 deletion Zend/tests/lsb_008.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ ZE2 Late Static Binding class name "static"
<?php
class static {
}
?>
--EXPECTF--
Parse error: %s error,%sexpecting %s in %s on line %d
Fatal error: Cannot use 'static' as class name as it is reserved in %s on line %d

3 changes: 2 additions & 1 deletion Zend/tests/lsb_009.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ ZE2 Late Static Binding interface name "static"
<?php
interface static {
}
?>
--EXPECTF--
Parse error: %s error,%sexpecting %s in %s on line %d
Fatal error: Cannot use 'static' as interface name as it is reserved in %s on line %d

19 changes: 0 additions & 19 deletions Zend/tests/traits/error_013.phpt

This file was deleted.

15 changes: 0 additions & 15 deletions Zend/tests/traits/language018.phpt

This file was deleted.

15 changes: 0 additions & 15 deletions Zend/tests/traits/language019.phpt

This file was deleted.

49 changes: 41 additions & 8 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,12 @@ void zend_init_compiler_data_structures(TSRMLS_D) /* {{{ */
zend_stack_init(&CG(context_stack));

CG(encoding_declared) = 0;
CG(tokenbufptr) = -1;
CG(tokenbufsize) = ZEND_INIT_TOKEN_BUF_SIZE;
CG(tokenbuffer) = emalloc(sizeof(token_buf) * (CG(tokenbufsize) + 1));
CG(tokenbuffer)++->token = 0;
CG(tokenbuf_in_class) = -1;
CG(tokenbuf_fn_decl) = -1;
}
/* }}} */

Expand Down Expand Up @@ -236,6 +242,7 @@ void shutdown_compiler(TSRMLS_D) /* {{{ */
zend_hash_destroy(&CG(filenames_table));
zend_llist_destroy(&CG(open_files));
zend_stack_destroy(&CG(context_stack));
efree(--CG(tokenbuffer));
}
/* }}} */

Expand Down Expand Up @@ -1516,6 +1523,25 @@ int zend_do_verify_access_types(const znode *current_access_type, const znode *n
}
/* }}} */

void zend_prepare_typehint(const znode *token, znode *result) /* {{{ */
{
if ((Z_STRLEN(token->u.constant) == 5 && !strncasecmp(Z_STRVAL(token->u.constant), "array", 5)) || (Z_STRLEN(token->u.constant) == 8 && !strncasecmp(Z_STRVAL(token->u.constant), "callable", 8))) {
result->op_type = IS_CONST;
efree(Z_STRVAL(token->u.constant));
switch (Z_STRLEN(token->u.constant)) {
case 5:
Z_TYPE(result->u.constant) = IS_ARRAY;
break;
case 8:
Z_TYPE(result->u.constant) = IS_CALLABLE;
break;
}
} else {
*result = *token;
}
}
/* }}} */

void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference, znode *fn_flags_znode TSRMLS_DC) /* {{{ */
{
zend_op_array op_array;
Expand Down Expand Up @@ -4943,9 +4969,13 @@ void zend_do_begin_class_declaration(const znode *class_token, znode *class_name

lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant));

if (!(strcmp(lcname, "self") && strcmp(lcname, "parent"))) {
if (!(strcmp(lcname, "self") && strcmp(lcname, "parent") && strcmp(lcname, "static"))) {
efree(lcname);
zend_error(E_COMPILE_ERROR, "Cannot use '%s' as class name as it is reserved", Z_STRVAL(class_name->u.constant));
if (parent_class_name) {
zend_error(E_COMPILE_ERROR, "Cannot use '%s' as class name as it is reserved", Z_STRVAL(class_name->u.constant));
} else {
zend_error(E_COMPILE_ERROR, "Cannot use '%s' as interface name as it is reserved", Z_STRVAL(class_name->u.constant));
}
}

/* Class name must not conflict with import names */
Expand Down Expand Up @@ -6735,7 +6765,6 @@ int zendlex(znode *zendlval TSRMLS_DC) /* {{{ */
}

again:
Z_TYPE(zendlval->u.constant) = IS_LONG;
retval = lex_scan(&zendlval->u.constant TSRMLS_CC);
switch (retval) {
case T_COMMENT:
Expand Down Expand Up @@ -6932,9 +6961,11 @@ void zend_do_begin_namespace(const znode *name, zend_bool with_bracket TSRMLS_DC
if (name) {
lcname = zend_str_tolower_dup(Z_STRVAL(name->u.constant), Z_STRLEN(name->u.constant));
if (((Z_STRLEN(name->u.constant) == sizeof("self")-1) &&
!memcmp(lcname, "self", sizeof("self")-1)) ||
!memcmp(lcname, "self", sizeof("self")-1)) ||
((Z_STRLEN(name->u.constant) == sizeof("parent")-1) &&
!memcmp(lcname, "parent", sizeof("parent")-1))) {
!memcmp(lcname, "parent", sizeof("parent")-1)) ||
((Z_STRLEN(name->u.constant) == sizeof("static")-1) &&
!memcmp(lcname, "static", sizeof("static")-1))) {
zend_error(E_COMPILE_ERROR, "Cannot use '%s' as namespace name", Z_STRVAL(name->u.constant));
}
efree(lcname);
Expand Down Expand Up @@ -7002,9 +7033,11 @@ void zend_do_use(znode *ns_name, znode *new_name, int is_global TSRMLS_DC) /* {{
lcname = zend_str_tolower_dup(Z_STRVAL_P(name), Z_STRLEN_P(name));

if (((Z_STRLEN_P(name) == sizeof("self")-1) &&
!memcmp(lcname, "self", sizeof("self")-1)) ||
((Z_STRLEN_P(name) == sizeof("parent")-1) &&
!memcmp(lcname, "parent", sizeof("parent")-1))) {
!memcmp(lcname, "self", sizeof("self")-1)) ||
((Z_STRLEN_P(name) == sizeof("parent")-1) &&
!memcmp(lcname, "parent", sizeof("parent")-1)) ||
((Z_STRLEN_P(name) == sizeof("static")-1) &&
!memcmp(lcname, "static", sizeof("static")-1))) {
zend_error(E_COMPILE_ERROR, "Cannot use %s as %s because '%s' is a special class name", Z_STRVAL_P(ns), Z_STRVAL_P(name), Z_STRVAL_P(name));
}

Expand Down
12 changes: 12 additions & 0 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ typedef struct _zend_compiler_context {
HashTable *labels;
} zend_compiler_context;

typedef struct _token_buf {
zval zv;
int token;
unsigned char *text;
unsigned int len;
unsigned char *start;
unsigned char *end;
char *doc_comment;
zend_uint doc_comment_len;
uint lineno;
} token_buf;

typedef struct _zend_literal {
zval constant;
zend_ulong hash_value;
Expand Down
14 changes: 12 additions & 2 deletions Zend/zend_globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ typedef struct _zend_declarables {
typedef struct _zend_vm_stack *zend_vm_stack;
typedef struct _zend_ini_entry zend_ini_entry;


struct _zend_compiler_globals {
zend_stack bp_stack;
zend_stack switch_cond_stack;
Expand Down Expand Up @@ -155,6 +154,13 @@ struct _zend_compiler_globals {
zval ***static_members_table;
int last_static_member;
#endif

token_buf *tokenbuffer;
int tokenbufptr;
int tokenbufsize;
int tokenbuf_in_class;
int tokenbuf_fn_decl;
zend_uchar tokenbuf_mode;
};


Expand Down Expand Up @@ -293,7 +299,7 @@ struct _zend_php_scanner_globals {
int yy_state;
zend_stack state_stack;
zend_ptr_stack heredoc_label_stack;

/* original (unfiltered) script */
unsigned char *script_org;
size_t script_org_size;
Expand All @@ -306,6 +312,10 @@ struct _zend_php_scanner_globals {
zend_encoding_filter input_filter;
zend_encoding_filter output_filter;
const zend_encoding *script_encoding;

/* doc comments */
char *doc_comment;
zend_uint doc_comment_len;
};

#endif /* ZEND_GLOBALS_H */
Expand Down
Loading