]> granicus.if.org Git - php/commitdiff
Disallow self etc outside classes at compile-time
authorNikita Popov <nikic@php.net>
Mon, 27 Apr 2015 19:14:58 +0000 (21:14 +0200)
committerNikita Popov <nikic@php.net>
Wed, 29 Apr 2015 18:51:08 +0000 (20:51 +0200)
Also fix a bug with return types where "self" was rejected inside
a class, but not on a method.

Fallout: A couple of tests changed to more generic error messages.

Zend/tests/class_name_as_scalar_error_005.phpt
Zend/tests/class_name_as_scalar_error_006.phpt
Zend/tests/class_name_as_scalar_error_007.phpt
Zend/tests/return_types/024.phpt
Zend/tests/return_types/025.phpt
Zend/tests/return_types/026.phpt
Zend/tests/return_types/027.phpt
Zend/tests/self_class_const_outside_class.phpt
Zend/tests/typehints/self_on_closure_in_method.phpt [new file with mode: 0644]
Zend/zend_compile.c

index 39de69ffb3f6e44248b59dc2d25252318b51fa44..27700fd64601021685c7ae086f511da0b2ee2567 100644 (file)
@@ -7,4 +7,4 @@ $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
+Fatal error: Cannot use "static" when no class scope is active in %s on line 3
index a4cc9a528b5c230b067273c7b1747a56ef9ad91a..a1078d0c7e9a724897026bdaa77e1d257f23dbd7 100644 (file)
@@ -7,4 +7,4 @@ $x = parent::class;
 
 ?>
 --EXPECTF--
-Fatal error: Cannot access parent::class when no class scope is active in %s on line %d
+Fatal error: Cannot use "parent" when no class scope is active in %s on line 3
index 2bfa5f38f83041a8287cb7ac393ea71c2fdc7c8b..684470d211e7990bee157c70f0b7cdeaaf06a0da 100644 (file)
@@ -7,4 +7,4 @@ var_dump(self::class);
 
 ?>
 --EXPECTF--
-Fatal error: Cannot access self::class when no class scope is active in %s on line %d
+Fatal error: Cannot use "self" when no class scope is active in %s on line 3
index 9f2691f2144fd2b4b36b98fffe4bc8d22be554fa..0579973fbc969c57818ca0573d2bdc3aed0201f5 100644 (file)
@@ -7,4 +7,4 @@ Return type of self is not allowed in function
 function test(): self {}
 
 --EXPECTF--
-Fatal error: Cannot declare a return type of self outside of a class scope in %s on line 3
+Fatal error: Cannot use "self" when no class scope is active in %s on line 3
index 650fb0857480243e5ff840c1f6b65a3dc0badd2b..16a608bacd5ab5a310bf1b4608a3218ad7510eaf 100644 (file)
@@ -7,4 +7,4 @@ Return type of self is not allowed in closure
 $c = function(): self {};
 
 --EXPECTF--
-Fatal error: Cannot declare a return type of self outside of a class scope in %s on line 3
+Fatal error: Cannot use "self" when no class scope is active in %s on line 3
index 44ae82a3561f1391022a9f477165932aeafdb6a6..5693e95c7eaef14241c9b71a6fdcb47334773ef4 100644 (file)
@@ -6,4 +6,4 @@ Return type of parent is not allowed in function
 function test(): parent {}
 
 --EXPECTF--
-Fatal error: Cannot declare a return type of parent outside of a class scope in %s on line 3
+Fatal error: Cannot use "parent" when no class scope is active in %s on line %d
index 4d615b4bf6e88c1f677b8a5994fc2502e3556f8f..1abc7f733a14882152e112f7a01cb8d08bacf8a6 100644 (file)
@@ -6,4 +6,4 @@ Return type of parent is not allowed in closure
 $c = function(): parent {};
 
 --EXPECTF--
-Fatal error: Cannot declare a return type of parent outside of a class scope in %s on line 3
+Fatal error: Cannot use "parent" when no class scope is active in %s on line 3
index 0ef03c286f67bceeb828aa82546a46405197f0ca..541f3da3817c1b3333fa48fa82303a9af6c00b87 100644 (file)
@@ -5,4 +5,4 @@ Accessing self::FOO outside a class
 var_dump(self::FOO);
 ?>
 --EXPECTF--
-Fatal error: Cannot access self:: when no class scope is active in %s on line %d
+Fatal error: Cannot use "self" when no class scope is active in %s on line %d
diff --git a/Zend/tests/typehints/self_on_closure_in_method.phpt b/Zend/tests/typehints/self_on_closure_in_method.phpt
new file mode 100644 (file)
index 0000000..2d5bd82
--- /dev/null
@@ -0,0 +1,19 @@
+--TEST--
+self return type on closure in a method
+--FILE--
+<?php
+
+class A {
+    public function test() {
+        return function() : self {
+            return $this;
+        };
+    }
+}
+
+var_dump((new A)->test()());
+
+?>
+--EXPECT--
+object(A)#1 (0) {
+}
index d4a9df36aa39388795294594a955e1e3ace7a6bb..c92a25a705389c043b33d02741a2ae3f65d53b06 100644 (file)
@@ -1612,6 +1612,27 @@ uint32_t zend_get_class_fetch_type(zend_string *name) /* {{{ */
 }
 /* }}} */
 
+static uint32_t zend_get_class_fetch_type_ast(zend_ast *name_ast) /* {{{ */
+{
+       /* Fully qualified names are always default refs */
+       if (name_ast->attr == ZEND_NAME_FQ) {
+               return ZEND_FETCH_CLASS_DEFAULT;
+       }
+
+       return zend_get_class_fetch_type(zend_ast_get_str(name_ast));
+}
+/* }}} */
+
+void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */
+{
+       if (fetch_type != ZEND_FETCH_CLASS_DEFAULT && !CG(active_class_entry)) {
+               zend_error_noreturn(E_COMPILE_ERROR, "Cannot use \"%s\" when no class scope is active",
+                       fetch_type == ZEND_FETCH_CLASS_SELF ? "self" :
+                       fetch_type == ZEND_FETCH_CLASS_PARENT ? "parent" : "static");
+       }
+}
+/* }}} */
+
 ZEND_API zend_string *zend_get_compiled_variable_name(const zend_op_array *op_array, uint32_t var) /* {{{ */
 {
        return op_array->vars[EX_VAR_TO_NUM(var)];
@@ -2018,19 +2039,11 @@ static inline zend_bool zend_can_write_to_variable(zend_ast *ast) /* {{{ */
 
 static inline zend_bool zend_is_const_default_class_ref(zend_ast *name_ast) /* {{{ */
 {
-       zend_string *name;
-
        if (name_ast->kind != ZEND_AST_ZVAL) {
                return 0;
        }
 
-       /* Fully qualified names are always default refs */
-       if (!name_ast->attr) {
-               return 1;
-       }
-
-       name = zend_ast_get_str(name_ast);
-       return ZEND_FETCH_CLASS_DEFAULT == zend_get_class_fetch_type(name);
+       return ZEND_FETCH_CLASS_DEFAULT == zend_get_class_fetch_type_ast(name_ast);
 }
 /* }}} */
 
@@ -2077,6 +2090,8 @@ static zend_op *zend_compile_class_ref(znode *result, zend_ast *name_ast, int th
                        opline->op2_type = IS_CONST;
                        opline->op2.constant = zend_add_class_name_literal(CG(active_op_array),
                                zend_resolve_class_name(name, type));
+               } else {
+                       zend_ensure_valid_class_fetch_type(fetch_type);
                }
 
                zend_string_release(name);
@@ -4147,7 +4162,7 @@ ZEND_API void zend_set_function_arg_flags(zend_function *func) /* {{{ */
 }
 /* }}} */
 
-void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_method) /* {{{ */
+void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
 {
        zend_ast_list *list = zend_ast_get_list(ast);
        uint32_t i;
@@ -4173,15 +4188,13 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_
                        if (type != 0) {
                                arg_infos->type_hint = type;
                        } else {
-                               if (zend_is_const_default_class_ref(return_type_ast)) {
+                               uint32_t fetch_type = zend_get_class_fetch_type_ast(return_type_ast);
+                               if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
                                        class_name = zend_resolve_class_name_ast(return_type_ast);
                                        zend_assert_valid_class_name(class_name);
                                } else {
+                                       zend_ensure_valid_class_fetch_type(fetch_type);
                                        zend_string_addref(class_name);
-                                       if (!is_method) {
-                                               zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare a return type of %s outside of a class scope", class_name->val);
-                                               return;
-                                       }
                                }
 
                                arg_infos->type_hint = IS_OBJECT;
@@ -4302,10 +4315,12 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, zend_bool is_
                                if (type != 0) {
                                        arg_info->type_hint = type;
                                } else {
-                                       if (zend_is_const_default_class_ref(type_ast)) {
+                                       uint32_t fetch_type = zend_get_class_fetch_type_ast(type_ast);
+                                       if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
                                                class_name = zend_resolve_class_name_ast(type_ast);
                                                zend_assert_valid_class_name(class_name);
                                        } else {
+                                               zend_ensure_valid_class_fetch_type(fetch_type);
                                                zend_string_addref(class_name);
                                        }
 
@@ -4626,7 +4641,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
                zend_stack_push(&CG(loop_var_stack), (void *) &dummy_var);
        }
 
-       zend_compile_params(params_ast, return_type_ast, is_method);
+       zend_compile_params(params_ast, return_type_ast);
        if (uses_ast) {
                zend_compile_closure_uses(uses_ast);
        }
@@ -6329,13 +6344,10 @@ void zend_compile_resolve_class_name(znode *result, zend_ast *ast) /* {{{ */
 {
        zend_ast *name_ast = ast->child[0];
        uint32_t fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast));
+       zend_ensure_valid_class_fetch_type(fetch_type);
 
        switch (fetch_type) {
                case ZEND_FETCH_CLASS_SELF:
-                       if (!CG(active_class_entry)) {
-                               zend_error_noreturn(E_COMPILE_ERROR,
-                                       "Cannot access self::class when no class scope is active");
-                       }
                        if (CG(active_class_entry)->ce_flags & ZEND_ACC_TRAIT) {
                                zval class_str_zv;
                                zend_ast *class_str_ast, *class_const_ast;
@@ -6354,11 +6366,7 @@ void zend_compile_resolve_class_name(znode *result, zend_ast *ast) /* {{{ */
                        break;
                case ZEND_FETCH_CLASS_STATIC:
                case ZEND_FETCH_CLASS_PARENT:
-                       if (!CG(active_class_entry)) {
-                               zend_error_noreturn(E_COMPILE_ERROR,
-                                       "Cannot access %s::class when no class scope is active",
-                                       fetch_type == ZEND_FETCH_CLASS_STATIC ? "static" : "parent");
-                       } else {
+                       {
                                zval class_str_zv;
                                zend_ast *class_str_ast, *class_const_ast;
 
@@ -6611,15 +6619,12 @@ void zend_compile_const_expr_resolve_class_name(zend_ast **ast_ptr) /* {{{ */
 {
        zend_ast *ast = *ast_ptr;
        zend_ast *name_ast = ast->child[0];
-       uint32_t fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast));
        zval result;
+       uint32_t fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast));
+       zend_ensure_valid_class_fetch_type(fetch_type);
 
        switch (fetch_type) {
                case ZEND_FETCH_CLASS_SELF:
-                       if (!CG(active_class_entry)) {
-                               zend_error_noreturn(E_COMPILE_ERROR,
-                                       "Cannot access self::class when no class scope is active");
-                       }
                        ZVAL_STR_COPY(&result, CG(active_class_entry)->name);
                        break;
         case ZEND_FETCH_CLASS_STATIC: