On Thu, 05 Aug 2010 14:54:21 +0100, Gustavo Lopes <glo...@nebm.ist.utl.pt> wrote:

Hi

I've modified Closure::bindTo/Closure::bindTo so that they accept another
argument that defines the new scope. This can either be an object (its
class is used as the scope) or a class name.
[...]

Sending renamed attachments.

--
Gustavo Lopes
---- EXPECTED OUTPUT
int(-4)
Testing with scope given as object
int(21)
int(22)
Testing with scope as string
int(23)
int(24)

Fatal error: Using $this when not in object context in %s on line %d
---- ACTUAL OUTPUT
int(-4)
Testing with scope given as object
int(11)
int(12)
Testing with scope as string
int(21)

Fatal error: Function name must be a string in 
D:\Users\Cataphract\Documents\php-trunk\Zend\tests\closure_039.php on line 40
---------------------------------------
d:\users\cataphract\documents\php-trunk\zend\zend_opcode.c(373) : Block 
0x029c17c0 status:
Beginning:      Cached
Freed (invalid)
    Start:      OK
      End:      OK
---------------------------------------
---------------------------------------
d:\users\cataphract\documents\php-trunk\zend\zend_opcode.c(373) : Block 
0x029c17c0 status:
Beginning:      Cached
Freed (invalid)
    Start:      OK
      End:      OK
---------------------------------------
---------------------------------------
d:\users\cataphract\documents\php-trunk\zend\zend_opcode.c(373) : Block 
0x029c17c0 status:
Beginning:      Cached
Freed (invalid)
    Start:      OK
      End:      OK
---------------------------------------
---- FAILED
Index: tests/closure_036.phpt
===================================================================
--- tests/closure_036.phpt      (revision 301876)
+++ tests/closure_036.phpt      (working copy)
@@ -1,5 +1,5 @@
 --TEST--
-Closure 036: Rebinding closures
+Closure 036: Rebinding closures, keep calling scope
 --FILE--
 <?php
 
@@ -20,7 +20,7 @@
 
 $ca = $a->getIncrementor();
 $cb = $ca->bindTo($b);
-$cb2 = Closure::bind($b, $ca);
+$cb2 = Closure::bind($ca, $b);
 
 var_dump($ca());
 var_dump($cb());
Index: tests/closure_038.phpt
===================================================================
--- tests/closure_038.phpt      (revision 0)
+++ tests/closure_038.phpt      (revision 0)
@@ -0,0 +1,58 @@
+--TEST--
+Closure 038: Rebinding closures, change scope, different runtime type
+--FILE--
+<?php
+
+class A {
+       private $x;
+       
+       public function __construct($v) {
+               $this->x = $v;
+       }
+       
+       public function getIncrementor() {
+               return function() { return ++$this->x; };
+       }
+}
+class B extends A {
+       private $x;
+       public function __construct($v) {
+               parent::__construct($v);
+               $this->x = $v*2;
+       }
+}
+
+$a = new A(0);
+$b = new B(10);
+
+$ca = $a->getIncrementor();
+var_dump($ca());
+
+echo "Testing with scope given as object", "\n";
+
+$cb = $ca->bindTo($b, $b);
+$cb2 = Closure::bind($ca, $b, $b);
+var_dump($cb());
+var_dump($cb2());
+
+echo "Testing with scope as string", "\n";
+
+$cb = $ca->bindTo($b, 'B');
+$cb2 = Closure::bind($ca, $b, 'B');
+var_dump($cb());
+var_dump($cb2());
+
+$cb = $ca->bindTo($b, NULL);
+var_dump($cb());
+
+?>
+--EXPECTF--
+int(1)
+Testing with scope given as object
+int(21)
+int(22)
+Testing with scope as string
+int(23)
+int(24)
+
+Fatal error: Using $this when not in object context in %s on line %d
\ No newline at end of file
Index: tests/closure_039.phpt
===================================================================
--- tests/closure_039.phpt      (revision 0)
+++ tests/closure_039.phpt      (revision 0)
@@ -0,0 +1,58 @@
+--TEST--
+Closure 039: Rebinding closures, change scope, same runtime type
+--FILE--
+<?php
+
+class A {
+       private $x;
+       
+       public function __construct($v) {
+               $this->x = $v;
+       }
+       
+       public function getIncrementor() {
+               return function() { return ++$this->x; };
+       }
+}
+class B extends A {
+       private $x;
+       public function __construct($v) {
+               parent::__construct($v);
+               $this->x = $v*2;
+       }
+}
+
+$a = new B(-5);
+$b = new B(10);
+
+$ca = $a->getIncrementor();
+var_dump($ca());
+
+echo "Testing with scope given as object", "\n";
+
+$cb = $ca->bindTo($b, $b);
+$cb2 = Closure::bind($ca, $b, $b);
+var_dump($cb());
+var_dump($cb2());
+
+echo "Testing with scope as string", "\n";
+
+$cb = $ca->bindTo($b, 'B');
+$cb2 = Closure::bind($ca, $b, 'B');
+var_dump($cb());
+var_dump($cb2());
+
+$cb = $ca->bindTo($b, NULL);
+var_dump($cb());
+
+?>
+--EXPECTF--
+int(-4)
+Testing with scope given as object
+int(21)
+int(22)
+Testing with scope as string
+int(23)
+int(24)
+
+Fatal error: Using $this when not in object context in %s on line %d
\ No newline at end of file
Index: tests/closure_040.phpt
===================================================================
--- tests/closure_040.phpt      (revision 0)
+++ tests/closure_040.phpt      (revision 0)
@@ -0,0 +1,45 @@
+--TEST--
+Closure 040: Rebinding closures, bad arguments
+--FILE--
+<?php
+
+class A {
+       private $x;
+       private static $xs = 10;
+       
+       public function __construct($v) {
+               $this->x = $v;
+       }
+       
+       public function getIncrementor() {
+               return function() { return ++$this->x; };
+       }
+       public function getStaticIncrementor() {
+               return static function() { return ++static::$xs; };
+       }
+}
+
+$a = new A(20);
+
+$ca = $a->getIncrementor();
+$cas = $a->getStaticIncrementor();
+
+$ca->bindTo($a, array());
+$ca->bindTo(array(), 'A');
+$ca->bindTo($a, array(), "");
+$ca->bindTo();
+$cas->bindTo($a, 'A');
+
+?>
+--EXPECTF--
+Notice: Array to string conversion in %s on line %d
+
+Warning: Class 'Array' not found in %s on line %d
+
+Warning: Closure::bindTo() expects parameter 1 to be object, array given in %s 
on line 25
+
+Warning: Closure::bindTo() expects at most 2 parameters, 3 given in %s on line 
%d
+
+Warning: Closure::bindTo() expects at least 1 parameter, 0 given in %s on line 
%d
+
+Warning: Tried to bind an instance to a static closure in %s on line %d
\ No newline at end of file
Index: tests/closure_041.phpt
===================================================================
--- tests/closure_041.phpt      (revision 0)
+++ tests/closure_041.phpt      (revision 0)
@@ -0,0 +1,40 @@
+--TEST--
+Closure 041: Closure->getScope()
+--FILE--
+<?php
+
+class A {
+       private $x;
+       private static $xs = 10;
+       
+       public function __construct($v) {
+               $this->x = $v;
+       }
+       
+       public function getIncrementor() {
+               return function() { return ++$this->x; };
+       }
+       public function getStaticIncrementor() {
+               return static function() { return ++static::$xs; };
+       }
+}
+
+$a = new A(20);
+
+$c = function() {};
+$ca = $a->getIncrementor();
+$cas = $a->getStaticIncrementor();
+
+var_dump($c->getScope());
+var_dump($ca->getScope());
+var_dump($cas->getScope());
+var_dump($cas->getScope(array()));
+
+?>
+--EXPECTF--
+NULL
+string(1) "A"
+string(1) "A"
+
+Warning: Closure::getScope() expects exactly 0 parameters, 1 given in %s on 
line %d
+NULL
\ No newline at end of file
Index: zend_closures.c
===================================================================
--- zend_closures.c     (revision 301876)
+++ zend_closures.c     (working copy)
@@ -76,39 +76,81 @@
 }
 /* }}} */
 
-/* {{{ proto Closure Closure::bindTo(object $to)
-   Bind a closure to another object */
-ZEND_METHOD(Closure, bindTo) /* {{{ */
+/* {{{ proto Closure Closure::bind(Closure $old, object $to [, mixed $scope = 
$this->getScope() ] )
+   Create a closure from another one and bind to another object and scope */
+ZEND_METHOD(Closure, bind) /* {{{ */
 {
-       zval *newthis;
-       zend_closure *closure = (zend_closure 
*)zend_object_store_get_object(getThis() TSRMLS_CC);      
+       zval *newthis, *zclosure, *scope_arg = NULL;
+       zend_closure *closure;
+       zend_class_entry *ce, **ce_p;
 
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o!", &newthis) == 
FAILURE) {
+       if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), 
"Oo!|z", &zclosure, zend_ce_closure, &newthis, &scope_arg) == FAILURE) {
                RETURN_NULL();
        }
 
-       zend_create_closure(return_value, &closure->func, 
newthis?Z_OBJCE_P(newthis):NULL, newthis TSRMLS_CC);
+       closure = (zend_closure *)zend_object_store_get_object(zclosure 
TSRMLS_CC);     
+
+       if ((newthis != NULL) && (closure->func.common.fn_flags & 
ZEND_ACC_STATIC)) {
+               zend_error(E_WARNING, "Tried to bind an instance to a static 
closure");
+       }
+
+       if (scope_arg != NULL) { /* scope argument was given */
+               if (IS_ZEND_STD_OBJECT(*scope_arg)) {
+                       ce = Z_OBJCE_P(scope_arg);
+               } else if (Z_TYPE_P(scope_arg) == IS_NULL) {
+                       ce = NULL;
+               } else {
+                       char *class_name;
+                       int class_name_len;
+                       zval tmp_zval;
+                       INIT_ZVAL(tmp_zval);
+
+                       if (Z_TYPE_P(scope_arg) == IS_STRING) {
+                               class_name = Z_STRVAL_P(scope_arg);
+                               class_name_len = Z_STRLEN_P(scope_arg);
+                       } else {
+                               tmp_zval = *scope_arg;
+                               zval_copy_ctor(&tmp_zval);
+                               convert_to_string(&tmp_zval);
+                               class_name = Z_STRVAL(tmp_zval);
+                               class_name_len = Z_STRLEN(tmp_zval);
+                       }
+
+                       if (zend_lookup_class_ex(class_name, class_name_len, 
NULL, 1, &ce_p TSRMLS_CC) == FAILURE) {
+                               zend_error(E_WARNING, "Class '%s' not found", 
class_name);
+                               RETURN_NULL();
+                       } else {
+                               ce = *ce_p;
+                       }
+                       zval_dtor(&tmp_zval);
+               }
+       } else { /* scope argument not given; do not change the scope by 
default */
+               ce = closure->func.common.scope;
+       }
+
+       zend_create_closure(return_value, &closure->func, ce, newthis 
TSRMLS_CC);
 }
 /* }}} */
 
-/* {{{ proto Closure Closure::bind(object $to, Closure $old)
-   Create a closure to with binding to another object */
-ZEND_METHOD(Closure, bind) /* {{{ */
+ZEND_METHOD(Closure, getScope)
 {
-       zval *newthis, *zclosure;
-       zend_closure *closure;  
+       zend_closure *closure;
+       zend_class_entry *ce;
 
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o!O", &newthis, 
&zclosure, zend_ce_closure) == FAILURE) {
+       if (zend_parse_parameters_none() == FAILURE) {
                RETURN_NULL();
        }
 
-       closure = (zend_closure *)zend_object_store_get_object(zclosure 
TSRMLS_CC);     
+       closure = (zend_closure *)zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       ce = closure->func.common.scope;
 
-       zend_create_closure(return_value, &closure->func, 
newthis?Z_OBJCE_P(newthis):NULL, newthis TSRMLS_CC);
+       if (ce != NULL) {
+               RETURN_STRINGL(ce->name, ce->name_length, 1);
+       } else {
+               RETURN_NULL();
+       }
 }
-/* }}} */
 
-
 static zend_function *zend_closure_get_constructor(zval *object TSRMLS_DC) /* 
{{{ */
 {
        zend_error(E_RECOVERABLE_ERROR, "Instantiation of 'Closure' is not 
allowed");
@@ -356,19 +398,25 @@
 }
 /* }}} */
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_bindto, 0, 0, 0)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_bindto, 0, 0, 1)
        ZEND_ARG_INFO(0, newthis)
+       ZEND_ARG_INFO(0, newscope)
 ZEND_END_ARG_INFO()
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_bind, 0, 0, 0)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_bind, 0, 0, 2)
+       ZEND_ARG_INFO(0, closure)
        ZEND_ARG_INFO(0, newthis)
-       ZEND_ARG_INFO(0, closure)
+       ZEND_ARG_INFO(0, newscope)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_getscope, 0, 0, 0)
+ZEND_END_ARG_INFO()
+
 static const zend_function_entry closure_functions[] = {
        ZEND_ME(Closure, __construct, NULL, ZEND_ACC_PRIVATE)
-       ZEND_ME(Closure, bindTo, arginfo_closure_bindto, ZEND_ACC_PUBLIC)
        ZEND_ME(Closure, bind, arginfo_closure_bind, 
ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       ZEND_MALIAS(Closure, bindTo, bind, arginfo_closure_bindto, 
ZEND_ACC_PUBLIC)
+       ZEND_ME(Closure, getScope, arginfo_closure_getscope, ZEND_ACC_PUBLIC)
        {NULL, NULL, NULL}
 };
 
---- EXPECTED OUTPUT
int(1)
Testing with scope given as object
int(21)
int(22)
Testing with scope as string
int(23)
int(24)

Fatal error: Using $this when not in object context in %s on line %d
---- ACTUAL OUTPUT
int(1)
Testing with scope given as object
int(21)
int(22)
Testing with scope as string
---------------------------------------
d:\users\cataphract\documents\php-trunk\zend\zend_opcode.c(373) : Block 
0x02741730 status:
Beginning:      Cached
Freed (invalid)
    Start:      OK
      End:      OK
---------------------------------------
int(23)
int(24)
---------------------------------------
d:\users\cataphract\documents\php-trunk\zend\zend_opcode.c(373) : Block 
0x02741730 status:
Beginning:      Cached
Freed (invalid)
    Start:      OK
      End:      OK
---------------------------------------

Fatal error: Using $this when not in object context in 
D:\Users\Cataphract\Documents\php-trunk\Zend\tests\closure_038.php on line 11
---------------------------------------
d:\users\cataphract\documents\php-trunk\zend\zend_opcode.c(373) : Block 
0x02741730 status:
Beginning:      Cached
Freed (invalid)
    Start:      OK
      End:      OK
---------------------------------------
---------------------------------------
d:\users\cataphract\documents\php-trunk\zend\zend_opcode.c(373) : Block 
0x02741730 status:
Beginning:      Cached
Freed (invalid)
    Start:      OK
      End:      OK
---------------------------------------
---------------------------------------
d:\users\cataphract\documents\php-trunk\zend\zend_opcode.c(373) : Block 
0x02741730 status:
Beginning:      Cached
Freed (invalid)
    Start:      OK
      End:      OK
---------------------------------------
---- FAILED

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to