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