Commit: 8991ed016fa257c9f8ba42580c34568b3c2ce3e5
Author: Lars Strojny <lstro...@php.net> Sat, 19 Jan 2013 01:00:47
+0100
Parents: cd2b03d5ae835d333bec98e62592fd149fef544c
Branches: PHP-5.5
Link:
http://git.php.net/?p=php-src.git;a=commitdiff;h=8991ed016fa257c9f8ba42580c34568b3c2ce3e5
Log:
Class Name Resolution As Scalar Via "class" Keyword
Changed paths:
M NEWS
A Zend/tests/class_name_as_scalar.phpt
A Zend/tests/class_name_as_scalar_error_001.phpt
A Zend/tests/class_name_as_scalar_error_002.phpt
A Zend/tests/class_name_as_scalar_error_003.phpt
A Zend/tests/class_name_as_scalar_error_004.phpt
A Zend/tests/class_name_as_scalar_error_005.phpt
A Zend/tests/class_name_as_scalar_error_006.phpt
M Zend/zend_compile.c
M Zend/zend_compile.h
M Zend/zend_language_parser.y
M Zend/zend_vm_def.h
M Zend/zend_vm_execute.h
diff --git a/NEWS b/NEWS
index 6395b34..3bb0373 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,8 @@ PHP
NEWS
- Core:
. Fixed bug #63980 (object members get trimmed by zero bytes). (Laruence)
+ . Implemented RFC for Class Name Resolution As Scalar Via "class" Keyword.
+ (Ralph Schindler, Nikita Popov, Lars)
- General improvements:
. Fixed bug #63874 (Segfault if php_strip_whitespace has heredoc). (Pierrick)
diff --git a/Zend/tests/class_name_as_scalar.phpt
b/Zend/tests/class_name_as_scalar.phpt
new file mode 100644
index 0000000..38e55b1
--- /dev/null
+++ b/Zend/tests/class_name_as_scalar.phpt
@@ -0,0 +1,77 @@
+--TEST--
+class name as scalar from ::class keyword
+--FILE--
+<?php
+
+namespace Foo\Bar {
+ class One {
+ // compile time constants
+ const A = self::class;
+ const B = Two::class;
+ }
+ class Two extends One {
+ public static function run() {
+ var_dump(self::class); // self compile time lookup
+ var_dump(static::class); // runtime lookup
+ var_dump(parent::class); // runtime lookup
+ var_dump(Baz::class); // default compile time lookup
+ }
+ }
+ class Three extends Two {
+ // compile time static lookups
+ public static function checkCompileTime(
+ $one = self::class,
+ $two = Baz::class,
+ $three = One::A,
+ $four = self::B
+ ) {
+ var_dump($one, $two, $three, $four);
+ }
+ }
+ echo "In NS\n";
+ var_dump(Moo::CLASS); // resolve in namespace
+}
+
+namespace {
+ use Bee\Bop as Moo,
+ Foo\Bar\One;
+ echo "Top\n";
+ var_dump(One::class); // resolve from use
+ var_dump(Boo::class); // resolve in global namespace
+ var_dump(Moo::CLASS); // resolve from use as
+ var_dump(\Moo::Class); // resolve fully qualified
+ $class = One::class; // assign class as scalar to var
+ $x = new $class; // create new class from original scalar assignment
+ var_dump($x);
+ Foo\Bar\Two::run(); // resolve runtime lookups
+ echo "Parent\n";
+ Foo\Bar\Three::run(); // resolve runtime lookups with inheritance
+ echo "Compile Check\n";
+ Foo\Bar\Three::checkCompileTime();
+}
+
+?>
+--EXPECTF--
+In NS
+string(11) "Foo\Bar\Moo"
+Top
+string(11) "Foo\Bar\One"
+string(3) "Boo"
+string(7) "Bee\Bop"
+string(3) "Moo"
+object(Foo\Bar\One)#1 (0) {
+}
+string(11) "Foo\Bar\Two"
+string(11) "Foo\Bar\Two"
+string(11) "Foo\Bar\One"
+string(11) "Foo\Bar\Baz"
+Parent
+string(11) "Foo\Bar\Two"
+string(13) "Foo\Bar\Three"
+string(11) "Foo\Bar\One"
+string(11) "Foo\Bar\Baz"
+Compile Check
+string(13) "Foo\Bar\Three"
+string(11) "Foo\Bar\Baz"
+string(11) "Foo\Bar\One"
+string(11) "Foo\Bar\Two"
diff --git a/Zend/tests/class_name_as_scalar_error_001.phpt
b/Zend/tests/class_name_as_scalar_error_001.phpt
new file mode 100644
index 0000000..1c7aa7e
--- /dev/null
+++ b/Zend/tests/class_name_as_scalar_error_001.phpt
@@ -0,0 +1,13 @@
+--TEST--
+class name as scalar from ::class keyword error using static in class constant
+--FILE--
+<?php
+
+namespace Foo\Bar {
+ class One {
+ const Baz = static::class;
+ }
+}
+?>
+--EXPECTF--
+Fatal error: static::class cannot be used for compile-time class name
resolution in %s on line %d
diff --git a/Zend/tests/class_name_as_scalar_error_002.phpt
b/Zend/tests/class_name_as_scalar_error_002.phpt
new file mode 100644
index 0000000..59b7a2e
--- /dev/null
+++ b/Zend/tests/class_name_as_scalar_error_002.phpt
@@ -0,0 +1,13 @@
+--TEST--
+class name as scalar from ::class keyword error using parent in class constant
+--FILE--
+<?php
+
+namespace Foo\Bar {
+ class One {
+ const Baz = parent::class;
+ }
+}
+?>
+--EXPECTF--
+Fatal error: parent::class cannot be used for compile-time class name
resolution in %s on line %d
diff --git a/Zend/tests/class_name_as_scalar_error_003.phpt
b/Zend/tests/class_name_as_scalar_error_003.phpt
new file mode 100644
index 0000000..9299041
--- /dev/null
+++ b/Zend/tests/class_name_as_scalar_error_003.phpt
@@ -0,0 +1,13 @@
+--TEST--
+class name as scalar from ::class keyword error using static in method
signature
+--FILE--
+<?php
+
+namespace Foo\Bar {
+ class One {
+ public function baz($x = static::class) {}
+ }
+}
+?>
+--EXPECTF--
+Fatal error: static::class cannot be used for compile-time class name
resolution in %s on line %d
diff --git a/Zend/tests/class_name_as_scalar_error_004.phpt
b/Zend/tests/class_name_as_scalar_error_004.phpt
new file mode 100644
index 0000000..c00037f
--- /dev/null
+++ b/Zend/tests/class_name_as_scalar_error_004.phpt
@@ -0,0 +1,13 @@
+--TEST--
+class name as scalar from ::class keyword error using parent in method
signature
+--FILE--
+<?php
+
+namespace Foo\Bar {
+ class One {
+ public function baz($x = parent::class) {}
+ }
+}
+?>
+--EXPECTF--
+Fatal error: parent::class cannot be used for compile-time class name
resolution in %s on line %d
diff --git a/Zend/tests/class_name_as_scalar_error_005.phpt
b/Zend/tests/class_name_as_scalar_error_005.phpt
new file mode 100644
index 0000000..39de69f
--- /dev/null
+++ b/Zend/tests/class_name_as_scalar_error_005.phpt
@@ -0,0 +1,10 @@
+--TEST--
+class name as scalar from ::class keyword error using static non class context
+--FILE--
+<?php
+
+$x = static::class;
+
+?>
+--EXPECTF--
+Fatal error: Cannot access static::class when no class scope is active in %s
on line %d
\ No newline at end of file
diff --git a/Zend/tests/class_name_as_scalar_error_006.phpt
b/Zend/tests/class_name_as_scalar_error_006.phpt
new file mode 100644
index 0000000..a4cc9a5
--- /dev/null
+++ b/Zend/tests/class_name_as_scalar_error_006.phpt
@@ -0,0 +1,10 @@
+--TEST--
+class name as scalar from ::class keyword error using parent in non class
context
+--FILE--
+<?php
+
+$x = parent::class;
+
+?>
+--EXPECTF--
+Fatal error: Cannot access parent::class when no class scope is active in %s
on line %d
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 43b8916..b574ad6 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -2119,6 +2119,53 @@ void zend_resolve_non_class_name(znode *element_name,
zend_bool check_namespace
}
/* }}} */
+void zend_do_resolve_class_name(znode *result, znode *class_name, int
is_static TSRMLS_DC) /* {{{ */
+{
+ char *lcname;
+ int lctype;
+ znode constant_name;
+
+ lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant),
class_name->u.constant.value.str.len);
+ lctype = zend_get_class_fetch_type(lcname, strlen(lcname));
+ switch (lctype) {
+ case ZEND_FETCH_CLASS_SELF:
+ if (!CG(active_class_entry)) {
+ zend_error(E_COMPILE_ERROR, "Cannot access
self::class when no class scope is active");
+ }
+ zval_dtor(&class_name->u.constant);
+ class_name->op_type = IS_CONST;
+ ZVAL_STRINGL(&class_name->u.constant,
CG(active_class_entry)->name, CG(active_class_entry)->name_length, 1);
+ *result = *class_name;
+ break;
+ case ZEND_FETCH_CLASS_STATIC:
+ case ZEND_FETCH_CLASS_PARENT:
+ if (is_static) {
+ zend_error(E_COMPILE_ERROR,
+ "%s::class cannot be used for
compile-time class name resolution",
+ lctype == ZEND_FETCH_CLASS_STATIC ?
"static" : "parent"
+ );
+ }
+ if (!CG(active_class_entry)) {
+ zend_error(E_COMPILE_ERROR,
+ "Cannot access %s::class when no class
scope is active",
+ lctype == ZEND_FETCH_CLASS_STATIC ?
"static" : "parent"
+ );
+ }
+ constant_name.op_type = IS_CONST;
+ ZVAL_STRINGL(&constant_name.u.constant, "class",
sizeof("class")-1, 1);
+ zend_do_fetch_constant(result, class_name,
&constant_name, ZEND_RT, 1 TSRMLS_CC);
+ break;
+ case ZEND_FETCH_CLASS_DEFAULT:
+ zend_resolve_class_name(class_name,
ZEND_FETCH_CLASS_GLOBAL, 1);
+ *result = *class_name;
+ break;
+ }
+
+ efree(lcname);
+
+}
+/* }}} */
+
void zend_resolve_class_name(znode *class_name, ulong fetch_type, int
check_ns_name TSRMLS_DC) /* {{{ */
{
char *compound;
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 0f58b55..8042dd5 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -638,6 +638,8 @@ void zend_verify_namespace(TSRMLS_D);
void zend_do_use(znode *name, znode *new_name, int is_global TSRMLS_DC);
void zend_do_end_compilation(TSRMLS_D);
+void zend_do_resolve_class_name(znode *result, znode *class_name, int
is_static TSRMLS_DC);
+
void zend_do_label(znode *label TSRMLS_DC);
void zend_do_goto(const znode *label TSRMLS_DC);
void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int
pass2 TSRMLS_DC);
diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index 781d806..ccbc9b1 100644
--- a/Zend/zend_language_parser.y
+++ b/Zend/zend_language_parser.y
@@ -942,6 +942,7 @@ common_scalar:
static_scalar: /* compile-time evaluated scalars */
common_scalar { $$ = $1; }
+ | static_class_name_scalar { $$ = $1; }
| namespace_name { zend_do_fetch_constant(&$$, NULL,
&$1, ZEND_CT, 1 TSRMLS_CC); }
| T_NAMESPACE T_NS_SEPARATOR namespace_name { $$.op_type =
IS_CONST; ZVAL_EMPTY_STRING(&$$.u.constant); zend_do_build_namespace_name(&$$,
&$$, &$3 TSRMLS_CC); $3 = $$; zend_do_fetch_constant(&$$, NULL, &$3, ZEND_CT, 0
TSRMLS_CC); }
| T_NS_SEPARATOR namespace_name { char *tmp =
estrndup(Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); memcpy(&(tmp[1]),
Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); tmp[0] = '\\';
efree(Z_STRVAL($2.u.constant)); Z_STRVAL($2.u.constant) = tmp;
++Z_STRLEN($2.u.constant); zend_do_fetch_constant(&$$, NULL, &$2, ZEND_CT, 0
TSRMLS_CC); }
@@ -959,6 +960,7 @@ static_class_constant:
scalar:
T_STRING_VARNAME { $$ = $1; }
+ | class_name_scalar { $$ = $1; }
| class_constant { $$ = $1; }
| namespace_name { zend_do_fetch_constant(&$$, NULL, &$1,
ZEND_RT, 1 TSRMLS_CC); }
| T_NAMESPACE T_NS_SEPARATOR namespace_name { $$.op_type =
IS_CONST; ZVAL_EMPTY_STRING(&$$.u.constant); zend_do_build_namespace_name(&$$,
&$$, &$3 TSRMLS_CC); $3 = $$; zend_do_fetch_constant(&$$, NULL, &$3, ZEND_RT, 0
TSRMLS_CC); }
@@ -1200,6 +1202,14 @@ class_constant:
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM T_STRING {
zend_do_fetch_constant(&$$, &$1, &$3, ZEND_RT, 0 TSRMLS_CC); }
;
+static_class_name_scalar:
+ class_name T_PAAMAYIM_NEKUDOTAYIM T_CLASS {
zend_do_resolve_class_name(&$$, &$1, 1 TSRMLS_CC); }
+;
+
+class_name_scalar:
+ class_name T_PAAMAYIM_NEKUDOTAYIM T_CLASS {
zend_do_resolve_class_name(&$$, &$1, 0 TSRMLS_CC); }
+;
+
%%
/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 3f9cc12..25ac453 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -3563,6 +3563,9 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT,
VAR|CONST|UNUSED, CONST)
}
ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var,
*value);
zval_copy_ctor(&EX_T(opline->result.var).tmp_var);
+ } else if (Z_STRLEN_P(opline->op2.zv) == sizeof("class")-1 &&
strcmp(Z_STRVAL_P(opline->op2.zv), "class") == 0) {
+ /* "class" is assigned as a case-sensitive keyword from
zend_do_resolve_class_name */
+ ZVAL_STRINGL(&EX_T(opline->result.var).tmp_var,
ce->name, ce->name_length, 1);
} else {
zend_error_noreturn(E_ERROR, "Undefined class constant
'%s'", Z_STRVAL_P(opline->op2.zv));
}
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 614249f..ab69ee2 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -3731,6 +3731,9 @@ static int ZEND_FASTCALL
ZEND_FETCH_CONSTANT_SPEC_CONST_CONST_HANDLER(ZEND_OPCO
}
ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var,
*value);
zval_copy_ctor(&EX_T(opline->result.var).tmp_var);
+ } else if (Z_STRLEN_P(opline->op2.zv) == sizeof("class")-1 &&
strcmp(Z_STRVAL_P(opline->op2.zv), "class") == 0) {
+ /* "class" is assigned as a case-sensitive keyword from
zend_do_resolve_class_name */
+ ZVAL_STRINGL(&EX_T(opline->result.var).tmp_var,
ce->name, ce->name_length, 1);
} else {
zend_error_noreturn(E_ERROR, "Undefined class constant
'%s'", Z_STRVAL_P(opline->op2.zv));
}
@@ -15595,6 +15598,9 @@ static int ZEND_FASTCALL
ZEND_FETCH_CONSTANT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE
}
ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var,
*value);
zval_copy_ctor(&EX_T(opline->result.var).tmp_var);
+ } else if (Z_STRLEN_P(opline->op2.zv) == sizeof("class")-1 &&
strcmp(Z_STRVAL_P(opline->op2.zv), "class") == 0) {
+ /* "class" is assigned as a case-sensitive keyword from
zend_do_resolve_class_name */
+ ZVAL_STRINGL(&EX_T(opline->result.var).tmp_var,
ce->name, ce->name_length, 1);
} else {
zend_error_noreturn(E_ERROR, "Undefined class constant
'%s'", Z_STRVAL_P(opline->op2.zv));
}
@@ -25170,6 +25176,9 @@ static int ZEND_FASTCALL
ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPC
}
ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var,
*value);
zval_copy_ctor(&EX_T(opline->result.var).tmp_var);
+ } else if (Z_STRLEN_P(opline->op2.zv) == sizeof("class")-1 &&
strcmp(Z_STRVAL_P(opline->op2.zv), "class") == 0) {
+ /* "class" is assigned as a case-sensitive keyword from
zend_do_resolve_class_name */
+ ZVAL_STRINGL(&EX_T(opline->result.var).tmp_var,
ce->name, ce->name_length, 1);
} else {
zend_error_noreturn(E_ERROR, "Undefined class constant
'%s'", Z_STRVAL_P(opline->op2.zv));
}
--
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php