编写代码的“四项基本原则”
习惯1:命名非常重要!
最重要的命名注意事项
习惯2:适当的使用注释
phpdocumentor 文档生成手册对php的注释有一个很标准的规范
Copy /**
* @name 名字 (常用)
* @abstract 申明变量/类/方法 (常用)
* @access 指明这个变量、类、函数/方法的存取权限
* @author 函数作者的名字和邮箱地址 (常用)
* @category 组织packages
* @copyright 指明版权信息(常用)
* @const 指明常量(常用)
* @deprecate 指明不推荐或者是废弃的信息
* @example 示例
* @exclude 指明当前的注释将不进行分析,不出现在文挡中
* @final 指明这是一个最终的类、方法、属性,禁止派生、修改。
* @global 指明在此函数中引用的全局变量(常用)
* @include 指明包含的文件的信息(常用)
* @link 定义在线连接
* @module 定义归属的模块信息
* @modulegroup 定义归属的模块组
* @package 定义归属的包的信息(常用)
* @param 定义函数或者方法的参数信息(常用)
* @return 定义函数或者方法的返回信息(常用)
* @see 定义需要参考的函数、变量,并加入相应的超级连接。
* @since 指明该api函数或者方法是从哪个版本开始引入的(常用)
* @static 指明变量、类、函数是静态的。(常用)
* @throws 指明此函数可能抛出的错误异常 , 极其发生的情况
* @todo 指明应该改进或没有实现的地方
* @var 定义说明变量 / 属性。(常用)
* @version 定义版本信息(常用)
*/
注释的信息很全面,可能有很多我们用不到,(常用)部分是我们经常用到的。
示例:
文件头部模板
Copy /**
*这是一个什么文件
*
*此文件程序用来做什么的(详细说明,可选。)。
* @author revin<509129@qq.com>
* @version $Id$
* @since 1.0
*/
函数/方法头部注释(最重要)
Copy /**
* some_func
* 函数的含义说明
*
* @access public (可选)
* @param mixed $arg1 参数一的说明
* @param mixed $arg2 参数二的说明
* @param mixed $mixed 这是一个混合类型
* @since 1.0
* @return array
*/
public function thisIsFunction ($string , $integer , $mixed) { return array ();}
param参数这块变量type和返回的类型,具体可以从这里查找资料 Definition of a ‘Type’
void , 用于函数或者方法返回,当无任何返回时使用
类的注释
Copy /**
* 类的介绍
*
* 类的详细介绍(可选。)。
* @author revin<509129@qq.com>
* @since 1.0
*/
class Test
{
}
习惯3:使用一个变量,需要初始化
Copy function getDocs ($id) {
$ids = implode ( ',' , $id ) ;
$query = $this -> db -> query ( "SELECT * FROM doc WHERE id IN ($ids)" ) ;
while ($doc = $this -> db -> fetch_array ( $query ) ) {
$doc[ 'rawtitle' ] = $doc[ 'title' ];
$doc[ 'title' ] = htmlspecialchars ( $doc[ 'title' ] ) ;
$docs = $doc;
}
return $docs;
}
以上代码的问题:$docs
没有做初始化, $docs = array();
习惯4:优先使用单引号
单引号中的变量不会被解析,双引号中的数组会被解析
另一种情况: $row[‘id’]的效率是$row[id]的7倍(原因是不加''
会先去常量里找,没有找到再去数组中找,所以浪费了时间)
习惯5:用“1==$a” 替换 “$a==1”
常量和变量判断时,常量放前面的原则,原因看下面示例:
Copy //...
// 获取用户信息
$user = array ();
if ($uid = 0 ) {
$user[ 'uid' ] = 0 ;
$user[ 'groupid' ] = 0 ;
} else {
$user = $this -> getUserById ( $uid ) ;
}
//...
起初我也不太明白为什么要这么写,后来才明白用意,比如上面的错误代码片段,if ($uid = 0) {
,并不会报错,如果养成习惯用“1==$a” 替换 “$a==1”
方式,即使少写了一个=,会语法报错及时发现代码错误
Copy //...
// 获取用户信息
$user = array ();
if ( 0 = $uid) {
$user[ 'uid' ] = 0 ;
$user[ 'groupid' ] = 0 ;
} else {
$user = $this -> getUserById ( $uid ) ;
}
//...
习惯6:防御式编程思想
示例:
dedecms 注册部分代片段:
虽然前端javascript做了验证,但是仍然可以绕过js的验证
Copy $userid = trim ( $userid ) ;
$pwd = trim ( $userpwd ) ;
$pwdc = trim ( $userpwdok ) ;
$rs = CheckUserID ( $userid , '用户名' ) ;
if ($rs != 'ok' )
{
ShowMsg ( $rs , '-1' ) ;
exit ();
}
if ( strlen ( $userid ) > 20 || strlen ( $uname ) > 36 )
{
ShowMsg ( '你的用户名或用户笔名过长,不允许注册!' , '-1' ) ;
exit ();
}
if ( strlen ( $userid ) < $cfg_mb_idmin || strlen ( $pwd ) < $cfg_mb_pwdmin)
{
ShowMsg ( "你的用户名或密码过短,不允许注册!" , "-1" ) ;
exit ();
}
if ($pwdc != $pwd)
{
ShowMsg ( '你两次输入的密码不一致!' , '-1' ) ;
exit ();
}
$uname = HtmlReplace ( $uname , 1 ) ;
//用户笔名重复检测
if ($cfg_mb_wnameone == 'N' )
{
$row = $dsql -> GetOne ( "SELECT * FROM `#@__member` WHERE uname LIKE '$uname' " ) ;
if ( is_array ( $row ) )
{
ShowMsg ( '用户笔名或公司名称不能重复!' , '-1' ) ;
exit ();
}
}
if ( ! CheckEmail ( $email ) )
{
ShowMsg ( 'Email格式不正确!' , '-1' ) ;
exit ();
}
dedecms短消息代码片段:
Copy <? php
/**
* 会员短消息,发送到一个
*
* @version $Id: member_pmone.php 1 11:24 2010年7月20日Z tianya $
* @package DedeCMS.Administrator
* @copyright Copyright (c) 2007 - 2010, DesDev, Inc.
* @license http://help.dedecms.com/usersguide/license.html
* @link http://www.dedecms.com
*/
require_once ( dirname ( __FILE__ ) . "/config.php" );
CheckPurview ( 'member_Pm' ) ;
//检查用户名的合法性
function CheckUserID ($uid , $msgtitle = '用户名' , $ckhas = true)
{
global $cfg_mb_notallow , $cfg_mb_idmin , $cfg_md_idurl , $cfg_soft_lang , $dsql;
if ($cfg_mb_notallow != '' )
{
$nas = explode ( ',' , $cfg_mb_notallow ) ;
if ( in_array ( $uid , $nas ) )
{
return $msgtitle . '为系统禁止的标识!' ;
}
}
if ($cfg_md_idurl == 'Y' && preg_match ( "#[^a-z0-9]#i" , $uid ) )
{
return $msgtitle . '必须由英文字母或数字组成!' ;
}
if ($cfg_soft_lang == 'utf-8' ) $ck_uid = utf82gb ( $uid ) ;
else $ck_uid = $uid;
for ($i = 0 ; isset ( $ck_uid[$i] ) ;$i ++ )
{
if ( ord ( $ck_uid[$i] ) > 0x80 )
{
if ( isset ( $ck_uid[$i + 1 ] ) && ord ( $ck_uid[$i + 1 ] ) > 0x40 )
{
$i ++ ;
}
else
{
return $msgtitle . '可能含有乱码,建议你改用英文字母和数字组合!' ;
}
}
else
{
if ( preg_match ( "#[^0-9a-z@\.-]i#" , $ck_uid[$i] ) )
{
return $msgtitle . '不能含有 [@]、[.]、[-]以外的特殊符号!' ;
}
}
}
if ($ckhas)
{
$row = $dsql -> GetOne ( "SELECT * FROM `#@__member` WHERE userid LIKE '$uid' " ) ;
if ( is_array ( $row ) ) return $msgtitle . "已经存在!" ;
}
return 'ok' ;
}
if ( ! isset ( $action ) ) $action = '' ;
if ($action == "post" )
{
$floginid = $cuserLogin -> getUserName () ;
$fromid = $cuserLogin -> getUserID () ;
if ($subject == '' )
{
ShowMsg ( "请填写信息标题!" , "-1" ) ;
exit ();
}
$msg = CheckUserID ( $msgtoid , "用户名" , false ) ;
if ($msg != 'ok' )
{
ShowMsg ( $msg , "-1" ) ;
exit ();
}
$row = $dsql -> GetOne ( "Select * From `#@__member` where userid like '$msgtoid' " ) ;
if ( ! is_array ( $row ) )
{
ShowMsg ( "你指定的用户不存在,不能发送信息!" , "-1" ) ;
exit ();
}
$subject = cn_substrR ( HtmlReplace ( $subject , 1 ), 60 ) ;
$message = cn_substrR ( HtmlReplace ( $message , 0 ), 1024 ) ;
$sendtime = $writetime = time () ;
//发给收件人(收件人可管理)
$inquery = "INSERT INTO `#@__member_pms` (`floginid`,`fromid`,`toid`,`tologinid`,`folder`,`subject`,`sendtime`,`writetime`,`hasview`,`isadmin`,`message`)
VALUES ('$floginid','$fromid','{$row['mid']}','{$row['userid']}','inbox','$subject','$sendtime','$writetime','0','0','$message'); " ;
$dsql -> ExecuteNoneQuery ( $inquery ) ;
ShowMsg ( '短信已成功发送' , 'member_pmone.php' ) ;
exit ();
}
require_once (DEDEADMIN . "/templets/member_pmone.htm" );
习惯7:用自己可控的环境参数
不要相信外部的一切输入!
习惯8:PHP去掉 ?> 结束标记
纯 PHP 代码 ,最好在文件末尾删除 PHP 结束标记(官方推荐建议),
其实目的的原因是防止?>
打出空格等,导致出错,难以排查.
避免页面乱码
Copy header ( "Content-type: text/html;charset=utf-8" ) ;
Copy <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
习惯10:坚持字符编码统一
php文件的编码,静态模板文件的编码,数据库的编码
比如说全用utf8 或者全用gbk
习惯11:error_reporting(7)
error_reporting() 设置 PHP 的报错级别并返回当前级别。
写代码时,开启最高级别, 正式环境关闭
Copy error_reporting ( E_ALL ) ;
ini_set ( 'display_errors' , true ) ;
1 E_ERROR 致命的运行错误。错误无法恢复,暂停执行脚本。
2 E_WARNING 运行时警告(非致命性错误)。非致命的运行错误,脚本执行不会停止。
4 E_PARSE 编译时解析错误。解析错误只由分析器产生。
error_reporting(7) = 1 +2 + 4
1的二进制 01 , 2的二进制为10 4的二进制为100 相加 等于 111 ,换算成十进制为7
系统调试时候使用如下方式
Copy D_BUG ? error_reporting(7) : error_reporting(0);
其他请查看:PHP error_reporting() 错误控制函数功能详解 ,PHP错误级别 error_reporting() 函数详解
习惯12:优先使用PHP内置函数
filter_var() 函数
比如:filter_var() 函数
通过指定的过滤器过滤变量。比如邮箱验证,IP验证均可使用,不需要再用繁琐的正则。
在zf2中 , php-elastcisearch中,monolog中guzzle中均有使用。
Copy filter_var ( variable , filter , options )
URL-encode 字符串,去除或编码特殊字符。
HTML 转义字符 '"<>& 以及 ASCII 值小于 32 的字符。
删除所有字符,除了字母、数字以及 !#$%&'*+-/=?^_`{|}~@.[]
删除所有字符,除了字母、数字以及 $-_.+!*'(),{}|\^~[]`<>#%";/?:@&=
如果是 "1", "true", "on" 以及 "yes",则返回 true,如果是 "0", "false", "off", "no" 以及 "",则返回 false。否则返回 NULL。
根据 regexp,兼容 Perl 的正则表达式来验证值。
示例:
IP的验证:
Copy $ip = '192.168.0.11000' ;
var_dump ( filter_var ( $ip , FILTER_VALIDATE_IP )) ;
if ( ! filter_var ( $ip , FILTER_VALIDATE_IP ) ) {
echo "IP不合法" ;
}
验证通过则返回原值,验证失败色返回false
邮箱验证
Copy $email = '509129' ;
if ( ! filter_var ( $email , FILTER_VALIDATE_EMAIL ) ) {
echo "邮箱地址不合法" ;
}
验证URL
Copy /**
* @param string $host
*
* @return string
*/
function prependMissingScheme ($host)
{
if ( ! filter_var ( $host , FILTER_VALIDATE_URL , FILTER_FLAG_SCHEME_REQUIRED ) ) {
$host = 'http://' . $host;
}
return $host;
}
格式化true和false
FILTER_VALIDATE_BOOLEAN
如果是 "1", "true", "on" 以及 "yes",则返回 true,
如果是 "0", "false", "off", "no" 以及 "",则返回 false
Copy /**
* Formats a boolean value as a string
*
* @param string | integer | bool $value Value to convert to a boolean 'true' / 'false' value
*
* @return string
*/
public static function formatBooleanAsString ($value)
{
return filter_var ( $value , FILTER_VALIDATE_BOOLEAN ) ? 'true' : 'false' ;
}
验证浮点数
Copy if ( filter_var ( $value , FILTER_VALIDATE_FLOAT ) === false) {
echo "不是浮点数" ;
}
参数的详情说明请看:PHP filter_var() 函数
pathinfo函数
pathinfo() 函数以数组的形式返回文件路径的信息。
示例:
获取文件的后缀
Copy $filename = 'abc.txt' ;
function getExtension ($filename) {
return pathinfo ( $filename , PATHINFO_EXTENSION ) ;
}
echo getExtension ( $filename ) ;
更多查看:PHP pathinfo() 函数
其他有用的php内置:
usort — 使用用户自定义的比较函数对数组中的值进行排序
rawurlencode — 按照 RFC 1738 对 URL 进行编码
parse_url — 解析 URL,返回其组成部分
http_build_query — 生成 URL-encode 之后的请求字符串
levenshtein — 计算两个字符串之间的编辑距离
get_defined_vars — 返回由所有已定义变量所组成的数组
str_word_count — 返回字符串中单词的使用情况
习惯13:屏蔽错误非常低效
Copy $file = @ file ( 'non_existent_file' ) or
die ( "Failed opening file:error was not exist" );
养成不用@的好习惯
习惯14:时刻备份源代码
总结:
不要随便相信网上的那些PHP优化50则之类的东西,记住一切都有时效性,php版本时效性等等,善于自己去验证与平日里总结验证。