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