Skip to content

Commit

Permalink
Fix bug 666222
Browse files Browse the repository at this point in the history
This also adds some smaller, isolated tests related to bug 66622.
  • Loading branch information
morrisonlevi authored and smalyshev committed Jun 9, 2014
1 parent 3834385 commit f47976d
Show file tree
Hide file tree
Showing 12 changed files with 226 additions and 5 deletions.
22 changes: 22 additions & 0 deletions Zend/tests/closure_049.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Closure 049: static::class in static closure in non-static method.

--FILE--
<?php

class A {
function foo() {
$f = static function() {
return static::class;
};
return $f();
}
}

class B extends A {}

$b = new B;

var_dump($b->foo());
--EXPECT--
string(1) "B"
22 changes: 22 additions & 0 deletions Zend/tests/closure_050.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Closure 050: static::class in non-static closure in non-static method.

--FILE--
<?php

class A {
function foo() {
$f = function() {
return static::class;
};
return $f();
}
}

class B extends A {}

$b = new B;
var_dump($b->foo());

--EXPECT--
string(1) "B"
21 changes: 21 additions & 0 deletions Zend/tests/closure_051.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
Closure 051: static::class in static closure in static method.

--FILE--
<?php

class A {
static function foo() {
$f = static function() {
return static::class;
};
return $f();
}
}

class B extends A {}

var_dump(B::foo());

--EXPECT--
string(1) "B"
21 changes: 21 additions & 0 deletions Zend/tests/closure_052.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
Closure 052: static::class in non-static closure in static method.

--FILE--
<?php

class A {
static function foo() {
$f = function() {
return static::class;
};
return $f();
}
}

class B extends A {}

var_dump(B::foo());

--EXPECT--
string(1) "B"
22 changes: 22 additions & 0 deletions Zend/tests/closure_053.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Closure 053: self::class in static closure in non-static method.

--FILE--
<?php

class A {
function foo() {
$f = static function() {
return self::class;
};
return $f();
}
}

class B extends A {}

$b = new B;
var_dump($b->foo());

--EXPECT--
string(1) "A"
22 changes: 22 additions & 0 deletions Zend/tests/closure_054.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Closure 054: self::class in non-static closure in non-static method.

--FILE--
<?php

class A {
function foo() {
$f = function() {
return self::class;
};
return $f();
}
}

class B extends A {}

$b = new B;
var_dump($b->foo());

--EXPECT--
string(1) "A"
21 changes: 21 additions & 0 deletions Zend/tests/closure_055.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
Closure 055: self::class in static closure in static method.

--FILE--
<?php

class A {
static function foo() {
$f = static function() {
return self::class;
};
return $f();
}
}

class B extends A {}

var_dump(B::foo());

--EXPECT--
string(1) "A"
21 changes: 21 additions & 0 deletions Zend/tests/closure_056.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
Closure 056: self::class in non-static closure in static method.

--FILE--
<?php

class A {
static function foo() {
$f = function() {
return self::class;
};
return $f();
}
}

class B extends A {}

var_dump(B::foo());

--EXPECT--
string(1) "A"
37 changes: 37 additions & 0 deletions Zend/tests/closure_bug66622.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
Bug 66622: Closures do not correctly capture the late bound class (static::) in some cases

--FILE--
<?php
class A {
static function name() { return 'A'; }
function foo() {
$fn = function() { return static::name(); };
echo static::name() . ' vs ' . $fn() . "\n";
}
function bar() {
$fn = static function() { return static::name(); };
echo static::name() . ' vs ' . $fn() . "\n";
}
static function baz() {
$fn = function() { return static::name(); };
echo static::name() . ' vs ' . $fn() . "\n";
}
}
class B extends A {
static function name() { return 'B'; }
}

function test() {
(new B)->foo();
(new B)->bar();
(new B)->baz();
B::baz();
}
test();

--EXPECT--
B vs B
B vs B
B vs B
B vs B
4 changes: 1 addition & 3 deletions Zend/zend_closures.c
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
}
}

closure->this_ptr = NULL;
/* Invariants:
* If the closure is unscoped, it has no bound object.
* The the closure is scoped, it's either static or it's bound */
Expand All @@ -497,10 +498,7 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
Z_ADDREF_P(this_ptr);
} else {
closure->func.common.fn_flags |= ZEND_ACC_STATIC;
closure->this_ptr = NULL;
}
} else {
closure->this_ptr = NULL;
}
}
/* }}} */
Expand Down
9 changes: 8 additions & 1 deletion Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -5179,6 +5179,7 @@ ZEND_VM_HANDLER(153, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED)
{
USE_OPLINE
zend_function *op_array;
int closure_is_static, closure_is_being_defined_inside_static_context;

SAVE_OPLINE();

Expand All @@ -5187,7 +5188,13 @@ ZEND_VM_HANDLER(153, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED)
zend_error_noreturn(E_ERROR, "Base lambda function for closure not found");
}

zend_create_closure(&EX_T(opline->result.var).tmp_var, (zend_function *) op_array, EG(scope), EG(This) TSRMLS_CC);
closure_is_static = op_array->common.fn_flags & ZEND_ACC_STATIC;
closure_is_being_defined_inside_static_context = EX(prev_execute_data) && EX(prev_execute_data)->function_state.function->common.fn_flags & ZEND_ACC_STATIC;
if (closure_is_static || closure_is_being_defined_inside_static_context) {
zend_create_closure(&EX_T(opline->result.var).tmp_var, (zend_function *) op_array, EG(called_scope), NULL TSRMLS_CC);
} else {
zend_create_closure(&EX_T(opline->result.var).tmp_var, (zend_function *) op_array, EG(scope), EG(This) TSRMLS_CC);
}

CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
Expand Down
9 changes: 8 additions & 1 deletion Zend/zend_vm_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -6487,6 +6487,7 @@ static int ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER
{
USE_OPLINE
zend_function *op_array;
int closure_is_static, closure_is_being_defined_inside_static_context;

SAVE_OPLINE();

Expand All @@ -6495,7 +6496,13 @@ static int ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER
zend_error_noreturn(E_ERROR, "Base lambda function for closure not found");
}

zend_create_closure(&EX_T(opline->result.var).tmp_var, (zend_function *) op_array, EG(scope), EG(This) TSRMLS_CC);
closure_is_static = op_array->common.fn_flags & ZEND_ACC_STATIC;
closure_is_being_defined_inside_static_context = EX(prev_execute_data) && EX(prev_execute_data)->function_state.function->common.fn_flags & ZEND_ACC_STATIC;
if (closure_is_static || closure_is_being_defined_inside_static_context) {
zend_create_closure(&EX_T(opline->result.var).tmp_var, (zend_function *) op_array, EG(called_scope), NULL TSRMLS_CC);
} else {
zend_create_closure(&EX_T(opline->result.var).tmp_var, (zend_function *) op_array, EG(scope), EG(This) TSRMLS_CC);
}

CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
Expand Down

0 comments on commit f47976d

Please sign in to comment.