PHP闭包(Closure)初探

 

2013-05-01 23:42 by 豆浆油条 – melon, 1204 阅读, 3 评论, 收藏, 编辑

不知不觉发现PHP已经出到了5.5版本,而自己一直在用PHP5.2,让我看起来像深山出来的小伙子一样,又土又落后。在我习惯在javascript中使用闭包之后,忽然间对PHP的闭包打起了兴趣。

于是乎在网上下了个WAMP集成开发环境,是PHP5.3版本的(PHP5.3开始引入了闭包的特性),不得不说WAMP安装使用真的很方便。简单配置了一下,开始动手。

匿名函数

提到闭包就不得不想起匿名函数,也叫闭包函数(closures),貌似PHP闭包实现主要就是靠它。声明一个匿名函数是这样:

$func = function() {
    
}; //带结束符

可以看到,匿名函数因为没有名字,如果要使用它,需要将其返回给一个变量。匿名函数也像普通函数一样可以声明参数,调用方法也相同:

复制代码

$func = function( $param ) {
    echo $param;
};
$func( 'some string' ); //输出: //some string

复制代码

顺便提一下,PHP在引入闭包之前,也有一个可以创建匿名函数的函数:create function,但是代码逻辑只能写成字符串,这样看起来很晦涩并且不好维护,所以很少有人用。

实现闭包

将匿名函数在普通函数中当做参数传入,也可以被返回。这就实现了一个简单的闭包。

下边有三个例子

复制代码

//例一
//在函数里定义一个匿名函数,并且调用它
function printStr() {
    $func = function( $str ) {
        echo $str;
    };
    $func( 'some string' );
}

printStr();



//例二
//在函数中把匿名函数返回,并且调用它
function getPrintStrFunc() {
    $func = function( $str ) {
        echo $str;
    };
    return $func;
}

$printStrFunc = getPrintStrFunc();
$printStrFunc( 'some string' );




//例三
//把匿名函数当做参数传递,并且调用它
function callFunc( $func ) {
    $func( 'some string' );
}

$printStrFunc = function( $str ) {
    echo $str;
};
callFunc( $printStrFunc );

//也可以直接将匿名函数进行传递。如果你了解js,这种写法可能会很熟悉
callFunc( function( $str ) {
    echo $str;
} );

复制代码

连接闭包和外界变量的关键字:USE

闭包可以保存所在代码块上下文的一些变量和值。PHP在默认情况下,匿名函数不能调用所在代码块的上下文变量,而需要通过使用use关键字。

换一个例子看看:

复制代码

function getMoney() {
    $rmb = 1;
    $dollar = 6;
    $func = function() use ( $rmb ) {
        echo $rmb;
        echo $dollar;
    };
    $func();
}
getMoney(); //输出: //1 //报错,找不到dorllar变量

复制代码

可以看到,dollar没有在use关键字中声明,在这个匿名函数里也就不能获取到它,所以开发中要注意这个问题。

有人可能会想到,是否可以在匿名函数中改变上下文的变量,但我发现是不可以的:

复制代码

function getMoney() {
    $rmb = 1;
    $func = function() use ( $rmb ) {
        echo $rmb;
        //把$rmb的值加1
        $rmb++;
    };
    $func();
    echo $rmb;
}
getMoney(); //输出: //1 //1

复制代码

啊,原来use所引用的也只不过是变量的一个副本而已。但是我想要完全引用变量,而不是复制。

要达到这种效果,其实在变量前加一个 & 符号就可以了:

复制代码

function getMoney() {
    $rmb = 1;
    $func = function() use ( &$rmb ) {
        echo $rmb;
        //把$rmb的值加1
        $rmb++;
    };
    $func();
    echo $rmb;
}
getMoney(); //输出: //1 //2

复制代码

好,这样匿名函数就可以引用上下文的变量了。如果将匿名函数返回给外界,匿名函数会保存use所引用的变量,而外界则不能得到这些变量,这样形成‘闭包’这个概念可能会更清晰一些。

根据描述改变一下上面的例子:

复制代码

function getMoneyFunc() {
    $rmb = 1;
    $func = function() use ( &$rmb ) {
        echo $rmb;
        //把$rmb的值加1
        $rmb++;
    };

深入浅出 Zend Framework 编码规范

Zend 在着手开发 Zend Framework 之前,制定了一系列开发人员所应该遵守的规范。

具体规定参见http://framework.zend.com/wiki/display/ZFDEV/Zend+Framework+Contributor+Guide

其中关于编码规定的有 :

第四条 PHP Coding Standard (draft) – 这是所有参与 Zend Framework 这个项目的人员所要遵守的基本的 php 编码约定。

第十条 ZF Coding Standards (RC) – 则是第四条的补充,即 Zend Framework 组件开发人员所必须遵守的编码约定。

在手册附件B中我们看到的实际上是第四条。

在这里我将结合第四条对 ZF Coding Standards (RC) 分五章做详细讲解,尽量做到每个标准都附上例子。

建议在阅读下面内容前先读读 PEAR 编码约定。

原文 : http://kbs.kimbs.cn/blog/list/post/22/title/zend-framework-coding-standards-for-php-file-formatting

Zend Framework 中的 Php 编码标准 () – 文件格式

1. 对于只含有 php 代码的文件,我们将在文件结尾处忽略掉 “?>” 。这是为了防止多余的空格或者其它字符影响到代码。

实际上这个问题只有当你不开启压缩或缓存输出时才会出现

// php.ini – 禁止压缩输出及缓存输出
zlib.output_compression = off
output_buffering = off

// foo.php – 注意这个时候有一些空格或换行符掉在了 “?>” 之后,当然这在页面上是看不到的
[php] 
<?php
$foo = ‘foo’;
?>

[/php]

// index.php – 在包含 foo.php 的同时,实际上我们已经输出了一些空格或换行了[php]
<?php
include ‘foo.php’;
session_start();
?>
[/php]

这时我们将看到一个警告 (warning) “… Cannot send session cache limiter – headers already sent …” 这是因为我们在 session_start() 之前输出了一些看不到的字符。

2. __halt_compiler() 允许的任意的二进制代码的内容,被 Zend Framework 中的 PHP 文件或由它们产生的文件所禁止。

实际上这个规定是为了防止一些极个别的情况发生,例如上例中 :

// foo.php – 我们已经禁用了 “?>”
[php]
<?php
//
假如我们想通过 __halt_compiler() 输出某些东西
if (defined(‘__COMPILER_HALT_OFFSET__’)) {
    $fp = fopen(__FILE__, ‘r’);
    fseek($fp, __COMPILER_HALT_OFFSET__);
    var_dump(stream_get_contents($fp));
}

// 在程序结束时
__halt_compiler();

[/php]则在执行 index.php 时我们依然会看到警告,而只要把 __halt_compiler() 注释掉就不会报错了。需要注意的是 __halt_compiler() 实际上时语言结构,而非函数或方法。

3. 缩进由四个空格组成,禁止使用制表符 TAB

这个主要是为了代码美观整齐。因为在不同的编辑器里, TAB 制表符的长度是不一样的,而空格则是一样的。实际上这已经成为编写代码的默认标准之一。 

例如

[php]
if ($x == 1) {
    $indented_code = 1;
    if ($new_line == 1) {
        $more_indented_code = 1;
    }
}

[/php]

4. 变量赋值必须保持相等间距和排列。

例如

[php]
$variable1 = “demo”; 
$var2      = “demo2”;

[/php]

5. 每行代码长度应控制在80个字符以内,最长不超过120个字符。

因为 linux 读入文件一般以80列为单位,就是说如果一行代码超过80个字符,那么系统将为此付出额外操作指令。这个虽然看起来是小问题,但是对于追求完美的程序员来说也是值得注意并遵守的规范。

例如,下面是在 Zend/Navigation.php 中的一段代码

 

6. 每行结尾不允许有多余的空格。

这在很多 IDE 或者编辑器里都是可以设置的,例如在 save 保存操作执行时,顺便去掉多余的空格。

7. 行结束标志遵循 Unix 文本文件的约定,行必需以单个换行符 “\n” (LF) 结束。换行符在文件中表示为10,或16进制的 0x0A
不要使用苹果操作系统的回车 “\r” (0x0D) Windows 系统的回车换行组合如 “\r\n” (0x0D,0x0A)

这个规定没有贬低苹果机或者 Windows 的意思,而事实确实是,LAMP 还是主流,所以遵循其文件约定也是为了在 liunx 系统上表现更好的性能。

 

原文
 
http://kbs.kimbs.cn/blog/list/post/24/title/zend-framework-coding-standards-for-php-naming-conventions

Zend Framework 中的 Php 编码标准 () – 命名约定
1.
类命名总是对应于其所属文件的目录结构。

这源于 PEAR 命名约定,能让人从类名就能联想到该类的存放位置。

例如

Zend_Acl_Role
就代表 Zend/Acl/Role.php 文件。 

  

[php]
require_once ‘Zend/Acl/Role/Interface.php’;
class Zend_Acl_Role implements Zend_Acl_Role_Interface
{
    ……
}
[/php]

这是规定实际上提供了一种伪命名空间机制,这在 php5.3 php6 里面都将得到支持,到时候 Zend Framework 可能会有一翻变革。

2. 类名只允许有字母和数字字符,在大部分情况下不鼓励使用数字。下划线只允许做路径分隔符。

这也源自 PEAR 命名约定,只不过 Zend Framework 在其基础上制定了更严格的规定。

例如

Zend_Db_Table
是允许的。
Zend_Db_Table2
则是不鼓励的。
Zend-Db-Table
是不允许的。

3. 当你为其他开发人员提供 API 接口的时候,如果他们需要用一个复合的名称来确认这个接口,那么你必须把这个名称用下滑线 (underline) 隔开,而不是使用驼峰的形式。而且最好是以常量 (contant) 的形式。

这个规定可能有点难懂,让我们举个例子吧。例如在实例化 Zend_Db_Adapter_xxx 实例时,我们通常需要将 adapter 的名称作为参数传入到 Zend_Db::factory() 并由该工厂返回所需实例。

我们可以实例化 MySQL PDO 驱动

[php]
$db = Zend_Db::factory(‘PDO_MYSQL’, $config);
[/php]

也可以实例化 MsSQL PDO 驱动

[php]
$db = Zend_Db::factory(‘PDO_MSSQL’, $config);
[/php]

4. 如果类名包含多个单词,每个单词的第一个字母必须大写,连续的大写是不允许的。

例如

Zend_PDF
是不允许的。
Zend_Pdf
才是标准写法。

5. Zend 及其合作伙伴开发的 Zend Framework 类的命名必须总是以 “Zend_” 作为开头,并放在 “Zend/” 目录下。

例如

Zend_Db
是由 Zend 开发的,它通常以 “Zend/Db.php” 的形式存放。
My_Db
则是用户自己定义或继承自 Zend_Db 的类,所以它不得以 “Zend_” 作为开头,而且通常以 “My/Db.php” 的形式存放。

6. 接口类 (interface) 的定义必须遵循类名的定义规范,不同的是必须要以 _Interface 作为结尾。

例如

Zend_Controller_Dispatcher_Interface
对应的是 Zend/Controller/Dispatcher/Interface.php

 

7. 对于文件名,只有字母、数字字符、下划线和短横线 (“-“) 可用,空格是绝对不允许的。

例如

Zend/Controller/Front.php
是标准写法。
My/Controller/Common-Action_2.php
也是允许的。
My/Controller/Common Action.php
则是禁止的。

8. 包含任何 PHP 代码的任何文件应当以 “.php” 扩展名结尾,视图脚本默认拓展名是 “.phtml”,也可以用 “.html” 等代替。

例如

Zend/Controller/Front.php
是标准 php 文件写法,index.phtml 是默认视图脚本名。

我相信很多人都见过以下的一些 php 的拓展名

.php4
.php5
.phpx
.class.php
.include.php
html (
开玩笑?)

而在 Zend Framework 中你只会见到一种,那就是 “.php”

9. 函数名只允许由数字或字母组成,下划线是不允许的,首字母要小写,其后每个单词首字母要大写,即所谓的 “camelCaps” 规则。

例如

[php]
filterInput()
getElementById()
widgetFactory()
[/php]

等都是规范的写法。



[php]
FilterInput3()
getelementbyid()
widget_factory()
[/php]

等都是不允许的。

10. 函数名越详细越好,应该能够描述清楚该函数的功能。

例如

[php]
getOne($id) //
描述的不清不楚。
getOneRecordById($id) //
则非常详细而清晰。
[/php]

11. 对于对象成员的访问,我们必须始终使用 “get” “set” 方法。

例如

[php]
class Foo
{
    protected $_testObj;

    public function getTestObj()
    {
        return $this->_testObj;
    }

    public function setTestObj($testObj)
    {
        $this->_testObj = $testObj;
    }
}
[/php]

12. 当我们使用了某些设计模式的时候,我们的方法中应该含有该模式名。

例如

[php]
abstract class Zend_Cache
{
    // ……

    public static function factory($frontend, $backend,
        $frontendOptions = array(), $backendOptions = array(), 
        $customFrontendNaming = false, $customBackendNaming = false, 
        $autoload = false)
    {
        // ……
    }
}
[/php]

13. 当类成员函数被声明为 private 或者 protected 时,函数必须以下划线 “_” 为开头,一般情况下的函数不含下划线。

例如

[php]
class Zend_Foo
{
    protected function _fooBar()
    {
        // …
    }
}
[/php]

14. 如果我们需要把一些经常使用的函数或方法定义为全局函数,那么应该把它们以静态 (static) 的形式定义在类中。

例如

[php]
class Zend_Debug
{
    // ……

    public static function dump($var, $label = null, $echo = true)
    {
        // ……
    }
}
[/php]

15. 在类中被声明为 static 的函数或变量不应该再被声明为 private 私有,而应该为 protected 或者public ,如果只是不想被子类继承,则应该用 final 声明它们。

例如

[php]
class Foo
{
    final public static function fooFinally()
    {
    }
}

class Bar extends Foo
{
}

// This is wrong
Bar::fooFinally();

// Use Foo::fooFinally() instead
Foo::fooFinally();
[/php]

16. 函数或方法的初始中括号应该在函数声明的下一行顶格。

例如

[php]
function Myfunction($parameter1)
{
}
[/php]

17. 当函数参数不一定需要被赋值的时候,用 “null” 来代替 “false” 作为函数参数的默认值,除非该参数是 boolean 值。

例如,考虑如下代码

[php]
public function foo($required, $optional = null)
{
    if (isset($optional)) {
        echo ‘Echo something only when $optional is set and != null’;
    }
}

public function foo($required, $optional = false)
{
    if (isset($optional)) {
        echo ‘Always echo something’;
    }
}
[/php]

18. 变量只允许由字母组成,数字或下划线都是不允许的。

例如

[php]
$foo //
是正确的
$foo_foo //
是错误的
$foo2 //
也是错误的
[/php]

19. 被声明为 private 或者 protected 的类成员属性必须由下划线 “_” 作为开头,这也是唯一一种允许变量中出现下划线的情况,而声明为 public 的成员属性则在任何时候都不允许含有下划线。

例如

[php]
class Zend_Foo
{
    private $_barPrivate;
    protected $_barProtected;
    public $barPublic;
}
[/php]

20. 变量名必须像函数名那样,即首字母要小写,其后每个单词首字母要大写,即所谓的 “camelCaps” 规则。

例如

[php]
$compatibilityMode
$registryClassName
[/php]

21. 建议用描述性的变量的命名,变量名越详细越好,以至于像 $i $n 等等都是不鼓励使用的,特别是在超过20行的循环里面。

例如 Zend_Search_Lucene 中的一段代码

[php]
// read segmentInfos
for ($count = 0; $count < $segments; $count++) {
    $segName = $segmentsFile->readString();
    ……
}
[/php]

22. 常量 constant 必须仅包括字母,数字和下划线,而且必须全部大写,各个单词之间用下划线分割。

例如

[php]
“MY_CONSTANT_ONE” //
是允许的
“MYCONSTANTTWO” //
是不允许的
“my_constant_two” //
也是不允许的
[/php]

23. 常量应该在类中由 const 声明并定义,全局范围内的 define 是不鼓励使用的。

例如

[php]
class Zend_Acl
{
    const TYPE_ALLOW = ‘TYPE_ALLOW’;
    const TYPE_DENY  = ‘TYPE_DENY’;
    ……
}
[/php]

24. php 文档说明不一样的是,Zend Framework 中的 boolean 值和 null 值都是用小写的。

例如 Zend_Mail 中的一段代码

[php]
public function setMessageId($id = true)
{
    if ($id === null || $id === false) {
        return $this;
    } elseif ($id === true) {
        $id = $this->createMessageId();
    }
    ……
}
[/php]

原文
 http://kbs.kimbs.cn/blog/list/post/25/title/zend-framework-coding-standards-for-php-coding-style

Zend Framework 中的 Php 编码标准 () – 编码风格

1. PHP 代码必须以完整的形式来定界,短定界符只能用在视图。

例如

[php]
<?php
// Php code here
?>
   
// index.phtml
<?= ‘hello’ ?>
[/php]

2. 当一个字符串是纯文字组成的时候(即不含有变量),则必须总是以单引号()或者撇号(`)作为定界符。

例如
 
[php]
$a = ‘Example String’;
$b = `Excute Something`;
[/php]

3. 当一个字符串含有撇号(`)的时候,我们允许使用双引号()来定界字符串,特别是在些 SQL 语句的时候。

例如

[php]
$sql = “SELECT `id`, `name` from `people` WHERE `name`=’Fred’ OR `name`=’Susan'”;
[/php]

4. 变量替换中的变量只允许用 $+变量名
的形式。

例如

[php]
$greeting = “Hello $name, welcome back!”;   //
允许
$greeting = “Hello {$name}, welcome back!”; //
允许
$greeting = “Hello ${name}, welcome back!”; //
不允许
[/php]

5. 多个字符串必须用点号 “.” 来连接,且字符串与点号间必须用一个空格隔开。

例如

[php] 
$company = ‘Zend’ . ‘Technologies’;
[/php]

6. 当用点号 “.” 连接各字符串的时候,我们允许把它分割成多行以增强可读性。在这种情况下,点号 “.” 必须与等于号 “=” 对齐。

例如

[php]
$sql = “SELECT `id`, `name` FROM `people` “
     . “WHERE `name` = ‘Susan’ “
     . “ORDER BY `name` ASC “;
[/php]

7. 任何负数都不允许作为数组的索引,数组索引必须以任何非负数作为开头,而且强烈建议以0作为默认开头。

例如

[php]
$sampleArray = array(-1 => -1, 0 => 0); //
错误
$sampleArray = array(0 => -1, 1 => 0);  //
正确
$sampleArray = array(1 => -1, 2 => 0);  //
正确
[/php]

8. 当用 array 类型符号来构造数组的时候,必须在每个逗号之后加上一个空格来增强可读性。

例如

[php]
$sampleArray = array(1, 2, 3, ‘Zend’, ‘Studio’);
[/php]

9. 多行的索引数组同样允许用 array 类型符号来构造,只是我们需要为每行的每个值加上必要的空格来保持其整齐美观。

例如

[php]
$sampleArray = array(1,        2,  3,  ‘Zend’,
                     ‘Studio’, $a, $b, $c,
                     56.44,    $d, 500);
[/php]

10. 当使用 array 类型符声明关联数组的时候,我们鼓励把它分成多个行,只是我们必须同时保证每行的键与值的对齐,以保持美观。

例如

[php]
$sampleArray = array(‘firstKey’  => ‘firstValue’,
                     ‘secondKey’ => ‘secondValue’);
[/php]

11. 中括号的开始必须在类名的下一行顶格。

例如

[php]
class foo
{
    //
正确 {} 写法
}
class foo {
    //
错误 {} 写法
}
[/php]

12. 类定义必须拥有符合 phpDocumentor 标准的注释块。

例如

[php]
/**
*
类定义注释
*/
class Zend_Class
{
}
[/php]

13. 类中的所有代码都必须用4个空格来进行缩进。

例如

[php]
class Zend_Class
{
    $spaces = ‘4 spaces’;
    if ($spaces == ‘4 spaces’) {
        echo ‘is permitted !’;
    }
}
class Zend_Class
{
  $spaces = ‘less then 4 spaces’;
  if ($spaces != ‘4 spaces’) {
    echo ‘is not permitted !’;
  }
}
[/php]

14. 每个 php 文件只允许声明一个类。在类文件里面写其它代码是允许的,但并不鼓励这样做。假如真要附加代码的话,必须用空行来分隔。

例如

[php]
<?php
//
允许但并不鼓励这样做
class Zend_Class
{
    static $foo = ‘foo’;
}
echo Zend_Class::$foo;

<?php
/**
* 在同一文件里声明超过两个类是不允许的
*/
class Class_One
{
}
class Class_Two
{
}
?>
[/php]

15. 任何类变量的声明都必须放在类顶部,先于任何函数的声明。

例如

[php]
//
正确
class right
{
    public $foo = ‘
先于函数定义‘;
    public function fun()
    {
    }
}
//
错误
class wrong
{
    public function fun()
    {
    }
    public $foo = ‘
后于函数定义‘;
}
[/php]

16. 不允许用 var 符号来声明变量,类成员变量必须以 privateprotected public 来声明。其次,把类成员声明为 public 而直接引用虽然是允许的,但通常更好的方法是使用 get set 方法来访问类成员。

例如

[php]
class foo
{
    var $unpermitted = ‘
这是不允许的!’;
    private $privateVariable = ‘
私有变量‘;
    protected $protectedVariable = ‘
保护变量‘;
    public $publicVariable = ‘
公共变量‘;
    public function setPrivateVariable($value)
    {
        $this->privateVariable = $value;
    }
    public function getPrivateVariable()
    (
        return $this->privateVariable;
    )
}
$foo = new foo();
//
不鼓励使用
echo $foo->publicVariable;
//
鼓励使用
echo $foo->getPrivateVariable();
[/php]

17. 方法和函数必须总是用 privateprotected 或者 public 来声明其作用域。

例如
 
[php]
class foo
{
    function goo()
    {
        //
不允许的
    }
    private function bar()
    {
        //
正确
    }
    protected function baz()
    {
        //
正确
    }
    public function zoo()
    {
        //
正确
    }
}
[/php]

18. 按照 php 社区的习惯,静态 static 方法应该声明其作用域。

例如

[php]
class foo
{
    static function baz()
    {
        //
不允许的
    }
    public static function bar()
    {
        //
正确
    }
}
[/php]

19. 对于类成员函数和方法,中括号的开始必须位于函数和方法名的下一行顶格。

例如

[php]
class foo
{
    public function braceIsUnderneath()
    {
        //
正确
    }
    public function braceIsNotUnderneath() {
        //
错误
    }
}
[/php]

20. 在函数名与参数括号之间不允许出现多余的空格。

例如

[php]
class foo
{
    public function someSpacesAfterMe  ($a)
    {
        //
错误
    }
    public function noSpacesAfterMe($a)
    {
        //
正确
    }
}
[/php]

21. 引用只允许定义在函数参数中,实时传递引用是禁止的。

例如

[php]
function defineRefInMethod(&$a)
{
    $a = ‘a’;
}
function callTimePassRef($a)
{
    $a = ‘a’;
}
$b = ‘b’;
$c = ‘c’;
//
允许的
defineRefInMethod($b);
echo $b; // ‘a’
//
禁止的
callTimePassRef(&$c);
echo $c; // ‘a’
[/php]

22. 函数返回值不可以用括号包住,不然会降低可读性,而且假如以后函数修改为返回引用的话,这将会抛出一个异常。

例如

[php]
class foo
{
    public $bar = ‘bar’;
    public function goo()
    {
        return ($this->bar);
    }
    public function & zoo()
    {
        return ($this->bar);
    }
}
$foo = new foo();
//
看起来没问题
echo $foo->goo();
/**
*
程序会抛出一个 notice :
* “Only variable references should be returned by reference”
*/
echo $foo->zoo();
[/php]

23. 鼓励尽量使用类型提示,特别是在模块设计中。

例如

[php]
class Zend_Component
{
    public function foo(SomeInterface $object)
    {}

    public function bar(array $options)
    {}
}
[/php]

24. 尽量避免同时使用异常处理和类型提示,来检验参数的有效性。

例如

[php]
class Not_Zend_Class
{
}
class foo
{
    public function bar(Zend_Class $zc)
    {
    }
    public function goo($zc)
    {
        if (!$zc instanceof Zend_Class) {
            throw new Exception(‘$zc
不是 Zend_Class 的实例‘);
        }
    }
}
$foo = new foo();
$zc  = new Not_Zend_Class();
/**
* Catchable fatal error
错误显示 :
* “Argument 1 passed to foo::bar() must be an instance of Zend_Class,
* instance of Not_Zend_Class given”
*/
$foo->bar($zc);
/**
*
错误信息 : ‘$zc 不是 Zend_Class 的实例
*/
try {
    $foo->goo($zc);
} catch (Exception $e) {
    echo $e->getMessage();
}
[/php]

25. 函数参数必须用
逗号+空格
来分隔。

例如

[php]
//
正确
threeArguments(1, 2, 3);
//
错误
threeArguments(1,2,3);
[/php]

26. 对于参数为数组的函数,参数中的数组应该分成多行以增强可读性。

例如

[php]
threeArguments(array(1, 2, 3), 2, 3);
threeArguments(array(1,        2,  3,    ‘Zend’,
                     ‘Studio’, $a, $b,   $c,
                     56.44,    $d, 500), 2,      3);
[/php]

27. 基于 “if”, “else” “else if” 的条件控制里,我们必须用空格间隔开语句和括号。

例如

[php]
//
正确
if ($spaceOutSide) {
    // …
} else if ($spaceOutSide) {
    // …
} else {
    // …
}
//
错误
if($noSpaceOutSide){
    // …
}else if($noSpaceOutSide){
    // …
}else{
    // …
}
[/php]

28. 在条件控制语句的条件括号内,必须用空格将操作符与其它元素隔开。如果遇到很长的逻辑判断,则鼓励用内嵌括号来分割各个逻辑。

例如

[php]
if (($a != 2) and ($b == 1)) {
    $a = $b;
}
[/php]

29. 中括号的开始 “{” 必须与条件控制语句位于同一行,结束 “}” 必须总是独占一行且顶格,控制流程内容必须用4个空格进行缩进。

例如
 
[php]
//
正确
if ($braceSameLine) {
    echo ‘
很好 : 4空格缩进‘;
}
//
错误
if ($braceNotSameLine)
{
  echo ‘
不好
2空格缩进‘;
}
[/php]

30. 在某些场合,php 允许条件控制语句舍弃中括号 “{}” ,但是根据此编码规范,所有 “if”, “else if” “else” 语句都必须使用中括号 “{}” 定界内容。

例如
 
[php]
//
“{}” 定界内容是正确的
if ($a != 2) {
    $a = 2;
} else if ($a == 3) {
    $a = 4;
} else {
    $a = 7;
}
//
没有用 “{}” 定界是错误的
if ($a != 2)
    $a = 2;
else if ($a == 3)
    $a = 4;
else
    $a = 7;
[/php]

31. “elseif” 语句是不允许的,必须使用 “else if”

例如
 
[php]
if (true) {
    //
} else if {
    //
正确
}
if (true) {
    //
} elseif {
    //
错误
}
[/php]

32. “switch” 条件控制语句中,必须用空格将待测参数与其它元素分隔开。

例如
 
[php]
switch ($num) {
    //
正确
}
switch($num){
    //
错误
}
[/php]

33. “switch” 语句的内容必须以4个空格缩进,“case” 条件控制的内容必须再加4个空格进行缩进。

例如
 
[php]
switch ($indentedSpaces) {
  case 2:
    echo “
错误“;
    break;
    case 4:
        echo “
正确“;
        break;
    default:
        break;
}
[/php]

34. “switch” 语句中应该总是包括 “default” 控制。

例如

[php] 
switch ($isWithDefault) {
    case false:
        break;
    default:
        echo “
‘default’ 控制是正确的“;
        break;
}
switch ($isWithDefault) {
    case false:
        echo “
没有 ‘default’ 控制是错误的“;
        break;
}
[/php]

35. 有时候我们需要在 “case” 语境中省略掉 “break” “return” ,这个时候我们必须为这些 “case” 语句加上 “// break intentionally omitted” 注释。

例如
 
[php]
switch ($numPeople) {
    case 1: // break intentionally omitted
    case 2:
        break;
    default:
        break;
}
[/php]

36. 关键词 “global” 是不允许使用的,请用 $GLOBAL[xxx] 替代。

例如
 
[php]
$a = ‘a’;
function foo()
{
    //
错误
    global $a;
    $foo = $a;
    //
正确
    $foo = $GLOBALS[‘a’];
    return $foo;
}
echo foo();
[/php]

原文
 http://kbs.kimbs.cn/blog/list/post/26/title/zend-framework-coding-standards-for-php-inline-documentation

Zend Framework 中的 Php 编码标准 () – 内嵌文档

1. Docblocks start always with “/*” or “/**”. The use of “#” is not allowed. The “//” is only allowed for comments within functions.

For example :
[php]
/**
* Class docblock
*/
class foo
{
    /* Variable docblock */
    public $foo;

    /**
     * Function docblock
     */
    public function __construct()
    {
        // Commentted hy ‘//’ only allowed within function
        $this->foo = ‘foo’;
        # Commentted by ‘#’ is not allowed within function
        $this->foo = ‘bar’;
    }
}
[/php]

2. A docblock must contain a short description and minimum one parameter. Optionally a long description and multiple parameters can be added.

For example : 
[php]
/**
* One short description and one parameter at least
*
* @param
*/

/**
* Long description and multiple parameters below
*
* One empty line to seperate the
* SHORT_DESCRIPTION,
* LONG_DESCRIPTION,
* PARAMETERS
*
* @param
* @todo
*/
[/php]

3. All docblock parts which are not keywords have to be under each other with the same indenting.

For example : 
[php]
/**
* Right
*
* @component All descriptive parts
* @uses      have the same indenting
*/

/**
* Wrong
*
* @component All descriptive parts
* @uses don’t have the same indenting
*/
[/php]

4. Also when describing parameters the keywords, parameters, and description have to have the same indenting to be under each other.

For example : 
[php]
/**
* Right, all descriptions are indented
*
* @param string  $value      Description of this value
* @param integer $othervalue Another description
*/

/**
* Wrong, not indenting
*
* @param string $value Description of this value
* @param integer $othervalue Another description
*/
[/php]

5. Each file which is delivered with the Zend Framework must have the following header block : 

[php]
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category    Zend
* @package     __PACKAGENAME__
* @subpackage  __SUBPACKAGENAME__
* @copyright   Copyright (c) 2005-__ENDDATE__ Zend Technologies USA Inc. (http://www.zend.com)
* @license     http://framework.zend.com/license/new-bsd     New BSD License
* @version     $Id: $
* @depreciated Since 0.0.1
*/
[/php]

This format can be used in any project, for example : 
[php]
<?php
/**
* Project name
*
* LICENSE
*
* License information
* in multiple lines (maybe)
*
* @category    Category name
* @package     Package of this file
* @subpackage  Subpackage which include this file
* @copyright   Copyright information
* @license     License info for short
* @version     $ Id and version description $
* @depreciated Since xxx
*/
[/php]

6. When other filestypes are used like *.SH, *.BAT, *.JS and so on, the header block must also be contained as header comment. Only when a filetype does not support comments like *.CSS the header block can be omited.

For example :
// zf.bat

REM Zend Framework
REM
REM LICENSE
REM
REM This source file is subject to the new BSD license that is bundled
REM with this package in the file LICENSE.txt.
REM It is also available through the world-wide-web at this URL:
REM http://framework.zend.com/license/new-bsd
REM If you did not receive a copy of the license and are unable to
REM obtain it through the world-wide-web, please send an email
REM to license@zend.com so we can send you a copy immediately.
REM
REM Zend
REM Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
REM http://framework.zend.com/license/new-bsd     New BSD License

// zf.sh

#############################################################################
# Zend Framework
#
# LICENSE
#
# This source file is subject to the new BSD license that is bundled
# with this package in the file LICENSE.txt.
# It is also available through the world-wide-web at this URL:
http://framework.zend.com/license/new-bsd
# If you did not receive a copy of the license and are unable to
# obtain it through the world-wide-web, please send an email
# to license@zend.com so we can send you a copy immediately.
#
# Zend
# Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
http://framework.zend.com/license/new-bsd     New BSD License
#############################################################################

// *.js
The same with php files.

7. The @package clause has to be the component which this file is part of it, there must exist exact one @package clause per header.

For example : 
[php]
/**
* …
*
* @package    Zend_Controller
* @subpackage …
* @copyright  …
* @license    …
*/
abstract class Zend_Controller_Router_Route_Abstract implements Zend_Controller_Router_Route_Interface
{
    // …
}
[/php]

Another example : 
[php]
/**
* …
*
* @package    Zend_Db
* @subpackage …
* @copyright  …
* @license    …
*/
class Zend_Db_Adapter_Mysqli extends Zend_Db_Adapter_Abstract
{
    // …
}
[/php]

8. The @subpackage clause has to be a logical separation within this component. Logical separations occur when there are several directories which seperate parts of the same component. Only files which are in the main directory of the framework which is “Zend” can omit the subpackage. There must exist maximum one @subpackage clause per header.

First example : 
[php]
/**
* @category   …
* @package    …
* @subpackage Adapter
* @copyright  …
* @license    …
*/
class Zend_Db_Adapter_Mysqli extends Zend_Db_Adapter_Abstract
{
    // …
}
[/php]

Second example : 
[php]
/**
* @category   …
* @package    …
* @subpackage Table
* @copyright  …
* @license    …
*/
class Zend_Db_Table_Select extends Zend_Db_Select
{
    // …
}
[/php]

Third example for no subpackage : 
[php]
/**
* @category   Zend
* @package    Zend_Auth
* @copyright  …
* @license    …
*/
class Zend_Auth
{
    // …
}
[/php]

9. The @copyright clause has to include the actual year of the release of the framework.

For example : 
[php]
/**
* @category   …
* @package    …
* @copyright  Copyright (c) 2005-2008 Zend …
* @license    …
*/
class Zend_Acl
{
    // …
}
[/php]

10. If this file contains a depreciated class it must have the optional @depreciated clause.

For example : 
[php]
<?php
/**
* …
*
* @depreciated Since 1.1.1
*/
class depreciateMe
{
}
[/php]

11. All classes/files which are required must contain a @see clause.

For example : 
[php]
/**
* @see Zend_Controller_Dispatcher_Interface
*/
require_once ‘Zend/Controller/Dispatcher/Interface.php’;
[/php]

12. A class or interface in Zend Framework must have a class header which clauses must be added in the below seen order :

[php]
/**
* Description of the class
*
* More descriptive text which
* is allowed to span multiple lines
*
* @category    Zend
* @package     __PACKAGE__
* @subpackage  __SUBPACKAGE__
* @uses        __USES__
* @see         __SEE__
* @since       __SINCE__
* @copyright   Copyright (c) 2005-__ENDDATE__ Zend Technologies USA Inc. (http://www.zend.com)
* @license     http://framework.zend.com/license/new-bsd     New BSD License
* @depreciated Since 0.0.1
*/
class Zend_Controller_Dispatcher extends Zend_Controller_Dispatcher_Class implements Zend_Controller_Dispatcher_Interface
[/php]

13. The @uses clause in header has to contain the extended or implemented classname. When multiple classes or interfaces are used, you must include a @uses clause for every class or interface. When no class is extended or interface is implemented then the @uses clause must be ommitted.

For example : 
[php]
/**
* …
*
* @uses       ArrayAccess
* @uses       Iterator
* @uses       Countable
* @package    …
* @copyright  …
* @license    …
*/
class Zend_Dojo_Data implements ArrayAccess,Iterator,Countable
{
    // …
}
[/php]

14. The @see clause in header is optional and can be added to link to another component within the framework. You can add multiple clauses.

For example : 
[php]
/**
* …
*
* @see        Zend_Parent
* @see        Zend_Interface
* @package    …
* @copyright  …
* @license    …
*/
class Zend_Class extends Zend_Parent implements Zend_Interface
{
    // …
}
[/php]

15. The @since clause can be added optionally to include the version since which this class is available. It must include the complete version number of the framework. Only one since clause is allowed to be added.

For example : 
[php]
/**
* …
*
* @category   …
* @package    …
* @copyright  …
* @license    …
* @since      Class available since Release 0.6.0
*
* @todo       …
*/
class Zend_Console_Getopt
{
    // …
}
[/php]

16. Each function must have a function header.

For example : 
[php]
class Zend_Foo
{
    /**
     * Full text description of this function.
     * It described all possible things
     * but should not extend the line length of 100 characters.
     *
     * @param  boolean $value Value for checking
     * @since  Version 1.2.3
     * @see    Zend_Anything
     * @throws Zend_Exception When value is false
     * @return array
     */
    public function bar($value)
    {
        require_once(‘Zend/Anything.php’);
        if ($value) {
            return array(‘foo’ => $value);
        } else {
            throw new Zend_Exception(‘Throw me !’);
        }
    }
}
[/php]

17. All parameters of the function must be available. The types allowed are listed here :

(a) boolean
(b) integer
(c) string
(d) float
(e) array
(f) false
(g) true
(h) null
(i) Zend_Xxx (must be an existing class of the framework)

18. If more than one type could be used then the possible types have to be seperated with “|” like show above.

For example : 
[php]
/**
* @param array|null $config configurations
*/
[/php]

19. If a parameter can be omitted the description must prepend a (optional) to the description.

For example : 
[php]
/**
* @param array|null $config (optional) My optional value
*/
[/php]

20. If the function can throw an exception the @throws clause must be declared. Multiple exception types must be seperated with “|”. Also a description must be added why the exception is thrown.

For example : 
[php]
class Zend_XmlRpc_Server extends Zend_Server_Abstract
{
    /**
     * Handle an xmlrpc call (actual work)
     *
     * @param  Zend_XmlRpc_Request $request
     * @return Zend_XmlRpc_Response
     * @throws Zend_XmlRpcServer_Exception|Exception
     * Zend_XmlRpcServer_Exceptions are thrown for internal errors; otherwise,
     * any other exception may be thrown by the callback
     */
    protected function _handle(Zend_XmlRpc_Request $request)
    {
        // …
    }
}
[/php]

21. A @return clause must always be defined. Only for class constructor and destructor the @return clause must be ommitted. If multiple types can be returned the types must be seperated with “|”. An description can be appended, but is not necessary.

For example : 
[php]
class Zend_Form implements Iterator, Countable, Zend_Validate_Interface
{
    /**
     * Overloading: access to elements, form groups, and display groups
     *
     * @param  string $name
     * @return Zend_Form_Element|Zend_Form|null
     */
    public function __get($name)
    {
        // …
    }
}
[/php]

22. If the class itself is returned (fluid interface) then the description “Provides a fluent interface” must be added.

For example : 
[php]
class Zend_Auth
{
    /**
     * Sets the persistent storage handler
     *
     * @param  Zend_Auth_Storage_Interface $storage
     * @return Zend_Auth Provides a fluent interface
     */
    public function setStorage(Zend_Auth_Storage_Interface $storage)
    {
        $this->_storage = $storage;
        return $this;
    }
}
[/php]

23. If the function does not return any value then the return value must be set to “void”.

For example : 
[php]
class Zend_Application
{
    /**
     * Run the application
     *
     * @return void
     */
    public function run()
    {
        $this->getBootstrap()->run();
    }
}
[/php]

24. Keep in mind that the “void” and “null” are not the same. Null means that a empty variable is returned. But void means that nothing is returned. This is a small but important difference. Therefor when return is even not called, void has to be declared in the function docblock.

For example :
[php]
class Zend_Class
{
    /**
     * @return void
     */
    public function returnTheVoid()
    {
        // Return nothing
    }
    /**
     * @return null
     */
    public function returnTheNull()
    {
        return null;
    }
}
[/php]

Zend Framework 中的 Php 编码标准 () – 错误与异常

原文
 http://kbs.kimbs.cn/blog/list/post/27/title/zend-framework-coding-standards-for-php-errors-and-exceptions

1. Zend Framework 的代码应该不存在 E_STRICT 兼容问题。在开启错误报告 error_reporting 级别为 E_ALL | E_STRICT 时,Zend Framework 的代码不应该抛出任何警告(E_WARNING, E_USER_WARNING),任何通知(E_NOTICE, E_USER_NOTICE)以及任何兼容问题(E_STRICT)。

这就是说,Zend Framework 尽量避免代码写法上的错误。而如果真的发生程序中断,也只能是逻辑错误。

2. Zend Framework 不应该存在 Php 错误,如果我们不得不遇到错误的话,请用异常来处理。Zend Framework 中有专门的异常类来为用户提供友好的异常处理。

例如
 
[php]
class Zend_Exception extends Exception
{
}

class Zend_Db_Exception extends Zend_Exception
{
}

class Zend_Db
{
    public static function factory($adapter, $config = array())
    {
        // …
        if (!is_array($config)) {
            /**
             * @see Zend_Db_Exception
             */
            require_once ‘Zend/Db/Exception.php’;
            throw new Zend_Db_Exception(‘Adapter parameters must be in’
                                      . ‘an array or a Zend_Config object’);
        }
    }
}
[/php]

3. 在框架模块内的异常统一用 new 关键字构造是公认的良好习惯。

例如
 
[php]
require_once ‘Zend_Component_SpecificException.php’;
class Zend_Component
{
    public function foo($condition)
    {
        if ($condition) {
            throw new Zend_Component_SpecificException(
                ‘
一些友好的信息‘);
        }
    }
}
[/php]

4. 异常必须延迟加载。

例如
 
[php]
//
正确
if ($condition) {
    require_once ‘Zend_Component_SpecificException.php’;
    throw new Zend_Component_SpecificException(
        ‘
一些友好信息‘);
} else {
    // …
}

// 错误
require_once ‘Zend_Component_SpecificException.php’;
if ($condition) {
    throw new Zend_Component_SpecificException(
        ‘
一些友好信息‘);
} else {
    // …
}
[/php]

5. 通俗地讲,如果用户希望 Zend Framework 模块做出一些超出其能力范围的工作时,那么抛出异常则是明智而正确的选择。相反,假如该模块能够处理用户的需求,但用户却给出各种意想不到的输入,这个时候,模块就不应该抛出异常,而应该正常运行下去。

例如
 
[php]
if ($canNotPerformThisAction) {
    require_once ‘Zend/Exception.php’;
    throw new Zend_Exception(‘
不能执行此动作 !’);
}

$foo = ‘bar’;
if ($foo == ‘foo’) {
    echo ‘对的‘;
} else {
    //
没必要抛出异常
}
[/php]

6. 避免抛出 Exception 基类异常,而应该尽量使用派生异常类,这可以让人清楚知道问题所在。

例如
 
[php]
class Zend_Db
{
    public static function factory($adapter, $config = array())
    {
        if (!is_array($config)) {
            throw new Exception(‘
我们根本不知问题发生在哪儿。‘);
        }
        if (!is_array($config)) {
            require_once ‘Zend/Db/Exception.php’;
            throw new Zend_Db_Exception(“
我们都知道问题出在 Zend_Db 这儿。“);
        }
    }
}
[/php]

7. 尽量避免去捕捉 Exception 基类异常。如果在 try 语句里面可能抛出多种异常的话,那么我们应该为各种异常准备各自独立的 catch 块,而不是仅用一个 catch 块去捕捉 Exception 基类异常。

例如
 
[php]
// index.php
try {
    $app->run();
} catch (Zend_Db_Exception $e) {
    die(‘
数据库异常 !’);
} catch (Zend_Acl_Exception $e) {
    die(‘
权限分配异常 !’);
} catch (Zend_Auth_Exception $e) {
    die(‘
身份认证异常 !’);
} catch (Zend_Exception $e) {
    //
所有其它异常
}
[/php]

8. 我们通常需要在类中通过拓展多个异常类来区分各种不同的情况。例如,我们需要创建两个异常类来区分参数错误用户缺乏权限两种情况。

例如
 
[php]
class Zend_Db_Exception extends Zend_Exception
{
    //
数据库异常基类
}

class Zend_Db_Select_Exception extends Zend_Db_Exception
{
    // 用于处理 select 类异常
}

class Zend_Db_Table_Exception extends Zend_Db_Exception
{
    // 用于处理数据库表的异常
}
[/php]

9. 不要把所有诊断信息都放在异常的 message 里,我们可以在任何需要的时候创建自己的异常类的成员和方法,来为 catch 语句提供帮助。我们需要做的就是在 constructor 构建异常类时,传入正确的参数信息。

例如
 
[php]
class Zend_Exception extends Exception
{
}

class My_Exception extends Zend_Exception
{
    private $_importantDiagnostic;
    public function setImportantDiagnostic($value)
    {
        $this->_importantDiagnostic = $value;
    }
    public function getImportantDiagnostic()
    {
        return $this->_importantDiagnostic;
    }
    public function __construct($message = null, $code = 0, $value)
    {
        parent::__construct($message, $code);
        $this->setImportantDiagnostic($value);
    }
}

try {
    if ($isMyFault) {
        throw new My_Exception(‘没有信息‘, 0, ‘信息在这儿‘);
    }
} catch (My_Exception $e) {
    echo $e->getImportantDiagnostic();
}
[/php]

10. 在错误发生的时候,程序不应该保持沉默,甚至对异常置之不理。而是应该要么修正它,要么抛出新的异常来代替它。

例如
 
[php]
try {
} catch (My_Exception $e) {
    tryToCorrectIt($e);
} catch (My_Exception $e) {
    throw new My_Exception($e->getMessage(), ‘110’);
} catch (My_Exception $e) {
    //
不作为是愚蠢的行为!
}
[/php]

11. 我们应该为我们程序的不同层面准备不同的异常处理。例如,我们不应该把数据逻辑层的错误(即俗称 SQLException)搬到业务逻辑层。

例如
 
[php]
class My_Dao_User_Exception extends Zend_Db_Table_Exception
{
    //
数据层 User 异常类
}

class My_Dao_User extends Zend_Db_Table_Abstract
{
    // 数据层 User 对象
}

class My_Bis_User_Exception extends Zend_Exception
{
    // 业务层 User 异常类
}

/**
* 业务层 User 对象
*/
class My_Bis_User
{
    private $_daoUser = null;

    public function setDaoUser($user)
    {
        $this->_daoUser = $user;
    }

    public function getDaoUser()
    {
        return $this->_daoUser;
    }

    public function throwSomething()
    {
        try {
            if ($wrong) {
                throw new My_Dao_User_Exception(‘你不应该把数据层的异常搬到这里!‘);
            } else if ($right) {
                throw new My_Bis_User_Exception(‘
这才是正确的。‘);
            }
        } catch (Zend_Exception $e) {
            // …
        }
    }
}
[/php]

12. 不要把异常处理机制当成控制流程,或者仅仅是返回某些值。

例如
 
[php]
function itIsWrong()
{
    try {
        //
做些小动作
    } catch (Exception_One $e) {
        doSomething($e);
    } catch (Exception_Two $e) {
        doSomething($e);
    } catch (Exception_Three $e) {
        return true;
    }
}

function isRight()
{
    try {
        // 做些小动作
    } catch (Exception_One $e) {
        correctIt($e);
    } catch (Exception_Two $e) {
        correctIt($e);
    } catch (Exception_Three $e) {
        //
从不 return;
        throw new Exception(‘
我们抛出它,而不是返回什么东西!‘);
    }
}
[/php]

13. 在用 catch 语句块处理异常的时候,我们应该首先释放多余的内存资源,如数据库连接,网络资源连接等。Php 并不提供类似 finally 之类的语句来进行垃圾处理。

例如
 
[php]
try {
    $db = getDbConnection();
    throw new Exception(‘
发生了错误。‘);
} catch (Exception $e) {
    unset($db);
    handleExceptionAndGoOn($e);
}
[/php]