PHP5研究室 - 国内Android用户的第一选择! http://www.phpv.net/ 为Android手机提供免费应用软件,ROM,免费游戏,破解刷机,汉化工具! 同时为Android开发者提供交流平台. Thu, 18 Jan 2018 06:10:21 +0000 Chiron ver1.0 zh-cn hourly 1 谈PHP 闭包特性在实际应用中的问题 http://www.phpv.net/html/1703.html http://www.phpv.net/html/1703.html#comment Tue, 13 Oct 2009 10:15:52 +0000 抽烟的蚊子 http://www.phpv.net/html/1703.html 原文链接: http://justafewlines.com/2009/10/whats-wrong-with-php-closures/

PHP5.3 新版本跟随了很多新特性, 其中比较惹眼的特性之一就是支持了闭包。那么以后,我们也可以和那帮写 Ruby、Javascript 等等“高科技语言”的家伙们一样,写出非常酷的代码吗?呃,其实大部分情况下是可以的,而有些方面还是令人非常的困扰,下面慢慢道来。

很多语言的都提供了非常优雅和漂亮的操作数组的方法。在下面的例子中,会使用 PHP5.3 以及其他语言提供的闭包功能,用于展示如何“客观的”操作迭代数组。

译注:原文作者比较火星,我不了解 Groovy 以及 Scala 语言,所以这里我加上 Javascript 的实现。

在开始之前先说明下,本例子仅仅是阐明观点,并没有考虑性能等其他方面的因素。

“货比三家”

用个简单的例子开始,有下面个数组:

$nums = array(10, 20, 30, 40);

需要找出数组中大于 15 的项。那么,不考虑闭包的情况下,我们或许会这样写:

$res = array();
foreach ($nums as $n) {
if ($n > 15) {
$res[] = $n;
}
}

如果语言本身有闭包支持的,那么或许会这样写(Groovy 语言)

def res = nums.findAll { it > 15 }

或者使用 Scala 语言

val res = nums filter (_ > 15)

译注:Javascript 1.6 的话会是如下

var res = nums.filter(function(c){return c > 15});

因为循环操作已被抽象起来,所以可以看到 Groovy 、Scala (以及 Javascript) 都很漂亮得用一行就可以搞定。

当然,如果使用 PHP5.3 的闭包,也可以做到

$res = array_filter($nums, function($v) { return $v > 15; });

PHP 在这方面使用了比 Scala 更多的字符,但对比先前的例子,它更简短并且能更好得阅读。

顺便说下,上面的 PHP 代码实际上是使用了 Lambda 解析式,并不是个真正的闭包,这个 并不是我们目前关注的重点。详细阐述 PHP 闭包以及 Lambda 解析式的资料,可以参考这里

目前看来感觉都还不错,那么我们再的题目增加点难度:找到所有大于 15 的项, 然后乘以 2 再加上作用域中的的某个变量值以后再返回。

Groovy 的实现:

def x = 1
def res = nums .findAll { it > 15 } .collect { it * 2 + x }

Scala 的实现:

val x = 1
val res = nums filter (_ > 15) map (_ * 2 + x)

译注,Javascript 的实现:

var i = 1;
var res = nums.filter(function(c){return c > 15}).map(function(c){return c * 2 + i});

以及 PHP:

$x = 1;
$res = array_map(
function($v) use ($x) { return $v * 2 + $x; },
array_filter(
$nums,
function($v) { return $v > 15; })
);

光从代码量方面,现在看起来 PHP 与其他语言有出入了。先抛开代码字面上本身 的审美不谈,上面的 PHP 代码还有个额外的问题。

例如,如果需要使用数组的键而非值作比较,怎么办?是的,上面的代码就办不到了。同时,从语法角度上说,上面的代码非常难以阅读。

返璞归真,这时还是得返回老土的思路去解决问题:

$x = 1;
$res = array();
foreach ($nums as $n) {
if ($n > 15) {
$res[] = $n * 2 + $x;
}
}

呼,这样看起来又很清楚了。但这个时候你或许又会迷惑了:“那还瞎折腾啥,这不就是个数组操作吗?”。

是的,好戏还在后头。这个时候该让 PHP 的某些高级特性出场,来搞定这看似有自残倾向 的“无聊问题”。

ArrayObject – 对数组的封装

PHP 有个称作 SPL 的标准库,其中包含了个叫做 ArrayObject 的类,它能提供“像数组一 样操作类”的功能,例如

$res = new ArrayObject(array(10, 20, 30, 40));
foreach ($res as $v) {
echo "$vn";
}

ArrayObject 是个内置的类,所以你可以像其他类类操作一样封装它。

Arr - 包上糖衣

既然我们已经有了 ArrayObject 以及闭包这些特性,我们就可以开始尝试封装它:

class Arr extends ArrayObject
{
static function make($array)
{
return new self($array);
}

function map($func)
{
$res = new self();
foreach ($this as $k => $v) {
$res[$k] = $func($k, $v);
}
return $res;
}

function filter($func)
{
$res = new self();
foreach ($this as $k => $v) {
if ($func($k, $v)) {
$res[$k] = $v;
}
}
return $res;
}
}

好了,万事俱备。下面重写的 PHP 代码就可以解决上面提到的问题,并且看起来语法上“差 不多”了:

$res = Arr::make($nums)
->filter(function($k, $v) { return $v > 15; })
->map(function($k, $v) { return $v * 2; });

上面的代码与传统方式有何不同呢?首先,它们可以递归并形成作用链式的调用,因此可以 添加更多的类似操作。

同时,可以通过回调的两个参数分别操作数组的键以及值其项 - $k 对应键以及 $v 对应值 。这使得我们可以在闭包中使用键值,这在传统的 PHP 函数 array_fliter 中是无法实现的。

另外个带来的额外好处就是更加一致 API 调用。使用传统的 PHP 函数操作,它们有可能第一个参数是个闭包,或者是个数组,抑或是多个数组…总之谁知道呢?

这里是 Arr 类的完整源代码,还包含了其他有用的函数(类似 reduce 以及 walk),其实它 们的实现其实方式和代码类似。

博弈

这个问题其实很难回答 - 这需要根据代码的上下文以及程序员自身等众多因素决定。其实 ,当我第一眼看见 PHP 的闭包实现时,我感觉似乎回到了那很久以前的 Java 时期,当时 我在开始使用匿名内置类(anonymous inner classes)来实现闭包。当然,这虽然可以做到, 但看起来实在是些画蛇添足。PHP 闭包本身是没错,只是它的实现以及语法让我感到非常的困惑。

其他具有闭包特性的语言,它们可以非常方便的调用闭包并同时具有优雅的语法。在上面的例子 中,在 Scala 中使用传统的循环也可以工作,但你会这样写吗?而从另个方面,那么有人 说上面这个题目使用 PHP 的闭包也可以实现,但一般情况下你会这样写吗?

可以确定,PHP 闭包在些情况下可以成为锐利的军刀(例如延时执行以及资源调用方面), 但在传统的迭代以及数组操作面前就显得有些为难。不要气馁不管怎么样, 返璞归真编写具有兼容性的、清爽的代码以及 API 是最重要的。

结束语

像所有后来加上的语法特性一样(记得当年 Java 的 Generics 特性不?以及前几年的 PHP OOP 特性),它们都需要时间磨合以及最终稳定下来。随着 PHP5.3 甚至将来的 PHP6 逐渐普及,越来越多的技巧和特性相信在不远的将来逐渐的被聪明的程序员挖掘出来。

回到最初文章开头那个题目,对比

$res = Arr::make($nums)
->filter(function($k, $v) { return $v > 15; })
->map(function($k, $v) { return $v * 2; });

以及

val res = nums filter (_ > 15) map (_ * 2)

两者之间的区别。归根结底它们仅是语法而已,本质上都是殊途同归解决了同个问题。程序 语言的应用特性不同,自然孰优孰劣也就无从比较。

最后,这里有此篇文章的代码示例, 相信可以找到更多如何使用 PHP 进行函数式迭代(当然不仅仅是这些)的心得。

-- Split --

不靠谱之博主心得

坦白讲,虽然在 PHP5.0 之前就了解过提出的新增闭包等功能,但在看到 PHP5.3 提供的闭 包以及 Lambda 功能后,与原本心理期待的还是有些出入。

甚至相对于熟悉的 JavaScript,PHP 的闭包在我看来,像是“别的语言都有了,所以我也要有” 的这种心态下的产物。

但正如上文中所言,相比 JavaScript 等其他动态语言,PHP 出于自身的应用以及实现的哲学 出发,与其他开发语言不尽相同。

因此在某些特性的调用方式、实现方法也会不一样,这难免会让熟悉另外具有类似功能的语言 的人感到的不适应。

从 PHP5.3 推出至今,还不到半年的时间,相比 JavaScript 等这些早已具有闭包等特性的 动态语言相比,自然是显得非常稚嫩。

同时,广大的开发者对于 PHP5.3 提供的包括闭包在内的新特性还在持观望态度。PHP 的闭包特性目前还是存在于实验室中,其应用于实际开发如要突破的不仅仅是语言特性 ,还要经过效率、安全性等方面的考验。

但相信,如原文作者所言,随着 PHP 版本的推进,PHP 的闭包应用场合会越来越频繁。像 当年 PHP4 转换到 PHP5 一样,对语言新特性的适应,其实是种痛并快乐着的过程。

]]>
在 PHP 中创建更好的名称空间 http://www.phpv.net/html/1697.html http://www.phpv.net/html/1697.html#comment Mon, 20 Jul 2009 15:36:17 +0000 admin http://www.phpv.net/html/1697.html PHP V5.3 支持为 PHP 类、常量和函数提供名称空间。使用名称空间避免命名冲突,并为 PHP 代码提供上下文。这些技巧为构建名称空间提供一些指导原则,从而充分利用名称空间带来的好处。

在 PHP V5.3 中引入的名称空间是为 PHP 类、常量和函数提供上下文的一种方式,从而可以将使用相同名称的元素看作是惟一的。惟一的名称避免了命名冲突,当两个类或函数使用相同的名称时就会发生这 种情况。有时这些 PHP 类表示现实世界中的相同对象,但它们的行为是完全不同的。名称空间能够确保您拥有正确的 PHP 类、常量或函数,并且要使用您的 PHP 类的人能够确保他们使用了正确的类。

代码中的名称空间就像现实世界中的上下文。考虑一个表示现实世界中的汽车对象的类。例如,通过 Internet 销售汽车的公司使用的 Automobile 类的行为可能与保险销售公司使用的 Automobile 类完全不同。

作为应用程序开发人员,您可能使用其他人编写的组件。您不能保证其他人永远不使用您已经使用的类名,但这些类的行为却大相径庭。在出现名称空间之前,PHP 开发人员通常将上下文构建到类名中,例如 My_Enterprise_PersonXML_Validator

清单 1 显示了一个位于名称空间中的类。


清单 1. 在名称空间中声明类
				
<?php
namespace IBM;
class Foo {
...
}
?>

下面给出了一个例子,展示了如何在名称空间中引用类。


清单 2. 在名称空间中引用类
				
<?php
$foo = new \IBM\Foo();
?>

在向所有类添加名称空间之前定义一个名称空间策略是个不错的 主意。尽管在某种程度上也可以不断地构建名称空间,但最好为名称空间确定一个通用结构,以方便名称空间的组织,并减少以后可能需要的修改。只要正确使用, 除了提供上下文之外,名称空间还可以用来组织 PHP 代码。

其他语言(比如 Java™ 和 C#)在很久以前就使用名称空间。在选择名称空间命名方式上,我使用的约定类似于这些语言的约定,因为许多开发人员都对此比较熟悉,便于他们理解。不过, 与 Java 语言不同的是,PHP 中的名称空间与类所在的目录之间没有联系。您可以给类、函数或常量选择任意的名称空间。您甚至可以对一个文件使用多个名称空间。同时,PHP 名称空间也不同于 C#,您可以对类以外的函数或常量使用名称空间。

顶级名称空间

如果您为某个组织构建名称空间,您可以使用组织名作为顶级域。一般情况下,使用组织名称创建顶级名称空间已经足以为 PHP 代码提供上下文,以及避免命名冲突,除非该组织编写大量用途不一的应用程序。

清单 3 的示例显示了如何声明顶级名称空间。


清单 3. 顶级名称空间
				
<?php
namespace IBM;
...
?>







次级名称空间

次级名称空间 是顶级名称空间内部的名称空间。当顶级名称空间还不足以为 PHP 类建立上下文时,它们提供进一步说明。

在创建次级名称空间时,不要凭一时的兴趣而过多地创建,这很重要。随着次级名称空间的增多,组织和引用它们就会变得越来越困难。如果您希望名称空间发挥双重作用,即避免命名冲突和组织 PHP 代码,那么就要更加注意这点了。

在决定为了方便组织代码应该向另一个名称空间引入多少个次级名称空间时,我尝试将该数量限制为 7 个(上下浮动不超过 2),以利用数字 7 更加容易记住这个优势。这并不总是奏效的,但我将它作为一个指导原则,以确保不将名称空间划分为过多的次级名称空间。

清单 4 的示例显示了在顶级名称空间中声明次级名称空间。


清单 4. 次级名称空间
				
<?php
namespace IBM\DeveloperWorks;
...
?>

反斜杠(\)将次级名称空间 “developerWorks” 与顶级名称空间 “IBM” 分开。

在声明次级名称空间时,您可以使用两个常见技巧,或同时使用它们。获取名称空间的常见地方是项目名或应用程序名;另一个地方是域名。

通过项目定义

如果您使用组织名作为顶级名称空间,并且想通过次级名称空间来进一步提供上下文,那么可以使用项目名或应用程序名作为次级名称空间。例如,如果您构建一个称为 Greeter 的新应用程序(用于获取用户的名称并问候他们),那么清单 5 中的名称空间将为称为 Prompt 的类提供完整的上下文。


清单 5. 使用应用程序名作为次级名称空间
				
<?php
namespace IBM\Greeter;
class Prompt {
...
}
?>

由于 Prompt 可能是多个应用程序或库的类名,所以为该名称空间添加组织名和项目名能够让这个 Prompt 类与其他同名的类区分开来。

通过域定义

使用域名是另一种选择次级名称空间的常见方式,如 清单 6 所示。它也可以用于项目名之后,是否使用取决于您对可重用性的计划(见 “根据可重用性命名”)。

是对更大的问题域的一组分类。域的一个例子是在更大型的应用程序中处理帐户、客户和产品的 “Account”、“Customers” 或 “Products”。


清单 6. 使用域作为次级名称空间
				
<?php
namespace IBM\MyApp\Account;
class Address {
...
}
?>







根据可重用性命名

除了应用支持可重用性的模块概念之外,类和名称空间的命名方式也能够实现可重用性。有时不良的命名方式会损害可重用性,因为不佳的名称暗示着类仅能用于特定目的。同样,错误地应用名称空间可能会不必要地局限类的使用范围,让它们的重用变得困难。

在 使用组织名的顶级名称空间中,应该保留 “Common”、“Core”、“Lib” 等可跨应用程序重用的名称空间。一个常见的例子是验证,其中针对整个企业的库存单位(SKU)、帐号或发票号的规则是一样的,从而获得合适的规则和长度。 对于 Validator 类,类似清单 7 的名称空间是不错的选择。


清单 7. 使用通用的 validation 名称空间
				
<?php
namespace MyCompany\Common\Validation;
class NotNullValidator {
...
}
?>

在这里,组织名用作顶级域(“MyCompany”)。“Common” 名称空间用作项目。即使在编写这个类的同时也许正在编写一个特定的应用程序,该类一样可以在组织的任何项目中使用。最后,“Validation” 用作类的域。







使用别名

尽管名称空间能够帮助您组织类并避免命名约定,但其缺点是名称过长。幸运的是,PHP 支持使用别名,因此可以在代码中使用更短的别名。清单 8 提供了一个示例。


清单 8. 使用别名
				
<?php
use MyCompany\Common\Validation as Validators;
?>







命名约定

名称空间命名使用单词首字母大写或 PASCAL 命名约定,这与其他 PHP 约定一样,比如 PHP Extension 和 Application Repository (PEAR) 包命名和文件名。例如,清单 9 中的名称空间比 清单 10 中的名称空间要好。


清单 9. 单词首字母大写或 PASCAL 命名
				
<?php
namespace MyNamespace;
?>

避免使用与其他 PHP 约定冲突的命名和大小写约定。


清单 10. 使用糟糕的大小写约定
				
<?php
namespace mynamespace;
...
?>





结束语

PHP 中的名称空间能够用于组织代码、避免命名冲突以及为类、函数和常量提供上下文。在名称空间中使用模式或约定让代码更易于理解,并且更易于引用和使用。

]]>
PHP 5.3.0 Released! http://www.phpv.net/html/1693.html http://www.phpv.net/html/1693.html#comment Wed, 01 Jul 2009 14:16:30 +0000 esayr http://www.phpv.net/html/1693.html 5.3.0 这就来了!
这个版本 release 得很自豪,是 PHP 5.* 系列的一个重大改进,包含了大量的新特性,修复了大量的BUG

新特性包括:命名空间,静态延迟绑定,闭包,可选的垃圾回收周期机制.
加了些 ext/phar, ext/intl   ext/fileinfo 的新扩展,修的BUG就超出140来个.

官方特意做了一个从PHP5.2迁移到PHP5.3的专题,点这里看
然后 ChangeLog 在这里.


最后,新版本点这里下载:



]]>
PHP 5.2.10 Released! http://www.phpv.net/html/1691.html http://www.phpv.net/html/1691.html#comment Fri, 19 Jun 2009 15:54:15 +0000 esayr http://www.phpv.net/html/1691.html
比如,bug #48378 (exif_read_data() 函数在处理某些损坏的jpeg文件时出错).

建议用户升级到最新版.

官方下载地址:



]]>
PHP V5.3 中的新特性第 5 部分: 从 PHP V5.2 升级到 PHP V5.3 http://www.phpv.net/html/1682.html http://www.phpv.net/html/1682.html#comment Wed, 15 Apr 2009 14:07:39 +0000 抽烟的蚊子 http://www.phpv.net/html/1682.html 2009 年 4 月 02 日

PHP V5.3 将于不久后发布。“PHP V5.3 中的新特性” 系列文章将持续介绍该发行版提供的令人兴奋的新特性。第 1 部分 介绍了 PHP 5.3 中对面向对象编程及对象处理所做的更改,第 2 部分 介绍了闭包函数及 lambda 函数。第 3 部分 探讨了名称空间,这是该 PHP 版本中最令人期待也是最具争议的特性之一。在 第 4 部分 中,我们进一步研究了 Phar,这是一种用于 PHP 的归档格式。在本系列的最后一部分中,了解从 PHP V5.2 升级到 PHP V5.3 时要考虑的一些事情。PHP V5.3 中的一些变化破坏了向后兼容性,有些特性在 PHP V5.3 中不受支持,在将来的版本中将被取消。本文还介绍 PHP V5.3 对 PHP 中一些已有特性的增强。

简介

本 系列着重介绍 PHP V5.3 中的新特性,例如名称空间、闭包、对象管理、面向对象编程和 Phar。虽然这些动人的新特性作为该语言的增补广受欢迎,但 PHP V5.3 同时也是为进一步优化 PHP 而设计的。它构建在流行、稳定的 PHP V5.2 的基础上,并对该语言作了增强,使之更加强大。在本文中,了解 PHP V5.3 中的变化,以及从 PHP V5.2 升级到 PHP V5.3 时需要考虑的一些事情。







语法变化

该语言新增了名称空间和闭包,增加了更多的保留字。从 PHP V5.3 开始,namespace 不再用作标识符。closure 类现在是一个保留类,但它仍然可以作为有效的标识符。清单 1 显示了一些例子,由于新增的保留字的缘故,有些语句在 PHP V5.3 中不再有效。


清单 1. 无效的 PHP 语句
				
// the function definition below will throw a fatal error in PHP 5.3, but is perfectly
// valid in 5.2
function namespace()
{
....
}

// same with this class definition
class Closure
{
....
}

PHP V5.3 中还增加了对 goto 语句的支持。现在,goto 是一个保留字。goto 语句在现代语言中不太常见(您也许记得在 BASIC 中如何使用 goto),但是有些情况下,goto 语句的确方便。清单 2 显示了一个如何使用 goto 语句的例子。


清单 2. PHP 中的 goto 语句
				
echo "This text will get outputted";
goto a;

echo "This text will get skipped";

a:
echo "This text will get outputted";

goto 的一个可能的用例是中断深度嵌套的循环和 if 语句。这将使代码阅读起来清晰很多。







函数和方法的变化

在 PHP V5.3 中,函数和方法没有大的变化,但还是有一些增强,以帮助解决 PHP 中的一些突出问题并提高性能。本节讨论一些较为显著的变化。

在之前版本的 PHP 中,数组函数 atsortnatcasesortusortuasortuksortarray_fliparray_unique 可以以参数形式传递对象而不是数组。然后,这些函数将对象的属性当做数组的键和值。PHP V5.3 中不再支持这一点,所以需要首先将对象转换成数组。清单 3 展示了如何修改代码。


清单 3. 为某些函数修改代码,将对象转换成数组
				
$obj = new stdClass;
$obj->a = '1';
$obj->b = '2';
$obj->c = '3';

print_r(array_flip($obj)); // will NOT work in PHP 5.3, but will in PHP 5.2

print_r(array_flip((array) $obj)); // will work in PHP 5.3 and 5.2

魔术类方法现在受到更严格的限制。下面的方法必须具有公共可见性:

  • __get
  • __set
  • __isset
  • __unset
  • __call

现在,当在静态上下文中使用 __call 时,为了应对上述变化,可以使用新的 __callStatic() 魔术方法。除了不接受参数的 __isString() 魔术方法以外,这些方法的必需参数都是强制性的,并且必须提供。清单 4 显示了如何使用这些方法以及它们的必需参数。


清单 4. 使用魔术方法
				
class Foo
{
public function __get($key) {} // must be public and have one parameter
public function __set($key,$val) {} // must be public and have two parameters

public function __toString() {} must be public and have no parameters
}

在 Windows 上,有些函数之前在 PHP 中不受支持,现在在 PHP V5.3 中获得支持。例如,getopt() 函数用于解析从命令行调用 PHP 脚本时使用的选项。用于编码和解码 Internet 地址的 inet_ntop()inet_pton() 函数,现在也可以在 Windows® 上使用。还有一些数学函数,例如 asinh()acosh()atanh()log1p()expm1(),现在在 Windows 上也受支持。







扩展的变化

PHP Extension C Library(PECL)一直都是 PHP 中的新扩展的来源地。当一个扩展已经成熟和稳定,并且被认为可以成为核心发行版中一个有用的功能,那么它通常在重大的版本变更时被添加进来。根据这条规 则,从 PHP V5.3 开始,下面的扩展会成为核心 PHP 发行版中的一部分。

FileInfo
提供帮助检测文件的内容类型和编码的函数,这些函数通过查看文件中的某些魔术字节字符序列进行检测。
intl
International Components for Unicode(ICU)库的一个包装器,提供用于 unicode 和全球化支持的函数。
Phar
第 4 部分 中讨论过的一个 PHP 归档工具。
mysqlnd
用于 MySQL 数据库访问的一个本地 PHP 驱动程序,是早期利用 libmysql 库的 MySQL 和 MySQLi 扩展的替代物。
SQLite3
用于使用 SQLite V3 数据库的一个库。

当一个扩展不再受到积极的维护,或者被认为不值得随核心 PHP 发行版一起发行时,它通常被转移到 PECL。在 PHP V5.3 的改造过程中,下面这些扩展被踢出核心 PHP 发行版,而放在 PECL 中进行维护。

ncurses
对 curses 的模拟,用于在命令行显示图形化的输出。
fpdf
用于在 PDF 文档中构建和使用表单和表单数据。
dbase
提供读写 dbase 兼容文件的支持。
fbsql
支持 Frontbase 数据库服务器上的数据库访问。
ming
一个开源库,用于创建 Flash 4 动画。

Sybase 扩展已经被完全移除,取而代之的是 sybase_ct 扩展。sybase_ct 扩展与前者完全兼容,应该是一个简易替代者(drop-in replacement)。这个更新的功能将使用 Sybase client 库,需要将这些库安装在 Web 服务器上。







构建的变化

PHP V5.3 着重改善了构建过程,因此更容易在所有平台上构建 PHP。为了维护 PHP 构建之间的一致性,并提供一组可靠的组件,在构建中不再禁用 PCRE、Reflection 和 SPL 扩展。现在,可以构建可分发的 PHP 应用程序,它们将使用这些扩展并且保证这些扩展是可用的。

一 个新的团队在去年接管了 PHP Windows 构建。这个小组将为 Windows 上的用户提供一些改进。新的构建将以 586 架构(Intel® Pentium® 或更高型号)为目标,并要求 Windows 2000/XP 或更高版本,另外去掉了对 Windows 98/NT 及之前版本的支持。将构建使用 Microsoft® Visual Studio® 2008 构建的 PHP 构建和针对 x86-64 架构的构建。当和 Microsoft IIS Web 服务器上的 FastCGI 或者和 Apache 一起使用时,在使用相同的编译器和架构进行构建的情况下,它们可以提供更高的性能。Windows 安装程序也将有所改进,以便更好地在 Microsoft IIS Web 服务器上配置 PHP。该团队专门为 Windows 上的 PHP 建立了一个网站(参见 参考资料)。







.ini 的变化

PHP 的一个重要特性是,可以使用 .ini 文件配置它的行为。在 PHP V5.3 中,与这个文件有关的一些有问题的指令已经被删除,例如 zend.ze1_compatibility_mode。现在,在使用这个文件时,灵活性有了巨大的提高。

对于 php.ini 文件有两个重大的改进:

  • 在 php.ini 文件中可以使用变量。这对于减少该文件的冗余非常方便,必要时更新文件也更加方便。清单 5 显示了一个例子。

    清单 5. php.ini 文件中的变量
    						 
    foo = bar

    [section]
    newfoo = ${bar}

    foonewfoo 有相同的值。
  • 和用 Apache 配置文件进行设置一样,可以进行 per-directory 和 per-site PHP ini 设置。这样做的优点是,在所有不同的可运行 PHP 的 SAPI 中,语法都是一致的。清单 6 显示了如何进行 PHP ini 设置。

    清单 6. per-site 和 per-directory .ini 设置
    						
    [PATH=/var/www/site1]

    ; directives here only apply to PHP files in the /var/www/site1 directory

    [HOST=www.example.com]

    ; directives here only apply to PHP files requested from the www.example.com site.

还 可以像对待 Apache HTTP Web 服务器上的 .htaccess 文件一样,在用户指定的、位于文件系统的 .ini 文件中创建这些 .ini 指令。这个文件的默认文件名由 user_ini.filename 指令指定。通过将这条指令设置为空值,可以禁用该特性。在用户指定的 .ini 文件中,任何 per-site 和 per-directory 指令都不能被覆盖。







放弃的特性

PHP V5.3 开始正式放弃一些较旧的函数,将来版本的 PHP 中将不再提供它们。当使用这些函数时,会遇到 E_DEPRECATED 错误。以下函数在 PHP V5.3 中被遗弃:

  • tick(declare(ticks=N)register_tick_function()),之前用于在 declare() 块中当解析器每执行 n 条语句时就进行一个函数调用。它们将被废除,因为它们的函数中有很多的中断,而且该特性不大常用。
  • define_syslog_variables(),该函数初始化所有与 syslog 相关的变量。该函数不是必需的,因为它定义的常量已经被全局定义。废除这个函数调用应该是有必要的。
  • ereg 正则表达式函数。建议使用 PCRE 正则表达式函数替代,因为它们更快,并且与其他语言和应用程序中使用的正则表达式更加一致。对 ereg 函数的支持将被废除,以使 PHP 可以标准化地使用一个正则表达式引擎。

建议在迁移到 PHP V5.3 时移除这些特性。将来主要的 PHP 发行版将取消对上述特性的支持。







结束语

PHP V5.3 有很多新的特性,同时也 “清除” 了一些内容。另外也存在一些向后兼容的问题。本文为将 Web 应用程序迁移到 PHP V5.3 提供了一些指南。要了解最新的关于 PHP V5.3 的详细信息,请参阅 PHP wiki,其中提供了可能影响到应用程序的任何其他变化的说明。

]]>
测试一下你是不是伪PHP程序员 http://www.phpv.net/html/1673.html http://www.phpv.net/html/1673.html#comment Mon, 02 Mar 2009 23:39:14 +0000 抽烟的蚊子 http://www.phpv.net/html/1673.html 应该具有什么样的技能,才算得上PHP程序员?你的技术又是在什么水平级别上? CU上的BS同学写了以下文章。欢迎各位对号入座。


1、PHP编程能力

说不清,留空


2、MySQL能力

在开发上的应用基于几个能力体现:
1、了解:知道用PHP连接数据库;懂得写一些简单的SQL;建一些简单的索引;懂得用工具简单操作一下数据库(增删改库表结构数据等等)。
2、熟悉:懂得在开发应用上设计数据库,建立一些有效的索引,用explain分析SQL性能,压力测试等等。
3、很熟悉:深入了解数据库索引、存储引擎原理以及运行机制,能有效地构建高性能可扩展的数据库结构/架构,有效地优化数据库性能配置并加以调试,分析数据库运行状态。
4、精通:简单地说具备以上所有能力的同时,有多年高负载分布式环境下的优化管理经验。


据我观察以及交往经验,70%的PHPer处在了解阶段,25%处于熟悉阶段,>4%很熟悉,精通的人基本就不是phper了。

70%这个群体最容易忽视MySQL,以为MySQL只是简单的存储媒介,没有优化意识,认为加个内存、CPU就能解决问题。
典型事件:join、order by、group by等语句性能一塌糊涂,数据库根本没有设计(仅限于拆成一个主表,N个附表等),搞不清字段类型及作用,碰到大表的复杂查询就没辙。


20%这个群体的人只是MySQL运行机制理解不透彻,对影响MySQL性能的关健因素把握不明确,不熟练。
典型事件:熟读手册,但说不清索引原理,不知道二叉树、HASH等算法对于数据库的作用


>4%的群体已经基本可以胜任DBA的职能。


3、OOP能力
1、了解:了解变量的作用域、类型,及其意义,了解继承机制等,懂得复用、封装概念。
2、熟悉:熟练应用接口、抽象等技术混合开发程序,并理解其中含义,一般研究过JAVA。
3、很熟悉:有过OOP架构设计经验,熟悉设计模式、UML,熟悉PHP对象运行机制,内容管理等。
4、精通:应该是架构师级别了,不限于PHP。

经常我们会碰到一些自称熟悉OOP却连public、private、protected、static都解释不清的人,是肯定没有经历过正规的OOP项目。

4、大型网站经验
1、了解:熟悉PHP开发下的缓存应用(memcache、APC等);接触过LVS、SQUID应用;
有一定的session处理方案;熟悉负载均衡;熟悉PHP数据连接池应用;了解PHP编程性能优化。
2、熟悉:掌握分布式缓存及缓存性能优化、熟悉存储系统、文件系统、数据库,开发可扩展平台。能结合负载均衡合理布置流量,对PHP运行性能进行监控与分析。
3、非常熟悉:具备系统分析师能力,已经超出phper环节...
4、精通:太深奥..


5、DOM开发能力
留空

6、*nux应用能力
...

以上只是个人拙见,仅希望能够以此抛砖引玉,希望大牛加以补充或更正。

有话想说?请在下方留言

]]>
PHP V5.3新特性 第1部分: 对象接口的变化 http://www.phpv.net/html/1647.html http://www.phpv.net/html/1647.html#comment Wed, 10 Dec 2008 15:30:56 +0000 admin http://www.phpv.net/html/1647.html


PHP V5 和面向对象编程

与 PHP V4 提供的特性相比,2004 年发布的 PHP V5 在面向对象编程(OOP)和设计方面向前迈出了很大的一步。它提供了一些必要的改进,例如类可见性、合适的构造函数和解构函数、输入提示和类反射 (class-reflection)API。它为在 PHP 中进行高级的面向对象编程敞开了大门,并允许实现更加简单的设计模式,以及更好的设计类和 API。

PHP V5.3 在 OOP 方面提供了大量渐进式补充。这些改进一直集中在语法补充和性能改进方面。首先,我们将查看静态方法和成员方面的新特性。


改进静态方法和成员处理

PHP V5 中的一个有用补充就是能够将一个方法或类成员指定为静态的(PHP V4 确实支持对方法和类成员的静态访问,但是不能够将方法或成员指定为专门用于静态访问)。静态访问特别适合实现单一设计模式,在这种模式中只存在一个类实例。

PHP V5.3 提供一些特性来增强对类的静态成员和方法的支持。我们将查看最近添加的一种魔术方法:__callStatic()

_callStatic() 魔术方法

PHP V5 提供了一些可用于类内部的特别定义的方法,称为魔术方法。当在类内部定义时,这些方法可以提供特殊的功能,并支持重载(允许一种方法接受不同类型的参数)和多态(允许不同数据类型使用相同的接口)。它们还允许通过 PHP 轻松地使用不同类型的 OOP 编程方法和设计模式。

在 PHP V5.3 中,添加了一种新的魔术方法:__callStatic()。它的工作方式类似于 __call() 魔术方法,后者的设计意图是处理那些没有在类中定义或对类不可见的方法的调用。然而,__callStatic() 是为了处理静态方法调用,这使我们能够更好地设计方法重载。下面给出了一个使用该方法的示例。


清单 1. 使用 __callStatic()__call() 的示例
class Foo 
{
public static function __callStatic(
$name,
$args
)
{
echo "Called method $name statically";
}

public function __call(
$name,
$args
)
{
echo "Called method $name";
}
}

Foo::dog(); // outputs "Called method dog statically"
$foo = new Foo;
$foo->dog(); // outputs "Called method dog"
需要注意,PHP 确实加强了对 __callStatic() 方法的定义;它必须是公共的,并且必须被声明为静态的。同样,__call() 魔术方法必须被定义为公共的,所有其他魔术方法都必须如此。

动态的静态调用

PHP 的一个优秀特性是可变变量。这表示可以使用某个变量的字符串值指定另一个变量的名称。换句话说,可以执行与下面类似的操作。


清单 2. 可变变量
x = 'y'; 
$$x = 'z';
echo $x; // outputs 'y'
echo $$x; //
outputs 'z'


这也适用于函数,甚至是类方法,如下所示。

清单 3. 可变函数和类方法名
class Dog
{
public function bark()
{
echo "Woof!";
}
}

$class = 'Dog'
$action = 'bark';
$x = new $class(); // instantiates the class 'Dog'
$x->$action(); // outputs "Woof!"


PHP V5.3 的一个新特性就是在进行静态调用时,能够使指定的类名成为一个变量。这提供了一些新的机会,如下所示。


清单 4. 可变的类命名
class Dog 
{
public static function bark()
{
echo "Woof!";
}
}

$class = 'Dog';
$action = 'bark';
$class::$action(); //outputs "Woof!"

这一补充完善了 PHP 的可变变量特性,允许将它们应用到涉及 PHP 的所有情形。

让我们查看一个有关静态方法和成员应用的更有用的增强:延迟静态绑定(late static binding)。


请点击下一页>>



]]>
PHP 5.2.7 发布 下载地址 [更新:PHP 5.2.8 紧急发布] http://www.phpv.net/html/1646.html http://www.phpv.net/html/1646.html#comment Thu, 04 Dec 2008 19:44:34 +0000 admin http://www.phpv.net/html/1646.html 在5.2.7中发现了一个magic_quotes_gpc的严重bug.所以发布了5.2.8.请大家下载5.2.8的版本.

linux系统点这里下载

windows系统点这里下载

--------------------------------------------------
经过5个RC版的开发,PHP 5.2.7于2008-12-04发布

离上一个版本2008-05-01的5.2.6过了7个多月,这个版本修正了大量的bug和安全漏洞,官方建议所有PHP用户升级到这个版本。




]]>
PHP5.3 新特性之"命名空间"介绍 http://www.phpv.net/html/1621.html http://www.phpv.net/html/1621.html#comment Wed, 06 Aug 2008 21:30:22 +0000 esayr http://www.phpv.net/html/1621.html 命名空间(namespace)。

这一特性在 PHP5.0x 时候就提出过,后来被取消并安排在 PHP6 中实现。而此次又再次“提前”到了 PHP5.3 发布,可见开发人员对其的重视以及谨慎的态度。

官方发布时说明文档的内容可能已过期(documentation maybe out dated),所以在这里简单的说明命名空间的用法:首先是声明一个命名空间,加入了新的关键字 namespace ,其应在类文件的开头

<?php
namespace Project::Module;

class User {
const STATUS_OK = true;

function register($data) {
...
}

...
}

然后在控制器中(可能是其他文件)就可以这样调用

$user = new Project::Module::User(); 
$user->register($register_info);

的确与平常的并无两样,但是我们可以将两个相互独立的类联系起来。比如

Project::Module::User; 
Project::Module::Blog;

这样就能从语言本身更容易描述和理解变量、类之间的关系,从而避免了“传统”上的 Project_Module_Blog 这样冗长的命名方式。

上面的说明可能很难说明使用命名空间带来了什么好处,新增加的 use 和 as 关键字或许能更好的说明问题。use 和 as 语句可以引用和声明 命名空间的“别名”。比如,上述的控制器中实例化类的代码可以这样写

use Project::Module;
$user = new Module::User();
$user->register($register_info);

甚至

use Project::Module::User as ModuleUser;
$user = new ModuleUser;
$user->register($register_info);

类中的常量也可以通过命名空间访问,比如上述类中的 STATUS_OK 就可以通过命名空间

Project::Module::User::STATUS_OK

访问。进一步的,也可以用别名简化那么长的“变量名称”

use Project::Module::User::STATUS_OK as STATUS_OK;
echo STATUS_OK;

顺便提下“超空间(The Global Namespace)”的概念。所谓的“超空间”,就是没有指定命名空间的变量、类和函数。比如

function foo() {
...
}

这的函数,可以使用 foo() 执行的同时,也可以使用 ::foo(); 这样执行。

最后,配合使用 autoload 函数即可载入指定命名空间的类。简单的函数如下

function __autoload( $classname ) {
$classname = strtolower( $classname );
$classname = str_replace( '::', DIRECTORY_SEPARATOR, $classname );
require_once( dirname( __FILE__ ) . '/' . $classname . '.class.php' );
}

这样,比如调用

__autoload('Project::Module::User');

就可以自动载入 Project_Module_User.class.php 文件(虽然这样看起来并不方便多少)。

]]>
PHP 5.2.6 发布 http://www.phpv.net/html/1595.html http://www.phpv.net/html/1595.html#comment Fri, 02 May 2008 12:56:58 +0000 抽烟的蚊子 http://www.phpv.net/html/1595.html 修复4个安全方面的问题,把 PCRE 更新到了版本 7.6
同时还修复了130多处BUG

推荐有需要的用户更新.

linux 系统点这里下载
WINDOWS安装包点这里下载   WINDOWS ZIP 解压包版本点这里下载

需要其它版本,请到官方去下载


以下是Version 5.2.6的ChangeLog:(英文)

  • Security Fixes
    • Fixed possible stack buffer overflow in FastCGI SAPI. (Andrei Nigmatulin)
    • Properly address incomplete multibyte chars inside escapeshellcmd() (Ilia, Stefan Esser)
    • Fixed security issue detailed in CVE-2008-0599. (Rasmus)
    • Fixed a safe_mode bypass in cURL identified by Maksymilian Arciemowicz. (Ilia)
    • Upgraded PCRE to version 7.6 (Nuno)
  • Fixed two possible crashes inside posix extension (Tony)
  • Fixed incorrect heredoc handling when label is used within the block. (Matt)
  • Fixed sending of uninitialized paddings which may contain some information. (Andrei Nigmatulin)
  • Fixed a bug in formatting timestamps when DST is active in the default timezone (Derick)
  • Fix integer overflow in printf(). (Stas, Maksymilian Aciemowicz)
  • Fixed potential memleak in stream filter parameter for zlib filter. (Greg)
  • Added Reflection API metadata for the methods of the DOM classes. (Sebastian)
  • Fixed weird behavior in CGI parameter parsing. (Dmitry, Hannes Magnusson)
  • Fixed a bug with PDO::FETCH_COLUMN|PDO::FETCH_GROUP mode when a column # by which to group by data is specified. (Ilia)
  • Fixed segfault in filter extension when using callbacks. (Arnar Mar Sig, Felipe)
  • Fixed faulty fix for bug Fixed bug #40189 (endless loop in zlib.inflate stream filter). (Greg)
  • Fixed bug #44742 (timezone_offset_get() causes segmentation faults). (Derick)
  • Fixed bug #44720 (Prevent crash within session_register()). (Scott)
  • Fixed bug #44703 (htmlspecialchars() does not detect bad character set argument). (Andy Wharmby)
  • Fixed bug #44673 (With CGI argv/argc starts from arguments, not from script) (Dmitry)
  • Fixed bug #44667 (proc_open() does not handle pipes with the mode 'wb' correctly). (Jani)
  • Fixed bug #44663 (Crash in imap_mail_compose if "body" parameter invalid). (Ilia)
  • Fixed bug #44650 (escapeshellscmd() does not check arg count). (Ilia)
  • Fixed bug #44613 (Crash inside imap_headerinfo()). (Ilia, jmessa)
  • Fixed bug #44603 (Order issues with Content-Type/Length headers on POST). (Ilia)
  • Fixed bug #44594 (imap_open() does not validate # of retries parameter). (Ilia)
  • Fixed bug #44591 (imagegif's filename parameter). (Felipe)
  • Fixed bug #44557 (Crash in imap_setacl when supplied integer as username) (Thomas Jarosch)
  • Fixed bug #44487 (call_user_method_array issues a warning when throwing an exception). (David Soria Parra)
  • Fixed bug #44478 (Inconsistent behaviour when assigning new nodes). (Rob, Felipe)
  • Fixed bug #44445 (email validator does not handle domains starting/ending with a -). (Ilia)
  • Fixed bug #44440 (st_blocks undefined under BeOS). (Felipe)
  • Fixed bug #44394 (Last two bytes missing from output). (Felipe)
  • Fixed bug #44388 (Crash inside exif_read_data() on invalid images) (Ilia)
  • Fixed bug #44373 (PDO_OCI extension compile failed). (Felipe)
  • Fixed bug #44333 (SEGFAULT when using mysql_pconnect() with client_flags). (Felipe)
  • Fixed bug #44306 (Better detection of MIPS processors on Windows). (Ilia)
  • Fixed bug #44242 (metaphone('CMXFXM') crashes PHP). (Felipe)
  • Fixed bug #44233 (MSG_PEEK undefined under BeOS R5). (jonathonfreeman at gmail dot com, Ilia)
  • Fixed bug #44216 (strftime segfaults on large negative value). (Derick)
  • Fixed bug #44209 (strtotime() doesn't support 64 bit timestamps on 64 bit platforms). (Derick)
  • Fixed bug #44206 (OCI8 selecting ref cursors leads to ORA-1000 maximum open cursors reached). (Oracle Corp.)
  • Fixed bug #44200 (A crash in PDO when no bound targets exists and yet bound parameters are present). (Ilia)
  • Fixed bug #44197 (socket array keys lost on socket_select). (Felipe)
  • Fixed bug #44191 (preg_grep messes up array index). (Felipe)
  • Fixed bug #44189 (PDO setAttribute() does not properly validate values for native numeric options). (Ilia)
  • Fixed bug #44184 (Double free of loop-variable on exception). (Dmitry)
  • Fixed bug #44171 (Invalid FETCH_COLUMN index does not raise an error). (Ilia)
  • Fixed bug #44166 (Parameter handling flaw in PDO::getAvailableDrivers()). (Ilia)
  • Fixed bug #44159 (Crash: $pdo->setAttribute(PDO::STATEMENT_ATTR_CLASS, NULL)). (Felipe)
  • Fixed bug #44152 (Possible crash with syslog logging on ZTS builds). (Ilia)
  • Fixed bug #44141 (private parent constructor callable through static function). (Dmitry)
  • Fixed bug #44113 (OCI8 new collection creation can fail with OCI-22303). (Oracle Corp.)
  • Fixed bug #44069 (Huge memory usage with concatenation using . instead of .=). (Dmitry)
  • Fixed bug #44046 (crash inside array_slice() function with an invalid by-ref offset). (Ilia)
  • Fixed bug #44028 (crash inside stream_socket_enable_crypto() when enabling encryption without crypto type). (Ilia)
  • Fixed bug #44018 (RecursiveDirectoryIterator options inconsistancy). (Marcus)
  • Fixed bug #44008 (OCI8 incorrect usage of OCI-Lob->close crashes PHP). (Oracle Corp.)
  • Fixed bug #43998 (Two error messages returned for incorrect encoding for mb_strto[upper|lower]). (Rui)
  • Fixed bug #43994 (mb_ereg 'successfully' matching incorrect). (Rui)
  • Fixed bug #43954 (Memory leak when sending the same HTTP status code multiple times). (Scott)
  • Fixed bug #43927 (koi8r is missing from html_entity_decode()). (andy at demos dot su, Tony)
  • Fixed bug #43912 (Interbase column names are truncated to 31 characters). (Ilia)
  • Fixed bug #43875 (Two error messages returned for $new and $flag argument in mysql_connect()). (Hannes)
  • Fixed bug #43863 (str_word_count() breaks on cyrillic "ya" in locale cp1251). (phprus at gmail dot com, Tony)
  • Fixed bug #43841 (mb_strrpos offset is byte count for negative values). (Rui)
  • Fixed bug #43840 (mb_strpos bounds check is byte count rather than a character count). (Rui)
  • Fixed bug #43808 (date_create never fails (even when it should)). (Derick)
  • Fixed bug #43793 (zlib filter is unable to auto-detect gzip/zlib file headers). (Greg)
  • Fixed bug #43703 (Signature compatibility check broken). (Dmitry)
  • Fixed bug #43677 (Inconsistent behaviour of include_path set with php_value). (manuel at mausz dot at)
  • Fixed bug #43663 (Extending PDO class with a __call() function doesn't work). (David Soria Parra)
  • Fixed bug #43647 (Make FindFile use PATH_SEPARATOR instead of ";"). (Ilia)
  • Fixed bug #43635 (mysql extension ingores INI settings on NULL values passed to mysql_connect()). (Ilia)
  • Fixed bug #43620 (Workaround for a bug inside libcurl 7.16.2 that can result in a crash). (Ilia)
  • Fixed bug #43614 (incorrect processing of numerical string keys of array in arbitrary serialized data). (Dmitriy Buldakov, Felipe)
  • Fixed bug #43606 (define missing depencies of the exif extension). (crrodriguez at suse dot de)
  • Fixed bug #43589 (a possible infinite loop in bz2_filter.c). (Greg)
  • Fixed bug #43580 (removed bogus declaration of a non-existent php_is_url() function). (Ilia)
  • Fixed bug #43559 (array_merge_recursive() doesn't behave as expected with duplicate NULL values). (Felipe, Tony)
  • Fixed bug #43533 (escapeshellarg('') returns null). (Ilia)
  • Fixed bug #43527 (DateTime created from a timestamp reports environment timezone). (Derick)
  • Fixed bug #43522 (stream_get_line() eats additional characters). (Felipe, Ilia, Tony)
  • Fixed bug #43507 (SOAPFault HTTP Status 500 - would like to be able to set the HTTP Status). (Dmitry)
  • Fixed bug #43505 (Assign by reference bug). (Dmitry)
  • Fixed bug #43498 (file_exists() on a proftpd server got SIZE not allowed in ASCII mode). (Ilia, crrodriguez at suse dot de)
  • Fixed bug #43497 (OCI8 XML/getClobVal aka temporary LOBs leak UGA memory). (Chris)
  • Fixed bug #43495 (array_merge_recursive() crashes with recursive arrays). (Ilia)
  • Fixed bug #43493 (pdo_pgsql does not send username on connect when password is not available). (Ilia)
  • Fixed bug #43491 (Under certain conditions, file_exists() never returns). (Dmitry)
  • Fixed bug #43483 (get_class_methods() does not list all visible methods). (Dmitry)
  • Fixed bug #43482 (array_pad() does not warn on very small pad numbers). (Ilia)
  • Fixed bug #43457 (Prepared statement with incorrect parms doesn't throw exception with pdo_pgsql driver). (Ilia)
  • Fixed bug #43450 (Memory leak on some functions with implicit object __toString() call). (David C.)
  • Fixed bug #43386 (array_globals not reset to 0 properly on init). (Ilia)
  • Fixed bug #43377 (PHP crashes with invalid argument for DateTimeZone). (Ilia)
  • Fixed bug #43373 (pcntl_fork() should not raise E_ERROR on error). (Ilia)
  • Fixed bug #43364 (recursive xincludes don't remove internal xml nodes properly). (Rob, patch from ddb@bitxtender.de)
  • Fixed bug #43301 (mb_ereg*_replace() crashes when replacement string is invalid PHP expression and 'e' option is used). (Jani)
  • Fixed bug #43295 (crash because of uninitialized SG(sapi_headers).mimetype). (Dmitry)
  • Fixed bug #43293 (Multiple segfaults in getopt()). (Hannes)
  • Fixed bug #43279 (pg_send_query_params() converts all elements in 'params' to strings). (Ilia)
  • Fixed bug #43276 (Incomplete fix for bug #42739, mkdir() under safe_mode). (Ilia)
  • Fixed bug #43248 (backward compatibility break in realpath()). (Dmitry)
  • Fixed bug #43221 (SimpleXML adding default namespace in addAttribute). (Rob)
  • Fixed bug #43216 (stream_is_local() returns false on "file://"). (Dmitry)
  • Fixed bug #43201 (Crash on using uninitialized vals and __get/__set). (Dmitry)
  • Fixed bug #43182 (file_put_contents() LOCK_EX does not work properly on file truncation). (Ilia)
  • Fixed bug #43175 (__destruct() throwing an exception with __call() causes segfault). (Dmitry)
  • Fixed bug #43128 (Very long class name causes segfault). (Dmitry)
  • Fixed bug #43105 (PHP seems to fail to close open files). (Hannes)
  • Fixed bug #43092 (curl_copy_handle() crashes with > 32 chars long URL). (Jani)
  • Fixed bug #43003 (Invalid timezone reported for DateTime objects constructed using a timestamp). (Derick)
  • Fixed bug #42978 (mismatch between number of bound params and values causes a crash in pdo_pgsql). (Ilia)
  • Fixed bug #42945 (preg_split() swallows part of the string). (Nuno)
  • Fixed bug #42937 (__call() method not invoked when methods are called on parent from child class). (Dmitry)
  • Fixed bug #42841 (REF CURSOR and oci_new_cursor() crash PHP). (Chris)
  • Fixed bug #42838 (Wrong results in array_diff_uassoc) (Felipe)
  • Fixed bug #42779 (Incorrect forcing from HTTP/1.0 request to HTTP/1.1 response). (Ilia)
  • Fixed bug #42736 (xmlrpc_server_call_method() crashes). (Tony)
  • Fixed bug #42692 (Procedure 'int1' not present with doc/lit SoapServer). (Dmitry)
  • Fixed bug #42548 (mysqli PROCEDURE calls can't return result sets). (Hartmut)
  • Fixed bug #42505 (new sendmail default breaks on Netware platform) (Guenter Knauf)
  • Fixed bug #42369 (Implicit conversion to string leaks memory). (David C., Rob).
  • Fixed bug #42272 (var_export() incorrectly escapes char(0)). (Derick)
  • Fixed bug #42261 (Incorrect lengths for date and boolean data types). (Ilia)
  • Fixed bug #42190 (Constructing DateTime with TimeZone Indicator invalidates DateTimeZone). (Derick)
  • Fixed bug #42177 (Warning "array_merge_recursive(): recursion detected" comes again...). (Felipe)
  • Fixed bug #41941 (oci8 extension not lib64 savvy). (Chris)
  • Fixed bug #41828 (Failing to call RecursiveIteratorIterator::__construct() causes a sefault). (Etienne)
  • Fixed bug #41599 (setTime() fails after modify() is used). (Derick)
  • Fixed bug #41562 (SimpleXML memory issue). (Rob)
  • Fixed bug #40013 (php_uname() does not return nodename on Netware (Guenter Knauf)
  • Fixed bug #38468 (Unexpected creation of cycle). (Dmitry)
  • Fixed bug #32979 (OpenSSL stream->fd casts broken in 64-bit build) (stotty at tvnet dot hu)

]]>
PHP PDO 学习笔记 http://www.phpv.net/html/1579.html http://www.phpv.net/html/1579.html#comment Tue, 13 Nov 2007 09:36:07 +0000 抽烟的蚊子 http://www.phpv.net/html/1579.html ■PDO为何物?
POD(PHP Data Object)扩展在PHP5中加入,PHP6中将默认识用PDO连接数据库,所有非PDO扩展将会在PHP6被从扩展中移除。该扩展提供PHP内置类 PDO来对数据库进行访问,不同数据库使用相同的方法名,解决数据库连接不统一的问题。
我是配置在windows下做开发用的。

■PDO的目标
  • 提供一种轻型、清晰、方便的 API
  • 统一各种不同 RDBMS 库的共有特性,但不排除更高级的特性。
  • 通过 PHP 脚本提供可选的较大程度的抽象/兼容性。
  • ■PDO的特点:
  • 性能。PDO 从一开始就吸取了现有数据库扩展成功和失败的经验教训。因为 PDO 的代码是全新的,所以我们有机会重新开始设计性能,以利用 PHP 5 的最新特性。
  • 能力。PDO 旨在将常见的数据库功能作为基础提供,同时提供对于 RDBMS 独特功能的方便访问。
  • 简单。PDO 旨在使您能够轻松使用数据库。API 不会强行介入您的代码,同时会清楚地表明每个函数调用的过程。
  • 运行时可扩展。PDO 扩展是模块化的,使您能够在运行时为您的数据库后端加载驱动程序,而不必重新编译或重新安装整个 PHP 程序。例如,PDO_OCI 扩展会替代 PDO 扩展实现 Oracle 数据库 API。还有一些用于 MySQL、PostgreSQL、ODBC 和 Firebird 的驱动程序,更多的驱动程序尚在开发。

    ■安装PDO
    我这里是WINDOWS下开发用的PDO扩展,要是你要在Linux下安装配置,请到别的地方寻找。
    版本要求:
    php5.1以及以后版本的程序包里已经带了;
    php5.0.x则要到pecl.php.net下载,放到你的扩展库,就是PHP所在的文件夹的ext文件夹下;
    手册上说5.0之前的版本不能运行PDO扩展。
     
    配置:
    修改你的php.ini配置文件,使它支持pdo.(php.ini这个东西没有弄懂的话,先弄清楚,要修改调用你的phpinfo()函数所显示的那个php.ini)
    extension=php_pdo.dll前面的分号去掉,分毫是php配置文件注释符号,这个扩展是必须的。
    往下还有
    ;extension=php_pdo.dll
    ;extension=php_pdo_firebird.dll
    ;extension=php_pdo_informix.dll
    ;extension=php_pdo_mssql.dll
    ;extension=php_pdo_mysql.dll
    ;extension=php_pdo_oci.dll
    ;extension=php_pdo_oci8.dll
    ;extension=php_pdo_odbc.dll
    ;extension=php_pdo_pgsql.dll
    ;extension=php_pdo_sqlite.dll
    各各扩展所对应的数据库是:
    Driver name Supported databases
    PDO_DBLIB FreeTDS / Microsoft SQL Server / Sybase
    PDO_FIREBIRD Firebird/Interbase 6
    PDO_INFORMIX IBM Informix Dynamic Server
    PDO_MYSQL MySQL 3.x/4.x
    PDO_OCI Oracle Call Interface
    PDO_ODBC ODBC v3 (IBM DB2, unixODBC and win32 ODBC)
    PDO_PGSQL PostgreSQL
    PDO_SQLITE SQLite 3 and SQLite 2
    你要使用哪种数据库,只要把相应的扩展前的注释符号";"去掉就可以了。

    使用PDO
    我这里假设你已经装好mysql了,要是没装的话,麻烦先想办法装上,我的是mysql5.0.22,黑夜路人用的是MySQL 4.0.26也可以用。

    数据库的连接:
    我们通过下面的例子来分析PDO连接数据库,

    <?php
    $dbms='mysql';    
    //数据库类型 Oracle 用ODI,对于开发者来说,使用不同的数据库,只要改这个,不用记住那么多的函数了
    $host='localhost';
    //数据库主机名
    $dbName='test';   
    //使用的数据库
    $user='root';     
    //数据库连接用户名
    $pass='';         
    //对应的密码
    $dsn="$dbms:host=$host;dbname=$dbName";
    //

    try {
        $dbh = new PDO($dsn, $user, $pass);
    //初始化一个PDO对象,就是创建了数据库连接对象$dbh
        echo "连接成功<br/>";
        /*你还可以进行一次搜索操作

        foreach ($dbh->query('SELECT * from FOO') as $row) {
            print_r($row);
    //你可以用 echo($GLOBAL); 来看到这些值
        }
        */

        $dbh = null;
    } catch (PDOException $e) {
        die ("Error!: " . $e->getMessage() . "<br/>");
    }
    //默认这个不是长连接,如果需要数据库长连接,需要最后加一个参数:array(PDO::ATTR_PERSISTENT => true) 变成这样:
    $db = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true));

    ?>


    数据库查询:
    上面我们已经进行了一次查询,我们还可以使用如下的查询:

    <?php
    $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); //设置属性
    $rs = $db->query("SELECT * FROM foo");
    $rs->setFetchMode(PDO::FETCH_ASSOC);
    $result_arr = $rs->fetchAll();
    print_r($result_arr);
    ?>


    以上因为用到setAttribute()方法,放上那两个参数,把字段名强制转换成大写。下面列出多有PDO::setAttribute()的参数:

    • PDO::ATTR_CASE: 强制列名变成一种格式,详细如下(第二个参数):

      • PDO::CASE_LOWER: 强制列名是小写.

      • PDO::CASE_NATURAL: 列名按照原始的方式

      • PDO::CASE_UPPER: 强制列名为大写.

    • PDO::ATTR_ERRMODE: 错误提示.

      • PDO::ERRMODE_SILENT: 不显示错误信息,只显示错误码.

      • PDO::ERRMODE_WARNING: 显示警告错误.

      • PDO::ERRMODE_EXCEPTION: 抛出异常.

    • PDO::ATTR_ORACLE_NULLS (不仅仅是ORACLE有效,别的数据库也有效): )指定数据库返回的NULL值在php中对应的数值

      • PDO::NULL_NATURAL: 不变.

      • PDO::NULL_EMPTY_STRING: Empty string is converted to NULL.

      • PDO::NULL_TO_STRING: NULL is converted to an empty string.

    • PDO::ATTR_STRINGIFY_FETCHES: Convert numeric values to strings when fetching. Requires bool.

    • PDO::ATTR_STATEMENT_CLASS: Set user-supplied statement class derived from PDOStatement. Cannot be used with persistent PDO instances. Requires array(string classname, array(mixed constructor_args)).

    • PDO::ATTR_AUTOCOMMIT (available in OCI, Firebird and MySQL): Whether to autocommit every single statement.

    • PDO::MYSQL_ATTR_USE_BUFFERED_QUERY (available in MySQL): Use buffered queries.

    例子中的$rs->setFetchMode(PDO::FETCH_ASSOC);PDOStatement::setFetchMode(),对返回类型的声明。
    有如下:
    PDO::FETCH_ASSOC
    -- 关联数组形式
    PDO::FETCH_NUM   -- 数字索引数组形式
    PDO::FETCH_BOTH  -- 两者数组形式都有,这是缺省的
    PDO::FETCH_OBJ   -- 按照对象的形式,类似于以前的 mysql_fetch_object()

    更多返回类型声明(PDOStatement::方法名)看手册。

    ★插入,更新,删除数据,

    $db->exec("DELETE FROM `xxxx_menu` where mid=43");


  • 简单的总结一下上面的操作:

    查询操作主要是PDO::query()PDO::exec()PDO::prepare()
    PDO::query()主要是用于有记录结果返回的操作,特别是SELECT操作,
    PDO::exec()主要是针对没有结果集合返回的操作,比如INSERT、UPDATE、DELETE等操作,它返回的结果是当前操作影响的列数。
    PDO::prepare()主要是预处理操作,需要通过$rs->execute()来执行预处理里面的SQL语句,这个方法可以绑定参数,功能比较强大,不是本文能够简单说明白的,大家可以参考手册和其他文档。

    获取结果集操作主要是:PDOStatement::fetchColumn()PDOStatement::fetch()PDOStatement::fetchALL()
    PDOStatement::fetchColumn() 是获取结果指定第一条记录的某个字段,缺省是第一个字段。
    PDOStatement::fetch() 是用来获取一条记录,
    PDOStatement::fetchAll()是获取所有记录集到一个中,获取结果可以通过PDOStatement::setFetchMode来设置需要结果集合的类型。

    另外有两个周边的操作,一个是PDO::lastInsertId()PDOStatement::rowCount()PDO::lastInsertId()是返回上次插入操作,主键列类型是自增的最后的自增ID。
    PDOStatement::rowCount()
    主要是用于PDO::query()PDO::prepare()进行DELETE、INSERT、UPDATE操作影响的结果集,对PDO::exec()方法和SELECT操作无效。



    事务和自动提交

        至此,您已经通过 PDO 连接到了 mysql,在发出查询之前,您应该理解 PDO 是如何管理事务的。如果之前没有接触过事务,那么首先要知道事务的 4 个特征:原子性(Atomicity)、一致性(Consistency)、独立性(Isolation)和持久性(Durability),即 ACID。用外行人的话说,对于在一个事务中执行的任何工作,即使它是分阶段执行的,也一定可以保证该工作会安全地应用于数据库,并且在工作被提交时,不 会受到来自其他连接的影响。事务性工作可以根据请求自动撤销(假设您还没有提交它),这使得脚本中的错误处理变得更加容易。
        事务通常是通过把一批更改积蓄起来、使之同时生效而实现的。这样做的好处是可以大大提高这些更新的效率。换句话说,事务可以使脚本更快,而且可能更健壮(不过需要正确地使用事务才能获得这样的好处)。
        不幸的是,并不是每种数据库都支持事务(Mysql5支持事务,mysql4我不知道),所以当第一次打开连接时,PDO 需要在所谓的“自动提交(auto-commit)”模式下运行。自动提交模式意味着,如果数据库支持事务,那么您所运行的每一个查询都有它自己的隐式事 务,如果数据库不支持事务,每个查询就没有这样的事务。如果您需要一个事务,那么必须使用 PDO::beginTransaction() 方法来启动一个事务。如果底层驱动程序不支持事务,那么将会抛出一个 PDOException(无论错误处理设置是怎样的:这总是一个严重错误状态)。在一个事务中,可以使用 PDO::commit() 或 PDO::rollBack() 来结束该事务,这取决于事务中运行的代码是否成功。
        当脚本结束时,或者当一个连接即将被关闭时,如果有一个未完成的事务,那么 PDO 将自动回滚该事务。这是一种安全措施,有助于避免在脚本非正常结束时出现不一致的情况 —— 如果没有显式地提交事务,那么假设有某个地方会出现不一致,所以要执行回滚,以保证数据的安全性。

    //例子来自http://www.ibm.com/developerworks/cn/db2/library/techarticles/dm-0505furlong/index.html
    try {
      $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2',
          array(PDO_ATTR_PERSISTENT => true));
      echo "Connected\n";
      $dbh->setAttribute(PDO_ATTR_ERRMODE, PDO_ERRMODE_EXCEPTION);
      $dbh->beginTransaction();
      $dbh->exec("insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')");
      $dbh->exec(
    "insert into salarychange (id, amount, changedate)
          values (23, 50000, NOW())"
    );
      $dbh->commit();
      
    } catch (Exception $e) {
      $dbh->rollBack();
      echo "Failed: " . $e->getMessage();
    }


    在上面的示例中,假设我们为一个新雇员创建一组条目,这个雇员有一个 ID 号,即 23。除了输入这个人的基本数据外,我们还需要记录雇员的薪水。两个更新分别完成起来很简单,但通过将这两个更新包括在 beginTransaction() 和 commit() 调用中,就可以保证在更改完成之前,其他人无法看到更改。如果发生了错误,catch 块可以回滚事务开始以来发生的所有更改,并打印出一条错误消息。

    并不是一定要在事务中作出更新。您也可以发出复杂的查询来提取数据,还可以使用那种信息构建更多的更新和查询。当事务在活动时,可以保证其他人在工作进行当中无法作出更改。事实上,这不是 100% 的正确,但如果您之前没有听说过事务的话,这样介绍也未尝不可。

    预处理语句和存储过程

    很多更成熟的数据库都支持预处理语句的概念。什么是预处理语句?您可以把预处理语句看作您想要运行的 SQL 的一种编译过的模板,它可以使用变量参数进行定制。预处理语句可以带来两大好处:

    • 查询只需解析(或准备)一次,但是可以用相同或不同的参数执行多次。当查询准备好后,数据库将分析、编译和优化执行该查询的计划。对于复杂的查 询,这个过程要花比较长的时间,如果您需要以不同参数多次重复相同的查询,那么该过程将大大降低应用程序的速度。通过使用预处理语句,可以避免重复分析/ 编译/优化周期。简言之,预处理语句使用更少的资源,因而运行得更快。
    • 提供给预处理语句的参数不需要用引号括起来,驱动程序会处理这些。如果应用程序独占地使用预处理语句,那么可以确保没有 SQL 入侵发生。(然而,如果您仍然将查询的其他部分建立在不受信任的输入之上,那么就仍然存在风险)。

    预处理语句是如此有用,以致 PDO 实际上打破了在目标 4 中设下的规则:如果驱动程序不支持预处理语句,那么 PDO 将仿真预处理语句。


    实例:PDO的应用例子:

    <?php
    $dbms='mysql';
    //数据库类型 Oracle 用ODI,对于开发者来说,使用不同的数据库,只要改这个,不用记住那么多的函数了

    $host='localhost';
    //数据库主机名

    $dbName='test';
    //使用的数据库

    $user='root';
    //数据库连接用户名

    $pass='';
    //对应的密码

    $dsn="$dbms:host=$host;dbname=$dbName";



    class db extends PDO {
        public function __construct(){
            try {
                parent::__construct("$GLOBALS[dsn]", $GLOBALS['user'], $GLOBALS['pass']);
             } catch (PDOException $e) {
             die("Error: " . $e->__toString() . "<br/>");
            }
        }
        
        public final function query($sql){
            try {
                return parent::query($this->setString($sql));
            }catch (PDOException $e){
                die("Error: " . $e->__toString() . "<br/>");
            }
        }
        
        private final function setString($sql){
            echo "我要处理一下$sql";
            return $sql;
        }
    }

    $db=new db();
    $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
    foreach ($db->query('SELECT * from xxxx_menu') as $row) {
        print_r($row);
    }
    $db->exec('DELETE FROM  `xxxx_menu` where mid=43');

    ?>





    参考:
    1.http://www.oracle.com/technology/global/cn/pub/articles/php_experts/otn_pdo_oracle5.html
    2.http://dev.csdn.net/author/heiyeshuwu/69c79a293895433dba178ce04caea20b.html
    3.http://www.ibm.com/developerworks/cn/db2/library/techarticles/dm-0505furlong/index.html
    4.PHP手册

  • ]]>
    PHP 5.2.5 发布 http://www.phpv.net/html/1578.html http://www.phpv.net/html/1578.html#comment Sun, 11 Nov 2007 14:48:12 +0000 esayr http://www.phpv.net/html/1578.html

      PHP开发小组日前发布了PHP 5.2.5,该版本主要用于改进PHP 5.2.x系列的稳定性,修正了超过60个bug,其中包括一些安全漏洞。官方推荐用户升级到该版本。

      更多关于PHP 5.2.5信息,可以参见PHP 5.2.5发行公告,也可在PHP 5更新列表中查看详细的升级信息。

    PHP 5.2.5的主要改进如下:
    • 限制dl()函数,使其只能接受文件名
    • 限制dl()函数参数的最大长度为MAXPATHLEN
    • 修正了htmlentities/htmlspecialchars不接受不完整的多字节数据串的缺陷
    • 修正了fnmatch()、setlocale()、glob()函数中的glibc实现可能存在的缓冲溢出漏洞
    • 修正了php.ini的mail.force_extra_parameters指令不受.htaccess控制的缺陷
    • 修正了当会话ID以非本地形式添加时的自动插入缺陷
    • 修正了在httpd.conf中通过php_admin_*设置的值可能被ini_set()函数覆盖的缺陷

      对于从PHP 5.0和PHP 5.1升级到PHP 5.2的用户来说,可参见这份升级指南,它讲述了PHP 5.2相对于其它版本来说的重大变更



    原文:

    [08-Nov-2007]

    The PHP development team would like to announce the immediate availability of PHP 5.2.5. This release focuses on improving the stability of the PHP 5.2.x branch with over 60 bug fixes, several of which are security related. All users of PHP are encouraged to upgrade to this release.

    Further details about the PHP 5.2.5 release can be found in the release announcement for 5.2.5, the full list of changes is available in the ChangeLog for PHP 5.

    Security Enhancements and Fixes in PHP 5.2.5:

    • Fixed dl() to only accept filenames. Reported by Laurent Gaffie.
    • Fixed dl() to limit argument size to MAXPATHLEN (CVE-2007-4887). Reported by Laurent Gaffie.
    • Fixed htmlentities/htmlspecialchars not to accept partial multibyte sequences. Reported by Rasmus Lerdorf
    • Fixed possible triggering of buffer overflows inside glibc implementations of the fnmatch(), setlocale() and glob() functions. Reported by Laurent Gaffie.
    • Fixed "mail.force_extra_parameters" php.ini directive not to be modifiable in .htaccess due to the security implications. Reported by SecurityReason.
    • Fixed bug #42869 (automatic session id insertion adds sessions id to non-local forms).
    • Fixed bug #41561 (Values set with php_admin_* in httpd.conf can be overwritten with ini_set()).

    For users upgrading to PHP 5.2 from PHP 5.0 and PHP 5.1, an upgrade guide is available here, detailing the changes between those releases and PHP 5.2.5.

    ]]>
    PHP对象相互引用的内存溢出 http://www.phpv.net/html/1574.html http://www.phpv.net/html/1574.html#comment Fri, 12 Oct 2007 09:24:53 +0000 抽烟的蚊子 http://www.phpv.net/html/1574.html 使用脚本语言最大的好处之一就是可利用其拥有的自动垃圾回收机制(释放内存)。你不需要在使用完变量后做任何释放内存的处理,PHP会帮你完成。

    当然,我们可以按自己的意愿调用 unset() 函数来释放内存,但通常不需要这么做。


    不过在PHP里,至少有一种情况内存不会得到自动释放,即便是手动调用 unset()。详情可考:http://bugs.php.net/bug.php?id=33595

    问题症状

    如果两个对象之间存在着相互引用的关系,如“父对象-子对象”,对父对象调用 unset() 不会释放在子对象中引用父对象的内存(即便父对象被垃圾回收,也不行)。

    有些糊涂了?我们来看下面的这段代码:

    <?php
    class Foo {
    function __construct()
    {
    $this->bar = new Bar($this);
    }
    }

    class Bar {
    function __construct($foo = null)
    {
    $this->foo = $foo;
    }
    }

    while (true) {
    $foo = new Foo();
    unset($foo);
    echo number_format(memory_get_usage()) . "\n";
    }
    ?>

    运行这段代码,你会看到内存使用率越来越高越来越高,直到用光光。

    ...
    33,551,616
    33,551,976
    33,552,336
    33,552,696
    PHP Fatal error: Allowed memory size of 33554432 bytes exhausted
    (tried to allocate 16 bytes) in memleak.php on line 17

    对大部分PHP程序员来讲这种情况不算是什么问题。

    可如果你在一个长期运行的代码中使用到了一大堆相互引用的对象,尤其是在对象相对较大的情况下,内存会迅速地消耗殆尽。


    Userland解决方案

    虽然有些乏味、不优雅,但之前提到的 bugs.php.net 链接中提供了一个解决方案。

    这个方案在释放对象前使用一个 destructor 方法以达到目的。Destructor 方法可将所有内部的父对象引用全部清除,也就是说可以将这部分本来会溢出的内存释放掉。

    以下是“修复后”的代码:

    <?php
    class Foo {
    function __construct()
    {
    $this->bar = new Bar($this);
    }
    function __destruct()
    {
    unset($this->bar);
    }
    }

    class Bar {
    function __construct($foo = null)
    {
    $this->foo = $foo;
    }
    }

    while (true) {
    $foo = new Foo();
    $foo->__destruct();
    unset($foo);
    echo number_format(memory_get_usage()) . "\n";
    }
    ?>

    注意那个新增的 Foo::__destruct()方法,以及在释放对象前对 $foo->__destruct() 的调用。现在这段代码解决了内存使用率一直增加的问题,这么一来,代码就可以很好的工作了。

    PHP内核解决方案?

    为什么会有内存溢出的发生?我对PHP内核方面的研究并不精通,但可以确定的是此问题与引用计数有关系。

    在 $bar 中引用 $foo 的引用计数不会因为父对象 $foo 被释放而递减,这时PHP认为你仍需要 $foo 对象,也就不会释放这部分的内存……大概是这样。

    这里确实可以看出我的无知,但大体意思是:一个引用计数没有递减,所以一些内存永远得不到释放。


    在前面提到的 bugs.php.net 链接中我看到修改垃圾回收的过程将会牺牲极大的性能,因为我对引用计数了解不多,所以我认为这是真的。


    与其改变垃圾回收的过程,为什么不用 unset() 对内部对象做释放的工作呢?(或者在释放对象的时候调用 __destruct()?)

    也许PHP内核开发者可以在此或其他地方,对这种垃圾回收处理机制做出修改。


    更新:Martin Fjordvald 在评论中提到了一个由 David Wang 为垃圾回收所写的补丁(其实它看起来更像“一整块布”——非常巨大。详情参见此邮件结尾的CVS导出信息。)确实存在(一封邮件),并受到了PHP内核开发成员的关注。问题是这个补丁要不要放到PHP5.3中并未得到太多支持我觉得一个不错的折中方案就是在 unset() 函数中调用对象中的 __destruct() 方法;


    转载或引用请注明来自PHP5研究室.
    ]]>
    PHP5 - CakePHP - RubyOnRails 对比 http://www.phpv.net/html/1555.html http://www.phpv.net/html/1555.html#comment Tue, 11 Sep 2007 10:46:36 +0000 抽烟的蚊子 http://www.phpv.net/html/1555.html
    作出决定前你需要考虑以下五个方面:

    1.成熟的解决方案;
    2.功能性;
    3.熟练使用者的数量(以组建一个团队);
    4.复杂性/易用性(新手和高手各取所需);
    5.你所选择的最优之处,以及其它两种的最不利之处。 维基百科上的这篇对比可供参考。

    不能访问以上网址,请使用代理.

    ]]>
    PHP 5.2.4 Released http://www.phpv.net/html/1554.html http://www.phpv.net/html/1554.html#comment Fri, 31 Aug 2007 13:15:18 +0000 easy http://www.phpv.net/html/1554.html PHP官方在今天发布新版本PHP 5.2.4
    主要改进稳定性方面,修补了120多个5.2.*的BUG.

    建议所有PHP5.*的用户升级到此版本.

     

    Security Enhancements and Fixes in PHP 5.2.4:

        * Fixed a floating point exception inside wordwrap() (Reported by Mattias Bengtsson)
        * Fixed several integer overflows inside the GD extension (Reported by Mattias Bengtsson)
        * Fixed size calculation in chunk_split() (Reported by Gerhard Wagner)
        * Fixed integer overflow in str[c]spn(). (Reported by Stanislav Malyshev)
        * Fixed money_format() not to accept multiple %i or %n tokens. (Reported by Stanislav Malyshev)
        * Fixed zend_alter_ini_entry() memory_limit interruption vulnerability. (Reported by Stefan Esser)
        * Fixed INFILE LOCAL option handling with MySQL extensions not to be allowed when open_basedir or safe_mode is active. (Reported by Stanislav Malyshev)
        * Fixed session.save_path and error_log values to be checked against open_basedir and safe_mode (CVE-2007-3378) (Reported by Maksymilian Arciemowicz)
        * Fixed a possible invalid read in glob() win32 implementation (CVE-2007-3806) (Reported by shinnai)
        * Fixed a possible buffer overflow in php_openssl_make_REQ (Reported by zatanzlatan at hotbrev dot com)
        * Fixed an open_basedir bypass inside glob() function (Reported by dr at peytz dot dk)
        * Fixed a possible open_basedir bypass inside session extension when the session file is a symlink (Reported by c dot i dot morris at durham dot ac dot uk)
        * Improved fix for MOPB-03-2007.
        * Corrected fix for CVE-2007-2872.

    ]]>
    PHP官方发布公告,近期将中止对PHP4版本的支持。 http://www.phpv.net/html/1552.html http://www.phpv.net/html/1552.html#comment Fri, 13 Jul 2007 17:09:10 +0000 easy http://www.phpv.net/html/1552.html PHP官方今天在网站上发布公告,中止PHP4版本的开发。

    声明中认为,PHP5从发布到现在已有整整三年时间,经过时间的考验,PHP5已十分成熟稳定并且高效。同时为PHP6的发布作准备。PHP4的支持将于年底中止。

    从2007年12月31日起,PHP4将不会再推出新版本,但仍然会修补重大BUG直到2008年8月8日。

    所以,呼吁用户,在今年余下的时间里。尽快的适应PHP5,并及时将老代码移植到PHP5或者更新的版本中来。以享受PHP的高效和完善的技术支持。

     

    公告原文:

    PHP 4 end of life announcement

    [12-Jul-2007]

    Today it is exactly three years ago since PHP 5 has been released. In those three years it has seen many improvements over PHP 4. PHP 5 is fast, stable & production-ready and as PHP 6 is on the way, PHP 4 will be discontinued.

    The PHP development team hereby announces that support for PHP 4 will continue until the end of this year only. After 2007-12-31 there will be no more releases of PHP 4.4. We will continue to make critical security fixes available on a case-by-case basis until 2008-08-08. Please use the rest of this year to make your application suitable to run on PHP 5.

    For documentation on migration for PHP 4 to PHP 5, we would like to point you to our migration guide. There is additional information available in the PHP 5.0 to PHP 5.1 and PHP 5.1 to PHP 5.2 migration guides as well.

    ]]>
    LAMP系统性能调优 第 2 部分:优化Apache和PHP http://www.phpv.net/html/1548.html http://www.phpv.net/html/1548.html#comment Tue, 12 Jun 2007 12:31:10 +0000 easy http://www.phpv.net/html/1548.html 2007 年 6 月 07 日

    如今,使用 LAMP(Linux®、Apache、MySQL 和 PHP/Perl)架构的应用程序不断被开发和部署。但是,服务器管理员常常对应用程序本身几乎没有控制能力,因为应用程序是别人编写的。这份 共三部分的系列文章 将讨论许多服务器配置问题,这些配置会影响应用程序的性能。第二篇文章重点讨论可为优化 Apache 和 PHP 而采取的措施。

    Linux、 Apache、MySQL 和 PHP(或 Perl)是许多 Web 应用程序的 LAMP 架构的基础。有很多基于 LAMP 组件的开源软件包可用于解决各种各样的问题。随着应用程序负载的增加,底层基础设施的瓶颈也会越来越明显,其表现形式就是响应用户请求的速度变慢。 上一篇文章 展示了调优 Linux 系统的方法,还介绍了 LAMP 和性能度量的基础知识。本文重点关注 Web 服务器组件:Apache 和 PHP。

    调优 Apache

    Apache 是一种高度可配置的软件。它具有大量特性,但每一种都代价高昂。从某种程度上来说,调优 Apache 来说就是以恰当的方式分配资源,还涉及到将配置简化为仅包含必要内容。

    配置 MPM

    ------------

    每次只能有一个 MPM 是活动的,必须使用 --with-mpm=(worker|prefork|event) 静态编译。

    每个请求使用一个进程的传统模型称为 prefork。较新的线程化模型称为 worker,它使用多个进程,每个进程又有多个线程,这样就能以较低的开销获得更好的性能。最新的 event MPM 是一种实验性的模型,为不同的任务使用单独的线程池。要确定当前使用的是哪种 MPM,可执行 httpd -l

    选择使用何种 MPM 取决于许多因素。在 event MPM 脱离实验状态之前,不应考虑这种模型,而是在使用线程和不使用线程之间作出选择。表面上看来,如果所有底层模块(包括 PHP 使用的所有库)都是线程安全的,线程要优于分叉(forking)。而 Prefork 是较为安全的选择;如果选择了 worker,则应该谨慎测试。性能收益还取决于您的发布版所附带的库及硬件。

    无论选择了哪种 MPM,都必须恰当地配置它。一般而言,配置 MPM 包括告知 Apache 怎样去控制有多少 worker 正在运行,它们是线程还是进程。prefork MPM 的重要配置选项如清单 1 所示。


    清单 1. prefork MPM 的配置
     StartServers 50 MinSpareServers 15 MaxSpareServers 30 MaxClients 225 MaxRequestsPerChild 4000 

    编译您自己的软件

    最初使用 UNIX® 时,我坚持为加入系统的一切编译软件。最终,维护更新给我带来了麻烦,所以我学会了如何构建包来简化这一任务。后来我意识到,大多数时候我都在重复做发布版做过的事情。现在,在很大程度上来说,我会尽可能坚持使用我所选择的发布版提供的一切,仅在必要的时候使用自己的包。

    类似地,您可能会发现,就可维护性而言,使用厂商提供的软件包要优于使用最新、最棒的代码。有些时候,性能调优和系统管理的目标会有所冲突。如果使用商业版的 Linux 或依赖于第三方支持,那么可能不得不考虑厂商的支持。

    如果您一意孤行,那么请学会如何构建能与您的发布版协同工作的包,请学会如何将其集成到补丁系统之中。这将确保软件,以及您作出的任何更改得到一致的构建,且能跨多个系统使用。还应订阅恰当的邮件列表和 RSS 提要来及时获得软件更新。

    prefork 模型会为每个请求创建一个新进程。多余的进程保持空闲,以处理传入的请求,这缩短了启动延迟。只要 Web 服务器出现,预先完成的配置就会立即启动 50 个进程,并尽力保持 10 到 20 个空闲服务器运行。进程数的硬性限制由 MaxClients 指定。尽管一个进程能够处理许多相继的请求,Apache 还是会取消连接数超过 4,000 以后的进程,这降低了内存泄漏的风险。

    配置线程化 MPM 与之类似,不同之处只是必须确定使用多少线程和进程。Apache 文档解释了所有必要的参数和计算。

    要经过几次尝试和出错之后才能选好要使用的值。最重要的值是 MaxClients。目标在于允许足够多的 workder 进程或线程运行,同时又不会导致服务器进行过度的交换。如果传入的请求超出处理能力,那么至少满足此值的那些请求会得到服务,其他请求被阻塞。

    如果 MaxClients 过高,那么所有客户机都将体验到糟糕的服务,因为 Web 服务器会试图换出一个进程,以使另一个进程能够运行。而设得过低意味着可能会不必要地拒绝服务。查看高负载下运行的进程数量和所有 Apache 进程所导致的内存占用情况对设置这个值很有帮助。如果 MaxClients 的值超过 256,必须将 ServerLimit 也设为同样的数值,请仔细阅读 MPM 的文档,了解相关信息。

    根据服务器的角色调优要启动和保持空闲的服务器数量。如果服务器仅运行 Apache,那么可以使用适中的值,如 清单 1 所示,因为这样就能充分利用机器。如果系统中还有其他数据库或服务器,那么就应该限制运行中的空闲服务器的数量。

    有效地使用选项和重写

    Apache 处理的每个请求都要履行一套复杂的规则,这些规则指明了 Web 服务器必须遵循的约束或特殊指令。对文件夹的访问可能按 IP 地址约束为某个特定文件夹,也可配置用户名和密码。这些选项还包含处理特定文件,例如,如果提供了一个目录列表,该如何处理的文件,或输出结果是否应压缩。

    这些配置以 httpd.conf 中容器的形式出现,例如 <Directory>,以便指定所用配置引用的是磁盘上的一个位置;再如 <Location>,表示引用是 URL 中的路径。清单 2 展示了一个实际的 Directory 容器。


    清单 2. 为根目录应用的一个 Directory 容器
     <Directory /> AllowOverride None Options FollowSymLinks </Directory> 

    在清单 2 中,位于一对 Directory/Directory 标记之间的配置应用于给定目录和该目录下的一切内容 ―― 在本例中,这个给定目录是根目录。此处,AllowOverride 标记指出,用户不允许重写任何选项(稍后将进一步介绍)。FollowSymLinks 选项被启用,它允许 Apache 查看之前的符号连接来为请求提供服务,即便文件位于包含 Web 文件的目录之外。这就意味着,如果 Web 目录中的一个文件是 /etc/passwd 的符号连接,Web 服务器将在请求时顺利为该文件提供服务。如果使用了 -FollowSymLinks,该特性就会被禁用,同样的请求将致使为客户机返回错误。

    最后这个场景正是导致两方面关注的原因所在。第一个方面与性能有关。如果禁用了 FollowSymLinks,Apache 就必须检查使用该文件名的所有组件(目录和文件本身),以确保它们不是符号连接。这会带来额外的开销(磁盘操作)。另外一个称为 FollowSymLinksIfOwnerMatch 的选项会在文件所有者与连接所有者相同时使用符号连接。为获得最佳性能,请使用 清单 2 中的选项。

    至此,有安全意识的读者应该有了警惕的感觉。安全性永远是功能性与风险之间的权衡。在我们的例子中,功能性是速度,而风险是允许对系统上的文件进行未经授权的访问。缓解风险的措施之一是 LAMP 应用服务器通常专注于一种具体功能,用户无法创建危险的符号连接。如果有必要启用符号连接,那么可以将其约束在文件系统的特定区域,如清单 3 所示。


    清单 3. 将 FollowSymLinks 约束为一个用户的目录
     <Directory /> Options FollowSymLinks </Directory> <Directory /home/*/public_html> Options -FollowSymLinks </Directory> 

    在清单 3 中,一个用户的主目录中的任何 public_html 目录及其所有子目录都移除了 FollowSymLinks 选项。

    如您所见,通过主服务器配置,可为每个目录单独配置选项。用户可以自行重写这种服务器配置(如果管理员通过 AllowOverrides 语句允许了这种操作),只需将一个 .htaccess 文件放入目录即可。该文件包含额外的服务器指令,每次请求包含 .htaccess 文件的目录时将加载并应用这些指令。尽管之前探讨过系统没有用户的问题,但许多 LAMP 应用程序都利用这种功能性来控制访问、实现 URL 重写,因此有必要理解其工作原理。

    即便 AllowOverrides 语句能阻止用户去做您不希望他们做的事,Apache 也必须检查 .htaccess 文件,看看是否有要完成的工作。父目录可以指定由来自子目录的请求处理的指令,这也就表示,Apache 必须搜索所请求文件的目录树的所有组件。可想而知,这会使每次请求都导致大量磁盘操作。

    最简单的解决方案是不允许重写,这能消除 Apache 检查 .htaccess 的需求。之后的任何特殊配置都将直接放在 httpd.conf 中。清单 4 显示为对一个用户的项目目录进行密码检查向 httpd.conf 增加的代码,而不是将其放入一个 .htaccess 文件并依赖于 AllowOverrides


    清单 4. 将 .htaccess 配置移入 httpd.conf
     <Directory /home/user/public_html/project/> AuthUserFile /home/user/.htpasswd AuthName "uber secret project" AuthType basic Require valid-user </Directory> 

    如果配置转移到 httpd.conf 中,且 AllowOverrides 被禁用,磁盘的使用就能减少。一个用户的项目可能不会吸引许多人来点击,但设想一下,将这项技术应用于一个忙碌的站点时会有多么强大。

    有时不可能彻底消除 .htaccess 文件的使用。例如,在清单 5 中,一个选项被约束到文件系统的特定部分,重写也可以是有作用域的。


    清单 5. 限定 .htaccess 检查的作用域
     <Directory /> AllowOverrides None </Directory> <Directory /home/*/public_html> AllowOverrides AuthConfig </Directory> 

    实现清单 5 之后,Apache 会在父目录中查找 .htaccess 文件,但会在 public_html 目录处停止,因为文件系统的其余部分禁用了此功能。例如,如果请求的是一个映射到 /home/user/public_html/project/notes.html 的文件,那么仅有 public_html 和 project 目录被搜索。

    关于每目录单独配置的最后一个提示就是:要按顺序依次进行。任何介绍 Apache 调优的的文章都会告诉您,应通过 HostnameLookups off 指令禁用 DNS 查找,因为试图反向解析连接到您的服务器的所有 IP 地址无疑是浪费资源。然而,基于主机名的任何约束都会迫使 Web 服务器对客户机的 IP 地址执行反向查找,对其结果进行正向查找,以验证该名称的真实性。因此,避免使用基于客户主机名的访问控制,在必须使用时限定其作用域,这些都是明智的做法。

    持久连接

    一个客户机连接到 Web 服务器时,允许客户机通过同一个 TCP 连接发出多个请求,这减少了与多个连接相关的延迟。在一个 Web 页面引用了多幅图片时,这就很有用:客户机可以通过一个连接先请求页面,再请求所有图片。其缺点在于服务器上的 worker 进程必须等待客户机要关闭的会话,之后才能转到下一个请求。

    Apache 使您能够配置如何处理持久连接(称为 keepalives)。httpd.conf 全局级的 KeepAlive 5 允许服务器在连接强制关闭之前处理一个连接上的 5 个请求。将此值设置为 0 将禁用持久连接。同样位于全局级上的 KeepAliveTimeout 确定在会话关闭之前,Apache 将等待另外一个连接多久。

    持久连接的处理并非 “一刀切” 式的配置。对于某些 Web 站点,禁用 keepalives 更合适(KeepAlive 0);而对于其他一些站点,启用它会带来巨大的收益。惟一的解决之道就是尝试使用这两种配置,自己观察哪种更合适。但若启用了 keepalives,使用较小的超时时间较为明智,例如 2,即 KeepAliveTimeout 2。这能确保希望发出另外一个请求的客户机有充足的时间,还能确保 worker 进程不会一直空闲,等待可能永远不会出现的下一个请求。

    压缩

    Web 服务器能够在将输出发回给客户机之前压缩它。这将使通过 Internet 发送的页面更小,代价是 Web 服务器上的 CPU 周期。对于那些负担得起 CPU 开销的服务器来说,这是提高页面下载速度的好办法 ―― 页面压缩后大小变为原来的三分之一这种事情并不罕见。

    图片通常已经是压缩过的,因此压缩应仅限于文本输出。Apache 通过 mod_deflate 提供压缩。尽管 mod_deflate 可轻松启用,但它涉及到太多的复杂性,很多手册都解释了这些复杂的内容。本文不会介绍压缩的配置,但提供了相应文档的链接(参见 参考资料 部分)。

    调优 PHP

    PHP 是运行应用程序代码的引擎。应该仅安装计划使用的那些模块,并配置您的 Web 服务器,使之仅为脚本文件(通常是以 .php 结尾的那些文件)使用 PHP,而非所有静态文件。

    操作码缓存

    请求一个 PHP 脚本时,PHP 会读取该脚本,并将其编译为 Zend 操作码,这是要执行的代码的一种二进制表示形式。随后,此操作码由 PHP 执行并丢弃。操作码缓存将保存这个编译后的操作码,并在下一次调用该页面时重用它。这会节省很多时间。有多种缓存可用,我比较常用的是 eAccelerator。

    要安装 eAccelerator,您的计算机上需要有 PHP 开发库。由于不同的 Linux 发布版存放文件的位置不同,所以最好直接从 eAccelerator 的 Web 站点获得安装说明(参见 参考资料 部分获得链接)。您的发布版也有可能已经包含了一个操作码缓存,只需安装即可。

    无论如何在系统上安装 eAccelerator,都有一些配置选项需要注意。配置文件通常是 /etc/php.d/eaccelerator.ini。eaccelerator.shm_size 定义共享高速缓存的大小,编译后的脚本就存储在这里。该值的单位是兆字节(MB)。根据您的应用程序确定恰当的大小。eAccelerator 提供了一个脚本来显示缓存的状态,其中包含内存占用,64MB 是个不错的选择(eaccelerator.shm_size="64")。如果您选择的值未被接受,那么必须修改内核的最大共享内存的大小。向 /etc/sysctl.conf 添加 kernel.shmmax=67108864,运行 sysctl -p 来使设置生效。kernel.shmmax 值的单位是字节。

    如果共享内存的分配超出极限,eAccelerator 必须将旧脚本从内存中清除。默认情况下,这是被禁用的;eaccelerator.shm_ttl = "60" 指定:当 eAccelerator 用完共享内存时,60 秒内未被访问的所有脚本都将被清除。

    另一种流行的 eAccelerator 替代工具是 Alternative PHP Cache(APC)。Zend 的厂商也提供了一种商业操作码缓存,包括一个进一步提高效率的优化器。

    php.ini

    PHP 的配置是在 php.ini 中完成的。四个重要的设置控制 PHP 可使用多少系统资源,如表 1 所列。


    表 1. php.ini 中与资源相关的设置
    设置 描述 建议值
    max_execution_time 一个脚本可使用多少 CPU 秒 30
    max_input_time 一个脚本等待输入数据的时间有多长(秒) 60
    memory_limit 在被取消之前,一个脚本可使用多少内存(字节) 32M
    output_buffering 数据发送给客户机之前,有多少数据(字节)需要缓存 4096

    具体数字主要取决于您的应用程序。如果要从用户处接收大文件,那么 max_input_time 可能必须增加,可以在 php.ini 中修改,也可以通过代码重写它。与之类似,CPU 或内存占用较多的程序也可能需要更大的设置值。目标就是缓解超标程序的影响,因此不建议全局禁用这些设置。关于 max_execution_time,还有一点需要注意:它表示进程的 CPU 时间,而不是绝对时间。因此一个进行大量 I/O 和少量计算的程序的运行时间可能远远超过 max_execution_time。这也是 max_input_time 可以大于 max_execution_time 的原因所在。

    PHP 可执行的日志记录数是可配置的。在生产环境中,禁用除最重要的日志以外的一切日志记录能够减少磁盘写操作。如果需要使用日志来排除问题,那么可以按需启用日志记录。error_reporting = E_COMPILE_ERROR|E_ERROR|E_CORE_ERROR 将启用足够的日志记录,使您发现问题,同时从脚本中消除大量无用的内容。

    ]]>
    PHP 5.2.3 发布 http://www.phpv.net/html/1547.html http://www.phpv.net/html/1547.html#comment Fri, 01 Jun 2007 12:16:58 +0000 easy http://www.phpv.net/html/1547.html PHP开发团队于六一儿童节释出 PHP 5.2.3 .此版本改进5.*系列的安全和稳定性.

    主要改动有:

    修复chunk_split() 函数的一个整数溢出.
    修复imagecreatefrompng处理时可能存在的无限循环问题.
    新增mysql_set_charset()函数,以便在程序运行时改变mysql的字符串编码.
    ....

    推荐所有5.*的用户升级到此版本.

    WIN系统点这里下载:PHP 5.2.3 zip package 9,617Kb
    linux系统点这里下载:PHP 5.2.3 (tar.gz) 9,123Kb

    原文如下:

    [01-Jun-2007] The PHP development team would like to announce the immediate availability of PHP 5.2.3. This release continues to improve the security and the stability of the 5.X branch as well as addressing two regressions introduced by the previous 5.2 releases. These regressions relate to the timeout handling over non-blocking SSL connections and the lack of HTTP_RAW_POST_DATA in certain conditions. All users are encouraged to upgrade to this release.

    Further details about the PHP 5.2.3 release can be found in the release announcement for 5.2.3, the full list of changes is available in the ChangeLog for PHP 5.

    Security Enhancements and Fixes in PHP 5.2.3:

    • Fixed an integer overflow inside chunk_split() (by Gerhard Wagner, CVE-2007-2872)
    • Fixed possible infinite loop in imagecreatefrompng. (by Xavier Roche, CVE-2007-2756)
    • Fixed ext/filter Email Validation Vulnerability (MOPB-45 by Stefan Esser, CVE-2007-1900)
    • Fixed bug #41492 (open_basedir/safe_mode bypass inside realpath()) (by bugs dot php dot net at chsc dot dk)
    • Improved fix for CVE-2007-1887 to work with non-bundled sqlite2 lib.
    • Added mysql_set_charset() to allow runtime altering of connection encoding.

    For users upgrading to PHP 5.2 from PHP 5.0 and PHP 5.1, an upgrade guide is available here, detailing the changes between those releases and PHP 5.2.3.

    ]]>
    LAMP 系统性能调优,第 1 部分: 理解 LAMP 架构 http://www.phpv.net/html/1546.html http://www.phpv.net/html/1546.html#comment Wed, 30 May 2007 02:00:57 +0000 easy http://www.phpv.net/html/1546.html 如今,使用 LAMP(Linux®、Apache、MySQL 和 PHP/Perl)架构的应用程序不断被开发和部署。 但是,服务器管理员对应用程序本身几乎没有控制能力,因为应用程序是别人编写的。这份共三部分的系列文章将讨论许多服务器配置问题,这些配置会影响应用程序的性能。第一篇文章讨论 LAMP 架构、一些性能度量技术以及一些基本的 Linux 内核、硬盘和文件系统调节。后续的文章将研究 Apache、MySQL 和 PHP 组件的调优。

    Linux、Apache、MySQL 和 PHP(或 Perl)是许多 Web 应用程序的基础 ―― 从 to-do 列表到 blog,再到电子商务站点。WordPress 和 Pligg 是两个支持大容量 Web 站点的常用软件包。这种架构简称为 LAMP。几乎每个 Linux 发布版都包含 Apache、MySQL、PHP 和 Perl,所以安装 LAMP 软件是非常容易的。

    安装的简便性使人误以为这些软件会自行顺利地运行,但是实际情况并非如此。最终,应用程序的负载会超出后端服务器自带设置的处理能力,应用程序的性能会降低。LAMP 安装需要不断监控、调优和评估。

    系统调优对于不同的人有不同的含义。本系列主要关注 LAMP 组件(Linux、Apache、MySQL 和 PHP)的调优。对应用程序本身进行调优是另一个复杂的问题。应用程序和后端服务器之间存在一种共生关系:未能适当调优的服务器甚至会使最好的应用程序在负载之下崩溃,而借助充分的调优,完全可以避免编写得很糟糕的应用程序使服务器缓慢如牛。幸运的是,正确的系统调优和监视可以指出应用程序中的问题。

    LAMP 架构

    对任何系统进行调优的第一步都是了解它的工作原理。按照最简单的形式,基于 LAMP 的应用程序是用 PHP 这样的脚本语言编写的,它们作为 Linux 主机上运行的 Apache Web 服务器的一部分运行。

    PHP 应用程序通过请求的 URL、所有表单数据和已捕获的任意会话信息从客户机获得信息,从而确定应该执行什么操作。如有必要,服务器会从 MySQL 数据库(也在 Linux 上运行)获得信息,将这些信息与一些 Hypertext Markup Language(HTML)模板组合在一起,并将结果返回给客户机。当用户在应用程序中导航时,这个过程重复进行;当多个用户访问系统时,这个过程会并发进行。但是,数据流不是单向的,因为可以用来自用户的信息更新数据库,包括会话数据、统计数据(包括投票)和用户提交的内容(比如评论或站点更新)。除了动态元素之外,还有静态元素,比如图像、JavaScript 代码和层叠样式表(CSS)。

    LAMP 的变体

    LAMP 最初是指 Linux、Apache、MySQL 和 PHP(或 Perl)。但是,如果管理员不擅长 Linux,那么可以在 Microsoft® Windows® 上运行 Apache、MySQL 和 PHP,这并非一种少见的情况。同样,也可以将 Apache 换成别的系统,比如 lighttpd,产生的仍然是 LAMP 风格的系统,但是首字母缩写不再是 LAMP 了。也可以改用另一种开放源码数据库(比如 PostgreSQL 或 SQLite)、商业数据库(比如 IBM® DB2®)或者免费的商业引擎(比如 IBM DB2 Express-C)。

    本文主要关注传统的 LAMP 架构,因为这种架构是最常见的,而且它的组件都是开放源码的。

    在研究 LAMP 系统中的请求流之后,就来看看可能出现性能瓶颈的地方。数据库提供许多动态信息,所以数据库对查询的响应延迟都会反映在客户机中。Web 服务器必须能够快速地执行脚本,还要能够处理多个并发请求。最后,底层操作系统必须处于良好的状态才能支持应用程序。通过网络在不同服务器之间共享文件的其他设置也可能成为瓶颈。

    度量性能

    持续地对性能进行度量在两个方面有帮助。首先,度量可以帮助了解性能趋势,包括好坏两方面的趋势。作为一个简单的方法,查看一下 Web 服务器上的中央处理单元(CPU)使用率,就可以了解 CPU 是否负载过重。同样,查看过去使用的总带宽并推断未来的变化,可以帮助判断什么时候需要进行网络升级。这些度量最好与其他度量和观测结合考虑。例如,当用户抱怨应用程序太慢时,可以检查磁盘操作是否达到了最大容量。

    性能度量的第二个用途是,判断调优是对系统性能有帮助,还是使它更糟糕了。方法是比较修改之前和之后的度量结果。但是,为了进行有效的比较,每次应该只修改一个设置,然后对适当的指标进行比较以判断修改的效果。每次只修改一个设置的原因应该是很明显的:同时做出的两个修改很可能会相互影响。选择用来进行比较的指标比较微妙。

    选择的指标必须能够反映应用程序用户感觉到的响应。如果一项修改的目标是减少数据库的内存占用量,那么取消各种缓冲区肯定会有帮助,但是这会牺牲查询速度和应用程序性能。所以,应该选择应用程序响应时间这样的指标,这会使调优向着正确的方向发展,而不仅仅是针对数据库内存使用量。

    可以以许多方式度量应用程序响应时间。最简单的方法可能是使用 curl 命令,见清单 1。


    清单 1. 使用 cURL 度量 Web 站点的响应时间
                    
    $ curl -o /dev/null -s -w %{time_connect}:%{time_starttransfer}:%{time_total}\
        http://www.canada.com
    0.081:0.272:0.779
    

    清单 1 给出对一个流行的新闻站点执行 curl 命令的情况。输出通常是 HTML 代码,通过 -o 参数发送到 /dev/null-s 参数去掉所有状态信息。-w 参数让 curl 写出表 1 列出的计时器的状态信息:


    表 1. curl 使用的计时器
    计时器 描述
    time_connect 建立到服务器的 TCP 连接所用的时间
    time_starttransfer 在发出请求之后,Web 服务器返回数据的第一个字节所用的时间
    time_total 完成请求所用的时间

    这些计时器都相对于事务的起始时间,甚至要先于 Domain Name Service(DNS)查询。因此,在发出请求之后,Web 服务器处理请求并开始发回数据所用的时间是 0.272 - 0.081 = 0.191 秒。客户机从服务器下载数据所用的时间是 0.779 - 0.272 = 0.507 秒。

    通过观察 curl 数据及其随时间变化的趋势,可以很好地了解站点对用户的响应性。

    当然,Web 站点不仅仅由页面组成。它还有图像、JavaScript 代码、CSS 和 cookie 要处理。curl 很适合了解单一元素的响应时间,但是有时候需要了解整个页面的装载速度。

    用于 Firefox 浏览器的 Tamper Data 扩展(参见 参考资料 一节中的链接)可以在日志中记录 Web 浏览器发出的每个请求,并显示每个请求所用的下载时间。使用这个扩展的方法是,选择 Tools > Tamper Data 来打开 Ongoing requests 窗口。装载要考察的页面,然后就会看到浏览器发出的每个请求的状态和装载每个元素所用的时间。图 1 给出装载 developerWorks 主页的结果。


    图 1. 用于装载 developerWorks 主页的请求细目
    用于装载 developerWorks 主页的请求细目

    每一行描述一个元素的装载情况。显示的数据包括发出请求的时间、装载所用的时间、大小和结果。Duration 栏列出装载元素本身所用的时间,Total Duration 栏列出所有子元素所用的时间。在图 1 中,装载主要页面所用的时间是 516 毫秒(ms),但是装载所有东西并显示整个页面所用的时间是 5101 ms。

    Tamper Data 扩展有一种有用的模式,将页面装载数据的输出绘制成图形。右击 Ongoing requests 窗口上半部分的任何地方,并选择 Graph all。图 2 显示图 1 中数据的图形化视图。


    图 2. 用于装载 developerWorks 主页的请求的图形化视图
    用于装载 developerWorks 主页的请求的图形化视图

    在图 2 中,每个请求的持续时间显示为深蓝色,并相对于页面装载的启始时间显示。所以,可以看出哪些请求使整个页面的装载变慢了。

    尽管关注的重点是页面装载时间和用户体验,但是也不要忽视核心系统指标,比如磁盘、内存和网络。有许多实用程序可以捕获这些信息;其中最有帮助的可能是 sarvmstatiostat。关于这些工具的更多信息,请参见 参考资料 一节。





    回页首


    基本系统调节

    在对系统的 Apache、PHP 和 MySQL 组件进行调优之前,应该花一些时间确保底层 Linux 组件的运行正常。还应该对正在运行的服务进行缩减,只运行需要的那些服务。这不但是一种良好的安全实践,而且可以节省内存和 CPU 时间。

    一些快速的内核调优措施

    大多数 Linux 发布版都定义了适当的缓冲区和其他 Transmission Control Protocol(TCP)参数。可以修改这些参数来分配更多的内存,从而改进网络性能。设置内核参数的方法是通过 proc 接口,也就是通过读写 /proc 中的值。幸运的是,sysctl 可以读取 /etc/sysctl.conf 中的值并根据需要填充 /proc,这样就能够更轻松地管理这些参数。清单 2 展示在互联网服务器上应用于 Internet 服务器的一些比较激进的网络设置。


    清单 2. 包含较为激进的网络设置的 /etc/sysctl.conf
                    
    # Use TCP syncookies when needed
    net.ipv4.tcp_syncookies = 1
    # Enable TCP window scaling
    net.ipv4.tcp_window_scaling: = 1
    # Increase TCP max buffer size
    net.core.rmem_max = 16777216
    net.core.wmem_max = 16777216
    # Increase Linux autotuning TCP buffer limits
    net.ipv4.tcp_rmem = 4096 87380 16777216 
    net.ipv4.tcp_wmem = 4096 65536 16777216
    # Increase number of ports available
    net.ipv4.ip_local_port_range = 1024 65000
    

    将这些设置添加到 /etc/sysctl.conf 的现有内容中。第一个设置启用 TCP SYN cookie。当从客户机发来新的 TCP 连接时,数据包设置了 SYN 位,服务器就为这个半开的连接创建一个条目,并用一个 SYN-ACK 数据包进行响应。在正常操作中,远程客户机用一个 ACK 数据包进行响应,这会使半开的连接转换为全开的。有一种称为 SYN 泛滥(SYN flood) 的网络攻击,它使 ACK 数据包无法返回,导致服务器用光内存空间,无法处理到来的连接。SYN cookie 特性可以识别出这种情况,并使用一种优雅的方法保留队列中的空间(细节参见 参考资料 一节)。大多数系统都默认启用这个特性,但是确保配置这个特性更可靠。

    启用 TCP 窗口伸缩使客户机能够以更高的速度下载数据。TCP 允许在未从远程端收到确认的情况下发送多个数据包,默认设置是最多 64 KB,在与延迟比较大的远程客户机进行通信时这个设置可能不够。窗口伸缩会在头中启用更多的位,从而增加窗口大小。

    后面四个配置项增加 TCP 发送和接收缓冲区。这使应用程序可以更快地丢掉它的数据,从而为另一个请求服务。还可以强化远程客户机在服务器繁忙时发送数据的能力。

    最后一个配置项增加可用的本地端口数量,这样就增加了可以同时服务的最大连接数量。

    在下一次引导系统时,或者下一次运行 sysctl -p /etc/sysctl.conf 时,这些设置就会生效。

    配置磁盘来提高性能

    磁盘在 LAMP 架构中扮演着重要的角色。静态文件、模板和代码都来自磁盘,组成数据库的数据表和索引也来自磁盘。对磁盘的许多调优(尤其是对于数据库)集中于避免磁盘访问,因为磁盘访问的延迟相当高。因此,花一些时间对磁盘硬件进行优化是有意义的。

    首先要做的是,确保在文件系统上禁用 atime 日志记录特性。atime 是最近访问文件的时间,每当访问文件时,底层文件系统必须记录这个时间戳。因为系统管理员很少使用 atime,禁用它可以减少磁盘访问时间。禁用这个特性的方法是,在 /etc/fstab 的第四列中添加 noatime 选项。清单 3 给出了一个配置示例。


    清单 3. 演示如何启用 noatime 的 fstab 示例
                    
    /dev/VolGroup00/LogVol00 /                      ext3    defaults,noatime        1 1
    LABEL=/boot             /boot                   ext3    defaults,noatime        1 2
    devpts                  /dev/pts                devpts  gid=5,mode=620  0 0
    tmpfs                   /dev/shm                tmpfs   defaults        0 0
    proc                    /proc                   proc    defaults        0 0
    sysfs                   /sys                    sysfs   defaults        0 0
    LABEL=SWAP-hdb2         swap                    swap    defaults        0 0
    LABEL=SWAP-hda3         swap                    swap    defaults        0 0
    

    在清单 3 中只修改了 ext3 文件系统,因为 noatime 只对驻留在磁盘上的文件系统有帮助。为让这一修改生效,不需要重新引导;只需重新挂装每个文件系统。例如,为了重新挂装根文件系统,运行 mount / -o remount

    有多种磁盘硬件组合,而且 Linux 不一定能够探测出访问磁盘的最佳方式。可以使用 hdparm 命令查明和设置用来访问 IDE 磁盘的方法。hdparm -t /path/to/device 执行速度测试,可以将这个测试结果作为性能基准。为了使结果尽可能准确,在运行这个命令时系统应该是空闲的。清单 4 给出在 hda 上执行速度测试的结果。


    清单 4. 在 /dev/hd 上执行的速度测试
                    
    # hdparm -t /dev/hda
    
    /dev/hda:
     Timing buffered disk reads:  182 MB in  3.02 seconds =  60.31 MB/sec
    

    这一测试说明,在这个磁盘上读取数据的速度是大约每秒 60 MB。

    在尝试一些磁盘调优选项之前,必须注意一个问题。错误的设置可能损害文件系统。有时候会出现一个警告,指出这个选项与硬件不兼容;但是,有时候没有警告消息。因此,在将系统投入生产之前,必须对设置进行彻底的测试。在所有服务器上都采用标准的硬件也会有所帮助。

    表 2 列出比较常用的一些选项。


    表 2. hdparm 的常用选项
    选项 描述
    -vi 向磁盘查询它支持的设置以及它正在使用的设置。
    -c 查询/启用 (E)IDE 32 位 I/O 支持。hdparm -c 1 /dev/hda 启用这个设置。
    -m 查询/设置每中断多扇区模式。如果设置大于零,设置值就是每个中断可以传输的最大扇区数量。
    -d 1 -X 启用直接内存访问(DMA)传输并设置 IDE 传输模式。hdparm 手册页详细说明了在 -X 后面可以设置的数字。只有在 -vi 说明目前并未使用最快速的模式的情况下,才需要进行这个设置。

    不幸的是,对于 Fiber Channel and Small Computer Systems Interface(SCSI)系统,调优依赖于具体的驱动器。

    必须将有帮助的设置添加到启动脚本中,比如 rc.local

    网络文件系统调优

    网络文件系统(NFS)是一种通过网络共享磁盘的方法。NFS 可以帮助确保每个主机具有相同数据的拷贝,并确保修改反映在所有节点上。但是,在默认情况下,NFS 的配置不适合大容量磁盘。

    每个客户机应该用 rsize=32768,wsize=32768,intr,noatime 挂装远程文件系统,从而确保:

    • 使用大的读/写块(数字指定最大块大小,在这个示例中是 32KB)。
    • 在挂起时 NFS 操作可以被中断。
    • 不持续更新 atime

    可以将这些设置放在 /etc/fstab 中,见 清单 3。如果使用自动挂装器,那么应该将这些设置放在适当的 /etc/auto.* 文件中。

    在服务器端,一定要确保有足够的 NFS 内核线程来处理所有客户机。在默认情况下,只启动一个线程,但是 Red Hat 和 Fedora 系统会启动 8 个线程。对于繁忙的 NFS 服务器,应该提高这个数字,比如 32 或 64。可以用 nfsstat -rc 命令评估客户机,了解是否有阻塞的现象,这个命令显示客户机远程过程调用(RPC)统计数据。清单 5 显示一个 Web 服务器的客户机统计数据。


    清单 5. 显示 NFS 客户机的 RPC 统计数据
                     
    # nfsstat -rc
    Client rpc stats:
    calls      retrans    authrefrsh
    1465903813   0          0       
    

    第二列 retrans 是零,这表示从上一次重新引导以来没有出现需要重新传输的情况。如果这个数字比较大,就应该考虑增加 NFS 内核线程。设置方法是将所需的线程数量传递给 rpc.nfsd,比如 rpc.nfsd 128 会启动 128 个线程。任何时候都可以进行这种设置。线程会根据需要启动或销毁。同样,这个设置应该放在启动脚本中,尤其是在系统上启用 NFS 的脚本。

    关于 NFS,最后要注意一点:如果可能的话,应该避免使用 NFSv2,因为 NFSv2 的性能比 v3 和 v4 差得多。在现代的 Linux 发行版中这应该不是问题,但是可以在服务器上检查 nfsstat 的输出,了解是否有任何 NFSv2 调用。

     

    本文讨论了 LAMP 的一些基本知识以及 LAMP 安装的一些简单 Linux 调优措施。除了 NFS 内核线程之外,可以设置本文中讨论的参数,然后就不用理会它们了。本系列中的后两篇文章主要关注 Apache、MySQL 和 PHP 调优。这些组件的调优与 Linux 的调优有很大的差异,因为随着通信量的增长、读写操作分布情况的变化和应用程序的演化,需要不断重新考察这些参数。

    ]]>
    访PHP之父Rasmus:创业不要贸然使用最先进技术 http://www.phpv.net/html/1544.html http://www.phpv.net/html/1544.html#comment Mon, 21 May 2007 10:10:35 +0000 easy http://www.phpv.net/html/1544.html
     Rasmus向新浪科技表示,在互联网公司中,技术并不是在任何时候都非常重要,他建议“创业不要贸然使用最先进的技术”。他还建议中国的工程师,要学好英文。

    新浪科技:你了解中国的软件工程师么?相对于美国的工程师,你认为他们有什么优势和弱点么?

    Rasmus Lerdorf:我觉得中国工程师有一个非常明显的长处,就是中国的搜索引擎工作者可以更好的理解多音节词的排列,而这是我们美国的研发者此则非常陌生。

    通常来说,我发现不同国家的研发者并没有完全的不同(还是有很多相同之处)。不同的玩家在不同的国家做着差不多相同的事情是一件非常有趣的事。在经济上他们是有所不同的,但是在感情和兴趣的重叠程度,比你想像的要大的多。

    新浪科技:在中美工程师间,如何更好的进行交流沟通呢?


    Rasmus Lerdorf:事实上,这是语言带来的问题,在开源项目中,比如PHP,我们有统一适用的C语言。但是不同语言的思想沟通,还是多少会受到一些语言的限制,尤其是当这个想法无法通过统一的技术语言来表达的时候。我也不清楚我们还需要多长时间来克服这个问题。

    虽然我很乐意建议美国的工程师都学一些中文,但这并不是很现实的事情。不论如何,英语成为了标准的互联网技术语言,因此,要想更好的进行交流,任务就落在了中国的工程师身上,他们可能需要更好的学习和使用英文。我不是美国人,母语也不是英文,所以我很理解和能够体会到不能使用母语工作的痛苦,但是我们必须面对这样的现实。

    新浪科技:互联网创业公司越来越多,你如何看待技术在互联网公司的作用?


    Rasmus Lerdorf:我不认为技术在任何时候都是非常重要的,事实上,在正确的时候,对市场做出正确的判断和决定是比任何细节更重要的。我见过很多的公司,他们因为太过于追求建立完美的结果,而贸然使用最先进的技术,但最后还是失败了。

    新浪科技:在中国各个领域间互联网的发展应用有所区别发展水平也不同,美国有没有同样的现象,你认为这是一个问题么?

    Rasmus Lerdorf:我认为这个现象在哪里都有。比如银行和金融系统更倾向于使用不同于 web2.0的技术。政府和军队系统也会对技术的使用有种种严格的要求,这些要求都会限制他们对技术种类的选择。

    我不确认这是否是什么大的问题,可能这会 限制一些公用的技术,例如php和mysol进入银行系统。如果你认为这的确是个问题,那么解决他的只是时间问题。开源通常情况下都是被技术普遍采用的。

    事实上我们并没有做任何的市场推广,也没有劝说CEO们选择使用我们的技术。事实上更好的技术总是会被大家所采用。市场推广在可评估和性价比为指导的领域 中更有效果。 ]]>