此书无中文版,但是写的极好!本来想翻译的,可是时间不允许了。
http://www.amazon.com/Advanced-PHP-Programming-George-Schlossnagle/dp/0672325616/ref=pd_rhf_dp_p_t_1
约定:加粗字体表示章节,由于时间关系解释性的说明全部省略。
《高级 PHP程序设计》
简介
PHP在企业级开发
一个编程语言满足下面6方面才能满足关键的商业应用:
快速原型设计和实施
现代编程范式的支持
可伸缩性
高性能
互操作性
可扩展性
平台和版本
本书主要是针对php5,更主要是使你的代码更快,更敏捷,设计的更好。
本书基于linux编写的。
平台和版本
平台和版本
第一部分 实施和开发方法
第一章 代码风格
选择适合你的代码风格
代码格式化和布局
包括行的长度,使用空白,使用SQL是最基本的技能。
缩进
本书使用缩进来表示代码块,但不能夸大其重要性。虽然php中不强制缩进,但是缩进是一个有用的工具。
考虑下面的代码
if($month == 'september' || $month == 'april' || $month == 'june' || $month ==
'november') { return 30;
}
else if($month == 'february') {
if((($year % 4 == 0) && !($year % 100)) || ($year % 400 == 0)) {
return 29;
}
else {
return 28;
}
}
else {
return 31;
}
和下面的代码比较,除了缩进都是相同的。
if($month == 'september' ||
$month == 'april' ||
$month == 'june' ||
$month == 'november') {
return 30;
}
else if($month == 'february') {
if((($year % 4 == 0) && ($year % 100)) || ($year % 400 == 0)) {
return 29;
}
else {
return 28;
}
}
else {
return 31;
}
后一段代码比前一段在逻辑上更好分辨。
当你使用tab代码缩进,你需要做出一致性选择使用硬或软tab,硬是常规选项,而软实际上是由一定量的空格表示,使用软的好处是他们总是相同,我比较喜欢软。当你使用硬,在多个开发人员使用不同的编辑器会造出不一致。
选择tab的宽度,四个空格的标签产生可读的代码。
行长
前面的第一段代码太长,这样不便于跟踪和调试,应该把长行分为多行,例如:
if($month == 'september' || $month == 'april' ||
$month == 'june' || $month == 'november') {
return 30;
}
可以缩进对齐条件
if($month == 'september' ||
$month == 'april' ||
$month == 'june' ||
$month == 'november')
{
return 30;
}
这个方法同样适合于函数的参数
mail("postmaster@example.foo",
"My Subject",
$message_body,
"From: George Schlossnagle <george@omniti.com>\r\n");
一般,我会80个字符就要换行,因为这是一个标准unix终端窗口的宽度。
使用空白
$lt = localtime();
$name = $_GET['name'];
$email = $_GET['email'];
$month = $lt['tm_mon'] + 1;
$year = $lt['tm_year'] + 1900;
$day = $lt['tm_day'];
$address = $_GET['address'];
通过空白进行逻辑分组
$name = $_GET['name'];
$email = $_GET['email'];
$address = $_GET['address'];
$lt = localtime();
$day = $lt['tm_day'];
$month = $lt['tm_mon'] + 1;
$year = $lt['tm_year'] + 1900;
SQL指引
$query = "SELECT FirstName, LastName FROM employees, departments WHERE
employees.dept_id = department.dept_id AND department.Name = 'Engineering'";
上面的sql组织的不好,可以从以下方面修改:
关键字大写;关键字换行;使用表的别名保持代码整洁
$query = "SELECT firstname,
lastname
FROM employees e,
departments d
WHERE e.dept_id = d.dept_id
AND d.name = 'Engineering'";
控制流结构
两种方式:条件和循环
控制结构使用大括号
php采用c语言风格,单行php条件语句不用使用大括号,例如下面的代码是正确的:
if(isset($name))
print "Hello $name";
但是,这样会引起混乱,应该总是使用大括号
if(isset($name)) {
print "Hello $name";
}
else {
print "Hello Stranger";
}
始终使用大括号
条件语句中三种使用括号的风格
BSD风格我比较喜欢
if ($condition)
{
// statement
}
GNU风格
if ($condition)
{
// statement
}
K&R 风格
if ($condition) {
// statement
}
for和foreach和while
如果for或者foreach循环可以做的事情不应该使用while循环
function is_prime($number)
{
if(($number % 2) != 0) {
return true;
}
$i = 0;
while($i < $number) {
// A cheap check to see if $i is even
if( ($i & 1) == 0 ) {
continue;
}
if ( ($number % $i ) == 0) {
return false;
}
$i++;
}
return true;
}
不小心会增加无限循环,使用for更加自然
function is_prime($number)
{
if(($number % 2) != 0) {
return true;
}
for($i=3; $i < $number; $i++) {
// A cheap check to see if $i is even
if( ($i & 1) == 0 ) {
continue;
}
if ( ($number % $i ) == 0) {
return false;
}
}
return true;
}
对数组迭代的时候foreach比for更好
$array = (3, 5, 10, 11, 99, 173);
foreach($array as $number) {
if(is_prime($number)) {
print "$number is prime.\n";
}
}
这样比使用for更快,因为避免计数器的使用。
使用break和continue控制循环
不需要循环的时候使用break跳出循环
$has_ended = 0;
while(($line = fgets($fp)) !== false) {
if($has_ended) {
}
else {
if(strcmp($line, '_END_') == 0) {
$has_ended = 1;
}
if(strncmp($line, '//', 2) == 0) {
}
else {
// parse statement
}
}
}
这个例子比前面的更短,而且避免了深层次的嵌套
while(($line = fgets($fp)) !== false) {
if(strcmp($line, '_END_') == 0) {
break;
}
if(strncmp($line, '//', 2) == 0) {
continue;
}
// parse statement
}
避免很深层的循环
常见的错误是在一个浅循环中使用深层嵌套
$fp = fopen("file", "r");
if ($fp) {
$line = fgets($fp);
if($line !== false) {
// process $line
} else {
die("Error: File is empty);
}
else { die("Error: Couldn't open file");
}
消除不必要的嵌套
$fp = fopen("file", "r");
if (!$fp) {
die("Couldn't open file");
}
$line = fgets($fp);
if($line === false) {
die("Error: Couldn't open file");
}
// process $line
命名符号
function test($baz)
{
for($foo = 0; $foo < $baz; $foo++) {
$bar[$foo] = "test_$foo";
}
return $bar;
}
下面的代码使用更有意义的变量名和函数名
function create_test_array($size)
{
for($i = 0; $i < $size; $i++) {
$retval[$i] = "test_$i";
}
return $retval;
}
三类命名规则:
全局变量要使用在全局范围
长时间存在的变量可存在于任何范围但要包含重要信息或大块代码的引用
临时变量用于小部分代码保持临时信息
常量和真正的全局变量
全局变量和常量要使用大写字母,便于辨识。
$CACHE_PATH = '/var/cache/';
...
function list_cache()
{
global $CACHE_PATH;
$dir = opendir($CACHE_PATH);
while(($file = readdir($dir)) !== false && is_file($file)) {
$retval[] = $file;
}
closedir($dir);
return $retval;
}
错误使用全局变量的原因:
它们可以在任何地方被改变不好定位;
污染了全局命名空间,例如使用一个全局变量命名为计数器$counter同时你还有另一个计数器也是$counter,随着代码的增长这种冲突越来越不可避免;
解决方案是使用一个“访问器”函数。
global $database_handle;
global $server;
global $user;
global $password;
$database_handle = mysql_pconnect($server, $user, $password);
可以使用如下的类:
class Mysql_Test {
public $database_handle;
private $server = 'localhost';
private $user = 'test';
private $password = 'test';
public function __construct()
{
$this->database_handle =
mysql_pconnect($this->server, $this->user, $this->password);
}
}
第二章中将探索更有效的方式处理这个例子,当我们处理单例模式和封装类时。
有时候,你需要访问一个特定的变量,像这样:
$US_STATES = array('Alabama', ... , 'Wyoming');
这个例子中类做了太多的事情,如果你想在这里避免全局变量,你可以使用一个访问函数全局数组使用一个静态变量。
function us_states()
{
static $us_states = array('Alabama', ... , 'Wyoming');
return $us_states;
}
长时间存在的变量
应该有简洁描述性的名称,长时间存在的变量不一定是全局的,甚至在主题范围内。
下面例子中变量名帮助理解代码的意思。
function clean_cache($expiration_time)
{
$cachefiles = list_cache();
foreach($cachefiles as $cachefile) {
if(filemtime($CACHE_PATH."/".$cachefile) > time() + $expiration_time) {
unlink($CACHE_PATH."/".$cachefile);
}
}
}
临时变量
临时变量的名称要简明扼要。由于临时变量通常只存在于一个小的代码块,所以他们并不需要有说明性名称。 特别是用于循环的数值变量应该始终被命名为J,K,L,M,和n
等。
比较这个例子
$number_of_parent_indices = count($parent);
for($parent_index=0; $parent_index <$number_of_parent_indices; $parent_index++) {
$number_of_child_indices = count($parent[$parent_index]);
for($child_index = 0; $child_index < $number_of_child_indices; $child_index++) {
my_function($parent[$parent_index][$child_index]);
}
}
例如
$pcount = count($parent);
for($i = 0; $i < $pcount; $i++) {
$ccount = count($parent[$i]);
for($j = 0; $j < $ccount; $j++) {
my_function($parent[$i][$j]);
}
}
这样会更妙
foreach($parent as $child) {
foreach($child as $element) {
my_function($element);
}
}
多词名称
$numElements = count($elements);
$num_elements = count($elements);
推荐第二种命名方法,原因是:
情况发生变化,你为了保持一致性不得不$CACHEDIR和$PROFANITYMACROSET
数据库不区分大小写;
非英语本土人士会在字典中更好的查到。
函数名
函数名和正常的变量名类似的处理方式,全部小写,多字要用下划线分割,推荐K&R风格:
function print_hello($name)
{
print "Hello $name";
}
foo() 和bar() 反映不出你的代码更多的信息,让你的代码看起来很不专业。
类名
参考官方的Java风格指南,类名应遵循以下规则:
一个类的名字第一个字母是大写;
下划线应该用来模拟嵌套的命名空间;
多字的类名应连接在一起,每个单词的首字母应大写。
class XML_RSS {}
class Text_PrettyPrinter {}
方法名
Java风格的串连字多字的方法名称是第一个字母后的单词第一个字母大写
class XML_RSS
{
function startHandler() {}
}
命名一致性
类似目的的变量保持类似的名称
$num_elements = count($elements);
...
$objects_cnt = count($objects);
推荐下面的风格
$max_elements;
$min_elements;
$sum_elements;
$prev_item;
$curr_item;
$next_item;
匹配变量名和架构名
与数据库中的记录相关联的变量名称应该始终有相匹配的名字。
$query = "SELECT firstname, lastname, employee_id
FROM employees";
$results = mysql_query($query);
while(list($firstname, $lastname, $employee_id) = mysql_fetch_row($results)) {
// ...
}
下面的代码容易混淆
$first_query = "SELECT a,b
FROM subscriptions
WHERE subscription_id = $subscription_id";
$results = mysql_query($first_query);
list($a, $b) = mysql_fetch_row($results);
// perform necessary logic
$new_a = $b;
$new_b = $a;
$second_query = "UPDATE subscriptions
SET a = '$new_a',
B = '$new_b'
WHERE subscription_id = $subscription_id";
Mysql_query($second_query);
开发人员为了保持列名和变量名在update中一致性
$first_query = "SELECT a,b
FROM subscriptions
WHERE subscription_id = $subscription_id";
$results = mysql_query($first_query);
list($b, $a) = mysql_fetch_row($results);
// perform necessary logic
$second_query = "UPDATE subscriptions
SET a = '$a',
B = '$b'
WHERE subscription_id = $subscription_id";
mysql_query($second_query);
这一代码会完全混乱不堪。
避免代码混乱
一致的代码风格让代码看一起是清晰的
避免短的开放的标签
php允许段标签
<?
print "Hello $username";
?>
但是不应该使用!
因为无法打印正常的XML内联文件
<?xml version="1.0" ?>
应该使用长标签
<?php
print "Hello $username";
? >