Attached is a fairly simple (and rough) patch that adds support for a
"special" __string() object method to ZE2.  I wrote it for fun, but I
thought there might be general interest in the idea, so I'm posting it
here.  The idea is inspired by Python's __str__ method [1].

Here's how it works in practice:

    class Foo { }

    class Stringy extends Foo
    {
        function __string()
        {
            return 'blah';
        }
    }

    $foo = new Foo();
    $stringy = new Stringy();

    print "$foo\n";
    print "$stringy\n";


    $a = (string)$foo;
    $b = (string)$stringy;

    print "$a\n";
    print "$b\n";

Output:

    Object id #1
    blah
    Object id #136238812
    blah

So, essentially, if an object offers a __string() method, it will be
used whenever a string representation of the object is requested.  I
can see this being useful for things like object serialization or as a
shortcut for things like the Exception class's toString() method.

The patch is attached.  Comments are welcome.

[1] http://www.python.org/doc/current/ref/customization.html

-- 
Jon Parise ([EMAIL PROTECTED]) :: The PHP Project (http://www.php.net/)
Index: zend.c
===================================================================
RCS file: /repository/ZendEngine2/zend.c,v
retrieving revision 1.248
diff -u -r1.248 zend.c
--- zend.c      1 Sep 2003 13:05:50 -0000       1.248
+++ zend.c      2 Sep 2003 08:16:58 -0000
@@ -225,12 +225,17 @@
                        expr_copy->value.str.val = estrndup("Array", 
expr_copy->value.str.len);
                        break;
                case IS_OBJECT:
-                       if (expr->value.obj.handlers->cast_object) {
+                       {
                                TSRMLS_FETCH();
-                               expr->value.obj.handlers->cast_object(expr, expr_copy, 
IS_STRING, 0 TSRMLS_CC);
-                       } else {
+                               if (expr->value.obj.handlers->cast_object) {
+                                       expr->value.obj.handlers->cast_object(expr, 
expr_copy, IS_STRING, 0 TSRMLS_CC);
+                               } else {
+                                       expr->value.obj.handlers->as_string(expr, 
expr_copy);
+#if 0
                                expr_copy->value.str.val = (char *) 
emalloc(sizeof("Object id #")-1 + MAX_LENGTH_OF_LONG);
                                expr_copy->value.str.len = 
sprintf(expr_copy->value.str.val, "Object id #%ld", (long)expr->value.obj.handle);
+#endif
+                               }
                        }
 #if 0
                        /* FIXME: This might break BC for some people */
Index: zend.h
===================================================================
RCS file: /repository/ZendEngine2/zend.h,v
retrieving revision 1.224
diff -u -r1.224 zend.h
--- zend.h      31 Aug 2003 09:35:54 -0000      1.224
+++ zend.h      2 Sep 2003 08:16:58 -0000
@@ -330,6 +330,7 @@
        union _zend_function *__get;
        union _zend_function *__set;
        union _zend_function *__call;
+       union _zend_function *__string;
 
        
        /* handlers */
Index: zend_API.h
===================================================================
RCS file: /repository/ZendEngine2/zend_API.h,v
retrieving revision 1.160
diff -u -r1.160 zend_API.h
--- zend_API.h  29 Aug 2003 23:27:22 -0000      1.160
+++ zend_API.h  2 Sep 2003 08:16:58 -0000
@@ -124,6 +124,7 @@
                class_container.__call = handle_fcall;  \
                class_container.__get = handle_propget; \
                class_container.__set = handle_propset; \
+               class_container.__string = NULL; \
                class_container.num_interfaces = 0; \
        }
 
Index: zend_compile.c
===================================================================
RCS file: /repository/ZendEngine2/zend_compile.c,v
retrieving revision 1.469
diff -u -r1.469 zend_compile.c
--- zend_compile.c      29 Aug 2003 08:51:43 -0000      1.469
+++ zend_compile.c      2 Sep 2003 08:17:00 -0000
@@ -1036,6 +1036,8 @@
                        CG(active_class_entry)->__get = (zend_function *) 
CG(active_op_array);
                } else if ((function_name->u.constant.value.str.len == 
sizeof(ZEND_SET_FUNC_NAME)-1) && (!memcmp(function_name->u.constant.value.str.val, 
ZEND_SET_FUNC_NAME, sizeof(ZEND_SET_FUNC_NAME)))) {
                        CG(active_class_entry)->__set = (zend_function *) 
CG(active_op_array);
+               } else if ((function_name->u.constant.value.str.len == 
sizeof(ZEND_STRING_FUNC_NAME)-1) && (!memcmp(function_name->u.constant.value.str.val, 
ZEND_STRING_FUNC_NAME, sizeof(ZEND_STRING_FUNC_NAME)))) {
+                       CG(active_class_entry)->__string = (zend_function *) 
CG(active_op_array);
                }
        } else {
                zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
@@ -1596,6 +1598,9 @@
        if (!ce->__call) {
                ce->__call = ce->parent->__call;
        }
+       if (!ce->__string) {
+               ce->__string = ce->parent->__string;
+       }
        if (!ce->destructor) {
                ce->destructor   = ce->parent->destructor;
        }
@@ -3436,6 +3441,7 @@
                ce->__get = NULL;
                ce->__set = NULL;
                ce->__call = NULL;
+               ce->__string = NULL;
                ce->create_object = NULL;
        }
 
Index: zend_compile.h
===================================================================
RCS file: /repository/ZendEngine2/zend_compile.h,v
retrieving revision 1.260
diff -u -r1.260 zend_compile.h
--- zend_compile.h      24 Aug 2003 16:35:58 -0000      1.260
+++ zend_compile.h      2 Sep 2003 08:17:00 -0000
@@ -796,6 +796,7 @@
 #define ZEND_GET_FUNC_NAME          "__get"
 #define ZEND_SET_FUNC_NAME          "__set"
 #define ZEND_CALL_FUNC_NAME         "__call"
+#define ZEND_STRING_FUNC_NAME       "__string"
 
 #endif /* ZEND_COMPILE_H */
 
Index: zend_object_handlers.c
===================================================================
RCS file: /repository/ZendEngine2/zend_object_handlers.c,v
retrieving revision 1.65
diff -u -r1.65 zend_object_handlers.c
--- zend_object_handlers.c      17 Aug 2003 19:11:48 -0000      1.65
+++ zend_object_handlers.c      2 Sep 2003 08:17:00 -0000
@@ -865,6 +865,59 @@
        return SUCCESS;
 }
 
+static zval* zend_std_call_user_string(zval *object TSRMLS_DC)
+{
+       zval __string_name;
+       zval *retval = NULL;
+       int call_result;
+
+       INIT_PZVAL(&__string_name);
+       ZVAL_STRINGL(&__string_name, ZEND_STRING_FUNC_NAME,
+                                sizeof(ZEND_STRING_FUNC_NAME)-1, 0);
+
+       /* go call the __string handler */
+       call_result = call_user_function_ex(NULL,
+                                                                               
&object,
+                                                                               
&__string_name,
+                                                                               
&retval,
+                                                                               0, 
NULL,
+                                                                               0, 
NULL TSRMLS_CC);
+
+       if (call_result == FAILURE) {
+               zend_error(E_ERROR, "Could not call __string handler for class %s",
+                                  Z_OBJCE_P(object)->name);
+       }
+
+       if (retval) {
+               retval->refcount--;
+       }
+
+       return retval;
+}
+
+void zend_std_string(zval *object, zval *str TSRMLS_DC)
+{
+       zend_object *zobj;
+       
+       zobj = Z_OBJ_P(object);
+
+       if (zobj->ce->__string) {
+               zval *retval = zend_std_call_user_string(object TSRMLS_CC);
+
+               if (retval && (Z_TYPE_P(retval) == IS_STRING))
+               {
+                       ZVAL_STRINGL(str, Z_STRVAL_P(retval), Z_STRLEN_P(retval), 1);
+                       return;
+               }
+       }
+
+       str->value.str.val = (char *) emalloc(sizeof("Object id #")-1 +
+                                                                                 
MAX_LENGTH_OF_LONG);
+       str->value.str.len = sprintf(str->value.str.val,
+                                                                "Object id #%ld",
+                                                                
(long)object->value.obj.handle);
+}
+
 zend_object_handlers std_object_handlers = {
        zend_objects_store_add_ref,                             /* add_ref */
        zend_objects_store_del_ref,                             /* del_ref */
@@ -890,6 +943,7 @@
        zend_std_object_get_class_name,                 /* get_class_name */
        zend_std_compare_objects,                               /* compare_objects */
        NULL,                                                                   /* 
cast_object */
+       zend_std_string,                                                /* as_string */
 };
 
 /*
Index: zend_object_handlers.h
===================================================================
RCS file: /repository/ZendEngine2/zend_object_handlers.h,v
retrieving revision 1.23
diff -u -r1.23 zend_object_handlers.h
--- zend_object_handlers.h      16 Aug 2003 20:12:01 -0000      1.23
+++ zend_object_handlers.h      2 Sep 2003 08:17:00 -0000
@@ -73,6 +73,9 @@
 typedef union _zend_function *(*zend_object_get_method_t)(zval *object, char *method, 
int method_len TSRMLS_DC);
 typedef union _zend_function *(*zend_object_get_constructor_t)(zval *object 
TSRMLS_DC);
 
+/* Used to query the string representation of the object */
+typedef void (*zend_object_string_t)(zval *object, zval *str TSRMLS_DC);
+
 /* Object maintenance/destruction */
 typedef void (*zend_object_add_ref_t)(zval *object TSRMLS_DC);
 typedef void (*zend_object_del_ref_t)(zval *object TSRMLS_DC);
@@ -111,6 +114,7 @@
        zend_object_get_class_name_t                    get_class_name;
        zend_object_compare_t                                   compare_objects;
        zend_object_cast_t                                              cast_object;
+       zend_object_string_t                                    as_string;
 } zend_object_handlers;
 
 extern zend_object_handlers std_object_handlers;
Index: zend_operators.c
===================================================================
RCS file: /repository/ZendEngine2/zend_operators.c,v
retrieving revision 1.160
diff -u -r1.160 zend_operators.c
--- zend_operators.c    17 Aug 2003 19:11:48 -0000      1.160
+++ zend_operators.c    2 Sep 2003 08:17:01 -0000
@@ -485,14 +485,14 @@
                        zend_error(E_NOTICE, "Array to string conversion");
                        break;
                case IS_OBJECT:
-                       if (op->value.obj.handlers->cast_object) {
+                       {
                                TSRMLS_FETCH();
-                               op->value.obj.handlers->cast_object(op, op, IS_STRING, 
1 TSRMLS_CC);
-                       } else {
-                               zval_dtor(op);
-                               op->value.str.val = estrndup_rel("Object", 
sizeof("Object")-1);
-                               op->value.str.len = sizeof("Object")-1;
-                               zend_error(E_NOTICE, "Object to string conversion");
+                               if (op->value.obj.handlers->cast_object) {
+                                       op->value.obj.handlers->cast_object(op, op, 
IS_STRING, 1 TSRMLS_CC);
+                               } else {
+                                       zval_dtor(op);
+                                       op->value.obj.handlers->as_string(op 
TSRMLS_CC);
+                               }
                        }
                        break;
                default:

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

Reply via email to