Skip to content

Commit

Permalink
"::class resolution as scalar" Feature
Browse files Browse the repository at this point in the history
* Allows for Name::class, self::class, static::class, parent::class resolution to a scalar based on current use rules and current namespace.
* Reuses existing keyword "class"
* Alters zend_compile.c\zend_resolve_class_name() to facilitate compile time class name resolution (and runtime by way of FCALL)
  • Loading branch information
ralphschindler committed Sep 8, 2012
1 parent d4d82aa commit 76703a0
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 8 deletions.
57 changes: 57 additions & 0 deletions Zend/tests/class_name_scalar.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
--TEST--
class name as scaler from ::class keyword
--FILE--
<?php

namespace Foo\Bar {
class One {}
class Two extends One {
public static function run() {
var_dump(self::class);
var_dump(static::class);
var_dump(parent::class);
var_dump(Baz::class);
}
}
class Three extends Two {}
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
}


?>
--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"
48 changes: 40 additions & 8 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -2109,6 +2109,8 @@ void zend_resolve_class_name(znode *class_name, ulong fetch_type, int check_ns_n
zval **ns;
znode tmp;
int len;
int lctype;
zend_op *opline;

compound = memchr(Z_STRVAL(class_name->u.constant), '\\', Z_STRLEN(class_name->u.constant));
if (compound) {
Expand Down Expand Up @@ -2153,7 +2155,7 @@ void zend_resolve_class_name(znode *class_name, ulong fetch_type, int check_ns_n
*class_name = tmp;
}
}
} else if (CG(current_import) || CG(current_namespace)) {
} else {
/* this is a plain name (without \) */
lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant));

Expand All @@ -2163,13 +2165,43 @@ void zend_resolve_class_name(znode *class_name, ulong fetch_type, int check_ns_n
zval_dtor(&class_name->u.constant);
class_name->u.constant = **ns;
zval_copy_ctor(&class_name->u.constant);
} else if (CG(current_namespace)) {
/* plain name, no import - prepend current namespace to it */
tmp.op_type = IS_CONST;
tmp.u.constant = *CG(current_namespace);
zval_copy_ctor(&tmp.u.constant);
zend_do_build_namespace_name(&tmp, &tmp, class_name TSRMLS_CC);
*class_name = tmp;
} else {
lctype = zend_get_class_fetch_type(lcname, strlen(lcname));
switch (lctype) {
case ZEND_FETCH_CLASS_SELF:
tmp.op_type = IS_CONST;
ZVAL_STRINGL(&tmp.u.constant, CG(active_class_entry)->name, strlen(CG(active_class_entry)->name), 1);
*class_name = tmp;
break;
case ZEND_FETCH_CLASS_STATIC:
case ZEND_FETCH_CLASS_PARENT:
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
opline->opcode = ZEND_DO_FCALL;
opline->result.var = get_temporary_variable(CG(active_op_array));
opline->result_type = IS_VAR;
if (lctype == ZEND_FETCH_CLASS_STATIC) {
LITERAL_STRINGL(opline->op1, estrndup("get_called_class", sizeof("get_called_class")-1), sizeof("get_called_class")-1, 0);
} else {
LITERAL_STRINGL(opline->op1, estrndup("get_parent_class", sizeof("get_parent_class")-1), sizeof("get_parent_class")-1, 0);
}
CALCULATE_LITERAL_HASH(opline->op1.constant);
opline->op1_type = IS_CONST;
GET_CACHE_SLOT(opline->op1.constant);
opline->extended_value = 0;
SET_UNUSED(opline->op2);
GET_NODE(class_name, opline->result);
break;
case ZEND_FETCH_CLASS_DEFAULT:
if (CG(current_namespace)) {
/* plain name, no import - prepend current namespace to it */
tmp.op_type = IS_CONST;
tmp.u.constant = *CG(current_namespace);
zval_copy_ctor(&tmp.u.constant);
zend_do_build_namespace_name(&tmp, &tmp, class_name TSRMLS_CC);
*class_name = tmp;
}
break;
}
}
efree(lcname);
}
Expand Down
6 changes: 6 additions & 0 deletions Zend/zend_language_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,7 @@ common_scalar:

static_scalar: /* compile-time evaluated scalars */
common_scalar { $$ = $1; }
| 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); }
Expand All @@ -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); }
Expand Down Expand Up @@ -1200,6 +1202,10 @@ class_constant:
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM T_STRING { zend_do_fetch_constant(&$$, &$1, &$3, ZEND_RT, 0 TSRMLS_CC); }
;

class_name_scalar:
class_name T_PAAMAYIM_NEKUDOTAYIM T_CLASS { zend_resolve_class_name(&$1, ZEND_FETCH_CLASS_GLOBAL, 1 TSRMLS_CC); $$ = $1; }
;

%%

/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
Expand Down

0 comments on commit 76703a0

Please sign in to comment.