PHP细节-01

字符串

  • 双引号或者heredoc其中的变量 以及 \ 开始的符合8进制16进制和特殊符号
  • 字符串底层是C语言的结构体,所以可以用[]{} 来访问某个字符
  • 字符串最大长度可以达到2G内存
  • C语言字符串\0代表字符串结束,但PHP结构体是有个长度字段,可以让二进制字符串安全
  • 用超出字符串长度的下标写入将会拉长字符串并以空格填充
  • UTF-8 编码

    16进制 Unicode 编码范围 0800 - FFFF
    2进制 1110xxxx 10xxxxxx 10xxxxxx
    为了通用优先选择UTF-8 3个字节,为了节省空间用GBK 2个字节

数组

  • key 可以是integer 或者 string (包含合法整形的字符串,浮点数和布尔值都会被转化为整形)
  • unset() 后,不会重建索引
  • 遍历中的引用分析

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?php
    $arr = [1,2,3];
    foreach ($arr as $key => &$value) {
    }
    echo $value; // 3 是 &$arr[2] = 3;

    foreach ($arr as $key => $value) {
    }

    var_dump($arr); // [1,2,2]

    // 循环1 $value = &$arr[2] = $arr[0] = 1;
    // 循环2 $value = &$arr[2] = $arr[1] = 2;
    // 循环3 $value = &$arr[2] = $arr[2] = 2;
  • 位运算:$a&1 == 0 偶数 反之奇数

正则表达式

定义

组成 = 元字符 + 普通字符
常见元字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
^ 		匹配字符串的开始
$ 匹配字符串的结束
. 匹配除换行以外的任意字符
\w 匹配字母或者数字或者下划线
\W 不匹配字母数字下划线
\s 匹配任意的空字符 相当于[\f\r\n\t\v]
\d 匹配任意的数字
\b 匹配单词的开始或者结束
\xxx 查找以八进制xxx规定的字符
\xdd 查找以十六进制dd对顶的字符
\uxxx 查找以十六进制 xxxx 规定的 Unicode 字符
[abcd] 匹配任意一个字符
[a-d] 匹配任意一个字符
[^abcd] 不匹配任意一个字符
[\u4e00-\u9fa5] 匹配任意单个汉字

常见限定符 (限定的是前面一个单元)

1
2
3
4
5
6
7
|		或关系
* 匹配0到多个,相当于{0,}
? 匹配0到1个,相当于{0,1}
+ 匹配至少1个字符,相当于{1,}
{n} 匹配n个字符
{n,} 匹配至少n个字符
{n,m} 匹配n到m个字符

环视

1
2
3
4
(?=exp) 	位置后面能匹配exp
(?!=exp) 位置后面不能匹配exp
(?<=exp) 位置前面能匹配exp
(?<!exp) 位置前面不能匹配exp

贪婪与懒惰

通常的行为是尽可能匹配多的字符(回溯)
只要在它后面加一个问号,匹配成功的前提是使用最少的重复

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
$string = 'aaabaab';
$pattern1 = '/a.{1,10}b/';
$pattern2 = '/a.{1,10}?b/';
preg_match_all($pattern1,$string,$matchs1);
preg_match_all($pattern2,$string,$matchs2);

var_dump($matchs1);
var_dump($matchs2);

array(1) {
[0]=>
array(1) {
[0]=>
string(7) "aaabaab"
}
}
array(1) {
[0]=>
array(2) {
[0]=>
string(4) "aaab"
[1]=>
string(3) "aab"
}
}

回溯

贪婪模式的回溯是影响性能

习惯

  • 优先使用单引号
  • 内置函数:

    1
    2
    1. 邮件过滤:$email = filter_var('hi@uiste.com', FILTER_VALIDATE_EMAIL);
    2. 获取文件扩展名:pathinfo($filename, PATHINFO_EXTENSION);
  • strtr 与 str_replace 函数前者优先级更高

  • yield 实现协程 生成器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    function createRange($number){
    for($i=0;$i<$number;$i++){
    yield time();
    }
    }

    $result = createRange(10); // 这里调用上面我们创建的函数
    foreach($result as $value){
    sleep(1);
    echo $value , PHP_EOL;
    }

    1554027375
    1554027376
    1554027377
    1554027378
    1554027379
    1554027380
    1554027381
    1554027382
    1554027383
    1554027384
    [Finished in 10.2s]

    我们来还原一下代码执行过程。

    首先调用 createRange 函数,传入参数10,但是 for 值执行了一次然后停止了,并且告诉 foreach 第一次循环可以用的值。
    foreach 开始对 $result 循环,进来首先 sleep(1) ,然后开始使用 for 给的一个值执行输出。
    foreach 准备第二次循环,开始第二次循环之前,它向 for 循环又请求了一次。
    for 循环于是又执行了一次,将生成的时间戳告诉 foreach .
    foreach 拿到第二个值,并且输出。由于 foreach 中 sleep(1) ,所以, for 循环延迟了1秒生成当前时间
    所以,整个代码执行中,始终只有一个记录值参与循环,内存中也只有一条信息。

    无论开始传入的 $number 有多大,由于并不会立即生成所有结果集,所以内存始终是一条循环的值。

    读取超大文件
    PHP开发很多时候都要读取大文件,比如csv文件、text文件,或者一些日志文件。这些文件如果很大,比如5个G。这时,直接一次性把所有的内容读取到内存中计算不太现实。

    <?php
    header("content-type:text/html;charset=utf-8");
    function readTxt()
    {
    # code...
    $handle = fopen("./test.txt", 'rb');

    while (feof($handle)===false) {
    # code...
    yield fgets($handle);
    }

    fclose($handle);
    }

    foreach (readTxt() as $key => $value) {
    # code...
    echo $value , PHP_EOL;
    }
  • 语法支持带来更高效率 用 ** 更快

    1
    2
    echo 2**3 , PHP_EOL;
    echo pow(2,3) , PHP_EOL;
  • 用 … 定义变成参数

  • <=> 大于为1,等于为0,小于为-1
  • if 使用技巧给定初始值,比增加else效率更高
  • if 使用技巧 三元运算符替换
  • 去掉多此一举的写法 直接return 出去,尽量精简代码
  • 根据二维数组中的某个键值排序:
    1
    2
    // 根据uv_price排序
    array_multisort(array_column($productData, 'uv_price'), SORT_DESC, $productData);

php坑人题

1
2
3
4
5
6
7
8
9
<?php
$a = 3;
$b = 6;
if($a = 5 || $b = 7){
$a++;
++$b;
}

echo $a . '-' . $b; //1-7
1
2
3
4
5
6
7
8
9
10
11
<?php
$count = 5;
function getCount()
{
static $count = 0;
return $count++;
}

echo ++$count . PHP_EOL; //6
echo getCount() . PHP_EOL; //0
echo getCount() . PHP_EOL; //1
1
2
<?php
echo count('1234') + count(null) + count(false) . PHP_EOL; // 1+0+1 = 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$a = 0.2+0.7;
$b = 0.9;
echo $a . PHP_EOL; // 0.9
echo $b . PHP_EOL; // 0.9
var_dump($a == $b); // bool(false)
// php在比较浮点数大小时,需要把浮点数转为字符串进行比较。

要使用 BC 这个函数库,要在编译 PHP 程序时加入 --enable-bcmath 的选项。

bcadd: 将二个高精确度数字相加。
bccomp: 比较二个高精确度数字。
bcdiv: 将二个高精确度数字相除。
bcmod: 取得高精确度数字的余数。
bcmul: 将二个高精确度数字相乘。
bcpow: 求一高精确度数字次方值。
bcscale: 配置程序中所有 BC 函数库的默认小数点位数。
bcsqrt: 求一高精确度数字的平方根。
bcsub: 将二个高精确度数字相减。