这篇文章是尝试将一系列基本的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!' ); |