编写代码的“四项基本原则”
习惯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版本时效性等等,善于自己去验证与平日里总结验证。