+ -
当前位置:首页 → 问答吧 → 第十三章 迁移

第十三章 迁移

时间:2008-01-25

来源:互联网

“进步并不是意味着等待直到所有其他的事情都已经发展了。”
                                                                                         ――Woodrow Wilson


13.1  介绍   Introduction
因为那么多新的特性,尤其是关于改进的面向对象的支持,让每一个PHP 4的脚本都能继续在PHP 5中运行几乎是不可能的。PHP的开发团队曾经尝试着让迁移到PHP 5尽量的平滑,但是你仍然可能遇到一些细小的无法兼容的问题。这章将讲述那些在你基于PHP 5运行PHP 4的脚本的时候可能出现的问题和如何更改脚本。

在阅读完本章后,你将了解到
  • 如何用一个php.ini的设置来回复一些PHP 5的操作到PHP 4的操作上。
  • 掌握到脚本中使用面向对象特性时的其他兼容性问题。
  • PHP 5的发布包中文件的新的名字与路径。
  • 如何使用一些被更改的函数。

13.2  对象模型  The Object Model
PHP 5有一个新的对象模型。通过这个新的模型,一些PHP 5的操作与PHP 4的操作在对象的处理方式上是不一样的。对于一些操作,你可以使用兼容模型让PHP 5回复到PHP 4的操作上。

13.3  传递对象到函数中  Passing Objects to Functions
就像先前提到的,PHP 5中较大的一个变化之一是如果你传递对象到一个函数中,它们可能不再被复制。尽管这通常是你想要的,但是有时你实际上需要的是让你的对象被复制。如果是那样的话,你的脚本将不再正确运行。看一下这个例子:
复制PHP内容到剪贴板
PHP代码:

<?php
class str {
var $string;
function str($string) {
$this->string = $string;
}
}
function display_quoted($string)
{
$string->string = addslashes($string->string);
echo $string->string;
}
$s = new str("Montreal's Finest Bagels\n");
display_quoted($s);
echo $s->string;
?>

因为在PHP 4中,传递$s对象到函数中将创建一个对象的拷贝,PHP 4中的输出是
引用:
Montreal\'s Finest Bagels
Montreal's Finest Bagels
在PHP 5中,传递的是对象的句柄而且对象实际上被修改了。所以,PHP 5产生不同的输出:
引用:
Montreal\'s Finest Bagels
Montreal\'s Finest Bagels
如果你想在PHP 5只是修改一个拷贝,一种办法就是你自己在把它传递到函数时拷贝(clone)对象。这可以通过clone运算符实现:
display_quoted(clone $s);
另外一种方法是通过设置php.ini的选项zend.ze1_compatibility_mode为1来关闭新的操作。或者,你可以在脚本里设置这个选项,但是你需要在传递对象到一个函数前就设置它。
提示:如果你的脚本是依靠拷贝传递的操作来运行的,而且想要在PHP 4和PHP 5中都可以运行的话,你就不能使用clone运算符,因为该运算符在PHP 4中不存在。clone运算符如果在PHP 4中运行的话,将抛出一个一个E_ERROR的错误。这种情况下,最好是使用兼容模式的设置来解决问题。

13.4  兼容模式  Compatibility Mode
在刚才的部分,一个建议的方案就是打开兼容模式。这个模式实际上除了在引用传递方面还更改了其他更多的操作。它还影响其他的Zend 引擎2(PHP 5)相关的更改。如果打开Zend 引擎1(PHP 4)的兼容模式将更改下面的内容:
  传递对象到一个函数中结果传递的是一个对象的拷贝(在刚才的部分讨论过)。
  如果一个对象没有属性的话,把它转变为一个布尔型、整型或者双精度型数据将得到0。
  在两个对象的属性有相同的内容时,对象比较的结果为真。

13.4.1  对象的转换计算  Casting Objects
在PHP 4中,如果对象有属性时,(int) $object将得到1,或者对象没有属性时得到0。这个特性在PHP 5中不再支持了,所以(int) $object总是产生结果1。下面的例子显示了这个操作:
复制PHP内容到剪贴板
PHP代码:

<?php
/* 关闭错误汇报机制 */
error_reporting(0);
class bagel {
}
$b = new bagel();
/* 更换为一个整型数 */
if ((int) $b) {
echo "Groovy baby!\n";
}
/* 开始兼容模式,并且更换为一个整型数 */
ini_set('zend.ze1_compatibility_mode', 1);
if ((int) $b) {
echo "Yeah baby!\n";
}
?>

在PHP 4中,这个例子的结果是没有输出。但是,在PHP 5中输出是
Groovy baby!

13.4.2  对象比较  Comparing Objects
当你用==运算符比较对象时,在PHP 5中的结果将会发生改变。在PHP 4中,如果所有对象的属性都是一样的,对象的比较将返回true。在PHP 5中,相等运算符只是在对象真的一样时才返回true,也就是说它们要有相同的对象句柄。兼容模式将开启旧的PHP 4的比较对象的方法:
复制PHP内容到剪贴板
PHP代码:

<?php
class bagel {
var $topping;
function bagel($topping)
{
$this->topping = $topping;
}
}
class icecream {
var $topping;
function icecream($topping)
{
$this->topping = $topping;
}
}
/* 初始化bagel和ice cream */
$bagel = new bagel('chocolate');
$icecream = new icecream('chocolate');
/* 在Zend引擎2中,这个对比将返回false */
if ($bagel == $icecream) {
echo "A bagel is the same as icecream! (1)\n";
}
/* 如果我们开启兼容模式,它将返回true */
ini_set('zend.ze1_compatibility_mode', 1);
 
if ($bagel == $icecream) {
echo "A bagel is the same as icecream! (2)\n";
}
?>

这个例子显示出兼容模式让bagel与ice cream认为是一样的类,只要你传递的topping是相同的:
A bagel is the same as icecream! (2)

作者: PHPChina   发布时间: 2008-01-25

13.5  其他改变  Other Changes
虽然兼容模式可以处理一些PHP 4和PHP 5的更改,但是它不能解决所有的问题。例如,PHP 5不允许赋值到$this,这对于一些新的PEAR类来说是一个问题(这里的时间是根据本书编写的时间计算的)。例如,Pager/Pager.php文件在它的构造函数中使用了下面的代码:
引用:
$mode = (isset($options['mode']) ? $options['mode'] : 'Jumping');
$pager_class = 'Pager_' . ucfirst($mode);
$pager_classfile = 'Pager' . DIRECTORY_SEPARATOR . $mode . '.php';
require_once $pager_classfile;
$this = new $pager_class($options);
另外一个无法通过兼容模式恢复的PHP 5的改变是get_class()的操作。

13.5.1  赋值到$this  Assigning to $this
当你在PHP 4中使用一个类中的一行代码把一个值赋值给$this时,根据一个选项、一个类被选中并且返回一个新创建的类的实例。简单化地,代码看起来如下(其中有问题的行用粗体字标出):
复制PHP内容到剪贴板
PHP代码:

<?php
class Jumping {
}
class Sliding {
}
class Pager {
function Pager($type)

{
$this = new $type;
}
}
$pager = new Pager('Jumping');
?>

赋值一个新的对象给$this在PHP 5中是不可行的。当脚本运行时,它会抛出下面的错误:
Fatal error: Cannot re-assign $this in /book/13-making-the-move/oo assign-to-this.php on line 11
这个问题的唯一解决办法就是重新设计类。这种情况下,一个可以同时在PHP 4和PHP 5中运行的选择是
复制PHP内容到剪贴板
PHP代码:

<?php
class Pager {
function Pager($options)
{
var_dump($options);
}
}
class Jumping extends Pager {
function Jumping($options)
{
Pager::Pager($options);
}
}
class Sliding extends Pager {
function Jumping($options)
{
Pager::Pager($options);
}
}
$pager = new Jumping('foo');
?>

赋值给$this还可以用来“仿效”一个异常处理,这是必要的,因为你不能从一个构造函数返回错误。例如,Net_Curl的PEAR包在它的构造函数中编写了如下代码:
复制PHP内容到剪贴板
PHP代码:
function Net_Curl()
{
...
$ch = curl_init();
if (!$ch) {
$this = new PEAR_Error("Couldn't initialize a new curl handle");
}
...
}

这被用来仿效一个异常处理。在PHP 5中,正确的方法应该是使用一个……异常。为了让它运行,PEAR_error类需要扩展内置的PHP的Exception类。这里的例子中,我们建议一个新的使用PEAR_Exception的PEAR的错误机制,但是PEAR项目在本书编写时还不知道它们是如何处理的。重写的构造函数可能看起来如下:
复制PHP内容到剪贴板
PHP代码:
function Net_Curl()
{
...
$ch = curl_init();
if (!$ch) {
throw PEAR_Exception("Couldn't initialize a new curl handle");
}
}

除了更改构造函数,使用这个类的代码还需要更改以便捕获异常,就像
复制PHP内容到剪贴板
PHP代码:
try {
$curl = new Net_Curl();
} catch {
...
}

不幸的是,这个代码将不能继续在PHP 4中运行了。你可以通过使用一个新的方法到类的实现来同时支持PHP 4和PHP 5――例如,通过一个单件模式。一个例子可能是
复制PHP内容到剪贴板
PHP代码:

<?php
require_once "PEAR.php";

class Net_Curl {
var $type;
function Net_Curl($type) {
$this->__construct($type);
}
function __construct($type) {
$this->type = $type;
}
function singleton($type) {
if ($type == "lala") {
return PEAR::raiseError("Unable to do foo.");
} else {
return new Net_Curl($type);
}
}
}
$instance = Net_Curl::singleton("lala");
if (PEAR::isError($instance)) {
die("Error: " . $instance->getMessage() . "\n");
}
echo $instance->type . "\n";
?>

提示:为了在你自己的代码中找到给$this的赋值,你可以使用UNIX的工具grep:
egrep -r '\$this\s+=' *
这个命令找出所有这个目录及其子目录下使用到$this的赋值的例子。

13.5.2  get_class
所有PHP 4总是用小写字母返回类的名字,但是在PHP 5中,get_class()函数返回的是保持大小写不变的类的名字:
复制PHP内容到剪贴板
PHP代码:

<?php
class BookPage {
}
$page = new BookPage;
$name = get_class($page);
echo $name, "\n";
?>

在PHP 4中的输出是bookpage而在PHP 5中是BookPage。如果你需要依赖的是PHP 4的操作,你可以使用下面的代码来替换:
$name = strtolower(get_class($page));
echo $name, "\n";
这个代码对于PHP 4和PHP 5都可以运行。

13.6  E_STRICT
处理先前讨论的真实的向后兼容的问题,还有着一些“取消的”特性。取消的特性会抛出一个E_STRICT的错误,该错误不属于E_ALL错误设置。想在PHP 4的代码中看那些不兼容的问题,你需要设置错误报告为E_ALL | E_STRICT。
提示:因为PHP 4不能理解E_STRICT常量,你可能需要使用数字版本的值来让脚本可以同时在PHP 4和PHP 5中运行。E_STRICT的数字的值是2048。为了显示所有的错误(E_ALL和 E_STRICT),你需要给error_reporting()函数设置4095的值,或者直接写入php.ini的设置。

13.6.1  自动创建对象  Automagically Creating Objects
在PHP 4中,下面的代码将自动地创建一个StdClass类的对象$person:
复制PHP内容到剪贴板
PHP代码:

<?php
$person->name = "Derick";
?>

PHP 5仍然允许这个操作,但是它将抛出一个E_STRICT错误“Creating default object from empty value”。为了避免这个错误,你可以在property-assignment之前使用$person = new StdClass;。这也是可以在PHP 4中运行的。

13.6.2  var和public  var And public
使用var来设定一个对象的属性现在不再支持。推荐使用public。使用var而不是public的话将抛出E_STRICT的错误“var:Deprecated. Please use the public/private/protected modifiers”。如果你的代码还需要在PHP 4中运行的话,你可以安全地忽略这个“错误”。

13.6.3  构造函数  Constructors
通过PHP 5,一个新的“统一的”构造函数的风格被引入:__construct()。如果你在迁移现有的PHP 4的代码并使用__construct()作为一个方法的名字的话,你可能得到意料之外的结果。如果PHP 4风格的构造函数(classname())和PHP 5风格的构造函数(__construct())同时被定义的话,PHP会抛出一个E_STRICT的错误:Redefining already defined constructor for class <classname>,就像你能够下面的例子的输出中看到的一样:
复制PHP内容到剪贴板
PHP代码:

<?php
class person {
var $name;
function __construct($name)
{
echo __FUNCTION__, "\n";
$this->name = $name;
}
function person($name)
{
echo __FUNCTION__, "\n";
$this->name = $name;
}
}
$person = new person('Derick');
?>

只有PHP 5风格的构造函数会被使用,无论哪一个在类中首先被声明。

13.6.4  继承的方法
Inherited Methods
考虑下面的例子:
复制PHP内容到剪贴板
PHP代码:

<?php
class magazine {
var $title;
function getTitle() {
return $this->title;
}
}
class issues extends magazine {
var $issues;
function getTitle($nr) {
return ($this->title. ' - '. $this->issues[$nr]);
}
}

$mag = new issues;
$mag->title = "Time";
$mag->issues = array (1 => 'Jan 2003', 2 => 'Feb 2003');
echo $mag->getTitle(2);
?>

在继承的类中getTitle()方法使用的形式不一样。它接收一个额外的参数($nr)。因为这个违反了面向对象编程的惯例,所以PHP 5将抛出一个E_STRICT的错误:issues::getTitle()的声明必须与magazine::getTitle()是兼容的。增加一个哑元变量到magazine::getTitle()方法,例如function getTitle($dummy),可以简单地处理这个问题。

13.6.5  在使用之前定义类  Define Classes Before Usage
最好在代码中开始使用类之前就声明你的类――例如,在一个包含文件中。虽然它不总是必须的,但是当你在使用PHP 5的高级的更多面向对象的特性时,你需要在使用类之前就声明它,例如接口。

作者: PHPChina   发布时间: 2008-01-25

13.7  其他兼容性问题  Other Compatibility Problems
除了目前为止讨论的关于迁移面向对象的代码到PHP 5遇到的问题,还有一些其他的更改造成了向后兼容性的问题。它们其中大多数是无害的,但是你最好了解它们。

13.7.1  命令行接口  Command-Line Interface
Windows平台下的CGI的执行文件的名字已经改变。这个改变对于脚本来说不会有影响,但是会影响运行CGI版本的PHP的Windows服务的启动。CGI的可执行文件现在叫做php-cgi.exe而不是php.exe。
另外,CLI可执行文件的位置也被更改了。它原来是被存放在发布包的CLI的子目录(cli/php.exe),但是它现在被放到了主目录,与php-cgi.exe存放的目录相同。
除了名字的更改,CLI接口将总是可以使用$argc和$argv变量。

13.7.2  注释记号  Comment Tokens
PHP的解析器更改了脚本中注释解析的方法。这个改变可以解析PHPDoc(指导语言)的注释(/** */)。
单行(//)和多行(/* .. */)的注释都可以在PHP 4和PHP 5中生成T_COMMENT记号。在PHP 5中新的PHPDoc风格的注释生成T_DOC_COMMENT。在PHP 4中,T_ML_COMMENT记号被定义了,但是从来没有使用过;T_ML_COMMENT在PHP 5中是没有定义的。看下面这个代码的片段的例子,了解PHP 5上记号处理器的运行:
comment.php
复制PHP内容到剪贴板
PHP代码:

<?php
// Single line
/* Multi
* line
*/
/**
* PHP Documentor style
*/
?>

tokenize.php
复制PHP内容到剪贴板
PHP代码:

<?php
$script = file_get_contents('comment.php');
foreach (token_get_all($script) as $token) {
if (count($token) == 2) {
printf ("%-25s [%s]\n", token_name($token[0]),
$token[1]);
} else {
printf ("%-25s [%s]\n", "", $token[0]);
}
}
?>

这里是php tokenize.php的输出(为了查看清楚重新排列的):
引用:
T_OPEN_TAG              [<?php\n]
T_WHITESPACE            [ ]
T_COMMENT              [// Single line\n]
T_WHITESPACE            [\n ]
T_COMMENT              [/* Mult
* line
*/]

T_WHITESPACE            [\n\n]
T_DOC_COMMENT         [/**
* PHP Documentor style
*/]
T_WHITESPACE            [\n]
T_CLOSE_TAG              [?>\n]
13.7.3  MySQL
MySQL客户端库在PHP 5中不再绑定了。当然,MySQL仍然可以支持。你将需要使用一个外部的库,总之这对于PHP 4是推荐的。你可以使用“旧的”的libmysql 3.23版本的库,它只能用来连接MySQL 3.23和MySQL 4.0.x;也可以使用新的libmysql 4.1版本的库,它可以用来连接MySQL 3.23 和 MySQL 4。你可能会问为什么不总是使用新的版本呢?很好,这是因为这个库是基于GPL的许可证发布的,而旧的3.23版本的库是基于LGPL许可证发布的。新的许可证可能会在你发布你的PHP应用的时候给你造成一些问题。如果你想使用MySQLi的扩展的话,你只能使用新的4.1版本的MySQL客户端库。你可以在使用旧的MySQL扩展的同时使用这个新的扩展,但是只能在你给两个扩展都使用相同的(4.1版本)的库的时候。实现它的一个样本配置行如下:
./configure --with-mysql=/usr --with-mysqli=/usr/bin/mysql_config
提示:查看http://www.php.net/manual/en/faq.databases.php#faq.databases.mysql.php5可以了解为什么PHP 5不再绑定MySQL库的一些原因。

13.8  函数中的改变  Changes in Functions
函数中的一些细小的改变也破坏了向后的兼容性。另外还有无数的函数其他的功能增加和新函数,但是这些都不会影响PHP 4和PHP 5之间的兼容性。

13.8.1  array_merge()
这个函数不再接收一个非数组的参数作为它的参数之一。在PHP 4中,它可以完全地使用标量类型,例如一个整型或者字符串(但是一个表现为“null”的变量除外),作为参数。这些类型都可以方便地作为一个元素包含到结果的数组中。PHP 5不再支持这个功能。如果你使用了一个标量类型,PHP 5将产生一个E_WARNING类型的错误并且返回一个空的数组。你可以通过

比较下面的脚本在PHP 4和PHP 5中的输出来查看这个操作:
复制PHP内容到剪贴板
PHP代码:

<?php
$array1 = array (1, 2, 3, 4);
$array2 = null;
$array3 = 'non-array';
$array4 = array ('a', 'b', 'c');
print_r(array_merge($array1, $array2, $array3, $array4));
?>

使用PHP 4的输出是
引用:
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => non-array
[5] => a
[6] => b
[7] => c
)
使用PHP 5的输出是
引用:
Warning: array_merge(): Argument #2 is not an array in /13-making the-move/array_merge.php on line 7
Warning: array_merge(): Argument #3 is not an array in /13-making the-move/array_merge.php on line 7
13.8.2  strrpos()和strripos()  strrpos() And strripos()
strrpos()和strripos()都是查找一个字符串在另外一个字符串中最后出现的位置,分别使用的是大小敏感和大小写不敏感的方式。在PHP 5中,在字符串中查找的是完整的$needle,而且是从后面开始查找,而在PHP 4中只查找这个$needle字符串的第一个字母。下面的例子显示了这个区别:
复制PHP内容到剪贴板
PHP代码:

<?php
$str = "This is a short string.";
var_dump(strrpos($str, "small"));
?>

在PHP 4中,这个返回位置16(“string”中“s”的索引):
int(16)
在PHP 5中这个返回
bool(false)
有可能更多的函数都会打破PHP 4和PHP 5之间的兼容性,但是它们也许还不被获知,也许作为一个Bug被修复了,或者不太值得注意。

13.9  总结  Summary
本章突出了一些PHP 5中影响基于PHP 4编写并运行的脚本的改变。PHP 5中的一个新的对象模型和新的面向对象特性意味着一些为PHP 4编写的面向对象的脚本将不再正确地在PHP 5中运行。如果你把一个对象转换为一个整型数,结果将总是1,而不是PHP 4中的0。当你在PHP 5中比较对象时,只有对象是相同的,有着相同的对象句柄时才返回真。这三个PHP 5的操作的变化可以通过打开php.ini文件中的Zend 1 兼容模式恢复到PHP 4的操作。但是,另外两个改变不能被恢复。在PHP 5中,你不能再将一个对象赋值给一个类中的$this。另外,在PHP 5中的get_class()函数返回的是保持大小写的类的名字。除了更改以外,一些特性不再支持。一个新的错误类型――E_STRICT――只要你在php.ini文件中设置了E_STRICT错误,它将警告你使用的是不再支持的特性。虽然PHP仍然允许通过赋值一个值到属性中来自动产生一个类StdClass的对象,但是如果你这么操作,将得到一个E_STRICT错误。还有,指定属性的var将不再支持,改为public。此外,一个新的构造函数――construct()――在PHP 5中引入。当PHP 5发现一个在继承的类中的函数与父类中名字相同的函数使用不同的参数时,它将抛出一个E_STRICT错误。

与面向对象的变化一样,一些其他的变化也破坏了向后兼容性。当在Windows上启动PHP时,一些文件在发布包中的名字和位置都被改变了。例如,CGI执行文件现在叫做php-cgi.exe而不是php.exe。解析器也更改了它处理注释记号的方法。MySQL不再默认支持而且客户端的库不再默认绑定,所以你需要使用一个外部的库。array_merge()函数不再接收一个非数组的参数,而且strrpos()和strripos()现在使用完整的$needle来查找一个字符串中的子字符串。还有许多其他的改变,包括函数增加的特性和新的函数,但是大部分的改变不会影响使用PHP 4运行现在的脚本。

作者: PHPChina   发布时间: 2008-01-25

又有新章节了。

作者: 125231896   发布时间: 2008-01-25

这张有些难度啊!好好学习,天天向上。

作者: whchao001   发布时间: 2008-01-25

细看了前几段,综合XML操作的那章来看, 这本书很值得去努力哦! 谢谢简老师做出的努力!
明天看完这章, 尝试着写.

作者: zwws   发布时间: 2008-01-25

支持啊~~

作者: gvtbs   发布时间: 2008-01-27

热门下载

更多