php tips

  1. 1. 那些有趣的代码片段
  2. 2. 安全两个原则
  3. 3. 对象的另一种理解
  4. 4. isset vs array_key_exist
  5. 5. is_null
  6. 6. print vs echo
  7. 7. include vs require
  8. 8. if($args) 与 if(!empty($args)) 的区别
  9. 9. 关于垃圾回收及composer
  • 关于引用传递
  • php中的’0’和javascript中的’0’
  • strrev
  • strlen()与mb_strlen()
  • 变量的8种类型
  • 关于时间复杂度
    • explode 分割字符可以不是单字符

    • 强制类型转换的值不论是否有定义,转换后都会有值

    • E_ALL ^ E_NOTICE 和 E_ALL & ~E_NOTICE 是等效的

    • 无论是函数调用还是文件包含,像 FILE 这种变量,他写在哪个文件,那么他的值就在哪个文件,而不受调用者影响

    • 静态变量是在编译时解析的,不能跟表达式,比如:static $int = sqrt(121)

    • 子类重载父类的属性和方法时,可见性不能比父类小

    • 自 PHP 5.4 起可用 callable 类型指定回调类型 callback

      1. 一个 PHP 的函数以 string 类型传递其名称。可以使用任何内置或用户自定义函数,但除了语言结构例如:array(),echo,empty(),eval(),exit(),isset(),list(),print 或 unset()

      2. 一个已实例化的对象的方法被作为数组传递,下标 0 包含该对象,下标 1 包含方法名

      3. 静态类方法也可不经实例化该类的对象而传递,只要在下标 0 中包含类名而不是对象。自 PHP 5.2.3 起,也可以传递 ‘ClassName::methodName’

      4. 也可传递 closure 给回调参数

    • 对浮点数,数字字符串,纯字母都能进行自增/减运算,对其他类型的值进行自增/减运算不会产生什么效果

    • call_user_func_array 如果回调函数默认设置需要接受的参数是引用传递的时候,按值传递,结果将会输出一个警告, 这时数组参数中的参数需要按引用传递

    • call_user_func 如果回调函数默认设置需要接受的参数是引用传递的时候,无论怎样传参都无法满足

    • 常量NAMESPACE的值是包含当前命名空间名称的字符串。在全局的,不包括在任何命名空间中的代码,它包含一个空的字符串

    • 关键字 namespace 可用来显式访问当前命名空间或子命名空间中的元素。它等价于类中的 self 操作符
    • 命名空间不会因为被包含而影响其在包含文件中的使用方式

    • mysqli::real_escape_string 相比 addslashes 会针对真实数据库环境的字符集做出更好的处理,但是必须在建立数据库链接的基础上

    • 字符串类型变量可以直接当做一个数组,通过数字下面来访问字符串中的字符或者是通过 str_split 函数把字符串分割成单字符数组

    那些有趣的代码片段

    1
    2
    3
    function multi ($a, $b) {
    $b = is_array($b) ? $b : array_slice(func_get_args(), 1);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function incr()
    {
    static $count = 0;
    $count++;
    echo $count;
    if ($count < 10) {
    incr();
    }
    echo $count;
    $count--;
    }
    incr();
    // 结果输出:1234567891010987654321

    不使用临时变量,交换2数的数值
    下面三种方法不能对自身进行交换

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 不仅对数字有效,对字符串也是有效的
    function swap1(&$x, &$y)
    {
    $x = $x ^ $y;
    $y = $x ^ $y;
    $x = $x ^ $y;
    }
    // 当心越界
    function swap2(&$x, &$y)
    {
    $x = $x + $y;
    $y = $x - $y;
    $x = $x - $y;
    }
    // 当心越界、第二个数不能为0
    function swap3(&$x, &$y)
    {
    $x = $x * $y;
    $y = $x / $y;
    $x = $x / $y;
    }

    命名空间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    namespace Myns;
    class Test
    {
    }
    // ok
    new namespace\Test;
    // ok
    new Test;
    // ok
    $a = __NAMESPACE__ . '\Test';
    new $a;
    // ok
    $a = 'Myns\Test';
    new $a;
    // failed, Class 'Myns\Myns\Test' not found
    new Myns\Test;

    这样的自增运算

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?php
    $i1 = 0;
    $i2 = 0;
    $a= ++$i1 + ++$i1 + ++$i1;
    $b= $i2++ + $i2++ + $i2++;
    echo $a;
    echo PHP_EOL;
    echo $b;
    // result:
    // 6
    // 3

    安全两个原则

    1. 永远不要相信用户输入的东西。
    2. 将自己需要输出的数据进行转义。

    对象的另一种理解

    对象”是一个容器,封装了“属性”(property)和“方法”(method)

    所谓“属性”,就是对象的状态;所谓“方法”,就是对象的行为(完成某种任务)。比如,我们可以把动物抽象为animal对象,“属性”记录具体是那一种动物,“方法”表示动物的某种行为(奔跑、捕猎、休息等等)

    isset vs array_key_exist

    最准确的array_key_exists的效率确是最差,而empty和isset的效率相差无几

    如果数组不可能出现值为NULL的情况,建议使用isset
    如果数组中经常出现值为NULL的情况,建议使用array_key_exists
    如果数组中可能出现值为NULL,但是较少的情况,建议结合isset与array_key_exists使用,如“if (isset($arr[‘key’]) || array_key_exists(‘key’, $arr)){/do somthing/}”。此方法兼顾了性能和准确性,但是代码变长了。

    is_null

    null不区分大小写:$a = null; $a = NULL 没有任何区别
    仅在变量的值为“null”时,检测结果才为true,0、空字符串、false、空数组都检测为false
    变量未初始化时,程序将会报错

    isset 主要用来判断变量是否被初始化过
    empty 可以将值为 “假”、“空”、“0”、“NULL”、“未初始化” 的变量都判断为TRUE
    is_null 仅把值为 “NULL” 的变量判断为TRUE

    echo和print的区别php中echo和print的功能基本相同(输出),但是两者之间还是有细微差别的。echo输出后没有返回值,但print有返回值,当其执行失败时返回flase。因此可以作为一个普通函数来使用
    例如执行下面的代码后变量$r的值将为1。
    代码:

    $r = print “Hello World”;

    这意味着print可用在一些复杂的表达式中,而echo则不行。但是,因为echo语句不要求返回任何数值,所已在代码中echo语句的运行效率要略微快于print语句。

    include vs require

    include()与require()的功能也基本相同(包含),但在用法上也有一些不同,include()是有条件包含函数,而require()则是无条件包含函数。例如在下面代码中,如果变量$a为真,则将包含文件a.php:
    代码:

    1
    2
    3
    if ($a) {
    include("a.php");
    }

    而require()则和include()不同,不管$a取何值,下面的代码将把文件a.php包含进文件里:
    代码:

    1
    2
    3
    if ($a) {
    require("a.php");
    }

    if($args) 与 if(!empty($args)) 的区别

    第一种方法在$args未定义的情况下会出现警告,在PHP中出现任何Error都会造成不小的性能损失,这主要是因PHP存在错误处理机制造成的。

    第二种方法下empty能够处理未定义的参数,可以避免警告。另外empty是指令而非函数,运行效率并不会比第一种慢太多。

    简而言之,如果你能保证$args已经定义,大可使用第一种。如果不行,则建议使用第二种。

    关于垃圾回收及composer

    (重要资料)[http://cn2.php.net/manual/zh/features.gc.php]

    gc_disable() 不是完全关闭 gc ,而是关闭检查循环引用计数。

    检查循环引用计数是因为 php 的 gc 是基于引用计数,在 php 5.2 及以前循环引用会导致内存泄漏。

    composer 的这个地方是在进行依赖包检查,对于安装了大量包的项目来说,这是比较耗时、耗内存的操作,并且这部分代码(依据设计/依据提交者的看法)无需考虑循环引用计数问题。

    composer在运行的时候会创建大量的对象,这些对象会触发GC机制,而这些对象需要被使用,所以GC无法清除,因此,使用gc_disable禁用GC之后,会节省cpu时间,效率更高。因为占用内存太大,并且频繁触发 gc ,导致效率降低。

    由于PHP的GC是基于引用计数的,为了能够回收循环引用的对象,会在ref count减少但不到0的时候,试图检测并回收循环引用的孤岛对象,但当有效对象的数量及互相引用较大(比如composer中代表包、版本和互相的依赖关系)的时候,这种搜索的开销就会变得非常巨大,造成大量的CPU计算

    关于引用传递

    一般在输出参数有多个的时候可以考虑使用引用

    php中的’0’和javascript中的’0’

    在php和js中转化布尔类型,php中被转换为false,而js中却是true

    strrev

    只能翻转英文

    strlen()与mb_strlen()

    strlen 计算字符串的字节长度
    mb_strlen 根据编码计算字符串字符的长度

    gbk 中 一个中文字符2个字节
    utf8中 一个中文字符3个字节

    变量的8种类型

    整型 浮点型 字符串 布尔型 数组 对象 资源 null

    关于时间复杂度

    PHP的时间复杂度还得算上php的自身函数所占用的时间复杂度