PHP最佳编程实践(英译)

这篇文章是尝试将一系列基本的PHP知识点起来,这些可以被认为是PHP中处理普遍的令人困惑的难题时的最佳实践。也是对PHP编程涉及技术的最优指引。英译原文《PHP Best Practices-A short, practical guide for common and confusing PHP tasks》

存储密码

作者认为使用md5或者sha1加密用户密码是不安全的,因为黑客能够很容易的破解。

最安全的方式是使用bcrypt算法加密。phpass图书馆提供此类及相关下载.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Include the phpass library
require_once('phpass-0.3/PasswordHash.php');
// Initialize the hasher without portable hashes
$hasher = new PasswordHash(8, false);
// Hash the password.  
//$hashedPassword will be a 60-character string.
$hashedPassword = $hasher->HashPassword(
                   'my super cool password');
// You can now safely store the contents of $hashedPassword
//INSERT INTO DB
// Check if a user has provided the correct password
$hasher->CheckPassword('the wrong password',
                   $hashedPassword); // false
$hasher->CheckPassword('my super cool password',
                   $hashedPassword); // true

连接数据库

作者建议使用PDO和预处理操作数据库。

因为PDO是统一的数据库操作接口,可以使用相同的代码连接不同类型的数据库,只需要简单的修改一下数据库连接字符串。而使用预处理则可以有效的防止SQL注入攻击。

PHP标签

作者建议使用,因为其它标签如:, <%>会与其它语言相混淆。不过在使用时,尽量只写开始标记<?php,这样可以防止此文件被其它文件包含时,因末尾多输出的空行而提示HTTP header输出错误。

类的自动加载

作者建议使用spl_autoload_register()注册自动加载函数。

PHP提供了多种类的自动加载,较老的方式是使用魔术方法__autoload(),但是你只能定义一次__autoload()函数,当你的被包含文件中也使用此函数的话,就会造成冲突。较好的方式是使用spl_autoload_register()

1
2
3
4
5
6
7
8
9
// First, define your auto-load function.
function MyAutoload($className){
   include_once($className . '.php');
}
// Next, register it with PHP.
spl_autoload_register('MyAutoload');
// Try it out!
// auto-loader will kick in and include MyClass.php.
$var = new MyClass();

使用单引号还是双引号

作者认为没有区别.

使用单引号,则程序会把其中内容直接输出。使用双引号,程序则先会对其中的转意字符,如:\r,\n或者PHP变量进行转意后再输出。似乎使用单引号效率会更高一点,但实际情况是,这对于应用程序来说,影响可以忽略不计,但是不管你使用哪种形式,尽量保持一致。

定义常量使用define()还是const

作者建议使用define(),除非考虑到可读性、类常量、优化等方面。

关于这两者的区别

1. define()是在运行时定义常量,const是在编译时定义常量。这使得const有非常鲜明额速度优势,如果不是开发非常大型的软件的话,就没必要担心。

2. define()将常量置于全局范围,你可以在常量名中定义命名空间,这意味着你可以定义类常量。

3. define()允许你在常量名或常量值中使用表单式,而const则两者都不行。

4. define()可以在if语句中使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Let's see how the two methods treat namespaces
namespace MiddleEarth\Creatures\Dwarves;
const GIMLI_ID = 1;
define('MiddleEarth\Creatures\Elves\LEGOLAS_ID', 2);
echo(\MiddleEarth\Creatures\Dwarves\GIMLI_ID);  // 1
// 2; note that we used define(),
// but the namespace is still recognized
echo(\MiddleEarth\Creatures\Elves\LEGOLAS_ID);  
//constants representing ways to enter Mordor.
define('TRANSPORT_METHOD_SNEAKING', 1 << 0); // OK!
//Compile error! const can't use expressions as values
const TRANSPORT_METHOD_WALKING = 1 << 1;
// Next, conditional constants.
define('HOBBITS_FRODO_ID', 1);
if($isGoingToMordor){
   // OK!
   define('TRANSPORT_METHOD', TRANSPORT_METHOD_SNEAKING);
   // Compile error: const can't be used in an if block
   const PARTY_LEADER_ID = HOBBITS_FRODO_ID ;
}
// Finally, class constants
class OneRing{
   const MELTING_POINT_DEGREES = 1000000; // OK!
   // Compile error: can't use define() within a class
   define('SHOW_ELVISH_DEGREES', 200);
}

缓存PHP操作码

作者建议使用APC。

安装PHP稳定版本后,每次程序运行,所有PHP脚本都要被编译和执行。花费大量时间编译相同的脚本,在大型网站中会导致性能问题。解决方法是使用APC缓存。APC缓存是以键值对的形式进行存取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Store some values in the APC cache.
apc_store('username-1532', 'Frodo Baggins');
apc_store('username-958', 'Aragorn');
apc_store('username-6389', 'Gandalf');
// Now any PHP script can access them
$value = apc_fetch('username-958', $success);
if($success === true)
   print($value); // Aragorn
// $success will be set to boolean false
// because this key doesn't exist.
$value = apc_fetch('username-1', $success);
if($success !== true)
   print('Key not found');
// This key will no longer be available.
apc_delete('username-958');

PHP和memcached

作者建议,如果是分布式缓存,请使用memcached,否则的话,使用APC。

PHP和正则

作者建议使用PCRE(preg_*)成员函数。

PHP正则有PCRE(preg_*)和POSIX(ereg_*)两种类型的函数,但是POSIX在PHP5.3.0版本以后已经不建议使用。

校验邮箱

作者建议使用filter_var()函数

在WEB程序中,我们经常干的可能就是校验邮箱。使用正则可以进行校验,但是较简单的方法是使用filter_var()。

1
2
3
4
// Returns "sgamgee@example.com". This is a valid email address.
filter_var('sgamgee@example.com', FILTER_VALIDATE_EMAIL);
// Returns boolean false! This is *not* a valid email address.
filter_var('sauron@mordor', FILTER_VALIDATE_EMAIL);

过滤HTML、输入和输出

作者建议使用htmlentities()做简单过滤,使用 HTML Purifier做比较复杂的过滤。

在显示用户输出的时候,需要把HTML标签移除,防止XSS等攻击。不要使用正则进行过滤,因为HTML是一门比较复杂的语言,当你用正则匹配的时候,有可能失败。

检验值是否为null或为假

作者建议使用===进行检验。

一个变量为null,则可能是变量尚未被赋值,或者被赋值null,或者使用unset().使用==进行检验,则是判断值是否为0或者空。isset()是判断一个变量是否被设置。is_null()是检验是否为null,is_bool()是是否为boolean,但是更简单的是使用===,比较快速,也使程序相对比较整洁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$x = 0;
$y = null;
// Is $x null?
if($x == null)
   print('Oops! $x is 0, not null!');
// Is $y null?
if(is_null($y))
   print('Great, but could be faster.');
if($y === null)
   print('Perfect!');
// Does the string abc contain the character a?
if(strpos('abc', 'a'))
   // return the position of the first character 0 .
   // But PHP interpretes 0 as false
   // so we never reach this str.
   print('Found it!');
//to see if strpos() returns 0, or boolean false.  
if(strpos('abc', 'a') !== false)
   print('Found it for real this time!');

发表评论

电子邮件地址不会被公开。 必填项已用*标注