Skip to content

Warn on "parent" without parent at compile-time #3404

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 1 commit 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
8 changes: 0 additions & 8 deletions Zend/tests/bug75573.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ Bug #75573 (Segmentation fault in 7.1.12 and 7.0.26)
class A
{
var $_stdObject;
function initialize($properties = FALSE) {
$this->_stdObject = $properties ? (object) $properties : new stdClass();
parent::initialize();
}
function &__get($property)
{
if (isset($this->_stdObject->{$property})) {
Expand All @@ -31,10 +27,6 @@ class A

class B extends A
{
function initialize($properties = array())
{
parent::initialize($properties);
}
function &__get($property)
{
if (isset($this->settings) && isset($this->settings[$property])) {
Expand Down
5 changes: 5 additions & 0 deletions Zend/tests/class_name_as_scalar.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ namespace Foo\Bar {
// compile time constants
const A = self::class;
const B = Two::class;

}
class Two extends One {
const A = parent::class;

public static function run() {
var_dump(self::class); // self compile time lookup
var_dump(static::class); // runtime lookup
Expand All @@ -18,6 +21,8 @@ namespace Foo\Bar {
}
}
class Three extends Two {
const B = parent::class;

// compile time static lookups
public static function checkCompileTime(
$one = self::class,
Expand Down
4 changes: 3 additions & 1 deletion Zend/tests/class_name_as_scalar_error_002.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ namespace Foo\Bar {
}
?>
--EXPECTF--
Fatal error: parent::class cannot be used for compile-time class name resolution in %s on line %d
Deprecated: Cannot use "parent" without a parent class in %s on line %d

Fatal error: Cannot use "parent" without a parent class in %s on line %d
4 changes: 3 additions & 1 deletion Zend/tests/class_name_as_scalar_error_004.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ namespace Foo\Bar {
}
?>
--EXPECTF--
Fatal error: parent::class cannot be used for compile-time class name resolution in %s on line %d
Deprecated: Cannot use "parent" without a parent class in %s on line %d

Fatal error: Cannot use "parent" without a parent class in %s on line %d
11 changes: 11 additions & 0 deletions Zend/tests/compile_time_parent_error_01.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Using "parent" in class without parent results in compile-time error
--FILE--
<?php
class InvalidClass {
function method(parent $p) {}
}

?>
--EXPECTF--
Deprecated: Cannot use "parent" without a parent class in %s on line %d
11 changes: 11 additions & 0 deletions Zend/tests/compile_time_parent_error_02.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Using "parent" in class without parent results in compile-time error
--FILE--
<?php
class InvalidClass {
function method(): parent {}
}

?>
--EXPECTF--
Deprecated: Cannot use "parent" without a parent class in %s on line %d
13 changes: 13 additions & 0 deletions Zend/tests/compile_time_parent_error_03.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Using "parent" in class without parent results in compile-time error
--FILE--
<?php
class InvalidClass {
function method() {
echo parent::class;
}
}

?>
--EXPECTF--
Deprecated: Cannot use "parent" without a parent class in %s on line %d
13 changes: 13 additions & 0 deletions Zend/tests/compile_time_parent_error_04.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Using "parent" in class without parent results in compile-time error
--FILE--
<?php
$obj = new class() {
function method() {
echo parent::class;
}
}

?>
--EXPECTF--
Deprecated: Cannot use "parent" without a parent class in %s on line %d
17 changes: 17 additions & 0 deletions Zend/tests/compile_time_parent_error_05.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Using "parent" in class without parent results in compile-time error
--FILE--
<?php
class InvalidClass {
function method() {
$obj = new class() {
function method() {
echo parent::class;
}
};
}
}

?>
--EXPECTF--
Deprecated: Cannot use "parent" without a parent class in %s on line %d
61 changes: 52 additions & 9 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ void zend_init_compiler_data_structures(void) /* {{{ */
zend_stack_init(&CG(loop_var_stack), sizeof(zend_loop_var));
zend_stack_init(&CG(delayed_oplines_stack), sizeof(zend_op));
CG(active_class_entry) = NULL;
CG(active_class_entry_parent_name) = NULL;
CG(in_compilation) = 0;
CG(start_lineno) = 0;

Expand Down Expand Up @@ -1467,10 +1468,22 @@ static uint32_t zend_get_class_fetch_type_ast(zend_ast *name_ast) /* {{{ */

static void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */
{
if (fetch_type != ZEND_FETCH_CLASS_DEFAULT && !CG(active_class_entry) && zend_is_scope_known()) {
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");
if (fetch_type != ZEND_FETCH_CLASS_DEFAULT && zend_is_scope_known()) {
if (!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"
);
} else if (fetch_type == ZEND_FETCH_CLASS_PARENT
&& !CG(active_class_entry_parent_name))
{
zend_error_noreturn(
E_DEPRECATED,
"Cannot use \"parent\" without a parent class"
);
}
}
}
/* }}} */
Expand All @@ -1493,23 +1506,39 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a
}

fetch_type = zend_get_class_fetch_type(zend_ast_get_str(class_ast));
zend_ensure_valid_class_fetch_type(fetch_type);

switch (fetch_type) {
case ZEND_FETCH_CLASS_SELF:
zend_ensure_valid_class_fetch_type(fetch_type);
if (constant || (CG(active_class_entry) && zend_is_scope_known())) {
ZVAL_STR_COPY(zv, CG(active_class_entry)->name);
} else {
ZVAL_NULL(zv);
}
return 1;
case ZEND_FETCH_CLASS_STATIC:

case ZEND_FETCH_CLASS_PARENT:
if (zend_is_scope_known()) {
if (CG(active_class_entry_parent_name)) {
ZVAL_STR_COPY(zv, CG(active_class_entry_parent_name));
return 1;
}
if (constant) {
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot use \"parent\" without a parent class");
} else {
zend_error(E_DEPRECATED,
"Cannot use \"parent\" without a parent class");
}
}
ZVAL_NULL(zv);
return 1;

case ZEND_FETCH_CLASS_STATIC:
zend_ensure_valid_class_fetch_type(fetch_type);
if (constant) {
zend_error_noreturn(E_COMPILE_ERROR,
"%s::class cannot be used for compile-time class name resolution",
fetch_type == ZEND_FETCH_CLASS_STATIC ? "static" : "parent"
);
"static::class cannot be used for compile-time class name resolution");
} else {
ZVAL_NULL(zv);
}
Expand Down Expand Up @@ -6307,6 +6336,7 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
int extends_const;

zend_class_entry *original_ce = CG(active_class_entry);
zend_string *original_ce_parent_name = CG(active_class_entry_parent_name);
znode original_implementing_class = FC(implementing_class);

if (EXPECTED((decl->flags & ZEND_ACC_ANON_CLASS) == 0)) {
Expand Down Expand Up @@ -6423,6 +6453,15 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
}

CG(active_class_entry) = ce;
if (extends_ast) {
zend_string *extends_name = zend_ast_get_str(extends_ast);
CG(active_class_entry_parent_name) = zend_resolve_class_name(
extends_name,
extends_ast->kind == ZEND_AST_ZVAL ? extends_ast->attr : ZEND_NAME_FQ
);
} else {
CG(active_class_entry_parent_name) = NULL;
}

zend_compile_stmt(stmt_ast);

Expand Down Expand Up @@ -6504,6 +6543,10 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */

FC(implementing_class) = original_implementing_class;
CG(active_class_entry) = original_ce;
if (CG(active_class_entry_parent_name)) {
zend_string_release(CG(active_class_entry_parent_name));
}
CG(active_class_entry_parent_name) = original_ce_parent_name;
}
/* }}} */

Expand Down
1 change: 1 addition & 0 deletions Zend/zend_globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ struct _zend_compiler_globals {
zend_stack loop_var_stack;

zend_class_entry *active_class_entry;
zend_string *active_class_entry_parent_name;

zend_string *compiled_filename;

Expand Down