【转】PHP串行化与JSON

总目录
What 、Why、How   

What   
Why   
How   
PHP串行化语法   
PHP串行化实例   
在JavaScript中串行化为JSON—使用json2.js   
在JavaScript中串行化为JSON—使用prototype.js
  
PHP与JSON   
json_decode函数   
json_encode函数   
json_decode函数实例   
json_encode函数实例   

实践出真知
背景说明   
前台JavaScript部分   
后台PHP部分
  
我还有话要说

What 、Why、How

What

Ok,各位亲爱的朋友,让我们开始这个新概念的旅程,串行化这个话题可能大家以前都没有多加关注,事情其实起源于那天我随便翻翻PHP手册,发现这个串行化的函数,之后闲来无聊又做一个WordPress的插件,这个时候顺便用了一下串行化,发现在某些场合的确非常方便。
先来解释下串行化:简单来说,串行化即将变量转换成字节流的过程。串行化的提出,有效的解决了对象的保存和传输的问题,举例来说,我在JavaScript中建立了一个对象,我现在想将这个对象保存到服务器端的数据库中,那么我如何进行操作呢,这个时候往往就用到了对象的串行化。在JavaScript的串行化中不得不提JSON,JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写,同时也易于机器解析和生成。它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。这些特性使JSON成为理想的数据交换语言。
人们通常将JSON和XML进行比较,二者都是将对象扁平化(稍后我们解释这个“扁平化”)的一种手段,XML的特点是结构严谨,而JSON的特点则是简单易读、容易使用程序进行分析,因为它能够很简单的将一个对象转换为一个字符流的形式,例如如下代码:
复制内容到剪贴板
代码:

{"type":"human","name":"hanguofeng","age":22}
则是一个JSON表达式,他保存了一个对象,我们如何将它恢复为对象呢?很简单,如下:
复制内容到剪贴板
代码:

var animal_str = '{"type":"human","name":"hanguofeng","age":22}';
var animal2=eval('(' + animal_str + ')');
我们通过JavaScript的求值函数,将JSON表达式进行运算,并返回值,用以获得一个对象,到这里,我想你一定会和我一样,对JSON格式的创造者的思维佩服不已吧。
本来说讲串行化的,“不小心”谈到JSON,并且讲了这么多,呵呵,跑题了吗?没有,PHP的串行化和JSON是非常像的,一个PHP的串行化表达式如下:
复制内容到剪贴板
代码:

a:3:{s:4:"type";s:5:"human";s:4:"name";s:10:"hanguofeng";s:3:"age";s:2:"20";}
他看起来结构和JSON有些类似,实际上,这个表达式是如下数组的串行化结果:
复制内容到剪贴板
代码:

$animal =
array
(
"type" => "human",
"name" => "hanguofeng",
"age"   => "20"
);
OK,上面的一些介绍只是让你大致看到串行化和JSON是什么样的东西,你无须对这里的代码过分纠结,我们在后面会详细讲解的,下面我们来谈谈为什么要使用串行化。

Why
串行化首先是作为数据传输的方便而出现的,正如本文开始我提出的问题,我在JavaScript中建立了一个对象,我现在想将这个对象保存到服务器端的数据库中,应该如何做,这其实上是一个“我如何将一个对象从浏览器提交到服务器”的问题,在这个传输过程中,我们知道,实际上只能够传递字符流,字符流是一维(扁平)的,然而很多对象却是多维的,如果要传递的对象是一个字符串,那么很简单,我们直接将其作为传递的内容就可以了,如果要传递的对象是一个数组或者其他的结构呢,我们就需要用字符流来描述他,就比如在电话里面,我问你的名字是什么,你会告诉我,你的名字是张三、李四,而我问你,你的长相如何呢,你就需要用文字向我描述了,我们进行数据传递的媒介往往和这条电话线路一样,只能传递字符流,而我们描述对象的过程,实际上就是串行化的过程。
另外,串行化也可以用于对对象的持久化存储,也许你曾经也和我一样,想着在数据库的某一个字段中存储一个对象,现在我们可以非常简单的做到这一点,并且,你的这个数据库字段不需要设定为特殊格式,设定为varchar就可以了(当然,如果对象很大,你可能需要设定为text)。

How

PHP串行化语法
好了,我想What和Why的问题你都了解了,本节最后我们来讲点理论性强一些的内容,就是如何使用PHP串行化和反串行化数据,如何将JavaScript对象串行化(即变为JSON格式)和如何将其反串行化,最后则是如何将JSON和PHP的串行化建立关系。
PHP为我们提供了两个函数,用来进行串行化和反串行化的操作,这两个函数分别是:serialize()和unserialize(),他们适用于PHP4和PHP5,下面分别进行讲解:

serialize()
(PHP 4, PHP 5, PECL axis2:0.1.0-0.1.1)
serialize — 获得一个可存储的表述值
说明
string serialize ( mixed $value )
获得一个可存储的表述值
本函数用于无损的存储或者传递PHP变量值和结构。
如果需要将已经串行化的值转回PHP变量,可以使用unserialize()函数。
参数
value
即被串行化的表达式。serialize()处理除资源指针之外的所有类型,你甚至可以将含有指向自身元素的数组串行化。你串行化的含有循环指向的数组或者对象一样会被存储,其他的指向则会丢失。
当串行化对象时,PHP会尝试首先调用其成员函数__sleep()。这将允许对象在被串行化之前进行诸如最后的清理工作等。同样地,当使用unserialize()函数将对象恢复时,会调用成员函数__wakeup()。
返回值
   返回一个可以被存储在任何地点的包含对象的字节流表达式的字符串。
unserialize()
(PHP 4, PHP 5, PECL axis2:0.1.0-0.1.1)
unserialize — 从一个已存储的表达式中获得一个PHP变量值
说明
mixed unserialize ( string $str )
unserialize()获取一个简单类型的串行化变量并将其转换回PHP变量值。
参数
str
   串行化后的字符串
如果被反串行化的变量是一个对象,则成功恢复该对象的结构后,PHP将自动尝试执行该对象的__wakeup()成员函数(如果其存在)。
unserialize_callback_func指令:你可以设定在此过程中呗执行的回调函数,如果某个未被定义的类应当在反串行化时被实例化(以避免获得一个不完全的对象“__PHP_Incomplete_Class”)。你可以使用php.ini,ini_set()或者.htaccess来定义“unserialize_callback_func”。当一个未被定义的类被实例化时,它会被调用。屏蔽这个特性只需将其设为空即可。
返回值
返回转换后的数值,可能是布尔变量、实数、浮点数、字符串、数组或者对象。
假如传入的字符串不可以被反串行化,则返回FALSE,同时抛出NOTICE错误。
(以上译自PHP手册)

PHP串行化实例

数组的串行化和反串行化
OK,让我们来用实例学习一下,首先,请建立sample1.php文件,我们在这个文件中用如下语句来创建一个哈希数组:
复制内容到剪贴板
代码:

<?php
$animal =
array
(
"type" => "human",
"name" => "hanguofeng",
"age"   => "20"
);
?>
为了测试这个数组的值,你可以使用print_r()函数来输出数组,输出的结果如下:
复制内容到剪贴板
代码:
Array
(
[type] => human
[name] => hanguofeng
[age] => 20
)
那么我们将他来串行化一下,串行化的代码如下:
复制内容到剪贴板
代码:
<?php
$animal =
array
(
"type" => "human",
"name" => "hanguofeng",
"age"   => "20"
);
$animal_ser=serialize($animal);
echo($animal_ser);
?>
这里我们将数组$animal串行化,将返回的串行化字符串保存在变量$animal_ser中,并输出,输出的结果是:
复制内容到剪贴板
代码:
a:3:{s:4:"type";s:5:"human";s:4:"name";s:10:"hanguofeng";s:3:"age";s:2:"20";}
我们来简单对这个字符串进行一个解析:
a:3表示这是一个数组型的对象(a),他共有三个内置的对象(3)
大括号里面的部分是以逗号分割的对象表达式列表,以s:4:"type"为例,他表示一个字符串(s),长度为4位(4),值为“type”,即哈希数组的第一个元素的键。
后面的部分以此类推,我们不再赘述,你可以试试自己将各种对象串行化,看看串行化后的字符串是如何构建的。
下面来看数组的反串行化,即将我们上面生成的串行化字符串恢复为数组,代码如下:
复制内容到剪贴板
代码:
<?php
$animal_ser='a:3:{s:4:"type";s:5:"human";s:4:"name";s:10:"hanguofeng";s:3:"age";s:2:"20";}';
$animal = unserialize($animal_ser);
print_r($animal);
?>
在第一行中,我们假设$animal_ser的值为上面获得的串行化字符串,在第二行将该字符串恢复为开始的数组,并赋值给$animal,最后输出$animal这个数组,此时的输出和本节开始时输出的原始数组是一样的,即:
复制内容到剪贴板
代码:
Array
(
[type] => human
[name] => hanguofeng
[age] => 20
)
这样我们就完成了数组的反串行化。

拓展知识—自定义对象的串行化和反串行化

对数组进行串行化是一个基础操作,然而在实际的程序设计中,我们可能经常对其他类型的变量进行串行化,例如对某个自定义对象进行串行化,这里有一个我们自己编写的类A(保存在classa.inc中):
复制内容到剪贴板
代码:
<?php
class A {
var $one = 1;
function show_one() {
   echo $this->one;
}
}
?>
我们在如下代码中创建类的实例并对该实例进行串行化:
复制内容到剪贴板
代码:
<?php
include("classa.inc");
$a=new A;
echo(serialize($a));
?>
此时输出的内容为:
复制内容到剪贴板
代码:
O:1:"A":1:{s:3:"one";i:1;}
总体来看,这个串行化字符串输出了改对象当前的状态,即i的值为1。下面我们来逐个分析其中的细节。
O:1:由于当前的变量是一个自定义对象,因此该表征字符为“O”,表示Object。
后面的"A"标识了该变量是哪个类的实例,这里即A类。
大括号内即该实例的各个属性的名称和值。
而后我们将其进行反串行化:
复制内容到剪贴板
代码:
<?php
include("classa.inc");
$s = 'O:1:"A":1:{s:3:"one";i:1;}';
$a = unserialize($s);
$a->show_one();
?>
此时输出“1”,即调用了A类的show_one()方法。
你可以注意到虽然在实例的串行化字符串中并没有包含类的方法,但是我们将其反串行化后,仍然可以调用类的方法,这个特性在PHP4及以上版本中被支持(当然,你需要包含类的定义文件classa.inc)。
注:你可以参考PHP手册中Language Reference->Classes and Objects->Serializing objects - objects in sessions一节的内容。

在JavaScript中串行化为JSON—使用json2.js

JavaScript中没有直接串行化对象的内置方法,当然你可以自己写一个,不过我还是强烈推荐你在这里偷个小懒,使用现成的组件,JSON的官方网站
www.json.org提供了对JavaScript对象实现JSON串行化的代码库—json2.js,你可以从这里获得它。
获得完毕json2.js文件后,你可以打开这个文件,在文件的前部分包含了相当大量的注释信息,如果你的英文足够好,那么你可以省略我这一节,参考该文件的注释就可以了,如果作为程序员,你已经看够了大片的字母,想看看我的汉字+字母,那你可以向下继续了。
简单的翻译下这个注释:
可参考
http://www.JSON.org/js.html
该文件创建了一个包含两个方法的全局对象JSON,它的方法分别是:
复制内容到剪贴板
代码:
JSON.stringify(value, whitelist)
value    任意的JavaScript值,一般是一个对象或者数组
whitelist    一个可选的数组参数,用于判定对象值如何被串行化
这个方法通过一个JavaScript值来生成JSON文本。在进行串行化时,根据可选的参数whitelist,有三种可能:
如果某个对象有toJSON方法,那么则调用该方法,toJSON方法的返回值将被串行化。
否则,如果可选参数whitelist是一个数组,那么数组中的元素将被用来选择对象进行串行化时的的成员。
否则,如果没有使用whitelist参数,则对象的所有成员将被串行化。
如果值没有JSON的表现形式,例如undefined或者函数,则其不会被串行化。在对象中,这样的值会被忽略,而在数组中将会被null替换。
JSON.stringify(undefined)会返回undefined。日期将会被串行化为被引用的ISO日期。
例:
复制内容到剪贴板
代码:
var text = JSON.stringify(['e', {pluribus: 'unum'}]);
//text is '["e",{"pluribus":"unum"}]'
JSON.parse(text, filter)
该方法解析一个JSON文本,并生成一个组件或者数组,其可能抛出一个SyntaxError异常。
可选的filter参数是一个可过滤和转换结果的函数、它接受每个键和值,它的返回值用来替换源值。如果它返回所接收的值,那么结果不会被改变。如果他返回undefined,则该成员会被删除。
例:
复制内容到剪贴板
代码:
//解析文本,如果某个键包含字符串“date”,则将其值转换为日期
myData = JSON.parse(text, function (key, value) {
return key.indexOf('date') >= 0 ? new Date(value) : value;
});
上面的入门教程已经使你基本了解了json2.js的使用方法,这里关于该文件我就不再赘述了,只是有一个小提示,如果你想简单的解析一个JSON文本,那么可以使用eval()函数,改函数是JavaScript的内置函数,例如解析在JSON.stringify的案例中生成的JSON文本,可以使用:
复制内容到剪贴板
代码:
var myE = eval('["e",{"pluribus":"unum"}]');
来获得对象myE。

在JavaScript中串行化为JSON—使用prototype.js

如果你和我一样,喜欢在自己的项目中使用开源的JavaScript框架,那么你可能可以省去使用json2.js文件了,这里以protype.js为例,该文件可以在
http://www.prototypejs.org下载,由于本文不是在讲JavaScript框架,这里我假设你对prototype.js的使用已经有所了解了。
prototype.js中提供了对Object对象的toJSON方法,你可以使用Object.toJSON()方法来实现对对象的串行化,例如:
复制内容到剪贴板
代码:
var cat=
{
name:"hellokitty",
height:"6 apples"
}
alert(Object.toJSON(cat));
//将弹出对话框,内容为 {"name": "hellokitty", "height": "6 apples"}
另外,在prototype.js中还有另外的JSON支持,主要是在Ajax对象中对Ajax返回请求中JSON内容的解析。这里暂时与我们的内容无关,也不再介绍了。

PHP与JSON

在上面我们一起了解了PHP进行对象串行化的方法以及在JavaScript中进行将对象串行化为JSON的方法,你大致会质疑我为什么将二者放在一起,因为他们的语法实际是不完全一样的,然而,在PHP中,可以对JSON文本进行反串行化,也可以将PHP的对象串行化为JSON而非PHP风格的文本。这主要是靠json_decode和json_encode两个函数来完成的,需要特别说明的是,这两个函数在PHP 5 >= 5.2.0中才被支持,如果你要编写运行在PHP4环境下的程序,那么这两个函数是不可以使用的。

json_decode函数

语法
mixed json_decode ( string $json [, bool $assoc] )
获取一个JSON编码文本,并且将其转换为PHP变量
参数
json
被JSON编码的文本
assoc
当为TRUE时,返回的值为联合数组
返回值
返回一个对象,或者如果可选的assoc参数为TRUE,则一个联合数组将会被返回

json_encode函数

语法
string json_encode ( mixed $value )
该函数返回一个值的JSON表达式
参数
value
要被编码的值,可以为除resource外的任何类型参数
这个函数仅在UTF-8编码格式时起作用
返回值
当成功时返回编码后的JSON文本

json_decode函数实例

下面两个例子都基于我们的一个情景假设,即,我们有一个用户注册的模块,这个模块以“面向对象”的方式工作,在json_decode函数实例中,我们在前台将用户的注册信息变为一个类的属性,而后传递到后台的php文件(这里为了简便,就不用Ajax了)。在json_encode实例中,我们在html文件中引用一个js文件,地址指向php文件,在php文件中输出json编码后的用户对象(同样为了简便,我们直接生成一个对象而不从数据库中取信息),并在html中输出。
好了,先来看前台的页面json_encode.htm,这个页面模仿了通常的注册页面,在其上面有一个表单,当提交时,触发JavaScript函数,生成一个用户对象user,将表单内容设为用户对象的属性,生成JSON文本,以POST方式传递到后台的json_encode.php文件。在js_encode.php文件中,将JSON文本用json_decode函数解析为PHP对象,并输出。
好了,先来看json_encode.html文件,文件代码如下:
复制内容到剪贴板
代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>json_decode</title>
<script src="json2.js" type="text/javascript"></script>
<script type="text/javascript">
function JSON_test(o)
{
var user = {
       name:document.getElementById('txt_name').value,
       email:document.getElementById('txt_email').value,
       password:document.getElementById('txt_name').value
}
var json_string = JSON.stringify(user);

document.getElementById('txt_json').value=json_string;
alert("点击确定后将提交表单");
o.submit();
}
</script>
</head>
<body>
<form id="form1" name="form1" method="post" action="json_encode.php" onsubmit="JSON_test(this)">
   <label for="txt_name">姓名</label>
   <p>
<input type="text" name="txt_name" id="txt_name" />
   </p>
   <label for="txt_email">邮箱</label>
   <p>
<input type="text" name="txt_email" id="txt_email" />
   </p>
   <p>
<label for="txt_password">密码</label>
   </p>
   <p>
<input type="text" name="txt_password" id="txt_password" />
   </p>
   <p>
   <input type="text" name="txt_json" id="txt_json" />
<label for="button"></label>
<input type="submit" name="button" id="button" value="JSON" />
   </p>
</form>
</body>
</html>
当提交表单时,将触发JavaScript函数JSON_text(),该函数首先建立一个JavaScript对象user,将其name、email和password属性分别设为对应表单的值,而后使用json2.js文件的JSON.stringify方法将其转换为JSON文本json_string,最后设定隐藏域(这里为了使你看的清楚,我把这个隐藏域以文本框形式显示了)txt_json的值为json_string,并提交表单。
下面到json_encode.php文件,如下:
复制内容到剪贴板
代码:
<?php
$json_string = $_POST["txt_json"];
if(ini_get("magic_quotes_gpc")=="1")
{
$json_string=stripslashes($json_string);
}
$user = json_decode($json_string);
echo var_dump($user);
?>
在这个文件中,首先得到json_encode.html文件中POST表单域txt_json的值,放入变量$json_string中,而后判断,如果当前PHP的设定为magic_quotes_gpc=On,即传入的双引号等会被转义,这样json_decode函数无法解析,因此我们要将其反转义化。而后,使用json_decode函数将JSON文本转换为对象,保存在$user变量中,最终用echo var_dump($user);,将该对象dump输出出来,最终结果是:
复制内容到剪贴板
代码:
object(stdClass)#1 (3) {
   ["name"]=>
   string(10) "hanguofeng"
   ["email"]=>
   string(18) "example@domain.com"
   ["password"]=>
   string(10) "hanguofeng"
}
json_encode函数实例

在这个例子中,仍然是由两部分构成的,即json_enode.html和json_encode.php。在json_decode.html文件中,基本与json_decode.html文件的表单类似,但是不同的是,这次我们要从json_encode.php文件中获得相应用户的JSON文本,先来看这个PHP文件吧:
复制内容到剪贴板
代码:
<?php
Class user
{
public $name="";
public $email="";
public $password="";
};
$myUser = new user;
$myUser->name="hanguofeng";
$myUser->email="example@domain.com";
$myUser->password="hanguofeng";
$json_string = json_encode($myUser);
?>
var user = <?php echo($json_string)?>;
这个文件首先建立类user,而后获得一个user类的实例myUser,并设定其用户名、邮箱和密码,接下来使用json_encode函数将其转换为JSON文本,保存在变量$json_string中,最后输出一段JavaScript代码,以在JavaScript中建立全局变量user。
接下,我们需要在json_encode.html文件中引入json_encode.php文件,并得到user对象的各个属性,如下:
复制内容到剪贴板
代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>json_encode</title>
<script src="json_encode.php" type="text/javascript"></script>
</head>
<body>
<form id="form1" name="form1" method="post">
   <label for="txt_name">姓名</label>
   <p>
<input type="text" name="txt_name" id="txt_name" />
   </p>
   <label for="txt_email">邮箱</label>
   <p>
<input type="text" name="txt_email" id="txt_email" />
   </p>
   <p>
<label for="txt_password">密码</label>
   </p>
   <p>
<input type="text" name="txt_password" id="txt_password" />
   </p>
</form>
<script type="text/javascript" >
document.getElementById('txt_name').value=user.name;
document.getElementById('txt_email').value=user.email;
document.getElementById('txt_password').value=user.password;
</script>
</body>
</html>
在这个文件中,你需要注意两点,第一是,我们以这样的代码引入json_encode.php文件为JavaScript文件:
复制内容到剪贴板
代码:
<script src="json_encode.php" type="text/javascript"></script>

第二点则是:
我们在文本框代码后面加入JavaScript的代码,对文本框的value属性进行操作,分别设定其值为user对象的相应值。

实践出真知
背景说明   
前台JavaScript部分   
后台PHP部分

posted @ 2009-05-01 01:51  小鸡炖蘑菇  阅读(578)  评论(0编辑  收藏  举报