首頁  >  問答  >  主體

code - PHP 开发中有哪些不好的编程习惯需要避免

因为 @公子 公子指出了我在一个 回复 中的错误,我才开始逐渐意识到自己已经开始养成了一些很不好的编程习惯,比如嵌套循环,比如 N 多个条件嵌套,导致 } 符号堆积,以至于代码可读性极差。之前只重视了功能的实现与否,没有忽视了代码的可读性和性能方面的体现。
这边罗列下我所知道的一些不好的编程习惯:
1.嵌套循环
2.过多的条件嵌套
3.用户提交数据不进行过滤
4.函数体过长
5.变量命名清晰

我相信有不少 PHPer 都读过 《PHP 最佳实践》,或许我们大家可以集思广益,总结一个《PHP“最差”实践》,这样也可以当做一个警示录,时刻提醒自己优化代码,希望 PHP 开发经验丰富的前辈们能不吝赐教。:)

黄舟黄舟2749 天前989

全部回覆(12)我來回復

  • 怪我咯

    怪我咯2017-04-10 14:48:40

    1,拼凑SQL,如下

            $sql = "INSERT INTO `#@__archives`(id,typeid,typeid2,sortrank,flag,ismake,channel,arcrank,click,money,title,shorttitle,
            color,writer,source,litpic,pubdate,senddate,mid,voteid,notpost,description,keywords,filename,dutyadmin,weight)
            VALUES ('{$this->art['id']}','{$this->art['typeid']}','{$this->art['typeid2']}','{$this->art['sortrank']}','{$this->art['flag']}',
            '{$this->art['ismake']}','{$this->art['channelid']}','{$this->art['arcrank']}','{$this->art['click']}','{$this->art['money']}','{$this->art['title']}',
            '{$this->art['shorttitle']}','{$this->art['color']}','{$this->art['writer']}','{$this->art['source']}','{$this->art['litpic']}','{$this->art['pubdate']}',
            '{$this->art['senddate']}','{$this->art['adminid']}','{$this->art['voteid']}','{$this->art['notpost']}','{$this->art['description']}',
            '{$this->art['keywords']}','{$this->art['filename']}','{$this->art['adminid']}','{$this->art['weight']}');";
    

    2, 迟迟不肯return,代码挤在一起

    function readHosts(){
        global $config;
        $hosts = array();
        $fp = fopen(HOST_FILE, 'r');
        if($fp){
            while(!feof($fp)){
                $line = trim(fgets($fp));
                if($line!='' && substr($line, 0, 1)!='#'){
                    //将多个空格替换为一个,增加容错率
                    $line = preg_replace("/\s(?=\s)/","\\1",$line);
                    @list($host,$user,$password,$time) = explode(' ', $line);
                    $timeline = mktime((int)$time);
                    if($host && $user && $password){
                        if($timeline <= time()){
                            $hosts[] = array(
                                'dbhost' => $host,
                                'dbuser' => $user,
                                'dbpwd' => $password,
                                'dbname' => 'mysql',
                                'dbprefix' => 'dede_',
                                'dbcharset' => 'gbk',
                            );
                            msg('服务器'.$host.'加入发布列队');
                        }else{
                            msg('服务器'.$host.'没有到发布的时间');
                        }
                    }
                }
            }
            echo "\r\n";
        }
        fclose($fp);
        return $hosts;
    }
    

    3, 全局变量global, 以下的msg函数大量调用....

    function SendToSite($site){
        global $db,$log,$config;
        if($log->get_value($site['dbname'])==='finish') return;
        echo "=====================\r\n";
        msg('开始连接到数据库 '.$site['dbname']);
        if($db->select_db($site['dbname'])){
            msg('连接成功,收集站点信息');
            if($db->HasTable('archives')){
                msg('找到文章数据表,开始发布');
            }else{
                msg('没有找到文章数据表,可能表前缀不正确,跳过这个站点');
                return;
            }
        }else{
            msg('连接失败,跳过这个站点');
            return;
        }
    

    4,超长嵌套

        if(count($site_channel)>0) {
            $keylink = '<a href="'.$site_url.'" target="_blank">'.GBKTOUTF8($site_name).'</a>';
            msg('找到'.count($site_channel).'个文章栏目');
            echo "\r\n";
            foreach ($site_channel as $channel){
                if($log->get_value($site['dbname'].'_'.$channel['id'])==='finish'){
                    msg($site['dbname'].'栏目ID为'.$channel['id'].'今日已发布完成,跳过');
                    continue;
                }
                $sendnum = ReadIntFromStr($config['send_num']);
                if($sendnum <= $log->get_value($site['dbname'].'_'.$channel['id'])){
                    continue;
                }
                $keyword_title_num = ReadIntFromStr($config['keyword_title_num']);
                if($keyword_title_num>$sendnum) $keyword_title_num = $sendnum;
                $keyword_titles = array();
                for($i=0;$i<$keyword_title_num;$i++){
                    $num = rand(0,$sendnum);
                    if(!in_array($num, $keyword_titles)){
                        $keyword_titles[] = $num;
                    }else{
                        $i--;
                        continue;
                    }
                }
                $channel = array_map('GBKTOUTF8', $channel);
                msg($channel['typename'].'将发布'.$sendnum.'篇文章');
                for($i=$log->get_value($site['dbname'].'_'.$channel['id']);$i<$sendnum;$i++){
                    $txtfile = GetArtFile(DIR_TXTFILE);
                    if($txtfile){
                        if(in_array($i, $keyword_titles)){
                            $art = GetArtContent($channel['id'], $txtfile, $keylink, GBKTOUTF8($site_name));
                        }else{
                            $art = GetArtContent($channel['id'], $txtfile, $keylink);
                        }
                        if($art){
                            msg('正在向 ['.$channel['typename'].'] 中添加文章 《'.$art['title'].'》');
                            if($model->art_add($art)){
                                $backupfile=DIR_BACKUP.substr($txtfile, strlen(DIR_TXTFILE));
                                if(!file_exists(dirname($backupfile))) mkdirs(dirname($backupfile));
                                rename($txtfile, $backupfile);
                                msg('添加成功,备份txt文件');
                                $log->add($site['dbname'].'_'.$channel['id'], $i);
                                $log->save();
                            }else{
                                msg('文章添加失败');
                            }
                        }else{
                            msg('向 ['.$channel['typename'].'] 中添加文章失败,跳过');
                            continue;
                        }
                    }else{
                        msg('没有找到可用的txt文件');
                        break;
                    }
                    echo "\r\n";
                }
                $log->add($site['dbname'].'_'.$channel['id'], 'finish');
                $log->save();
            }
            $log->add($site['dbname'], 'finish');
            $log->save();
        }else{
            msg('没有找到文章栏目,跳过这个站点');
            echo "\r\n";
        }
    

    5, 过多使用@抑制错误,不使用异常处理, 类的方法没有明确声明访问权限.

    class db {
    
        private $config = array(
            'dbhost' => '',
            'dbname' => '',
            'dbuser' => '',
            'dbpwd' => '',
            'dbprefix' => '',
            'dbcharset' => ''
        );
        public $linkID = null;
        public $isconnect = FALSE;
    
        function __construct($config) {
            $this->config = $config + $this->config;
            $this->connect();
        }
    
        function connect(){
            if($this->config['dbhost'] && $this->config['dbname']){
                $this->linkID = @mysql_connect($this->config['dbhost'], $this->config['dbuser'], $this->config['dbpwd']);
                if(!$this->linkID){
                    //连接错误,直接退出
                    //echo mysql_error()."\r\n";
                    return FALSE;
                }
                if (@mysql_select_db($this->config['dbname'])) {
                    if($this->config['dbcharset'] != ''){
                        @mysql_query("SET NAMES '{$this->config['dbcharset']}'", $this->linkID);
                    }
                }
                $this->isconnect = TRUE;
            }
            return $this->isconnect;
        }
    }
    

    太多了..不好弄...

    回覆
    0
  • 高洛峰

    高洛峰2017-04-10 14:48:40

    https://github.com/justjavac/PHP-Best-Practices-zh_CN

    http://youngsterxyf.github.io/2013/06/01/php-best-practices/

    http://segmentfault.com/a/1190000000443795

    http://wulijun.github.io/php-the-right-way/

    http://thisinterestsme.com/php-best-practises/

    没仔细看,可能有重复。

    回覆
    0
  • 怪我咯

    怪我咯2017-04-10 14:48:40

    常规的别人都会说,我来说点激进的吧

    • 除了全局变量之外,全局function也不允许自己定义
    • 一个php文件里要么只有一个class声明,要么不存在class声明
    • 接上,必须用autoloader来加载类,而且规则越简单越好,PSR标准最好
    • 不允许修改public static $var
    • 参数是对象时必须在签名中声明类型,声明的类型中没有的成员不允许用
    • 不允许给对象动态添加属性。唯一的例外是stdClass
    • 必须使用单入口pattern,而且web server配置的documentroot下只能有index.php一个php文件

    回覆
    0
  • PHP中文网

    PHP中文网2017-04-10 14:48:40

    $arr = array();
    for($i = 0; $i < count($arr);$i ++)
    {
       //code here.
    }
    

    避免上面这种写法,因为循环每一次都会重新计算一次数组长度,可以这样写:

    $arr = array();
    $length = count($arr);
    for($i = 0; $i < $length;$i ++)
    {
       //code here.
    }
    

    回覆
    0
  • 巴扎黑

    巴扎黑2017-04-10 14:48:40

    之前翻译过一个针对新手的建议帖子 一共有三篇
    其中前两篇有比较多的对于不好用法的分析和解决方案

    http://ashitaka.me/%E8%AF%91/2014/01/05/40-php-tips-part-i/
    http://ashitaka.me/%E8%AF%91/2014/01/06/40-php-tips-part-ii/

    回覆
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-10 14:48:40

    拿自己举例,一个实现合并二维数组相同 key 的写法:
    我的 ugly 写法:

    $arr1 = array(
        array('num'=>5,'period'=>3),
        array('num'=>10,'period'=>3),
        array('num'=>15,'period'=>9)
    );
    
    $arr2 = array();
    
    foreach($arr1 as $k1 => $v1) {
        if(empty($arr2)) {
            $arr2[] = $v1;
        } else {
            foreach ($arr2 as &$v2) {
                if($v1['period'] == $v2['period']) {
                    $v2['num'] += $v1['num'];   
                } else {
                    $arr2[] = $v1;
                }
            }
        }
    }
    

    1.不必要的嵌套循环
    2.变量命名不清晰
    3.} 堆叠使得代码可读性极差

    @公子 的规范写法:

    rr = array(
        array('num'=>5,'period'=>3),
        array('num'=>10,'period'=>3),
        array('num'=>15,'period'=>9)
    );
    $temp = array();
    foreach($arr as $item) {
        list($n, $p) = array_values($item);
        $temp[$p] =  array_key_exists($p, $temp) ? $temp[$p]+$n : $n;
    }
    
    $arr = array();
    foreach($temp as $p => $n)
        $arr[] = array('num'=>$n, 'period'=>$p);
    
    print_r($arr);
    

    1.拆开循环
    2.利用 PHP 自带的数组函数简化了代码
    3.代码可读性较强

    回覆
    0
  • 高洛峰

    高洛峰2017-04-10 14:48:40

    如果你用netbeans8.0的话,超过2还是3层的嵌套,全局变量的不经判断直接使用,一个方法写的太长(应该对功能拆分).等等,都会直接给出警告提示.一个好的IDE对提高自己的代码书写规范性和可阅读新还是很有帮助的.

    回覆
    0
  • 怪我咯

    怪我咯2017-04-10 14:48:40

    慎用&
    例子:

    $arr = array('a'=>1, 'b'=>2);
    foreach($arr as &$v)
    {
        if($v == '2')
        {
            $v = 3;
        }
    }
    $v = 444;
    

    打印$arr['b'] 的值为 444;

    可以改成如下:

    $arr = array('a'=>1, 'b'=>2);
    foreach($arr as $k => $v)
    {
        if($v == '2')
        {
            $arr[$k] = 3;
        }
    }

    回覆
    0
  • 高洛峰

    高洛峰2017-04-10 14:48:40

    看看《代码大全》,看看底层类书籍,如《深入理解计算机系统》,很多东西理解地足够透彻了,写代码时自然会考虑很多东西,写出来的代码也就越来越优质。

    回覆
    0
  • 高洛峰

    高洛峰2017-04-10 14:48:40

    某些字符串函数与数组函数搞不清哪个是needle哪个是haystack...

    回覆
    0
  • 取消回覆