pollita                                  Wed, 05 May 2010 22:48:14 +0000

Revision: http://svn.php.net/viewvc?view=revision&revision=299037

Log:
Add JSON_Serializable interface
Objects implementing JSON_Serializable will have
their ->jsonSerialize() method called

Similar to serialize() and __sleep()

Changed paths:
    U   php/php-src/trunk/NEWS
    U   php/php-src/trunk/ext/json/json.c
    U   php/php-src/trunk/ext/json/php_json.h
    A   php/php-src/trunk/ext/json/tests/serialize.phpt

Modified: php/php-src/trunk/NEWS
===================================================================
--- php/php-src/trunk/NEWS      2010-05-05 22:08:27 UTC (rev 299036)
+++ php/php-src/trunk/NEWS      2010-05-05 22:48:14 UTC (rev 299037)
@@ -27,6 +27,7 @@
 - Added command line option --rz to CLI. (Johannes)
 - Added closure $this support back. (Stas)
 - Added SplObjectStorage::getHash() hook. (Etienne)
+- Added JSON_Serializable interface (json_encode() calls 
$obj->jsonSerialize()). (Sara)

 - default_charset if not specified is now UTF-8 instead of ISO-8859-1. (Rasmus)


Modified: php/php-src/trunk/ext/json/json.c
===================================================================
--- php/php-src/trunk/ext/json/json.c   2010-05-05 22:08:27 UTC (rev 299036)
+++ php/php-src/trunk/ext/json/json.c   2010-05-05 22:48:14 UTC (rev 299037)
@@ -37,6 +37,8 @@

 static const char digits[] = "0123456789abcdef";

+zend_class_entry *php_json_serializable_ce;
+
 #define PHP_JSON_HEX_TAG       (1<<0)
 #define PHP_JSON_HEX_AMP       (1<<1)
 #define PHP_JSON_HEX_APOS      (1<<2)
@@ -73,9 +75,25 @@
 };
 /* }}} */

+/* {{{ JSON_Serializable methods */
+ZEND_BEGIN_ARG_INFO(json_serialize_arginfo, 0)
+       /* No arguments */
+ZEND_END_ARG_INFO();
+
+static const zend_function_entry json_serializable_interface[] = {
+       PHP_ABSTRACT_ME(JSON_Serializable, jsonSerialize, 
json_serialize_arginfo)
+       { NULL, NULL, NULL }
+};
+
 /* {{{ MINIT */
 static PHP_MINIT_FUNCTION(json)
 {
+       zend_class_entry ce;
+
+       INIT_CLASS_ENTRY(ce, "JSON_Serializable", json_serializable_interface);
+       php_json_serializable_ce = zend_register_internal_interface(&ce 
TSRMLS_CC);
+       /* Note: Consider adding: interface JSON\Serializable extends 
JSON_Serializable {} for futureproofing... */
+
        REGISTER_LONG_CONSTANT("JSON_HEX_TAG",  PHP_JSON_HEX_TAG,  CONST_CS | 
CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("JSON_HEX_AMP",  PHP_JSON_HEX_AMP,  CONST_CS | 
CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("JSON_HEX_APOS", PHP_JSON_HEX_APOS, CONST_CS | 
CONST_PERSISTENT);
@@ -413,6 +431,39 @@
 }
 /* }}} */

+
+static void json_encode_serializable_object(smart_str *buf, zval *val, int 
options TSRMLS_DC)
+{
+       zend_class_entry *ce = Z_OBJCE_P(val);
+       zval *retval = NULL, fname;
+
+       ZVAL_STRING(&fname, "jsonSerialize", 0);
+
+       if (FAILURE == call_user_function_ex(EG(function_table), &val, &fname, 
&retval, 0, NULL, 1, NULL TSRMLS_CC) || !retval) {
+               zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Failed calling 
%s::serialize()", ce->name);
+               smart_str_appendl(buf, "null", sizeof("null") - 1);
+               return;
+    }
+
+       if (EG(exception)) {
+               /* Error already raised */
+               zval_ptr_dtor(&retval);
+               smart_str_appendl(buf, "null", sizeof("null") - 1);
+               return;
+       }
+
+       if ((Z_TYPE_P(retval) == IS_OBJECT) &&
+               (Z_OBJ_HANDLE_P(retval) == Z_OBJ_HANDLE_P(val))) {
+               /* Handle the case where jsonSerialize does: return $this; by 
going straight to encode array */
+               json_encode_array(buf, &retval, options TSRMLS_CC);
+       } else {
+               /* All other types, encode as normal */
+               php_json_encode(buf, retval, options TSRMLS_CC);
+       }
+
+       zval_ptr_dtor(&retval);
+}
+
 PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options 
TSRMLS_DC) /* {{{ */
 {
        JSON_G(error_code) = PHP_JSON_ERROR_NONE;
@@ -455,8 +506,13 @@
                        json_escape_string(buf, Z_STRVAL_P(val), 
Z_STRLEN_P(val), options TSRMLS_CC);
                        break;

+               case IS_OBJECT:
+                       if (instanceof_function(Z_OBJCE_P(val), 
php_json_serializable_ce TSRMLS_CC)) {
+                               json_encode_serializable_object(buf, val, 
options TSRMLS_CC);
+                               break;
+                       }
+                       /* fallthrough -- Non-serializable object */
                case IS_ARRAY:
-               case IS_OBJECT:
                        json_encode_array(buf, &val, options TSRMLS_CC);
                        break;


Modified: php/php-src/trunk/ext/json/php_json.h
===================================================================
--- php/php-src/trunk/ext/json/php_json.h       2010-05-05 22:08:27 UTC (rev 
299036)
+++ php/php-src/trunk/ext/json/php_json.h       2010-05-05 22:48:14 UTC (rev 
299037)
@@ -49,6 +49,7 @@

 PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options 
TSRMLS_DC);
 PHP_JSON_API void php_json_decode(zval *return_value, char *str, int str_len, 
zend_bool assoc, long depth TSRMLS_DC);
+extern zend_class_entry *php_json_serializable_ce;

 #endif  /* PHP_JSON_H */


Added: php/php-src/trunk/ext/json/tests/serialize.phpt
===================================================================
--- php/php-src/trunk/ext/json/tests/serialize.phpt                             
(rev 0)
+++ php/php-src/trunk/ext/json/tests/serialize.phpt     2010-05-05 22:48:14 UTC 
(rev 299037)
@@ -0,0 +1,80 @@
+--TEST--
+json_encode() Serialization tests
+--SKIPIF--
+<?php if (!extension_loaded("json")) print "skip"; ?>
+--FILE--
+<?php
+
+class NonSerializingTest
+{
+       public $data;
+
+       public function __construct($data)
+       {
+               $this->data = $data;
+       }
+}
+
+class SerializingTest extends NonSerializingTest implements JSON_Serializable
+{
+       public function jsonSerialize()
+       {
+               return $this->data;
+       }
+}
+
+class ValueSerializingTest extends SerializingTest
+{
+       public function jsonSerialize()
+       {
+               return array_values(is_array($this->data) ? $this->data : 
get_object_vars($this->data));
+       }
+}
+
+class SelfSerializingTest extends SerializingTest
+{
+       public function jsonSerialize()
+       {
+               return $this;
+       }
+}
+
+$adata = array(
+       'str'   => 'foo',
+       'int'   => 1,
+       'float' => 2.3,
+       'bool'  => false,
+       'nil'   => null,
+       'arr'   => array(1,2,3),
+       'obj'   => new StdClass,
+);
+
+$ndata = array_values($adata);
+
+$odata = (object)$adata;
+
+foreach(array('NonSerializingTest','SerializingTest','ValueSerializingTest','SelfSerializingTest')
 as $class) {
+       echo "==$class==\n";
+       echo json_encode(new $class($adata)), "\n";
+       echo json_encode(new $class($ndata)), "\n";
+       echo json_encode(new $class($odata)), "\n";
+}
+--EXPECT--
+==NonSerializingTest==
+{"data":{"str":"foo","int":1,"float":2.3,"bool":false,"nil":null,"arr":[1,2,3],"obj":{}}}
+{"data":["foo",1,2.3,false,null,[1,2,3],{}]}
+{"data":{"str":"foo","int":1,"float":2.3,"bool":false,"nil":null,"arr":[1,2,3],"obj":{}}}
+==SerializingTest==
+{"str":"foo","int":1,"float":2.3,"bool":false,"nil":null,"arr":[1,2,3],"obj":{}}
+["foo",1,2.3,false,null,[1,2,3],{}]
+{"str":"foo","int":1,"float":2.3,"bool":false,"nil":null,"arr":[1,2,3],"obj":{}}
+==ValueSerializingTest==
+["foo",1,2.3,false,null,[1,2,3],{}]
+["foo",1,2.3,false,null,[1,2,3],{}]
+["foo",1,2.3,false,null,[1,2,3],{}]
+==SelfSerializingTest==
+{"data":{"str":"foo","int":1,"float":2.3,"bool":false,"nil":null,"arr":[1,2,3],"obj":{}}}
+{"data":["foo",1,2.3,false,null,[1,2,3],{}]}
+{"data":{"str":"foo","int":1,"float":2.3,"bool":false,"nil":null,"arr":[1,2,3],"obj":{}}}
+
+

-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to