# 代码检查规范
## 一、代码风格检查
* A、空格与缩进要注意。
~~~
<php>
protected function main()
{
$this->keyword = CInput::clean('r','keyword',TYPE_STR);
$this->start = CInput::clean('r','start',TYPE_UINT);
$this->keyword = trim($this->keyword);
$this->num = 10;
$this->total = 0;
</php>
~~~
* B、if、while 代码块中均需要花括号,均需独立成行。
> 更详细的代码风格规范请见:http://wiki.kaixin009.com/index.php/PHP_%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83
## 二、安全检查
* A、对输入项应该有合法性检查(javascript 的或者 ajax 的)。
~~~
<javascript>
document.form1.btn_fb.disabled=true;
if (document.form1.word.value.strip() == "")
{
alert("记录的内容不能为空");
document.form1.word.value = "";
document.form1.word.focus();
document.form1.btn_fb.disabled=false;
return false;
}
</javascript>
~~~
* B、对于输入项,如果能确定枚举类型的,应该限定在能枚举的范围之内。
~~~
<php>
static protected $orderlist = array("pop", "week_pop", "shield");
if (!in_array($this->order, self::$orderlist))
{
$this->order = self::$orderlist[0];
}
</php>
~~~
* C、对于输出转义。 HTML 代码中应该用 htmlspecialchars 进行转义。对于模板中的 javascript 代码中的变量,应该用 addslashes 进行转义。 参见《php中的文本处理.txt》
* D、对于 mysql 语句拼接过程中的变量,如果需要整数的,用 intval 进行强制转换。如果是输入为字符串的,用 mysql_escape_string 。
对于 mysql 语句的拼接,除非明确知道是输入项是整数,比如经过了 intval,应该加 单引号进行字段值分隔。对于输入项为数组再 implode 的,应该检查是否输入为空数组。
~~~
<php>
function getNewList($uids, $start, $num)
{
if(!is_array($uids) || count($uids) == 0)
{
return false;
}
$date = date("Y-m-d" , strtotime("-7 day"));
$sql[0] = "select SQL_CALC_FOUND_ROWS * from s_user_record_new where privacy <> '2' and ctime >= '".$date."' and uid in ('".implode("', '", $uids)."') order by ctime desc limit ".intval($start).", ".intval($num);
$sql[1] = "select FOUND_ROWS()";
$info = $this->_doSql("s_user_record_new", 1, $sql);
if(false == $info)
{
return false;
}
return $info;
}
function updateNew($rid, $privacy)
{
$sql = "update s_user_record_new set privacy= '".mysql_escape_string($privacy)."' where rid = '".mysql_escape_string($rid)."'";
$info = $this->_doSql("s_user_record_new", 1, $sql);
if(false == $info)
{
return false;
}
return true;
}
</php>
~~~
* E、对于UGC内容的提交次数限制。比如日记,每分钟,每天都有限制。
1. 对于可能引发炒作传播的内容生产渠道
2. 对于可能引发攻击的内容生产渠道
* F、对于提交内容的前后端一致性检查。比如JS中限制提交次数限制,那么,在真正的接收业务逻辑中,要进行实质的检验。
~~~
<php>
if (trim($this->word) == "")
{
header("Location: /!record/index.php");
exit;
}
</php>
~~~
## 三、变量检查
* A、循环中的标记变量、计数变量是否进行了重置。
~~~
<php>
while ($info = mysql_fetch_array($result, MYSQL_ASSOC))
{
$needmon = false;
foreach($parts as $part)
{
if(...)
{
$needmon = true;
....
}
}
if($needmon)
{
$warncount ++;
break;
}
}
</php>
~~~
* B、循环条件中是否存在复杂运算。举个最简单的例子:
~~~
<php>
for($i=0;$i<$this->infos->rowNum(0);$i++)
{
$lids[] = $this->infos->get(0,$i,'lid');
$this->commentNums[] = array(APP_WHERE_ID,$this->infos->get(0,$i,'uid'),$this->infos->get(0,$i,'wid'));
}
for($j=0;$j<$localeinfos->rowNum();$j++)
{
$localeinfo = $localeinfos->getEx($j);
$this->locales[$localeinfo['lid']] = $localeinfo;
}
function rowNum($i)
{
return count($this->sqlres[$i]->rows);
}
function rowNum()
{
return count($this->sqlres->rows);
}
</php>
~~~
* C、全局变量,是否使用了 global 来声明。
~~~
<php>
global $multilog;
$multilog->addSysDebugLog("diary_content_bad_url_".$this->_uid."_".$this->_ip);
</php>
~~~
* D、对于后台及生产中的代码,文件路径不要写死,IP地址也不要写死。对于提示字符串等,也不要写死。要换用宏或者类常量来说明。
~~~
<php>
$datafile2=DATA_PATH."/cache/appinfo.log";
$cmd = "/usr/bin/rsync --bwlimit=25000 ".$datafile2." ".$ga_hive_log_receive_server."::TEMP/userinfo";
echo $cmd."\n";
</php>
~~~
对于人工操作的后台程序,最好能支持参数输入。
* E、if 语句分支过多时,可以考虑用查表法改造。
~~~
<php>
if ($host == "reg".COMMON_HOST)
{
return "reg";
}
else if ($host == "login".COMMON_HOST)
{
return "login";
}
else if ($host == "xyx".COMMON_HOST)
{
return "flashgame";
}
else if ($host == "tuan".COMMON_HOST)
{
return "teambuy";
}
</php>
<php>
$hostdir = array(
"reg".COMMON_HOST=>"reg",
"login".COMMON_HOST=>"login",
"xyx".COMMON_HOST=>"flashgame",
"tuan".COMMON_HOST=>"teambuy",
);
if(isset($hostdir[$host]))
{
return $hostdir[$host];
}
</php>
~~~
F、对于频繁调用的方法,不要在局部变量中声明大的数组变量(比如配置数组),可以用全局或者静态变量来代替
~~~
<php>
function getPublicStateIconArr()
{
static $status_icon_arr;
if(empty($status_icon_arr))
{
$status_icon_arr = array(
'(#开心)' => '<img src="http://'.IMG_HOST.'/i/state/happy.gif" title="开心" align="absmiddle" />',
'(#哭泣)' => '<img src="http://'.IMG_HOST.'/i/state/cry.gif" title="哭泣" align="absmiddle" />',
'(#伤心)' => '<img src="http://'.IMG_HOST.'/i/state/sorrow.gif" title="伤心" align="absmiddle" />',
'(#愤怒)' => '<img src="http://'.IMG_HOST.'/i/state/angry.gif" title="愤怒" align="absmiddle" />',
'(#惊讶)' => '<img src="http://'.IMG_HOST.'/i/state/amazed.gif" title="惊讶" align="absmiddle" />',
'(#想你)' => '<img src="http://'.IMG_HOST.'/i/state/missyou.gif" title="想你" align="absmiddle" />',
'(#幸福)' => '<img src="http://'.IMG_HOST.'/i/state/happiness.gif" title="幸福" align="absmiddle" />',
'(#纠结)' => '<img src="http://'.IMG_HOST.'/i/state/tanglement_3.gif" title="纠结" align="absmiddle" />',
'(#囧)' => '<img src="http://'.IMG_HOST.'/i/state/daze.gif" title="囧" align="absmiddle" />',
'(#闭嘴)' => '<img src="http://'.IMG_HOST.'/i/state/shutup.gif" title="闭嘴" align="absmiddle" />',
'(#yy)' => '<img src="http://'.IMG_HOST.'/i/state/yy.gif" title="yy" align="absmiddle" />',
'(#亲亲)' => '<img src="http://'.IMG_HOST.'/i/state/kiss.gif" title="亲亲" align="absmiddle" />',
'(#色)' => '<img src="http://'.IMG_HOST.'/i/state/leer.gif" title="色" align="absmiddle" />',
...
//以下为兼容旧的英文格式动态
':)' => '<img src="http://'. IMG_HOST . '/i/state/happy.gif" title="开心" align="absmiddle" />',
'//cry' => '<img src="http://' . IMG_HOST . '/i/state/cry.gif" title="哭泣" align="absmiddle" />',
':(' => '<img src="http://' .IMG_HOST. '/i/state/sorrow.gif" title="伤心" align="absmiddle" />',
'//angry' => '<img src="http://' .IMG_HOST. '/i/state/angry.gif" title="愤怒" align="absmiddle" />',
'//amazed' => '<img src="http://' .IMG_HOST. '/i/state/amazed.gif" title="惊讶" align="absmiddle" />',
'//heart' => '<img src="http://' .IMG_HOST. '/i/state/heart.gif" title="芳心" align="absmiddle" />',
'//loving' => '<img src="http://' .IMG_HOST. '/i/state/loving.gif" title="充满爱心" align="absmiddle" />',
'//candle' => '<img src="http://' .IMG_HOST. '/i/state/candle.gif" title="蜡烛" align="absmiddle" />',
'//riband' => '<img src="http://' .IMG_HOST. '/i/state/riband.gif" title="绿丝带" align="absmiddle" />',
'//cake' => '<img src="http://' .IMG_HOST. '/i/state/cake.gif" title="生日蛋糕" align="absmiddle" />',
'//sun' => '<img src="http://' .IMG_HOST. '/i/state/sun_3.gif" title="太阳" align="absmiddle" />',
'//rainbow' => '<img src="http://' .IMG_HOST. '/i/state/rainbow.gif" title="彩虹" align="absmiddle" />',
'//praise' => '<img src="http://' .IMG_HOST. '/i/state/praise_3.gif" title="赞扬" align="absmiddle" />',
'//crown' => '<img src="http://' .IMG_HOST. '/i/state/crown.gif" title="皇冠" align="absmiddle" />',
'//help' => '<img src="http://' .IMG_HOST. '/i/state/help_3.gif" title="需要帮助" align="absmiddle" />',
'//music' => '<img src="http://' .IMG_HOST. '/i/state/music_3.gif" title="音乐" align="absmiddle" />',
'//snow' => '<img src="http://' .IMG_HOST .'/i/state/snow_3.gif" title="雪花" align="absmiddle" />',
....
);
}
return $status_icon_arr;
}
</php>
~~~
G、用循环分段来处理大量数据,对于不能成段的数据不能漏了处理
~~~
<php>
foreach($lines as $line)
{
$flag = 0;
$uid = trim($line);
$uids[] = $uid;
$count ++;
if($count % 1000 == 0)
{
CUObject::$cache = array();
$ipdetails = $cuserreg->getRegIpDetails($uids);
$ips = CHandle::objs2ids($ipdetails, "regip");
$ret = $ciplocator->getLocations($ips);
$index = 0;
foreach($ips as $key=>$ip)
{
echo $uids[$index++]." ".$key." ".$ip." ".$ret[$key]."\n";
}
$uids = array();
}
}
if(count($uids) > 0)
{
$ipdetails = $cuserreg->getRegIpDetails($uids);
$ips = CHandle::objs2ids($ipdetails, "regip");
$ret = $ciplocator->getLocations($ips);
$index = 0;
foreach($ips as $key=>$ip)
{
echo $uids[$index++]." ".$key." ".$ip." ".$ret[$key]."\n";
}
}
</php>
~~~
## 四、性能检查
* A、考虑到每一次对中间层的调用都是有消耗的。
* B、保持效率意识。想到很费资源东西。比如打开关闭连接,网络操作,IO读取,这些都是很费资源的。应该想着怎么整合优化,严禁在循环操作中,重复做这些操作。(举例:在循环中获取用户信息。)
* C、正则表达式是一个比较慢的操作。能不用最好不用。
~~~
<php>
while(list($k, $v) = each($this->varvals))
{
$varvals_quoted[$k] = preg_replace(array('/\\\\/', '/\$/'), array('\\\\\\\\', '\\\\$'), $v);
}
while(list($k, $v) = each($this->varvals))
{
if(strpos($v, "$")===false
&& strpos($v, "\\")===false)
{
$varvals_quoted[$k] = $v;
}
else
{
$varvals_quoted[$k] = str_replace(array("\\", "$"), array("\\\\", "\\$"), $v);
}
}
</php>
* D、对于后台程序要加锁。
<php>
$lockfp = CLock::check($argv[0]);
$lockfp = CLock::check($argv[0].$argv[1]);
</php>
~~~
* E、对于后台程序中,大数据量的调用,要记得清空 CUObject 、CUser 等的缓存。
~~~
<php>
CUObject::$cache = array();
CUser::$info_cache = array();
CUser::$moreinfo_cache = array();
CUser::$verifycode_cache = array();
</php>
~~~
## 五、中间层调用的检查
* A、明确 cachetime 的使用。如果存在 cachetime 使用的,要检查查询语句,是否带有时间参数,检查时间参数的精度。
~~~
<php>
function getRatingRelationNewPageByUidsAndCtime($uids,$ctime,$status=null)
{
if(empty($uids))
{
return false;
}
$uids = array_unique($uids);
$uidstr = "(".implode(',',$uids).")";
$status_str = "";
if(!empty($status))
{
$status_str = " and status = ". mysql_escape_string($status). " ";
}
$ctime = date("Y-m-d H:00:00", strtotime($ctime));
$sql[0] = "select SQL_CALC_FOUND_ROWS rid,uid,ctime,status from s_user_rating_relation_new where uid in ".$uidstr.$status_str. " and ctime >= '". mysql_escape_string($ctime)."' order by ctime desc ";
$sql[1] = "select FOUND_ROWS()";
$info = $this->_doSql('s_user_rating_relation_new',1,$sql, 300);
...
}
</php>
~~~
* B、getList、getAll 是否正确使用。
* C、CUObject 对象的更新是否使用了正确的方法。而在继承 CUObject 的类中,是否正确更新非 UObject 数据表。
~~~
<php>
function deleteRating($rid)
{
return $this->_delData('s_rating_info', $rid, 'rid', $rid);
}
function updateRatingUserNum($rid,$addnum=1)
{
return $this->_chgDataEx('s_rating_info',$rid,'rid',$rid,array(),array('usernum'=>intval($addnum)));
}
function updateRatingVoteByRidAndVid($data,$rid,$vid)
{
return CHandle::_chgData('s_rating_vote',$rid,'vid',$vid,$data);
}
function updateRatingVoteCommentNum($rid,$vid)
{
return CHandle::_chgDataEx('s_rating_vote',$rid,'vid',$vid,array(),array('commentnum'=>1));
}
</php>
* D、Memcached 或者 kvdb 分组调用错误。比如用 getUObjectInstance 代替 getInstance,是错误的。
<php>
$mc = CMemCacheEx::getInstance();
$mc->delete("photo_num_".$uid);
$key = "appdata_".$uid."_".APP_PHOTO_ID;
$mc = CMemCacheEx::getUObjectInstance($key);
$mc->delete($key);
</php>
~~~
## 六、数据库代码检查
* A、索引与调用检查。
* B、数据库调用,检查 order by 。严格控制 SQL_CALC_FOUND_ROWS 的使用。
~~~
<php>
function getFanListNoOrder($uid, $start, $num, $cache_time=0)
{
return $this->_getList("s_user_friend_fan", $uid, "", "", $start, $num, $cache_time);
}
</php>
~~~
* C、绝对禁止使用mysql的联合查询。(宁可查两次数据库)不能使用联合查询一个重要的原因是分库分表。
* D、没有太复杂的查询。尤其注意 or 的使用。如果要用到 or ,是否可以考虑抓成两个简单查询再合并。
对于复杂的查询,可以考虑拆分成多个简单的查询来进行。
~~~
select * from s_user_comment where uid = '...' and (thread_cid in (...) or cid in (...)) and del = 0 order by cid desc;
~~~
可以拆分成两个:
~~~
select * from s_user_comment where uid = '...' and (thread_cid in (...)) and del = 0 ;
select * from s_user_comment where uid = '...' and (cid in (...)) and del = 0;
~~~
* E、注意不要把无效的查询扔到后台数据库去。
~~~
<php>
function getReplyListByTids($uid, $vuid, $tids)
{
if (empty($tids))
{
$tids = array("-1");
}
$where = "(thread_cid in ('".implode("', '", $tids)."'))";
if ($uid != $vuid)
{
$where .= " and del = 0";
}
$info = $this->_getAll("s_user_comment", $uid, $where, "order by cid");
return $info;
}
function getReplyListByTids($uid, $vuid, $tids)
{
if (empty($tids))
{
return CComment::getEmptyResult();
}
$where = "(thread_cid in ('".implode("', '", $tids)."'))";
if ($uid != $vuid)
{
$where .= " and del = 0";
}
$info = $this->_getAll("s_user_comment", $uid, $where, "order by cid");
return $info;
}
</php>
~~~
## 七、业务检查
* A、业页提交与重定向。(应该不能提交给自己)。
* B、对于一时间大量写数据库的操作,比如送礼物,应该走队列。
* C、构造函数的参数使用检查。(pri, host等)。
* D、平台和组件代码分开的问题。在组件代码中,不能直接初始化 CUser 和 CFriend 去取用户和好友关系信息,只能走 CModuleApp.php提供出来的接口。
* E、后台管理系统的程序,一定要有权限控制。请在上线前仔细检查。是否漏了权限控制。
~~~
<php>
checkLevel(KXADMIN_SENIOR_PROD, array(), "组件");
</php>
~~~
## 八、JS 检查
* A、是否只使用了 JQuery 框架。
* B、DOM 对象选择器使用检查。请参考 javascript 开发规范 http://wiki.kaixin009.com/index.php/JavaScript_%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83
* C、JSLint 检查(目前没有做到)。
- 关于我
- nginx
- 代理
- 实例1
- 使用Nginx实现反向代理
- nginx反向代理配置
- Nginx缓存原理及配置
- nginx 信号集
- rewrite
- nginx rewrite规则
- nginx rewrite 正则规则详解
- nginx+php-fpm fastcgi防止跨站、跨目录的安全设置
- 写了个shell脚本,实现nginx日志自动切割,并利用goaccess分析成HTML报表,然后通知管理人员查阅
- nginx禁止一些常见的不应该让用户访问的文件
- PHP
- 实现PHP多进程管理
- php代码规范
- Linux
- 常用命令
- scp
- touch
- nohup
- rsync
- ssh命令 远程登录
- chmod
- nginx日志切割脚本
- Linux使用ssh公钥实现免密码登录Linux
- 使用ssh公钥密钥自动登陆linux服务器
- Linux查看程序端口占用情况
- ssh 公钥私钥认证原理
- 路由图
- CentOS7防火墙设置
- linux下用户管理
- Linux 服务器安全技巧
- Linux shell 提取文件名和目录名
- Linux环境下设置命令别名(alias)
- Git
- 搭建Git服务器
- 手把手教你搭建git服务器
- Git 基本操作
- git删除文件夹/文件(不删除本地文件)
- git忽略文件和文件夹以及文件权限
- Git的四个区五种状态
- git init 和 git init --bare 的区别
- git仓库删除所有提交历史记录,成为一个干净的新仓库
- 如何正确使用Git Flow
- HTTP
- 前端跨域解决方案
- HTTP基本概念
- Redis
- Redis 数据类型及应用场景
- JavaScript
- WebSocket探秘
- socket与websocket的区别
- 基于 Swoole 的微信扫码登录
- Web前端开发规范手册
- 简单的学习了一下ES6
- ES6模块的import和export用法
- MySql
- MySql重置root密码的方法
- mysql 5.6.30 开启慢查询日志
- mysql查看锁表情况
- 我的设计稿
- 会计出纳系统
- 上门宝ERP系统
- 社交应用七大需求
- 出纳系统
- 利用SdfMpp搭建中大行软件系统
- B2C库存商品系统
- 软件
- 网上商城
- 思维逻辑
- 系统架构分层图
- 代码检查规范
- 研发计划
- 系统架构
- 我设计的架构架构图
- 电商网站架构案例
- 大型网站架构
- 服务熔断、降级、限流、异步RPC
- 软件版本号规范
- 第三方服务引擎
- kafka
- ElasticSearch
- 阿里云 推荐引擎
- 随记
- 人生最大的投资
- 产品研发团队管理的6个方面
- 我感觉自己根本不是一个创业者,更谈不上企业家,果然还是屌丝这个称呼更适合我。
- 学什么语音都应该养成的编程习惯
- 2018年5月23日 多云转阴 15-24°C 离职信
- 2017年12月25日
- 对“目标”的认知
- 个人简历
- API设计
- 理解OAuth 2.0
- OAuth 2.0四种授权方式小结
- 对 REST 的理解
- RESTful API 设计指南
- RESTful API 设计
- RPC
- 微服务架构方案
- SOA
- 理解RESTful架构
- RESTful API GET,DELETE,PUT和POST的响应状态码
- MongoDB
- MongoDB-无法启动的一个问题