0%

PHP杂谈

干冰不是冰,水银不是银,纯碱不是碱,熊猫不是猫,鲸鱼不是鱼,双氧水不是水,铅笔芯不是铅。

问题收集

一、问题列表

  1. echo、print、print_r

    • echo不是函数,没有返回值,print是一个函数有返回值。
    • print是打印字符,print_r 则是打印复合类型
  2. empty和isset的区别

    • isset

      • isset只能用于变量,检测常量是否已设置可使用defined()函数
      • 若变量不存在则返回false
      • 若变量存在且其值为null,也返回false
      • 若变量存在且值不为null,则返回true
      • 同时检查多个变量时,每个单项都符合上一条要求时才返回true,否则结果为false
      • 使用unset()释放变量之后,它将不再是isset()
    • empty

      • empty只能用于变量,传递任何其它参数都将造成Paser error而终止运行。
      • 若变量不存在则返回true
      • 若变量存在且其值为””、0、”0”、NULL、FALSE、array()、var $var以及没有任何属性的对象,则返回 true
      • 若变量存在且值不为””、0、”0”、NULL、FALSE、array()、var $var以及没有任何属性的对象,则返回false
    • array_key_exists和isset

      • isset()对于数组中为NULL的值不会返回TRUE,而array_key_exists()会。
      1
      2
      3
      $search_array = array('first' => null, 'second' => 4);
      var_dump(isset($search_array['first']));
      var_dump(array_key_exists('first', $search_array));
  1. include和require的区别
    • include产生一个警告并继续执行,而require则导致一个致命错误
    • incluce在用到时加载,require在一开始就加载
  1. 传值与传引用

    • 传值:是把实参的值赋值给行参,那么对行参的修改不会影响实参的值
    • 传引用:真正的以地址的方式传递参数传递以后,行参和实参都是同一个对象,对行参的修改将影响实参的值
  2. 抽象类和接口

    抽象类:一种特殊的,不能被实例化的类,只能作为其他类的父类使用,使用abstract关键字声明。

接口:是一种特殊的抽象类,也是一个特殊的类,使用interface声明。

  • 抽象类的继承使用关键字extends,而接口通过implements关键字来实现
  • 抽象类中有数据成员,可以实现数据的封装,但是接口没有数据成员
  • 抽象类中可以有构造方法,但是接口没有构造方法
  • 抽象类的方法可以通过private、protected、public关键字修饰(抽象方法不能是private),而接口中的方法只能使用public关键字修饰
  • 一个类只能继承于一个抽象类,而一个类可以同时实现多个接口
  • 抽象类中可以有成员方法的实现代码,而接口中不可以有成员方法的实现代码。

面向接口编程的优缺点

优点:

  • 程序结构清晰,使用方便
  • 接口经过合理设计后,有利于程序设计的规范化,并可以并行开发,提高工作效率
  • 实现了程序的可插拔性,对于不同的需求切换不同的实现,降低了耦合度,随着系统复杂性的提高这个优势会越来越明显
  • 允许多重实现,弥补了继承的缺陷

缺点:

  • 增加了设计的复杂度,不佳的接口的设计会对所有使用接口的层都有影响,并且并不是所有的程序的都需要使用接口,接口只有在系统的复杂性达到一定程度才能体现出它的优势,否则只是纯粹的增加工作量,当然选择接口是不会错的,这需要自己的衡量
  • 可能会降低代码的可复用性
  • 可能会降低程序的执行效率
  1. 封装、继承、多态
    • 封装:把对象的属性和行为结合为一个独立的整体,并尽可能隐藏对象的内部实现细节以增加安全性
    • 继承:从已有的类中派生出新的类称为子类,子类继承父类的数据属性和行为,并能根据自己的需求扩展出新的行为,提高了代码的复用性
    • 多态:一个父类被多个子类继承,父类的某个方法在多个子类中表现出不同的功能。

多重继承的实现:

  • 接口
  • trait
  1. PHP7中??和?:

  2. 获取毫秒数

1
2
3
4
5
function getMicroSecondTime() {
list($msec, $sec) = explode(' ', microtime());
$msectime = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
return $msectime;
}
  1. array_walk、foreach、for效率对比

  2. PHP执行代码的四个步骤

  3. PHP和Redis保持长连接

  4. 使用list函数的两个点:索引数组、下标对应。

  5. 不引入第三个变量交换两个变量的值

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

$a = "abc";
$b= "def";

$a = $a^$b;
$b = $b^$a;
$a = $a^$b;


list($a, $b) = array($b, $a);


$a = $a . $b;
$b = strlen( $b );
$b = substr( $a, 0, (strlen($a) - $b ) );
$a = substr( $a, strlen($b) );


$a = $b.','.$a ;
$a = explode(',', $a);
$b = $a[1];
$a = $a[0];


$a = $a + $b;
$b = $a - $b;
$a = $a - $b;
  1. demo
1
2
3
4
5
6
7
class A
{
public static function __callStatic($name, $arguments)
{
return (new static())->__call($name, $arguments);
}
}
  1. 静态变量
1
2
3
4
5
6
7
8
function test() {
static $num = 2;
$num = $num*$num;
print_r($num);
}
test(); //4
test(); //16
test(); //256
  1. 自增
1
2
$i=1;$i=$i++;echo $i;       //1 先赋值,然后执行++
$j=2;$j=2 && $j=3;echo $j; //1 先赋值,最后&&
  1. foreach
1
2
3
4
$arr = [1, 2, 3];
foreach($arr as &$val);
foreach($arr as $val);
print_r($arr);
  1. setcookie:setcookie('name','tom');var_dump($_COOKIE['name']);

  2. 位运算:15|8 =》15 7&6 =》6

  3. var_dump('01'==1);

  4. count(123);

  5. var_dump(count(strlen("http://php.net")));

  6. unset只会取消引用,不会销毁值。

  7. 对象本身就是引用传递。

  8. SwooleDistributed框架

  9. nginx和php-fpm通信

  10. PHP缓存技术

  11. opcode

  12. 如何设计秒杀系统

  13. 提高PHP执行效率

  14. null++等于1,null--无影响

  15. unset($_COOKIE['cookie_name'])不起作用,将过期时间设置为当前时间-1

  16. 运算符优先级

    • 尽管复制运算=比其它大多数的运算符的优先级低,PHP仍旧允许类似如下的表达式:if (!$a = foo()),在此例中foo()的返回值被赋给了$a。
1
2
3
4
5
6
$a = array(2);
$b = array();
var_dump(!empty($a) && $b = $a);#true
var_dump(!empty($a) && ($b = $a));#true
print_r($b);#[2]
var_dump(!empty($a) && $b);#true
  1. 负数和布尔型关系var_dump(-1 == true);#true

  2. 执行shell命令exec("cd /Users/yourname/Desktop && rm -rf rm.txt");

  3. 查看扩展信息php --ri package_name

    • php --ri redis,redis扩展5.1.1版本(当然还有其他的)把3.1.0b版本(同理)中的delete方法换成了del
  4. foreach问题

  5. A non well formed numeric value encountered

  6. 警惕传引用导致的问题

  7. phalcon操作数据库的几种形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$bind_params = [
'id' => $id,
];
$result = self::query()
->where('id = :id:')
->bind($bind_params)
->execute()
->getFirst(); 返回对象

$model->find([
'conditions' => $where,
'bind' => $bind,
'limit' => $page_size,
'offset' => $off_set,
]);

$mode = new self();
$sql = "SELECT id, name, decode(phone, '{$db_security_code}') phone FROM {$db_name}.{$tb_name}
WHERE id = '{$id}'";
$result = new Resultset(null, $mode, $mode->getReadConnection()->query($sql));
  1. 浮点数加减运算

    • 通过乘100的方式转化为整数加减,然后在除以100转化回来……
    • 使用number_format转化成字符串,然后在使用(float)强转回来……
    • php提供了高精度计算的函数库,实际上就是为了解决这个浮点数计算问题而生的。
    • bcadd — 将两个高精度数字相加
    • bccomp — 比较两个高精度数字,返回-1, 0, 1
    • bcdiv — 将两个高精度数字相除
    • bcmod — 求高精度数字余数
    • bcmul — 将两个高精度数字相乘
    • bcpow — 求高精度数字乘方
    • bcpowmod — 求高精度数字乘方求模,数论里非常常用
    • bcscale — 配置默认小数点位数,相当于就是Linux bc中的 * scale=”
    • bcsqrt — 求高精度数字平方根
    • bcsub — 将两个高精度数字相减
  2. 大文件上传

    • 修改PHP配置文件
    1
    2
    3
    . post_max_size = 50M      #PHP可接受的最大POST数据
    . upload_max_filesize = 50M  #文件上传允许的最大值
    . max_execution_time = 300   #每个脚本的最大执行时间,秒钟(0则不限制,不建议设0)
    • 修改Nginx配置文件(http项)
    1
    . client_max_body_size 50m   #客户端最大上传大小 50M
  3. composer安装

  • 方式一
1
2
3
4
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

:需要php开启openssl模块,No package 'openssl' found,安装openssl扩展时不带--with-openssl选项

  • openssl version
  • brew info openssl
  • brew install openssl
  • export OPENSSL_LIBS=”-L/usr/local/Cellar/openssl@1.1/1.1.1p/lib”
  • export OPENSSL_CFLAGS=”-I/usr/local/Cellar/openssl@1.1/1.1.1p/include”
  • pkg-config –list-all | grep openssl
  • cd /usr/local/Cellar/openssl@1.1/1.1.1d/lib/pkgconfig
  • ln -s pwd/openssl.pc /usr/local/lib/pkgconfig
  • 方式二

    • 直接下载composer.phar
    • mv ./composer.phar /usr/local/bin/composer
    • 测试compser安装是否成功
  • 使用

    • composer update orhanerday/open-ai
    • composer config -l #查看一下当前项目的composer镜像
    • composer list 显示所有命令
    • composer show 显示所有包信息
    • composer install 在 composer.json 配置中添加依赖库之后运行此命令安装
      • composer install –ignore-platform-reqs,Windows下无法安装pctnl扩展,忽略警告安装
    • composer install –ignore-platform-reqs
    • composer create-project laravel/laravel Laravel –prefer-dist “5.1.*” 创建项目
    • composer search packagename 搜索包
    • composer update 更新所有包
    • composer update monolog/monolog 更新指定包
    • composer remove monolog/monolog 移除指定的包
    • composer require monolog/monolog 添加指定包
    • composer require monolog/monolog:1.19 添加指定包和版本
    • composer require monolog/monolog=1.19
    • composer require monolog/monolog 1.19
    • composer dump-autoload
  1. 命令行相关php -h,常用:

    • php --re redis,查看redis.so扩展相关信息
    • php --ri redis,查看redis.so扩展配置信息
    • php -r 'echo ini_get("extension_dir");'执行php代码,如获取扩展目录
    • php -i | grep configure,查看php信息如查看编译安装选项
  2. 自动加载不可用于PHP的CLI交互模式。

  3. set_exception_handler系列函数

  4. register_shutdown_function系列函数

  5. pcntl_fork系列函数

  6. stream_socket_client系列函数

  7. php_sapi_name系列函数以及常量PHP_SAPI

  8. fastcgi_finish_request

  9. parse_str

  10. extension_loaded

  11. opcache

  12. 编译安装PHP

    • 下载源码sudo wget https://www.php.net/distributions/php-7.1.0.tar.gz
    • 加压文件sudo tar -zxvf php-7.1.0.tar.gz
    • 切换目录cd php-7.1.0
    • sudo ./configure --prefix=/usr/local/php/7.1 --enable-fpm,这一步没有问题
    • sudo make && sudo make install,报错
    1
    2
    3
    ld: symbol(s) not found for architecture x86_64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    make: *** [sapi/cli/php] Error 1
    • 解决

    • ls ~/Library/Developer/Xcode/DerivedData

    • rm -rf ~/Library/Developer/Xcode/DerivedData/*

    • 重新编译安装就好了

    • 编译安装完成后时缺少扩展

      编译安装php的时候,很多时候都是最小化安装,即除了必选编译参数外别的都不要,后期根据具体业务安装对应的扩展。复制php源码目录重命名如php-rename,进入到php-rename/ext目录,里面是所有的php扩展,缺少哪个自己安装就行了。此处以mysqli为例大概说一下步骤:

    • /path/phpize

    • ./configure –with-php-config=/path/php-config

    • make && make install

    • 修改 php.ini,增加 extension=mysqli.so

    • 重启php-fpm

    • openssl扩展

    • 假设前期通过brew已经安装了openssl,brew install openssl,安装在/usr/local/Cellar/openssl/1.0.2r

    • 假设已经安装过php7.3,进入源码包 cd /usr/local/src/php-7.3/ext/openssl

    • 复制cp config0.m4 config.m4

    • sudo /usr/local/php/7.3/bin/phpize

    • ./configure --with-openssl=/usr/local/Cellar/openssl/1.0.2r --with-php-config=/usr/local/php/7.3/bin/php-config

    • sudo make && sudo make install

    • vim php.ini

    • extension=/path/openssl.so

  13. 交换两个变量

    $a = 1;$b = 2;

  • 借助第三个变量

    1
    2
    3
    $c = $a;
    $a = $b;
    $b = $c;
  • 位运算

    1
    2
    3
    $a ^= $b;
    $b ^= $a;
    $a ^= $b;
  • []用法(PHP7)

    1
    2
    $array = array(1,2);
    [$array[0], $array[1]] = [$array[1], $array[0]];
  1. $a = ‘xyz’;echo (int)$a;// 0

  2. $str = ‘This_is_a_test_string’;转换成ThisIsATestString

    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
    // 方式一
    function underline1($str, $ucfirst = true) {
    while (($pos = strpos($str , '_')) !== false) {
    $str = substr($str , 0 , $pos).ucfirst(substr($str , $pos+1));
    }
    return $ucfirst ? ucfirst($str) : $str;
    }

    // 方式二
    function underline2($str, $ucfirst = true) {
    $arr = explode('_', $str);
    foreach ($arr as $k => $v) {
    $arr[$k] = ucfirst($v);
    }

    return $ucfirst ? join('',$arr) : lcfirst(join('',$arr));
    }

    // 方式三
    function underline3($str, $ucfirst = true) {
    $str = ucwords(str_replace('_', ' ', $str));
    $str = str_replace(' ','',lcfirst($str));

    return $ucfirst ? ucfirst($str) : $str;
    }

    // 方式四
    function underline4($str, $ucfirst = true) {
    $str = preg_replace('/_([A-Za-z])/e',"strtoupper('$1')",$str);

    return $ucfirst ? ucfirst($str) : $str;
    }

    // 方式五
    function underline5($str, $ucfirst = true) {
    $str = preg_replace_callback('/([-_]+([a-z]{1}))/i',function($matches) {
    return strtoupper($matches[2]);
    }, $str);
    return $ucfirst ? ucfirst($str) : $str;
    }

  3. $a = $b = 3;

    • $a = 5 && $b++;var_dump($a, $b);//bool(true) int(4)
    • $a = 5 || $b++;var_dump($a, $b);//bool(true) int(4)
  4. $arr1 = [1,2,3];$arr2 = [1,4,5];

    • print_r(array_merge($arr1, $arr2));
      • array_merge() 将一个或多个数组的单元合并起来,一个数组中的值附加在前一个数组的后面,返回作为结果的数组。
      • 如果输入的数组中有相同的字符串键名,则该键名后面的值将覆盖前一个值。
      • 然而如果数组包含数字键名,后面的值将不会覆盖原来的值,而是附加到后面。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Array
    (
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 1
    [4] => 4
    [5] => 5
    )
    • print_r($arr1 + $arr2);
    1
    2
    3
    4
    5
    6
    Array
    (
    [0] => 1
    [1] => 2
    [2] => 3
    )
  5. $arr = [2,5,1,4,9,7,3,8];实现升序排序【冒泡】

  6. 类型转换

    • $arr = [‘one’ => ‘a’, ‘two’ => null];
    • var_dump(isset($arr[‘two’]));// bool(false)
    • $arr = [‘one’ => ‘a’, ‘two’ => ‘null’];
    • var_dump(isset($arr[‘two’]));// bool(true)
    • 键值互换后的结果array_flip()
  7. char与varchar的区别

  8. mysql锁和事务的联系

  9. service层

  10. redis分布式锁

  11. student表(id,name) score表(id,sid,score,subject),求总分前十的学员

  12. 命名空间

  13. 抽象类和接口

  14. 位运算符

    • $a & $b And(按位与)
    • $a | $b Or(按位或)
    • $a ^ $b Xor(按位异或)
    • ~ $a Not(按位取反)
    • $a << $b Shift left(左移)
    • $a >> $b Shift right(右移)
  15. 语言结构

    • 语言结构:PHP语言的关键词、语言语法的一部分,它不可以被用户定义或者添加到语言扩展或者库中,可以有也可以没有变量和返回值。

    • 语言结构执行速度快的原因:函数都要先被PHP解析器(Zend引擎)分解成语言结构,所以函数比语言结构多了一层解析器解析,速度就相对慢了。

    • php中语言结构有哪些

    • echo()

    • print()

    • die()

    • isset()

    • unset()

    • include()

    • require()

    • array()

    • list()

    • empty()

    • 怎样判断是语言结构还是函数:使用function_exists或php -rf name

    • 语言结构与函数的区别

    • 语言结构比对应功能的函数快

    • 语言结构在错误处理上比较鲁莽,由于是语言关键词,所以不具备再处理的环节

    • 语言结构不能在配置项(php.ini)中禁用,函数则可以

    • 语言结构不能被用做回调函数

  • list使用

    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
    例一、连续索引数组
    $arr = array("book","pen","paper");
    list($a, $b, $c) = $arr;
    echo "<pre>";
    var_dump($a,$b,$c);
    输出:
    string(4) "book"
    string(3) "pen"
    string(5) "paper"

    例二、非连续索引数组
    $arr = array(
    "1" => "book",
    "3" => "pen",
    "5" => "paper"
    );
    list($a, $b, $c) = $arr;
    var_dump($a,$b,$c);
    输出:
    NULL
    string(4) "book"
    NULL

    例三、关联数组
    $arr = array(
    "aaa" => "book",
    "bbb" => "pen",
    "ccc" => "paper"
    );
    list($a, $b, $c) = $arr;
    var_dump($a,$b,$c);
    输出:
    NULL
    NULL
    NULL

    例四、混合数组
    $arr = array(
    "2" => "book",
    "bbb" => "pen",
    "0" => "paper"
    );
    list($a, $b, $c) = $arr;
    var_dump($a,$b,$c);
    输出:
    string(5) "paper"
    NULL
    string(4) "book"
  1. 抽象类和接口

    • 抽象类
    • 定义为抽象的类不能被实例化
    • 任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的
    • 被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现
    • 继承一个抽象类的时候,子类必须定义父类中的所有抽象方法,且这些方法的访问控制必须和父类中一样(或者更为宽松)
    • 子类方法的调用方式必须和父类匹配,即类型和所需参数数量必须一致
    • 接口
    • 使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容
    • 接口是通过 interface 关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的
    • 接口中定义的所有方法都必须是公有,这是接口的特性
    • 使用 implements 操作符实现一个接口,类中必须实现接口中定义的所有方法,否则会报一个致命错误
    • 类可以实现多个接口,用逗号来分隔多个接口的名称
    • 实现多个接口时,接口中的方法不能有重名
    • 接口也可以继承,通过使用extends操作符
    • 类要实现接口,必须使用和接口中所定义的方法完全一致的方式。否则会导致致命错误
    • 接口中的方法全部为抽象方法,不用加abstract
    • 接口中可以有属性,属性必须为常量
    • 接口也可以继承接口,同样用extends关键字
    • 对比
    • 共同点:抽象类和接口都不能实例化
    • 不同点
      • 对接口的继承(实现)使用implements,抽象类使用extends
      • 接口中不可以声明变量,但可以声明类常量,抽象类中可以声明各种变量
      • 接口没有构造函数,抽象类可以有
      • 接口中的方法默认为public,抽象类中的方法可以用public, protected,private修饰
      • 一个类可以继承(实现)多个接口,但只能继承一个抽象类
      • 抽象类可以继承(实现)接口
  2. 静态调用非静态方法:静态属性不能通过$this访问,非静态属性不能通过selfstatic进行访问,静态方法可以通过$this访问,非静态方法可以通过selfstatic进行访问。

    • 静态属性不能通过$this访问,非静态属性不能通过selfstatic进行访问
    • 静态方法可以通过$this访问,非静态方法可以通过selfstatic进行访问
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
class Access
{
public $name = '张三';
protected $age = 10;
private $sex = '男';

public static $weight = 80;
protected static $height = 170;
private static $addr = '北京';

public function callProperty()
{
echo $this->name, PHP_EOL;
echo $this->age, PHP_EOL;
echo $this->sex, PHP_EOL;

// 不支持
// echo self::$name, PHP_EOL;
// echo self::$age, PHP_EOL;
// echo self::$sex, PHP_EOL;

// 不支持
// echo static::$name, PHP_EOL;
// echo static::$age, PHP_EOL;
// echo static::$sex, PHP_EOL;
}

public function callStaticProperty()
{
// 不支持
// echo $this->weight, PHP_EOL;
// echo $this->height, PHP_EOL;
// echo $this->addr, PHP_EOL;

echo self::$weight, PHP_EOL;
echo self::$height, PHP_EOL;
echo self::$addr, PHP_EOL;

echo static::$weight, PHP_EOL;
echo static::$height, PHP_EOL;
echo static::$addr, PHP_EOL;
}

public function callMethod()
{
$this->a();
$this->b();
$this->c();

self::a();
self::b();
self::c();

static::a();
static::b();
static::c();
}

public function callStaticMethod()
{
$this->d();
$this->e();
$this->f();

self::d();
self::e();
self::f();

static::d();
static::e();
static::f();
}

public function a()
{
echo 'a', PHP_EOL;
}

protected function b()
{
echo 'b', PHP_EOL;
}

private function c()
{
echo 'c', PHP_EOL;
}

public static function d()
{
echo 'd', PHP_EOL;
}

protected static function e()
{
echo 'e', PHP_EOL;
}

private static function f()
{
echo 'f', PHP_EOL;
}
}

$a = new Access;

$a->callMethod();
$a->callStaticMethod();

$a->callProperty();
$a->callStaticProperty();
  1. 逻辑运算符短路作用

    • 短路与:有假必假,全真为真
    • 判断方式:从左到右依次判断,直到出现false为止将不再判断,直接得到结果为false(短路遇false就停)
    • 例子
    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
    $a = 3;
    $b = 3;
    $a = 5 && $b++;
    var_dump($a, $b);// bool(true) int(4)

    $a = 3;
    $b = 0;
    $a = 5 && $b++;
    var_dump($a, $b);// bool(false) int(1)

    $a = 3;
    $b = 3;
    $a && $b++;
    var_dump($a, $b);// int(3) int(4)


    $a = 0;
    $b = 1;
    var_dump($a && $b++);// bool(false)
    var_dump($a, $b);// int(1) int(1)【发生短路】

    $a = 1;
    $b = 0;
    var_dump($a && $b++);// bool(false)
    var_dump($a, $b);// int(1) int(1)

    $a = 1;
    $b = 0;
    var_dump($a && ++$b);// bool(true)
    var_dump($a, $b);// int(1) int(1)
    • 短路或:有真必真,全假为假
    • 判断方式:从左到右依次判断,直到出现true为止将不再判断,直接得到结果为true
    • 例子
    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
    $a = 3;
    $b = 3;
    $a = 5 || $b++;
    var_dump($a, $b);// bool(true) int(3)【发生短路】

    $a = 0;
    $b = 1;
    $a || $b++;
    var_dump($a, $b);// int(0) int(2)

    $a = 0;
    $b = 1;
    $a = 1 || $b++;
    var_dump($a, $b);// bool(true) int(1)【发生短路】

    $a = 0;
    $b = 1;
    $a = 0 || $b++;
    var_dump($a, $b);// bool(true) int(2)

    $a = 0;
    $b = 0;
    $a = 0 || $b++;
    var_dump($a, $b);// bool(false) int(1)

    $a = 0;
    $b = 0;
    $a = 1 || $b++;
    var_dump($a, $b);// bool(true) int(0)【发生短路】

    $a = 1;
    $b = 0;
    $a || $b++;
    var_dump($a, $b);// int(1) int(0)【发生短路】

    $a = 1;
    $b = 2;
    if ($a || $b = 5) {
    $b++;
    }
    echo $b;//3【发生短路】

注:赋值运算符=优先级高于逻辑运算符 and or

  1. 后期静态绑定

    PHP内核包括3个模块:PHP内核、zend引擎、PHP扩展层。php内核用于处理请求、文件流、错误处理等相关处理,zend引擎将源文件转换成机器语言(opcodes),然后在zend虚拟机上运行,PHP扩展层是一组函数、类库和流,PHP使用它们来执行一些特定的操作。其中,zend引擎由两个部分组成:编译器和执行器。编译器负责将php代码编译为可执行的opcode,执行器负责将执行编译器输出的opcode。当PHP拿到一段代码后,经过词法解析、语法解析等阶段后,源程序会被翻译成一个个指令(opcode),然后ZEND虚拟机顺次执行这些指令完成操作。

  2. 基本数据类型

    • 总览
类型 说明
Boolean 布尔类型
Integer 整型
Float 浮点型
String 字符串
Array 数组
Object 对象
Resource 资源类型
NULL 无类型
Callback/Callable 回调类型,又称为可调用类型
  • 分类
    • 9种原始数据类型
      • 四种标量类型:
        • boolean(布尔型)
        • integer(整型)
        • float(浮点型,也称作double)
        • string(字符串)
      • 三种复合类型:
        • array(数组)
        • object(对象)
        • callable(可调用)
      • 两种特殊类型:
        • resource(资源)
        • NULL(无类型)
    • 为了确保代码的易读性引入了一些伪类型
      • mixed(混合类型)
      • number(数字类型)
      • callback(回调类型,又称为callable)
      • array|object(数组|对象类型)
      • void (无类型)
      • $…(伪变量)
  1. 模拟post请求

    • 使用curl函数
    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
    # 方式一
    function curl_post($url='', $postdata='',$options=array())
    {
    $fields_string = http_build_query($postdata);
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
    // curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
    curl_setopt($ch, CURLOPT_TIMEOUT, 5);
    if (!empty($options)){
    curl_setopt_array($ch, $options);
    }
    $data = curl_exec($ch);
    curl_close($ch);
    return $data;
    }

    # 方式二
    function curl_post_one($url, $post) {
    $options = array(
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HEADER => false,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $post,
    );
    $ch = curl_init($url);
    curl_setopt_array($ch, $options);
    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
    }
    • 使用file_get_contents函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function file_get_contents_post($url, $post) {
    $options = array(
    'http' => array(
    'method' => 'POST',
    'content' => http_build_query($post),
    'header'=>"Content-Type: application/x-www-form-urlencoded"
    ),
    );
    $result = file_get_contents($url, false, stream_context_create($options));
    return $result;
    }
    • 使用socket
    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
    function socket_post($url, $post) {
    $urls = parse_url($url);
    if (!isset($urls['port'])) {
    $urls['port'] = 80;
    }
    $fp = fsockopen($urls['host'], $urls['port'], $errno, $errstr);
    if (!$fp) {
    echo "$errno, $errstr";
    exit();
    }
    $post = http_build_query($post);
    $length = strlen($post);
    $header = <<<EOT
    <span class="Apple-tab-span" style="white-space:pre"> </span>POST {$urls['path']} HTTP/1.1
    <span class="Apple-tab-span" style="white-space:pre"> </span>Host: {$urls['host']}
    <span class="Apple-tab-span" style="white-space:pre"> </span>Content-Type: application/x-www-form-urlencoded
    <span class="Apple-tab-span" style="white-space:pre"> </span>Content-Length: {$length}
    <span class="Apple-tab-span" style="white-space:pre"> </span>Connection: close
    <span class="Apple-tab-span" style="white-space:pre"> </span>{$post}
    <span class="Apple-tab-span" style="white-space:pre"> </span>
    EOT;

    fwrite($fp, $header);
    $result = '';
    while (!feof($fp)) {
    $result .= fread($fp, 512);
    }
    $result = explode("\r\n\r\n", $result, 2);
    return $result[1];
    }
    • 使用fgets函数模拟get请求
    1
    2
    3
    4
    5
    6
    7
    $fp = fopen($getUrl, 'r'); 
    stream_get_meta_data($fp);
    $data = '';
    while (!feof($fp)) {
    $data .= fgets($fp, 1024);
    }
    fclose($fp);
  2. 数据类型转换

    • 任何类型都可以转为NULL,通过(unset)$var转换。
    1
    2
    3
    4
    在下列情况下一个变量被认为是 NULL:
    A. 被赋值为 NULL;
    B. 尚未被赋值;
    C. 被 unset()。
    • 其他类型转为布尔类型boolean,根据原值的true、false决定转换后的结果,比如资源类型、对象转为true等。
    1
    2
    3
    4
    5
    6
    7
    以下值会被认为是false:
    A. 布尔值false本身;
    B. 整数0;
    C. 浮点型0.0;
    D. 字符串"0";
    E. 空字符串(非空格),如:""、'';
    F. NULL;
    • 其他类型转为整型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    字符串转换数值,如果该字符串没有包含 '.','e' 或 'E' 
    并且其数字值在整型的范围之内(由PHP_INT_MAX 所定义),
    该字符串将被当成 int 来取值,
    其它所有情况下都被作为 float 来取值。
    以下为具体规则:
    A. NULL:转为0;
    B. 布尔型:false转为0,true转为1;
    C. 浮点型:向下取整,比如(int)1.9转为1;
    D. 字符串:如果字符串以合法的数值开始,则使用该数值,否则为0;合法的数值有可选的正负号,后面跟一个或多个数字,可能有小数点,再跟可选的指数部分组成;
    var_dump('09:00' > '8:00');// bool(false)
    var_dump('09:00' > '08:00');// bool(true)
    E. 数组:非空数组转为1,空数组转为0;
    F. 对象:把对象转为1;
    G. 资源:转为分配给这个资源的唯一编号。
    • 其他类型转为浮点型,除了字符串类型外,其他类型转换规则与整型基本一致
    • 其他类型转为字符串
    1
    2
    3
    4
    5
    6
    7
    8
    以下是具体转换规则:
    A. NULL/FALSE:转为空字符串;
    B. TRUE:转为“1”;
    C. 整型:原样转为字符串
    D. 浮点:原样转为字符串;
    E. 数组:转为“Array”,会报Notice错误;
    F. 对象:PHP版本大于4将报错;
    G. 资源:转为“Resource id #xxx”,xxx 是 PHP 在运行时分配给该资源的唯一值。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    $a = 1;
    var_dump((string)$a);//string(1) "1"

    $b = 0;
    var_dump((string)$b);//string(1) "0"

    $c = -1;
    var_dump((string)$c);//string(2) "-1"

    $d = 1.22222;
    var_dump((string)$d);//string(7) "1.22222"

    $e = 4.1E+6;//同写法4.1E3,E可小写
    var_dump($e);//float(4100000)
    var_dump((string)$e);//string(7) "4100000"
  • 其他类型转为数组

    1
    2
    3
    4
    5
    6
    7
    8
    A. null 转为空数组,count((array)null) == 0;
    B. 如果将一个 integer、float、string、boolean、resource 类型转为数组,则将得到一个仅有一个元素的数组,其下标为0,该元素为此标 量的值。
    换句话说,(array)$a相当于array($a)。
    C. 如果将一个object类型转为array,则结果为一个数组,数组元素为该对象的全部属性(无方法),包括public、private、protected,其中:
    public 的属性不变,
    private 属性转换后的key加上了该类名的前缀,
    protected 属性的key加上了“*”作为前缀,这个前缀是类编译生成属性就已经加上了.

    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
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    $a = 1;
    print_r((array)$a);
    输出:
    Array
    (
    [0] => 1
    )

    $a = 0;
    print_r((array)$a);
    输出:
    Array
    (
    [0] => 0
    )

    $a = null;
    print_r((array)$a);
    输出:
    Array
    (
    )

    $a = true;
    print_r((array)$a);
    输出:
    Array
    (
    [0] => 1
    )

    $a = false;
    var_dump((array)$a);
    输出:
    array(1) {
    [0]=>
    bool(false)
    }

    $a = 'aaa';
    print_r((array)$a);
    输出:
    Array
    (
    [0] => aaa
    )

    class Test{
    public $public_var = 456;
    private $private_var = 123;
    protected $protected_var = 789;

    public function go()
    {
    echo 'go !';
    }

    private function eat()
    {
    echo 'eat !';
    }

    protected function sleep()
    {
    echo 'sleep !';
    }
    }
    $obj = new Test();
    echo "<pre>";print_r((array)$obj);
    输出:
    Array
    (
    [public_var] => 456
    [Testprivate_var] => 123
    [*protected_var] => 789
    )
  • 其他类型转换为对象

    1
    2
    3
    4
    其他任何类型的值被转换为对象,将会创建一个内置类stdClass的实例:
    A. 如果该值为NULL,则新的实例为空;
    B. array转为object将以键名成为属性名并具有相对应的值,数值索引的元素也将转为属性,但是无法通过“->”访问,只能遍历获取;
    C. 对于其他值,会以“scalar”作为属性名。
  1. PSR:PSR是PHP Standards Recommendation的简称,译为PHP标准规范,它是php-fig组织制定的一套规范,中文地址。其目的是通过框架作者或者框架的代表之间讨论,以最低程度的限制,制定一个协作标准,各个框架遵循统一的编码规范,避免各家自行发展的风格阻碍了PHP的发展,解决这个程序设计师由来已久的困扰。

目前已表决通过了6套标准,已经得到大部分PHP框架的支持和认可:

  1. 代码优化

    • save()、update()的使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    ## 通过设置对象属性来保存或添加数据,而不是直接传一个数组进去
    $data['id'] = 123;
    $data['name'] = 'new_name';
    $this->save($data);//不推荐写法,别人调用时不知道$data的具体内容

    $this->id = 123;
    $this->name = "new_name";
    $this->save();//推荐写法

    $data['id'] = 123;
    $data['name'] = 'new_name';
    $data['modified'] = '2019-01-31 10:10:10';
    $this->update($data);//不推荐写法,别人调用时不知道$data的具体内容

    $obj = $this->findFirst("id = '{$id}'");
    $obj->name = '123';
    $obj->modified = '2019-02-11 10:10:10';
    $obj->update();//推荐写法

  • 原先代码

    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
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    class ExternLoginController extends AppController {

    public function beforeFilter() {
    $this->loadModel('ExternLogin');
    }

    /**
    *
    * 外部机构登录
    * ----------------------------------------------------------
    * @access public
    * ----------------------------------------------------------
    * @author someone <someone@email.com>
    * ----------------------------------------------------------
    * @date 2017-05-24 14:35:36
    * ----------------------------------------------------------
    */
    public function index(){
    $this->layout = false;
    $query = $_REQUEST;
    $token = $query['token'];
    $code = $query['code'];
    $data = unserialize(self::decrypt($token,$code));

    // 解密失败
    if (!$data) {
    $result = ['status' => false,'code' => 4001];
    echo json_encode($result);
    exit;
    }
    $identify = $data['identify'];
    $goods_id = $data['goods_id'];

    // 查询班级信息
    if (isset($goods_id) && !empty($goods_id)) {
    $req['goods_id'] = $goods_id;
    $goods = $this->ExternLogin->getExternGoodsById($req);
    if (empty($goods['result'])) {
    $result = ['status' => false,'code' => 4002];
    echo json_encode($result);
    exit;
    }
    $goods_id = $goods['result']['goods_id'];
    $cid = $goods['result']['course_id'];
    $cate_level = $goods['result']['cate_level'];
    }

    // 根据员工ID获取商家、会员ID
    $post['employee_id'] = $data['employee_id'];
    $login_res = $this->ExternLogin->getMerchantAndMemberIdByEmployeeId($post);
    if (empty($login_res['result'])) {
    $result = ['status' => false,'code' => 4003];
    echo json_encode($result);
    exit;
    }

    // 拼装url参数,请求会员登录方法写入相应session
    $token = '3B166iwrt1Tzi8LDacCLaXHDqWtE7gVagmjwsu8ZeNHLq76ZtYvWetUan6O1';
    $sign = base64_encode($login_res['member_id'] . $token);//数据签名
    $mid = base64_encode($login_res['member_id']);
    $merchant_id = base64_encode($login_res['merchant_id']);
    $host = $_SERVER['REQUEST_SCHEME'] .'://'.$_SERVER['HTTP_HOST'];
    $sync_url = "{$host}/USyncLogins/login?t={$token}&mid={$mid}&merchant_id={$merchant_id}&s={$sign}& extern=1";
    $script = "<script type='text/javascript' src='{$sync_url}'></script>";

    // 根据请求标识跳转到对应页面
    $jump_url = '';
    $common_url = '?reference=aixuexi&';
    if($identify == 'addstudent') {
    $jump_url = $common_url . 'name=添加学员&path=/Students/add';
    } else if ($identify == 'checkstudent') {
    $jump_url = $common_url . 'name=查看学员&path=/Students/index';
    } else if ($identify == 'addotmclass') {
    $jump_url = $common_url . 'name=添加班级&path=/GoodsClassOtms/add';
    } else if ($identify == 'otmclassstudent') {
    $jump_url = $common_url . "name=班级名单&path=/GoodsClassOtms/signUpList/gid:{$goods_id}/op:2/cid: {$cid}/cate_level:{$cate_level}/status:2";
    } else if ($identify == 'checkotmclass') {
    $jump_url = $common_url . 'name=查看班级&path=/GoodsClassOtms/index';
    } else {
    $jump_url = '';
    }

    $script .= "<script>window.location.href = '/Homes/index{$jump_url}'</script>";
    die($script);
    }

    /**
    * 加密
    * ----------------------------------------------------------
    * @access public
    * ----------------------------------------------------------
    * @author someone <someone@email.com>
    * ----------------------------------------------------------
    * @date 2017-05-24 14:35:36
    * ----------------------------------------------------------
    */
    public static function encrypt($key, $data, $iv = '') {
    // ...
    }

    /**
    * 解密
    * ----------------------------------------------------------
    * @access public
    * ----------------------------------------------------------
    * @author someone <someone@email.com>
    * ----------------------------------------------------------
    * @date 2017-05-24 14:35:36
    * ----------------------------------------------------------
    */
    public static function decrypt($key, $data, $iv = '') {
    // ...
    }

    }
  • 优化后代码

    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
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    class ExternLoginController extends AppController {

    /**
    * 调用者
    *
    * @var string
    */
    private $caller = null;

    public function beforeFilter()
    {
    $this->loadModel('ExternLogin');
    }

    /**
    *
    * 外部机构登录
    * ----------------------------------------------------------
    * @access public
    * ----------------------------------------------------------
    * @author someone <someone@email.com>
    * ----------------------------------------------------------
    * @date 2017-05-24 14:35:36
    * ----------------------------------------------------------
    */
    public function index()
    {
    $this->layout = false;

    // 获取URL参数,【若用$this->get()会把url参数中的字符+替换成空格】
    $query = $_REQUEST;
    $this->caller = $query['caller'];
    $req_token = $query['token'];
    $req_code = $query['data'];

    // 调用者这判断
    $extern_caller = Configure::read('extern_caller');
    if (!in_array($this->caller, $extern_caller)) {
    // 非法调用
    exit(json_encode(['status' => false,'code' => 4000]));
    }

    // 解密参数
    $req_data = json_decode(self::decrypt($req_token, $req_code),true);
    if (!$req_data) {
    // 数据解密错误
    exit(json_encode(['status' => false,'code' => 4001]));
    }

    // 第三方进入页面地址
    $page_tag = $req_data['page_tag'];
    $extern_login_redirect_arr = Configure::read('extern_login_redirect');
    $redirect_url = isset($extern_login_redirect_arr[$page_tag]) ? $extern_login_redirect_arr[$page_tag] : '';
    if (empty($redirect_url)) {
    // 非法调用
    exit(json_encode(['status' => false,'code' => 4000]));
    }

    // 根据第三方法传入的user_id获取内部员工账号数据
    $login_params['employee_id'] = $req_data['user_id'];
    $login_params['caller'] = $this->caller;
    $login_res = $this->ExternLogin->getMerchantAndMemberIdByEmployeeId($login_params);
    if (empty($login_res['result'])) {
    // 账号未绑定或不存在
    exit(json_encode(['status' => false,'code' => 4002]));
    }

    // 进入班级相关页
    if (isset($req_data['goods_id']) && !empty($req_data['goods_id'])) {
    $redirect_url = $this->getRedirectUrlOtmClassStu($req_data['goods_id'], $redirect_url);
    }

    // TODO: 未来扩展业务,如:进入人事管理页等等,如果有则在下面像班级那样处理即可

    // 内部实现自动登录并跳转
    $this->loginAndRedirect($login_res, $redirect_url);
    }

    /**
    * 获取班级详情
    * ----------------------------------------------------------
    * @param string $goods_id 班级ID
    * @param string $redirect_url 页面跳转地址
    * ----------------------------------------------------------
    * @return string
    * ----------------------------------------------------------
    * @author someone <someone@email.com>
    * ----------------------------------------------------------
    * @date 2017-05-24 11:24
    */
    private function getRedirectUrlOtmClassStu($goods_id, $redirect_url)
    {
    $req['goods_id'] = $goods_id;
    $req['caller'] = $this->caller;
    $goods = $this->ExternLogin->getExternGoodsById($req);
    if (empty($goods['result'])) {
    // 班级未绑定或不存在
    exit(json_encode(['status' => false,'code' => 4003]));
    }
    $goods = $goods['result'];

    return str_replace(['{goods_id}', '{cid}', '{cate_level}'], [$goods['goods_id'], $goods['goods_id'], $goods['goods_id']]);
    }

    /**
    * 登录并跳转
    * ----------------------------------------------------------
    * 实现逻辑
    * 1. 模拟登录以写入相应session
    * 2. 跳转指定url
    * ----------------------------------------------------------
    * @param [type] $login_params
    * ----------------------------------------------------------
    * @return void
    * ----------------------------------------------------------
    * @date 2017-05-24 14:50
    */
    private function loginAndRedirect($params, $redirect_url)
    {
    $token = '3B166iwrt1Tzi8LDacCLaXHDqWtE7gVagmjwsu8ZeNHLq76ZtYvWetUan6O1';
    $sign = base64_encode($params['member_id'] . $token);//数据签名
    $mid = base64_encode($params['member_id']);
    $merchant_id = base64_encode($params['merchant_id']);
    $host = $_SERVER['REQUEST_SCHEME'] .'://'.$_SERVER['HTTP_HOST'];
    $sync_url = "{$host}/USyncLogins/login?t={$token}&mid={$mid}&merchant_id={$merchant_id}&s={$sign}& extern=1";
    $script = "<script type='text/javascript' src='{$sync_url}'></script>";

    $redirect_url = "?reference={$this->caller}&{$redirect_url}";
    $script .= "<script>window.location.href = '/Homes/index{$redirect_url}'</script>";
    die($script);
    }
    }
    • 优化项
      • 方法大括号写法
      • 大段if…else逻辑优化
      • 大段代码优化成小方法调用
  1. 引用返回-自定义函数前面加and符号

    • 引用返回用在当想用函数找到引用应该被绑定在哪一个变量上面时。
    • 不要用返回引用来增加性能,引擎足够聪明来自己进行优化
    • 仅在有合理的技术原因时才返回引用【这。。。】
    • 官方demo
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class Foo {
    public $value = 42;

    public function &getValue() {
    return $this->value;
    }

    public function getValueNone() {
    return $this->value;
    }
    }

    $obj = new Foo;
    $a = &$obj->getValue();
    $b = $obj->getValue();

    echo $a,PHP_EOL;
    echo $b,PHP_EOL;

    $a = 11;
    $b = $obj->getValue();
    echo $b,PHP_EOL;
    • 自定义demo
    • 通过$a=test()方式调用函数,只是将函数的值赋给$a而已,因而$a做任何改变都不会影响到函数中的$b
    • 通过$a=&test()方式调用函数,作用是将return $b中的$b变量的内存地址与$a变量的内存地址指向了同一个地方,即产生了相当于这样的效果($a=&b;),所以改变$a的值,也同时改变了$b的值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function &test()  
    {
    static $b = 0;//BZ也有疑问为啥要申明一个静态变量
    $b = $b+1;
    echo $b, PHP_EOL;
    return $b;
    }

    $a=test();//输出1

    $a=5;
    $a=test();//输出2

    $a=&test();//输出3

    $a=5;
    $a=test();//输出6
  2. 自动加载

    • 在编写面向对象(OOP)程序时,很多开发者为每个类新建一个PHP文件。这会带来一个烦恼:每个脚本的开头都需要包含(include)一个长长的列表。在PHP5中已经不再需要这样了。spl_autoload_register()函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载。通过注册自动加载器,脚本引擎在PHP出错失败前有了最后一个机会加载所需的类。
    • 尽管autoload()函数也能自动加载类和接口,但更建议使用spl_autoload_register()函数。spl_autoload_register()提供了一种更加灵活的方式来实现类的自动加载(同一个应用中,可以支持任意数量的加载器,比如第三方库中的)。因此不再建议使用autoload()函数,在以后的版本中它可能被弃用)在PHP7.2.0中已经废弃)。
    • Demo
    • 类文件A.php B.php
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class A
    {
    function test()
    {
    echo 'A';
    }
    }

    class B
    {
    function test()
    {
    echo 'B';
    }
    }
    • 实现

      • 方式1:include或require

        1
        2
        3
        4
        5
        6
        7
        include('A.php');
        include('B.php');
        $a = new A();
        $a->test();
        echo PHP_EOL;
        $b = new B();
        $b->test();
      • 方式2:__autoload函数

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        function __autoload($class)
        {
        include($class .'.php');
        }

        $a = new A;
        $a->test();
        echo PHP_EOL;
        $b = new B;
        $b->test();
      • 方式3:spl_autoload_register函数

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        function my_autoload($class)
        {
        include $class . '.php';
        }
        spl_autoload_register('my_autoload');
        $a = new A;
        $a->test();
        echo PHP_EOL;
        $b = new B;
        $b->test();

        或匿名函数

        spl_autoload_register(function($class) {
        include $class . '.php';
        });

        $a = new A;
        $a->test();
        echo PHP_EOL;
        $b = new B;
        $b->test();
      • 方式4:框架常用

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        class ClassAutoloader
        {
        public function __construct()
        {
        spl_autoload_register(array($this, 'loader'));
        }

        private function loader($className)
        {
        echo 'Trying to load ', $className, ' via ', __METHOD__, "()\n";
        include $className . '.php';
        }
        }

        $autoloader = new ClassAutoloader();
        $a = new A();
        $b = new B();
    • __audoload和spl_autoload_register对比

    • 可以按需多次写spl_autoload_register注册加载函数,加载顺序按谁先注册谁先调用。aotuload由于是全局函数只能定义一次,不够灵活,有多个autoload会报错。

    • SPL函数很丰富,提供了更多功能

    • spl_autoload_register可以被catch到错误,而__aotuload不能

    • spl自动加载常用函数

    • spl_​autoload_​call:尝试调用所有已注册的__autoload()函数来装载请求类

    • spl_​autoload_​extensions:注册并返回spl_autoload函数使用的默认文件扩展名

    • spl_​autoload_​functions:返回所有已注册的__autoload()函数

    • spl_​autoload_​register:注册给定的函数作为 __autoload 的实现

    • spl_​autoload_​unregister:注销已注册的__autoload()函数

    • spl_​autoload:__autoload()函数的默认实现

    • spl_​classes:返回所有可用的SPL类

    • 参考

  3. $_POST, php://input, $_GLOBALS

    • 只有Centent-Type的值为application/x-www.form-urlencoded和multipart/form-data两种类型时,$_POST才能获取到数据
    • 如果php无法识别Centent-Type类型,也就无法获取请求数据,这个时候,可以用$GLOBALS来获取。
    • php://input与$GLOBALS的功能是一样的,php://input需要的内存比较小,并且它不受 php.ini 配置文件的限制。
    • 如果Centent-Type的类型为multipart/form-data,使用php://input和$GLOBALS是获取不到数据的,除此之外,php://input都能获取到数据。
    • 仅当Centent-Type的类型为application/x-www.form-urlencoded时,使用php://input和$_POST获取到的数据才是一致的。
    • 使用方式:使用file_get_contents(‘php://input’)获取请求数据。

      博主使用php,有次跟java对接时,java同学说用的post方式发送的请求,php使用$_POST/$_REQUEST获取请求体时格式一直是错的,即把java的请求体当成了key,value字段是空的,最终使用file_get_contents(‘php://input’)才拿到了正确的请求。

  1. 编译安装php5.6和7.3区别

    • 问题
    1
    2
    3
    [28-Jun-2019 17:59:11] ERROR: [/usr/local/php/7.3/etc/php-fpm.conf:5] unknown entry 'user'
    [28-Jun-2019 17:59:11] ERROR: failed to load configuration file '/usr/local/php/7.3/etc/php-fpm.conf'
    [28-Jun-2019 17:59:11] ERROR: FPM initialization failed
    • 原因

    • 权限问题,这块儿还有待研究

    • 编译安装php5.6的时候,不知道什么原因,没有=/usr/local/php/7.3/etc/php-fpm.d目录,7.3有此目录

    • 7.3版本php-fpm.conf文件最后一句话include=/usr/local/php/7.3/etc/php-fpm.d/*.conf

    • 解决

    • 修改php-fpm所属用户和组就行了,要跟nginx的配置文件user yourself staff;保持一致

    • php-fpm.conf最后一句话:include=/usr/local/php/7.3/etc/php-fpm.d/*.conf

    • 修改include=/usr/local/php/7.3/etc/php-fpm.d目录下的www.conf

    1
    2
    user = yourself
    group = staff
  2. PHP5和PHP7

    • php标量类型和返回类型声明
    1
    2
    //主要分为两种模式,强制性模式和严格模式:1表示严格类型校验模式,作用于函数调用和返回语句;0表示弱类型校验模式。
    declare(strict_types=1)
    • NULL合并运算符
    1
    2
    3
    $site = isset($_GET['site']) ? $_GET['site'] : 'wo';
    #简写成
    $site = $_GET['site'] ??'wo';
    • 组合运算符
    1
    2
    3
    4
    // 整型比较
    print( 1 <=> 1); // 0
    print( 1 <=> 2); // -1
    print( 2 <=> 1); // 1
  • 常量数组

    1
    2
    3
    4
    5
    6
    // 使用 define 函数来定义常量数组
    define('sites', [
    'Google',
    'Jser',
    'Taobao'
    ]);
  • 匿名类

    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
    interface Logger { 
    public function log(string $msg);
    }

    class Application {
    private $logger;

    public function getLogger(): Logger {
    return $this->logger;
    }

    public function setLogger(Logger $logger) {
    $this->logger = $logger;
    }
    }

    $app = new Application;
    // 使用 new class 创建匿名类
    $app->setLogger(new class implements Logger {
    public function log(string $msg) {
    print($msg);
    }
    });

    $app->getLogger()->log("我的第一条日志");
  • Closure::call()方法增加,意思向类绑定个匿名函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class A { 
    private $x = 1;
    }
    // 之前版本定义闭包函数代码
    $getXCB = function() {
    return $this->x;
    };

    // 闭包函数绑定到类 A 上
    $getX = $getXCB->bindTo(new A, 'A');
    echo $getX();

    // PHP 7+ 代码
    $getX = function() {
    return $this->x;
    };
    echo $getX->call(new A);
  • CSPRNG(伪随机数产生器)。

    1
    2
    3
    引入几个 CSPRNG 函数:
    random_bytes() - 加密生存被保护的伪随机字符串。
    random_int() - 加密生存被保护的伪随机整数。
  • 异常

    1
    PHP 7 异常用于向下兼容及增强旧的assert()函数。
  • use语句改变

    1
    2
    // 导入同一个namespace下的类简写
    use some\namespace\{ClassA, ClassB, ClassC as C};
  • Session选项

    1
    2
    3
    4
    5
    6
    * session_start()可以定义数组
    session_start(&#91;
    'cache_limiter' => 'private',
    'read_and_close' => true,
    ]);
    * 引入了一个新的php.ini设置(session.lazy_write),默认情况下设置为 true,意味着session数据只在发生变化时才入。
  • PHP7移除的扩展

    1
    2
    3
    4
    mssql
    mysql
    sybase_ct
    ereg
  1. ThinkPHP3和ThinkPHP5

    • 入口文件的绑定:和大多开源PHP框架一样,TP也是个单一入口框架,它所有的请求都通过public/index.php进入,默认访问的是index模块下的Index控制器下的index方法。可以通过define('BIND_MODULE','home');自动访问home模块,亦或是通过convention.php中有一个auto_bind_module设为true实现自动绑定。

    • URL和路由

    • 5.x版本正式废除【类似/id/1方式可以通过get获取到id的方法】,严格来讲这样的url是不属于$_GET,5.x版本后可以通过param获取,具体使用可以通过请求部分查询。

    • 5.x版本URL访问不再支持普通URL模式,路由也不支持正则路由定义,而是全部改为规则路由配合变量规则(正则定义)的方式。

    • convention.php文件中通过修改url_route_onurl_route_must配置,来控制路由是否开启和是否强制使用路由。

    • 命名

    • 目录和文件名采用小写+下划线,并且以小写字母开头

    • 类库、函数文件统一以.php为后缀

    • 类文件名均以命名空间定义,并且命名空间的路径和类库文件所在路径一致(包括大小写)

    • 类名和类文件名保持一致,并统一采用驼峰法命名(首字母大写)

    • 函数

    • 系统已经不依赖任何函数,只是对常用的操作封装提供了助手函数

    • 单字母函数废弃,默认系统加载助手函数,具体参见如下

      3.2版本 5.0版本
      C config
      E exception
      G debug
      L lang
      T 废除
      I input
      N 废除
      D model
      M db
      A controller
      R action,如action(index/user)调用index控制器下的user方法,action(‘index’)调用本控制器下的方法
      B 废除
      U url
      W widget
      S cache
      F 废除
    • 请求对象Request和响应对象Response

    • 5.0新增了请求对象Request和响应对象Response,Request统一处理请求和获取请求信息,Response对象负责输出客户端或者浏览器响应,获取请求或响应对象的方法如下

    • 通过助手函数request()

    1
    2
    $request = request();
    var_dump($request);
    • 通过实例化Request类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    use think\Request;

    class Index
    {
    public function index()
    {
    $request = Request::instance();
    var_dump($request);
    }
    }
    • 通过注入Request实例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    use think\Request;

    class Index
    {
    public function index(Request $request)
    {
    var_dump($request);
    }
    }
    • 控制器
    • 应用类库的命名空间统一为app(可修改)而不是模块名
    • 控制器的类名默认不带Controller后缀,可以配置开启controller_suffix参数启用控制器类后缀
    • 控制器操作方法采用return方式返回数据,而非直接输出
    • 废除原来的操作前后置方法
    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
    ① 控制器写法
    3.2:
    namespace Home\Controller;
    use Think\Controller;
    class IndexController extends Controller
    {
    public function hello()
    {
    echo 'hello,thinkphp!';
    }
    }

    5.x:
    namespace app\index\controller;
    class Index
    {
    public function index()
    {
    return 'hello,thinkphp!';
    }
    }

    ②控制器命名
    3.2:IndexController.class.php
    5.x:Index.php
    • 模板
    • 在控制器中正确的输出模板,5.0在控制器中输出模板,使用方法如下:
      • 如果继承think\Controller的话可以使用:return $this->fetch(‘index/hello’)
      • 如果没有继承think\Controller的话使用:return view(‘index/hello’)
  • 模型
    • 数据库操作助手函数变化
      • M->db
        • M(‘User’)->where([‘name’=>’thinkphp’])->find();(3.2)
        • db(‘User’)->where(‘name’=>’thinkphp’)->find();(5.0)
      • D->model
        • D(‘User’)->where([‘name’=>’thinkphp’])->find();
        • model(‘User’)->where([‘name’=>’thinkphp’])->find();//或者$UserModel =new User();
      • U->url
  • 新版的模型查询增加了静态方法,如;
    • User::get(1);
    • User::all();
    • User::where(‘id’,’>’,10)->find(); 等等
  • 自动验证:5.x版本验证使用独立的\think\Validate类或者验证器进行验证,不仅适用于模型,在控制器也可直接调用
  • 配置文件
  • 异常处理:5.x版本对错误零容忍,默认情况下会对任何级别的错误抛出异常,并且重新设计了异常页面,展示了详尽的错误信息,便于调试。
  • 常量:5.x版本废弃了原来的大部分常量定义,仅仅保留了框架的路径常量定义,其余的常量可以使用App类或者Request类的相关属性或者方法来完成,或者自己重新定义需要的常量。废除的常量包括:
    REQUEST_METHOD、IS_GET IS_POST、IS_PUT、IS_DELETE、IS_AJAX __EXT__、COMMON_MODULE 、MODULE_NAME、CONTROLLER_NAME、ACTION_NAME、APP_NAMESPACE、APP_DEBUG、MODULE_PATH
  • 模板继承:模板继承是一项更加灵活的模板布局方式,模板继承不同于模板布局,甚至来说,应该在模板布局的上层。模板继承其实并不难理解,就好比类的继承一样,模板也可以定义一个基础模板(或者是布局),并且其中定义相关的区块(block),然后继承(extend)该基础模板的子模板中就可以对基础模板中定义的区块进行重载。因此,模板继承的优势其实是设计基础模板中的区块(block)和子模板中替换这些区块。每个区块由{block} {/block}标签组成。
  1. cgi、fast-cgi、php-cgi、php-fpm

    每种动态语言(PHP,Python等)的代码文件需要通过对应的解析器才能被服务器识别,而CGI协议就是用来使解释器与服务器可以互相通信。PHP文件在服务器上的解析需要用到PHP解释器,再加上对应的CGI协议,从而使服务器可以解析到PHP文件。

    • CGI(COMMON GATEWAY INTERFACE)公共网关接口,它的作用就是帮助服务器与web编程语言通信,是为了保证web server传递过来的数据是标准格式的,方便CGI程序(如php-cgi)的编写者。
    • 如果一个静态请求如xxx/index.html或/image.png等,那么web server会去文件系统中找到这个文件并发送给浏览
    • 如果一个动态请求如xxx/index.php或/index.jsp等,nginx会根据配置文件将这个请求简单处理后交给相应的解析器(如PHP)
    • 假如请求的是index.php,nginx会传哪些数据给PHP解析器呢?url要有吧,查询字符串也得有吧,POST数据也要有,HTTP Header不能少吧等等。CGI就是规定要传哪些数据、以什么样的格式传递给后方处理这个请求的协议。
    • 当web server收到/index.php这个请求后,会启动对应的CGI程序,这里就是PHP的解析器。接下来PHP解析器会解析php.ini文件,初始化执行环境,然后处理请求,再以CGI规定的格式返回处理后的结果,退出进程。web server再把结果返回给浏览器。
    • fast-cgi是用来提高CGI程序性能的,那么CGI程序的性能问题在哪呢?即上文中”PHP解析器会解析php.ini文件,初始化执行环境”这个过程。
    • 传统的cgi协议在每次连接请求时都会开启一个进程进行处理,处理完毕会关闭该进程。下次连接请求又要再次开启一个进程进行处理,cgi是一个进程只能处理一个请求,有多少个连接就有多少个cgi进程,过多的进程会消耗资源和内存。
    • fast-cgi则是一个进程可以处理多个请求。
    • fast-cgi具体是怎么做的呢?首先,fast-cgi会先启一个master进程,解析配置文件,初始化执行环境;然后再启动多个worker进程。当请求过来时master会传递给一个worker,然后立即可以接受下一个请求,这样就避免了重复的工作,效率自然是高。当worker进程不够用时,master进程可以根据配置预先启动几个worker等着;当然空闲worker太多时,也会停掉一些。
    • fastcgi只是一种协议(就像http协议一样,apache对它的实现是httpd,nginx对它的实现就叫nginx),不是进程。
    • php-cgi:php的解释器,php-cgi是php提供给web serve也就是http前端服务器的cgi协议接口程序。
    • 当每次接到http前端服务器的请求都会开启个php-cgi进程进行处理,开启的php-cgi的过程中会先要重载配置,数据结构以及初始化运行环境。如果更新了php配置,那么就需重启php-cgi才能生效。
    • php-cgi只是个CGI程序,本身只能处理请求返回结果,不会管理进程,所以就出现了一些能够调度php-cgi进程的程序,比如由lighthttpd分离出来的spawn-fcgi,再比如php-fpm。
    • php-fpm全称为PHP FastCGI Process Manager,是一个实现了fast-cgi的程序,被PHP官方收了。它不会像php-cgi一样每次连接都会重新开启一个程,处理完请求又关闭这个进程,而是允许一个进程对多个连接进行处理,处理完不会立即关闭这个进程,而是会接着处理下一个连接。
    • 它可以说php-cgi的一个管理程序,是对php-cgi的改进。
    • php-fpm会开启多个php-cgi程序,并且php-fpm常驻内存,每次web server服务器发连接过来的时候,php-fpm将连接信息分配给下面其中的一个子程序php-cgi进行处理,处理完毕这个php-cgi并不会关闭,而是继续等待一个连接,这也是fast-cgi加速的原理。
    • 由于php-fpm是多进程的,而一个php-cgi基本消耗7-25M内存,因此如果连接过多就会导内存消耗过大,引发一些问题,例如nginx里的502错误。
    • 参考
    • 简单说明CGI和动态请求是什么
    • PHP-FPM以及php-cgi, fast-cgi,以及与nginx的关系
    • 后记
    • cgi:它是一种协议。通过cgi协议,web server可以将动态请求和相关参数发送给专门处理动态内容的应用程序。
    • fastcgi:也是一种协议,只不过是cgi的优化版。cgi的性能较烂,fastcgi则在其基础上进行了改进。
    • php-cgi:fastcgi是一种协议,而php-cgi实现了这种协议。不过这种实现比较烂。它是单进程的,一个进程处理一个请求,处理结束后进程就销毁。
    • php-fpm:是对php-cgi的改进版,它直接管理多个php-cgi进程/线程。也就是说,php-fpm是php-cgi的进程管理器,因此它也算是fastcgi协议的实现。
    • cgi进程/线程:在php上,就是php-cgi进程/线程,专门用于接收web server的动态请求,调用并初始化zend虚拟机。
    • cgi脚本:被执行的php源代码文件。
    • zend虚拟机:对php文件做词法分析、语法分析、编译成opcode,并执行。最后关闭zend虚拟机。
    • cgi进程/线程和zend虚拟机的关系:cgi进程调用并初始化zend虚拟机的各种环境。
  2. 魔术常量-魔术方法-全局变量

    • 魔术常量(魔术变量):所谓的魔术常量就是PHP预定义的一些常量,这些常量会随着所在的位置而变化。

    • __LINE__ 获取文件中的当前行号。

    • __FILE__ 获取文件的完整路径和文件名。

    • __DIR__ 获取文件所在目录。

    • __FUNCTION__ 获取函数名称(PHP 4.3.0 新加)。

    • __CLASS__ 获取类的名称(PHP 4.3.0 新加)。

    • __METHOD__ 获取类的方法名(PHP 5.0.0 新加)。

    • __NAMESPACE__ 当前命名空间的名称(区分大小写)。

    • __TRAIT__ Trait 的名字(PHP 5.4.0 新加)。自 PHP 5.4 起此常量返回 trait 被定义时的名字(区分大小写)。Trait 名包括其被声明的作用区域(例如 Foo\Bar)。

    • 魔术方法

    • __construct():每次创建新对象(实例化对象)时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。

      • 如果子类中定义了构造函数则不会隐式调用其父类的构造函数
      • 要执行父类的构造函数,需要在子类的构造函数中调用 parent::__construct()
      • 如果子类没有定义构造函数则会如同一个普通的类方法一样从父类继承(除private)
    • __destruct():析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。

      • 析构函数在脚本关闭时调用,此时所有的 HTTP 头信息已经发出
      • 析构函数即使在使用exit()终止脚本运行时也会被调用,在析构函数中调用 exit()将会中止其余关闭操作的运行
      • 如果子类中定义了析构函数则不会隐式调用其父类的析构函数
      • 要执行父类的析构函数,必须在子类的析构函数体中显式调用 parent::__destruct()
      • 子类如果自己没有定义析构函数则会继承父类(除private)
    • __call($method, $arg):在对象中调用一个不可访问方法时,__call() 会被调用

      • public mixed __call(string $name, array $arguments),$name 参数是要调用的方法名称,$arguments 参数是一个枚举数组,包含着要传递给方法 $name 的参数
      • __callStatic($method, $arg):在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。
      • __set($key, $val):在给不可访问属性赋值或属性不存在会被调用。
      • __get($key):读取不可访问属性的值或属性不存在会被调用。
      • __isset($key):当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。
      • __unset($key):当对不可访问属性调用 unset() 时,__unset() 会被调用。
      • __sleep():方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。与之相反,unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
    • __wakeup():经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。

    • __toString():用于一个类被当成字符串时回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。

    • __invoke():当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。(本特性只在 PHP 5.3.0 及以上版本有效)

    • __set_state($array):自PHP 5.1.0起当调用 var_export() 导出类时,此静态 方法会被调用。本方法的唯一参数是一个数组,其中包含按 array(‘property’ => value, …) 格式排列的类属性。

    • __clone():当复制完成时,如果定义了 __clone() 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用,可用于修改属性的值(如果有必要的话)。

    • __debugInfo():当调用var_dump()打印对象时被调用(当你不想打印所有属性)适用于PHP5.6版本

    • 预定义变量

    • 预定义变量

      • 超全局变量
      • $php_errormsg — 前一个错误信息
      • $HTTP_RAW_POST_DATA — 原生POST数据
      • $http_response_header — HTTP 响应头
      • $argc — 传递给脚本的参数数目
      • $argv — 传递给脚本的参数数组
    • 超全局变量 — 超全局变量是在全部作用域中始终可用的内置变量

      • $GLOBALS — 引用全局作用域中可用的全部变量
      • $_SERVER — 服务器和执行环境信息
      • $_GET — HTTP GET 变量
      • $_POST — HTTP POST 变量
      • $_FILES — HTTP 文件上传变量
      • $_REQUEST — HTTP Request 变量
      • $_SESSION — Session 变量
      • $_ENV — 环境变量
      • $_COOKIE — HTTP Cookies
  3. new self和new static

    • 同类中使用
    • 初始化父类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class Father
    {
    public function getNewFather()
    {
    return new self();
    }

    public function getNewCaller()
    {
    return new static();
    }
    }

    $f = new Father();

    print get_class($f->getNewFather());//Father
    print get_class($f->getNewCaller());//Father
    • 子类中使用
    • 初始化子类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Son1 extends Father
    {

    }

    class Son2 extends Father
    {

    }

    $s1 = new Son1();
    $s2 = new Son2();

    print get_class($s1->getNewFather());//Father
    print get_class($s1->getNewCaller());//Son1

    print get_class($s2->getNewFather());//Father
    print get_class($s2->getNewCaller());//Son2
    • 对比
    • 区别只有在继承中才能体现出来,如果没有任何继承,那么这两者是没有区别的。
    • new self()返回同一个类的实例,new static()返回则是由调用者决定的
  4. use、namespace、trait

    • namespace
    • 在PHP中,命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题:
      • 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
      • 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。
    • 使用方法:
      • namespace的定义不区分大小写
      • 没有定义命名空间,就理解为使用顶级命名空间,实例化对象时可以在类前加上反斜杠,也可以不加
      • 实例化对象带上命名空间时,之间一定用反斜杠字符,而不是顺斜杠
      • 类在指定命名空间下, 实例化对象时一定要带上指定的命名空间
      • 命名空间声明后的代码便属于这个命名空间,即使有include或require也不影响
      • namespace里不包含类名称,即使存在与类名称同名的部分也不代表类
      • 一个php文件中可以存在多个命名空间,第一个命名空间前不能有任何代码
    • use:允许通过别名引用或导入外部的完全限定名称,是命名空间的一个重要特征。这有点类似于在类unix文件系统中可以创建对其它的文件或目录的符号连接。
    • 所有支持命名空间的PHP版本支持三种别名或导入方式:
      • 为类名称使用别名
      • 为接口使用别名
      • 为命名空间名称使用别名
    • PHP 5.6开始允许导入函数或常量或者为它们设置别名
    • 在PHP中别名是通过操作符use来实现的
    • 闭包可以从父作用域中继承变量,任何此类变量都应该用use语言结构传递进去
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    1.使用use
    $msg = "hello";
    $func = function ($arg) use ($msg) {
    return $msg . ' ' . $arg;
    };
    echo $func('world');
    // 输出: hello world

    2.不使用use
    $msg = "hello";
    $func = function ($arg) {
    return $msg . ' ' . $arg;
    };
    echo $func('world');
    // 输出:world
    • 使用方法:

      • use后没有as时,缩短的命名空间默认为最后一个反斜杠后的内容
      • 使用use,实例化对象时最前面没有反斜杠
      • 没使用use,实例化对象时命名空间最前面有反斜杠
      • namespace后不建议加类名,use后可以
      • 使用use之前需先require/include进来(autoload除外)
    • Trait:自PHP 5.4.0起,PHP实现了一种代码复用的方法,称为Trait

    • Trait 是为类似PHP的单继承语言而准备的一种代码复用机制。Trait为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用method。Trait和Class组合的语义定义了一种减少复杂性的方式,避免传统多继承和Mixin类相关典型问题。

    • Trait和Class相似,但仅仅旨在用细粒度和一致的方式来组合功能。无法通过Trait自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用的几个Class之间不需要继承。

  5. dingtalk

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
$result = file_get_contents("http://timor.tech/api/holiday/year");
$result = json_decode($result,true);

function request_by_curl($remote_server, $post_string) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $remote_server);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_HTTPHEADER, array ('Content-Type: application/json;charset=utf-8'));
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0);
// curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}

$hook = [
'webhook'=>'https://oapi.dingtalk.com/robot/send?access_token=xxxx',
'time'=>'19:00',
'text' => "update",
'title' => "test",
'messageUrl' => "http://baidu.com",
];

$url = $hook['webhook'];
$data = [
'msgtype' => 'link',
'link' => [
'text'=>$hook['text'],
'title' => $hook['title'],
'picUrl' => "",
'messageUrl' => $hook['messageUrl'],
]
];
$data_string = json_encode($data);
$result = request_by_curl($url, $data_string);
  1. AES加密
    • 高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年高级加密标准已然成为对称密钥加密中最流行的算法之一。
    • 该算法为比利时密码学家Joan Daemen和Vincent Rijmen所设计,结合两位作者的名字,以Rijndael为名投稿高级加密标准的甄选流程。(Rijndael的发音近于”Rhine doll”)
    • 使用
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
class Security {
public static function encrypt($input, $key) {
$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$input = Security::pkcs5_pad($input, $size);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td, $key, $iv);
$data = mcrypt_generic($td, $input);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
$data = base64_encode($data);
return $data;
}

private static function pkcs5_pad ($text, $blocksize) {
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}

public static function decrypt($sStr, $sKey) {
$decrypted= mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$sKey,
base64_decode($sStr),
MCRYPT_MODE_ECB
);
$dec_s = strlen($decrypted);
$padding = ord($decrypted[$dec_s-1]);
$decrypted = substr($decrypted, 0, -$padding);
return $decrypted;
}
}

class AES {
protected $cipher = MCRYPT_RIJNDAEL_256; //AES加密算法
protected $mode = MCRYPT_MODE_CBC; //采用cbc加密模式
protected $key; //密钥
protected $iv; //cbc模式加密向量,如为空将采用密钥代替

/**
* AES constructor.
* @param $key 密钥
* @param null $iv 向量 可选 如为空将采用密钥代替
* @throws Exception
*/
public function __construct($key, $iv = NULL) {
if (!extension_loaded("mcrypt")) {
throw new \Exception("mcrypt extension do not exist. it was DEPRECATED in PHP 7.1.0, and REMOVED in PHP 7.2.0. use OpenSSL instead");
}
$this->key = $key;
$this->iv = $iv;
}

/**
* 加密数据
* @param $data
* @return string
*/
public function encrypt($data) {
$td = mcrypt_module_open($this->cipher, '', $this->mode, '');
$key = hash("sha256", $this->key, true);
$iv = isset($this->iv) ? hash("sha256", $this->iv, true) : $key;
$data = $this->padding($data);
mcrypt_generic_init($td, $key, $iv);
$encryptedData = base64_encode(mcrypt_generic($td, $data));
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $encryptedData;
}

/**
* 解密数据
* @param $data
* @return bool|string
*/
public function decrypt($data) {
$td = mcrypt_module_open($this->cipher, '', $this->mode, '');
$key = hash("sha256", $this->key, true);
$iv = isset($this->iv) ? hash("sha256", $this->iv, true) : $key;
mcrypt_generic_init($td, $key, $iv);
$decrypted_data = mdecrypt_generic($td, base64_decode($data));
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $this->unPadding($decrypted_data);
}

/**
* 填充数据到分组大小的整数倍
* @param null $data
* @return string
*/
protected function padding($data = null) {
$blockSize = 32; //MCRYPT_RIJNDAEL_256算法的分组大小是32字节
$pad = $blockSize - (strlen($data) % $blockSize);
return $data . str_repeat(chr($pad), $pad);
}

/**
* 去掉填充的数据
* @param null $data
* @return bool|string
*/
protected function unPadding($data = null) {
$pad = ord($data[strlen($data) - 1]);
if ($pad > strlen($data)) {
return false;
}

if (strspn($data, chr($pad), strlen($data) - $pad) != $pad) {
return false;
}

return substr($data, 0, -1 * $pad);
}

/**
* @return mixed
*/
public function getSecretKey() {
return $this->key;
}

/**
* @param mixed $key
*/
public function setSecretKey($key) {
$this->key = $key;
}
/**
* @return null
*/
public function getIv() {
return $this->iv;
}

/**
* @param null $iv
*/
public function setIv($iv) {
$this->iv = $iv;
}
}


class Encryption{

//加密种子
public static $key_t = "sjiofssdsfd";

/**
* 加密函数
* @param array $data
* @return string
* @date 2019-05-15
*/
public static function encrypt($data) {
$txt = serialize($data);
$encrypt_key = md5(rand(0, 10000));//从0到10000取一个随机数
$ctr = 0;
$tmp = '';
for($i = 0; $i < strlen($txt); $i++) {
$ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
$tmp .= $encrypt_key[$ctr] . ($txt[$i] ^ $encrypt_key[$ctr++]);
}
$result = base64_encode(Encryption::key($tmp, Encryption::$key_t));
return $result;
}

/**
* 解密函数
* @param string $str
* @return array $result
* @date 2019-05-15
*/
public static function decrypt($txt) {
$txt = Encryption::key(base64_decode($txt), Encryption::$key_t);
$tmp = '';
for($i = 0; $i<strlen($txt); $i++) {
$md5 = $txt[$i];
$tmp .= $txt[++$i] ^ $md5;
}
$tmp_t = unserialize($tmp);
return $tmp_t;
}

/**
* 生成字符串
* @param string $str
* @return array $result
* @date 2019-05-15
*/
public static function key($txt, $encrypt_key) {
$encrypt_key = md5($encrypt_key);
$ctr = 0;
$tmp = '';
for($i = 0; $i<strlen($txt); $i++) {
$ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;
$tmp .= $txt[$i] ^ $encrypt_key[$ctr++];
}
return $tmp;
}
}
  1. PHP配置文件

  2. 扫码登录

    • 登录过程
    • 输入url打开web应用
    • 选择扫码登录,生成二维码
    • 打开app应用,选择扫一扫
    • 扫描web端的二维码,出现【确认登录】等字样(若长时间未扫描,则可能会出现二维码过期)
    • 点击【确认登录】扫描完成,同时web端登录成功(此过程不一定,有可能需要短信认证等过程)
    • 实现过程
    • 选择扫码登录后会向服务器发一个生成二维码的请求A
    • 服务器拿到请求A后,会随机生成一个uuid(或叫token),然后加一个过期时间存放起来(如存redis)
    • 同时服务器会根据uuid及别的自定义参数生成一个二维码,返回给web应用
    • web应用拿到二维码并将其展示给用户以扫描(可以有一些提示性文字)
    • 与此同时,web应用会通过轮询或者长轮询的方式向服务器请求登录是否成功
    • 另外一边用户打开app应用已是登录状态,通过扫一扫功能打开确认登录页面(通过二维码获取uuid等信息)
    • 用户点击确认登录后,app应用会带着uuid和用户信息向服务器发请求,进而服务器拿到uuid和用户信息
    • 服务器将uuid和用户账号绑定一起,并设置状态为已登录
    • web应用通过轮询方式得知用户已登录,然后跳转到对应页面(此过程不一定,视实现登录的技术而定),扫码登录完成
  3. 学习

  • 读官方手册
  • 读框架源码
  • 实现一个框架
  • pthread
  • swoole
  • register_shutdown_function
  • Core Dumps
  • class SplFixedArray implements IteratorAggregate, ArrayAccess, Countable, JsonSerializable,SplFixedArray未实现JsonSerializable接口的方法
  • 后期静态绑定
  1. rdkafka扩展,需要指定--with-rdkafka选项

    • sudo ./configure --with-php-config=/usr/local/Cellar/php@8.1/8.1.15/bin/php-config --with-rdkafka=/usr/local/Cellar/librdkafka/2.0.2/
  2. composer中的版本要求

    • ^~的出现是为了对扩展包进行版本锁定的
    • ^表示锁定主版本号
    • ~表示锁定次版本号
    • ^6.20表示版本的范围是6.20.0到6.99.999
    • ~6.20表示版本的范围是6.20.0到6.20.999
  3. PHP常见代码注释标识

    • @author:标明作者
    • @deprecated:即将被废弃,不再推荐使用
    • @example:示例
    • @inheritdoc:文档继承
    • @link,同@see:访问内部方法或类
    • @method:方法
    • @mixin:
    • @param:参数
    • @property
    • @return
    • @see
    • @throws
    • @var:变量
    • @internal
    • @version
    • @copyright:版权声明
    • @license:协议
    • @since:从指定版本开始的变动
    • @package:命名空间
    • @todo:待办
    • @uses:使用
    • 参考
  4. 表单重复提交,参考

    • 使用唯一索引
    • 使用Token机制
    • 使用悲观锁
    • 使用乐观锁
    • 使用分布式锁
    • 使用状态机制

学习资料

  1. GitHub-Chinese-Top-Charts
  2. PHP面试
  3. 参考三
  4. 参考四
  5. php删除数组中指定值的元素的几种方法
  6. 压缩编码哪种最好
  7. 上传xlsx
  8. PHP7内核剖析
  9. 垃圾回收机制
  10. xhprof

PHP优化

  1. 用单引号代替双引号来包含字符串

    • 因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会。
    • 只有ech能这么做,它是一种可以把多个字符串当作参数的“函数”号
  2. 类的方法定义成static,速度会提升将近4倍

  3. 用i+=1代替i=i+1

  4. echo比print快,并且使用echo的多重参数代替字符串连接,比如 echo $str1,$str1,$str2

  5. 在执行for循环之前确定最大循环数,不要每循环一次都计算最大值,最好运用 foreach 代替

  6. 注销那些不用的变量尤其是大数组,以便释放内存

  7. 尽量避免使用get,set,__autoload

  8. require_once()代价昂贵

  9. include文件时尽量使用绝对路径,因为它避免了PHP去include_path里查找文件的速度,解析操作系统路径所需的时间会更少

  10. 如果你想知道脚本开始执行的时刻,使用 $_SERVER[‘REQUEST_TIME’]要好于time()

  11. 函数代替正则表达式完成相同功能

  12. strtr > str_replace > preg_replace

  13. 如果一个字符串替换函数,可接受数组或字符作为参数,并且参数长度不太长,那么可以考虑额外写一段替换代码, 使得每次传递参数是一个字符, 而不是只写一行代码接受数组作为查询和替换的参数

  14. 使用选择分支语句(即switch case)好于使用多个if,else if语句

  15. @屏蔽错误消息的做法非常低效,极其低效

  16. 打开apache的mod_deflate模块,可以提高网页的浏览速度。

  17. 数据库连接当使用完毕时应关掉,不要用长连接

  18. foreach效率更高,尽量用foreach代替while和for循环

  19. 在方法中递增局部变量,速度是最快的,几乎与在函数中调用局部变量的速度相当

  20. 递增一个全局变量要比递增一个局部变量慢2倍

  21. 递增一个对象属性(如$this->prop++)要比递增一个局部变量慢3倍

  22. 递增一个未预定义的局部变量要比递增一个预定义的局部变量慢9至10倍

  23. 仅定义一个局部变量而没在函数中调用它,同样会减慢速度

    • PHP大概会检查看是否存在全局变量。
  24. 多维数组尽量不要循环嵌套赋值

  25. 派生类中的方法运行起来要快于在基类中定义的同样的方法

  26. 调用带有一个参数的空函数,其花费的时间相当于执行7至8次的局部变量递增操作

    • 类似的方法调用所花费的时间接近于 15 次的局部变量递增操作
  27. Apache解析一个PHP脚本的时间要比解析一个静态HTML页面慢2至10倍

    • 多用静态HTML页面,少用脚本
  28. 除非脚本可以缓存,否则每次调用时都会重新编译一次。引入一套PHP缓存机制通常可以提升25%至100%的性能,以免除编译开销

    • 对运算码(OP code)的缓存很有用,使得脚本不必为每个请求做重新编译
  29. 尽量做缓存,可使用memcached

    • memcached是一款高性能的内存对象缓存系统,可用来加速动态Web应用程序,减轻数据库负载
  30. 当操作字符串并需要检验其长度是否满足某种要求时, 你想当然地会使用 strlen()函数

    • 此函数执行起来相当快,因为它不做任何计算,只返回在zval结构中存储的已知字符串长度。但是,由于strlen()是函数,多多少少会有些慢,因为函数调用会经过诸多步骤,如字母小写化(译注:指函数名小写化,PHP 不区分函 数名大小写). 哈希查找,会跟随被调用的函数一起执行
    • 在某些情况下你可以使用isset()技巧加速执行你的代码
  31. 当执行变量i的递增或递减时,i的递增或递减时,i++会比++i慢一些,这种差异是PHP特有的

    • ++i更快是因为它只需要3条指令(opcodes),$i++则需要4条指令
    • 后置递增实际上会产生一个临时变量,这个临时变量随后被递增
    • 前置递增直接在原值上递增
  32. 并不是事必面向对象(OOP),面向对象往往开销很大,每个方法和对象调用都会消耗很多内存

  33. 并非要用类实现所有的数据结构,数组也很有用

  34. 不要把方法细分得过多,仔细想想你真正打算重用的是哪些代码

  35. 当你需要时,你总能把代码分解成方法

  36. 尽量采用大量的PHP内置函数

  37. 如果在代码中存在大量耗时的函数,你可以考虑用C扩展的方式实现它们

  38. 评估检验(profile)你的代码

    • Xdebug 调试器包含了检验程序,评估检验总体上可以显示出代码的瓶颈
  39. mod_zip可作为Apache模块,用来即时压缩你的数据,并可让数据传输量降低80%

  40. 用file_get_contents替代file. fopen. feof. fgets等系列方法

    • 注意file_get_contents在打开一个URL文件时候的PHP版本问题
  41. 尽量的少进行文件操作

  42. 优化Select SQL语句,在可能的情况下尽量少的进行Insert. Update操作

  43. 循环内部不要声明变量

  44. 多维数组尽量不要循环嵌套赋值;

  45. 对 global 变量,应该用完就 unset()掉

Lua

一、安装lua

  1. cd /usr/local/src

  2. sudo wget https://www.lua.org/ftp/lua-5.3.5.tar.gz

  3. sudo tar -zxvf lua-5.3.5.tar.gz

  4. cd lua-5.3.5

  5. sudo make macosx test

    • 查看下MakeFile文件内容cat MakeFile
    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
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
     # Makefile for installing Lua
    # See doc/readme.html for installation and customization instructions.

    # == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================

    # Your platform. See PLATS for possible values.
    PLAT= none

    # Where to install. The installation starts in the src and doc directories,
    # so take care if INSTALL_TOP is not an absolute path. See the local target.
    # You may want to make INSTALL_LMOD and INSTALL_CMOD consistent with
    # LUA_ROOT, LUA_LDIR, and LUA_CDIR in luaconf.h.
    INSTALL_TOP= /usr/local
    INSTALL_BIN= $(INSTALL_TOP)/bin
    INSTALL_INC= $(INSTALL_TOP)/include
    INSTALL_LIB= $(INSTALL_TOP)/lib
    INSTALL_MAN= $(INSTALL_TOP)/man/man1
    INSTALL_LMOD= $(INSTALL_TOP)/share/lua/$V
    INSTALL_CMOD= $(INSTALL_TOP)/lib/lua/$V

    # How to install. If your install program does not support "-p", then
    # you may have to run ranlib on the installed liblua.a.
    INSTALL= install -p
    INSTALL_EXEC= $(INSTALL) -m 0755
    INSTALL_DATA= $(INSTALL) -m 0644
    #
    # If you don't have "install" you can use "cp" instead.
    # INSTALL= cp -p
    # INSTALL_EXEC= $(INSTALL)
    # INSTALL_DATA= $(INSTALL)

    # Other utilities.
    MKDIR= mkdir -p
    RM= rm -f

    # == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======

    # Convenience platforms targets.
    PLATS= aix bsd c89 freebsd generic linux macosx mingw posix solaris

    # What to install.
    TO_BIN= lua luac
    TO_INC= lua.h luaconf.h lualib.h lauxlib.h lua.hpp
    TO_LIB= liblua.a
    TO_MAN= lua.1 luac.1

    # Lua version and release.
    V= 5.3
    R= $V.4

    # Targets start here.
    all: $(PLAT)

    $(PLATS) clean:
    cd src && $(MAKE) $@

    test: dummy
    src/lua -v

    install: dummy
    cd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD)
    cd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN)
    cd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC)
    cd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB)
    cd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN)

    uninstall:
    cd src && cd $(INSTALL_BIN) && $(RM) $(TO_BIN)
    cd src && cd $(INSTALL_INC) && $(RM) $(TO_INC)
    cd src && cd $(INSTALL_LIB) && $(RM) $(TO_LIB)
    cd doc && cd $(INSTALL_MAN) && $(RM) $(TO_MAN)

    local:
    $(MAKE) install INSTALL_TOP=../install

    none:
    @echo "Please do 'make PLATFORM' where PLATFORM is one of these:"
    @echo " $(PLATS)"
    @echo "See doc/readme.html for complete instructions."

    # make may get confused with test/ and install/
    dummy:

    # echo config parameters
    echo:
    @cd src && $(MAKE) -s echo
    @echo "PLAT= $(PLAT)"
    @echo "V= $V"
    @echo "R= $R"
    @echo "TO_BIN= $(TO_BIN)"
    @echo "TO_INC= $(TO_INC)"
    @echo "TO_LIB= $(TO_LIB)"
    @echo "TO_MAN= $(TO_MAN)"
    @echo "INSTALL_TOP= $(INSTALL_TOP)"
    @echo "INSTALL_BIN= $(INSTALL_BIN)"
    @echo "INSTALL_INC= $(INSTALL_INC)"
    @echo "INSTALL_LIB= $(INSTALL_LIB)"
    @echo "INSTALL_MAN= $(INSTALL_MAN)"
    @echo "INSTALL_LMOD= $(INSTALL_LMOD)"
    @echo "INSTALL_CMOD= $(INSTALL_CMOD)"
    @echo "INSTALL_EXEC= $(INSTALL_EXEC)"
    @echo "INSTALL_DATA= $(INSTALL_DATA)"

    # echo pkg-config data
    pc:
    @echo "version=$R"
    @echo "prefix=$(INSTALL_TOP)"
    @echo "libdir=$(INSTALL_LIB)"
    @echo "includedir=$(INSTALL_INC)"

    # list targets that do not create files (but not all makes understand .PHONY)
    .PHONY: all $(PLATS) clean test install local none dummy echo pecho lecho
  6. sudo make install

  7. lua -v

二、安装php的lua扩展

  1. 下载扩展包,传送门
  2. 解压sudo tar -zxvf lua-2.0.6.tgz
  3. 生成configure文件sudo /usr/local/php7.1/bin/phpize
  4. 检测,生成MakeFile,指定安装参数sudo ./configure --with-php-config=/usr/local/php7.1/bin/php-config --with-lua=/usr/local
    • 不加--with-lua=/usr/local选项报错
1
2
3
checking for lua in default path... found in /usr/local
checking for lua library in default path... not found
configure: error: Please reinstall the lua distribution - lua library should be in <lua-dir>/lib/
  1. 解决
    • 方式一:老老实实地加--with-lua=/usr/local
    • 方式二:sudo cp /usr/local/lib/liblua.a /usr/lib/liblua.a,此命令只在低版本的mac中可用,升级到Catalina后执行报错sudo cp /usr/local/lib/liblua.a /usr/lib/liblua.a cp: /usr/lib/liblua.a: Read-only file system,这是因为 Catalina 分离了系统文件和用户文件,系统文件都被 mount 到了只读分区,当然好处是显而易见的,防止系统文件被恶意篡改,处理方法如下:
      • 首先,禁止SIP(重启mac,command+r进入恢复模式,打开终端输入命令csrutil disable)
      • 重启后在terminal输入sudo mount -uw /,意思是把分区mount成可写模式,在系统重启后失效
      • 执行之前没有权限的操作,如复制、移动文件等
      • 重启后再启用SIP(重启mac,command+r进入恢复模式,打开终端输入命令csrutil enable)
      • 重新执行sudo ./configure --with-php-config=/usr/local/php7.1/bin/php-config
  2. 安装sudo make && sudo make install
  3. 加载扩展extension=lua.so(extension_dir之前已设置好)

三、使用

1
2
3
4
5
6
7
8
9
10
11
12
13
$lua = new Lua();
$lua->eval(<<<CODE
function dummy(foo, bar)
print(foo, ",", bar)
end
CODE
);
$lua->call("dummy", array("Hello", "php"));
echo "<br>";
$lua->dummy("Hello", "lua");
echo "<br>";
$res = $lua->call(array("table", "concat"), array(array(1=>11, 2=>22, 3=>33), "-"));
print_r($res);

四、参考

  1. 参考一
  2. 参考二

swoole

一、概念

  1. Swoole是由C语言开发的php扩展类,由于有着C语言的优势,swoole在内存管理、数据结构、通信协议解析等方面优势明显。它是一个面向生产环境的PHP异步网络通信引擎,使PHP开发人员可以编写高性能的异步并发TCP、UDP、Unix Socket、HTTP、WebSocket服务。Swoole可以广泛应用于互联网、移动通信、企业软件、云计算、网络游戏、物联网(IOT)、车联网、智能家居等领域。 使用PHP+Swoole作为网络通信框架,可以使企业IT研发团队的效率大大提升。

二、安装

  1. 下载源码包/usr/local/src目录下
  2. 解压 sudo tar -zxvf swoole-4.4.3.tgz
  3. 切换目录 cd swoole-4.4.3
  4. 运行sudo /usr/local/php/7.3/bin/phpize命令,生成configure可执行文件
  5. 运行配置sudo ./configure --with-php-config=/usr/local/php/7.3/bin/php-config
  6. 安装扩展sudo make && sudo make install
  7. 修改php.ini文件,增加extension=/path/swoole.so
  8. 重启php-fpm
  9. 查看是否安装成功php7.3 -m,显示swoole则成功

三、参考

  1. 参考一
  2. 参考二
  3. 参考三
  4. 参考四

Yaf-Yar-Yac

一、概念

  1. Yaf,全称Yet Another Framework,一个C语言编写的以PHP扩展形式提供高性能的PHP开发框架,它更快,更轻便,提供了Bootstrap,路由,分发,视图,插件等,是一个全功能的PHP框架。
  2. Yar,全称Yet Another RPC Framework,一个C语言编写的PHP扩展的RPC框架,它轻量级,可并行化,支持多种打包协议(msgpack,json,php)。
  3. Yac,全称Yet Another Cache,一个一个基于共享内存的高性能的缓存扩展。

二、安装

      本身就是以扩展的形式存在,和其他扩展安装没什么区别,自己gg一下就出来了。在php命令行下可通过命令php --ri extension_name获得扩展相关信息

  1. yaf配置信息

    • yaf.environ:环境名称,当用INI作为Yaf的配置文件时,这个指明了Yaf将要在INI配置中读取的节的名字,默认product。
    • yaf.library:全局类库的目录路径,默认NULL
    • yaf.cache_config:是否缓存配置文件(只针对INI配置文件生效),打开此选项可在复杂配置的情况下提高性能,默认0。
    • yaf.name_suffix:在处理Controller/Action/Plugin/Model的时候,类名中关键信息是否是后缀式,比如UserModel,而在前缀模式下则是ModelUser,默认1。
    • yaf.name_separator:在处理Controller,Action,Plugin,Model的时候,前缀和名字之间的分隔符,默认为空,也就是UserPlugin,加入设置为”_”,则判断的依据就会变成:”User_Plugin”,这个主要是为了兼容ST已有的命名规范,默认””。
    • yaf.forward_limit:forward最大嵌套深度,默认5。
    • yaf.use_namespace:开启的情况下,Yaf将会使用命名空间方式注册自己的类,比如Yaf_Application将会变成Yaf\Application,默认0。
    • yaf.use_spl_autoload:开启的情况下,Yaf在加载不成功的情况下,会继续让PHP的自动加载函数加载,从性能考虑,除非特殊情况,否则保持这个选项关闭,默认0。
  2. yar配置信息

    • yar.packager string:设置Yar的打包工具,可以是PHP(serialize),JSON,Msgpack(这个需要编译的时候指定–enable-msgpack).
    • yar.debug string:打开的时候,Yar会把请求过程的日志都打印出来(到stderr).
    • yar.connect_timeout integer:连接超时(毫秒为单位)
    • yar.timeout integer:处理超时(毫秒为单位)
    • yar.expose_info bool:如果关闭,则当通过浏览器访问Server的时候,不会出现Server Info信息.
  3. yac配置信息

    • yac.enable = 1
    • yac.keys_memory_size = 4M ; 4M can get 30K key slots,32M can get 100K key slots
    • yac.values_memory_size = 64M
    • yac.compress_threshold = -1
    • yac.enable_cli = 0 ; whether enable yac with cli,default 0

四、使用

  1. Yaf
  2. Yar
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
//filename operator.php
class Operator
{
public function add($a, $b) {
sleep(20);
return $this->_add($a, $b);
}

public function sub($a, $b) {
sleep(10);
return $a - $b;
}

public function mul($a, $b) {
sleep(5);
return $a * $b;
}

protected function _add($a, $b) {
return $a + $b;
}
}

$server = new Yar_Server(new Operator());
$server->handle();

方式一:传统调用方式:http://localhost/operator.php

//filename client.php

方式二:实例化new yar_client对象然后调用
$client = new yar_client("http://localhost/operator.php");
var_dump($client->add(1, 2));
var_dump($client->call("add", array(3, 2)));
var_dump($client->_add(1, 2));

方式三:回调
ini_set("yar.debug", false);
$client = new yar_client("http://localhost/operator.php");

function callback($ret, $callinfo) {
print_r($ret);
echo PHP_EOL;

print_r($callinfo);
echo PHP_EOL;
}

Yar_Concurrent_Client::call("http://localhost/operator.php", "add", array(1, 2), "callback");
Yar_Concurrent_Client::call("http://localhost/operator.php", "sub", array(2, 1), "callback");
Yar_Concurrent_Client::call("http://localhost/operator.php", "mul", array(2, 2), "callback");

echo 123;

Yar_Concurrent_Client::loop();

echo 456;
  1. Yac
1
2
3
4
5
6
7
8
$yac  = new Yac();
$info = $yac->info();
$set1 = $yac->set("foo", "bar");
$get1 = $yac->get('foo');

$set2 = $yac->set(["key1" => "val1","key2" => "val2"]);
$get2 = $yac->get("key1");
$get3 = $yac->get(array("key1", "key2"));

三、参考

  1. Yaf
  2. Yar
  3. Yac
  4. Yac
  5. Yaf配置

foreach问题

一、基础

  1. php4中引入了foreach结构,这是一种遍历数组的简单方式。相比传统的for循环,foreach能够更加便捷的获取键/值对。在php5之前,foreach仅能用于数组;php5之后,利用foreach还能遍历对象。
    • foreach开始执行时,数组内部的指针会自动指向第一个单元,这意味着不需要在foreach循环之前调用reset()
    • 由于foreach依赖内部数组指针,在循环中修改其值将可能导致意外的行为(可以很容易地通过在$value之前加上&来修改数组的元素)。
    • 数组最后一个元素的$value引用在foreach循环之后仍会保留,建议使用unset()来将其销毁(unset只会取消引用,不会销毁值)。(问题就出在这里)

二、demo

  1. 例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    $arr = [1, 2, 3];
    foreach($arr as &$val);
    foreach($arr as $val);
    print_r($arr);
    期望结果:
    Array
    (
    [0] => 1
    [1] => 2
    [2] => 3
    )
    实际结果:
    Array
    (
    [0] => 1
    [1] => 2
    [2] => 2
    )
  2. 分析

    其实,我们可以认为foreach($arr as $k => $v)结构隐含了如下两个赋值操作,分别将数组当前的’键’和当前的’值’赋给变量$k和$v,具体展开形如:

1
2
3
4
5
6
foreach($arr as $k => $v){
$v = currentVal();
$k = currentKey();
//TODO code
……
}

根据上述理论,现在我们重新来分析下第一个foreach:

  • 第1遍循环,$v是一个引用,即$v = &$arr[0],此时$arr变成[1,2,3]

  • 第2遍循环,同理可得:$v = &$arr[1],$arr变成[1,2,3]

  • 第3遍循环,同理可得:$v = &$arr[2],$arr变成[1,2,3]

  • 此时并未执行unset($v)操作,$v现在是数组最后一个元素的引用*

    随后代码进入了第二个foreach:

  • 第1遍循环,隐含操作$v=$arr[0]被触发,$v仍然是$arr[2]的引用,相当于$arr[2]=$arr[0],$arr变成[1,2,1]

  • 第2遍循环,$v=$arr[1],同理($v仍然是$arr[2]的引用)可得:$arr[2]=$arr[1],$arr变成[1,2,2]

  • 第3遍循环,$v=$arr[2],同理($v仍然是$arr[2]的引用)可得:$arr[2]=$arr[2],$arr变成[1,2,2]

    三、参考

  1. 参考一
  2. 参考二

Guzzle

一、定义

  1. Guzzle是一个PHP的HTTP客户端,用来轻而易举地发送请求,并集成到我们的WEB服务上。
    • 接口简单:构建查询语句、POST请求、分流上传下载大文件、使用HTTP cookies、上传JSON数据等等。
    • 发送同步或异步的请求均使用相同的接口。
    • 使用PSR-7接口来请求、响应、分流,允许你使用其他兼容的PSR-7类库与Guzzle共同开发。
    • 抽象了底层的HTTP传输,允许你改变环境以及其他的代码,如:对cURL与PHP的流或socket并非重度依赖,非阻塞事件循环。
    • 中间件系统允许你创建构成客户端行为。

二、使用

  1. 依赖

    • >=PHP 5.5.0
    • php.ini文件设置allow_url_fopen = On
    • cURL >= 7.19.4(可选)
    • openssl和zlib扩展(没有此扩展,使用composer安装guzzle的时候也会报错)
  2. 安装 composer require guzzlehttp/guzzle:~6.0,6.0版本要求php7.1以上

  3. 使用


PHPUnit

一、概念

      PHPUnit是用于PHP编程语言的单元测试框架。它是xUnit体系结构的一个实例,用于单元测试框架,该框架起源于SUnit并在JUnit中流行。PHPUnit由Sebastian Bergmann创建,其开发托管在GitHub上。

作用

  • 通过命令操控测试脚本
  • 测试性能
  • 测试代码覆盖率
  • 自动化的更新测试用例的参数数据
  • 各种格式的日志

使用规则

  • 一般被测试类的后面加上”Test”,比如要测试的类为Array,则测试用例的命名为ArrayTest。
  • 测试类ArrayTest继承于PHPUnit_Framework_TestCase
  • 测试方法必须为public权限,都是test开头,或者你也可以选择给其加注释@test来表明该函数为测试函数
  • 通过断言方法来对实际值和预期值进行断言,断言方法可以参照手册

二、使用

  1. 通过归档文件安装

    • 下载归档文件 wget http://phar.phpunit.cn/phpunit.phar
    • 增加可执行权限 chmod +x phpunit.phar
    • 全局可执行 sudo mv phpunit.phar /usr/local/bin/phpunit
      • 若安装两个php版本,则修改/usr/local/bin/phpunit,修改可执行文件php路径 #!/usr/local/bin/php5.6
    • 测试安装是否成功 phpunit --version
      • 本地源码安装了两个版本的php,5.6和7.3,执行完以上步骤不顶球用,一执行就报错
    1
    2
    3
    4
    5
    PHP Fatal error:  Uncaught PharException: phar "/usr/local/bin/phpunit" has a broken signature in /usr/local/bin/phpunit:27
    Stack trace:
    #0 /usr/local/bin/phpunit(27): Phar::mapPhar('phpunit-6.5.3.p...')
    #1 {main}
    thrown in /usr/local/bin/phpunit on line 27
  2. 通过composer安装

    • 全局安装 composer global require phpunit/phpunit
    • 创建项目 mkdir test
    • 切换目录 cd test
    • 初始化 composer init
    • 引入文件 composer require --dev phpunit/phpunit
    • 新建测试文件夹 mkdir tests
    • 新建phpunit.xml为了引入autoload.php
1
2
3
4
5
6
7
<phpunit bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="service">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
  • 新建测试脚本EqualsTest.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

use PHPUnit\Framework\TestCase;

class EqualsTest extends TestCase {
public function testFailure()
{
$this->assertEquals(1, 0);
}

public function testFailure2()
{
$this->assertEquals('bar', 'baz');
}

public function testFailure3()
{
$this->assertEquals("foo\nbar\nbaz\n", "foo\nbah\nbaz\n");
}
}
  • 执行 ../vendor/bin/phpunit EqualsTest.php
    • 通过归档文件安装时phpunit已建好,默认使用

三、参考

  1. 参考一
  2. 参考二
  3. 参考三

Workerman

一、概念

  1. Workerman是纯PHP开发框架,需要依赖很多额外的第三方PHP扩展来实现。它是一款开源高性能异步PHP socket即时通讯框架。支持高并发,超高稳定性,被广泛的用于手机app、移动通讯,微信小程序,手游服务端、网络游戏、PHP聊天室、硬件通讯、智能家居、车联网、物联网等领域的开发。支持TCP长连接,支持Websocket、HTTP等协议,支持自定义协议。拥有异步Mysql、异步Redis、异步Http、MQTT物联网客户端、异步消息队列等众多高性能组件。

二、使用

三、参考

  1. 参考一

断点上传和断点续传

一、定义

二、实现

三、参考

  1. 参考一
  2. 参考二
  3. 参考三

OAuth2.0

一、背景

      在OAuth之前,Web或移动应用要访问某些受限制的资源时,一般需要通过输入用户名/密码的形式进行验证,这种形式会带来一些已知的问题。OAuth的出现就是为了解决访问资源的安全性以及灵活性,使得第三方应用对资源的访问或者微服务间的调用更加安全和便捷。

二、实现

  1. 数据库
  2. 代码实现
  3. nginx配置
  4. 访问

三、参考

  1. 参考一
  2. 参考二

一分钟允许请求十次

一、概论

      出去面试的时候,问的最多的问题就是高并发相关的,比如针对秒杀、抢购系统,如何保证每个用户只能买一次;再比如高频应用接口,如果不加控制的话别人可以轻易的写一个脚本一直刷接口,从而导致整个服务变慢甚至是拖垮。秒杀、抢购系统方案都是成熟的,可以从产品、前端、后端等多个层面去实现,至于高频接口请求的拦截则仁者见仁智者见智了。比如要求某个接口一分钟只允许请求十次,且要考虑到请求时间的均衡性(如前59秒请求了一次,最后1秒请求了9次)等。

      BZ的方案是基于redis的hash实现(也可为别的数据类型),总的思路就是为每个用户设置一个key,value为请求次数,同时设置ttl为一分钟。考虑到请求时间的均衡性,还需要记录下每一次请求的时间。

1
uid : {"num" : 1, "modified_at" : 0}

二、实现

  1. 假设有三个用户A/B/C,用户ID分别为a/b/c
  2. 缓存预热,即分别初始化三个用户(项目中可以通过脚本设置,在此通过命令演示)
1
2
3
4
5
6
7
hmset user_a num 0 modified_at 0
hmset user_b num 0 modified_at 0
hmset user_c num 0 modified_at 0

expire user_a 600
expire user_b 60
expire user_c 60
  1. handle代码
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
require 'vendor/autoload.php';

$single_server = array(
'host' => '127.0.0.1',
'port' => 6379,
);

$redis = new Predis\Client($single_server);

$key = 'user_a';
$bit = 60;
$exist = $redis->exists($key);
try{
if ($exist) {
$user = $redis->hmget('user_a', ['num', 'modified_at']);
if ($user[0] >= 10) {
echo "too many request";
} else {
$sub = time() - $user[1];
if ($sub < $bit) {
echo "time too short";
} else {
$redis->hincrby('user_a', 'num', 1);
$redis->hset('user_a','modified_at',time());
echo "normal";
}
}
} else {
$user = $redis->hmset('user_a', ['num' => 1, 'modified_at' => 0]);
$expire = $redis->expire('user_a', 600);
echo 'init success';
}
} catch (Exception $e) {
var_dump($e->getMessage());
}

PHP-MongoDB操作

一、环境准备

  1. 安装PHP
  2. 安装MongoDB
  3. 安装MongoDB扩展
  4. 安装composer中文镜像

二、存储图片

  1. 下载安装驱动库
    • composer安装composer require mongodb/mongodb
      • 博主安装了多个PHP版本:/usr/local/php/7.1/bin/php /usr/local/bin/composer require mongodb/mongodb
  2. set.php,请求后会在以下两个集合中插入记录
    • fs.files
    • fs.chunks
1
2
3
4
5
6
7
8
9
10
<?php

require './vendor/autoload.php';

$ct = new MongoDB\Client("mongodb://127.0.0.1/");
$gc = $ct->test->selectGridFSBucket();
$name = mt_rand();
$file = fopen('./a.jpg','rb');
$gr = $gc->uploadFromStream($name, $file);
print_r($gr);
  1. get.php
1
2
3
4
5
6
7
8
9
10
11
12
<?php

require './vendor/autoload.php';

$ct = new MongoDB\Client();
$gc = $ct->test->selectGridFSBucket();
$obj = '61406e5703c06b5916587baf';//fs.files表_id,可修改为动态获取
$id = new \MongoDB\BSON\ObjectId($obj);
$str = $gc->openDownloadStream($id);
$cs = stream_get_contents($str);
header('Content-Type: image/jpeg; charset=UTF-8');
echo $cs;

三、使用PHP对MongoDB进行CURD操作(原生操作)

$manager = new MongoDB\Driver\Manager('mongodb://localhost:27017');

  1. 增加数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 方式一
$command3 = new MongoDB\Driver\Command([
'insert' => 'friend',
'documents' => [
['First Name' => "房", 'Last Name' => '瑞娜','Age' => 27],
['First Name' => "刘", 'Last Name' => '玉龙','Age' => 29],
]
]);
$cursor = $manager->executeCommand('test', $command3);
print_r($cursor->toArray());

// 方式二
$bulkWrite = new \MongoDB\Driver\BulkWrite(['ordered'=>true]);
$dbCollectionName = 'test.friend';
$data = [
'name' => '杨幂',
'actor' => '杨紫'
];
$bulkWrite->insert($data);
$cursor = $manager->executeBulkWrite($dbCollectionName,$bulkWrite);
print_r($cursor);exit;
  1. 删除数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 方式一
$command0 = new MongoDB\Driver\Command([
'delete' => 'friend',
'deletes' => [
[ 'q' => ['First Name' => '刘'],
'limit' => 0, // 0则删除全部,1则只删除一条
]
]
]);
$cursor = $manager->executeCommand('test', $command0);
print_r($cursor->toArray());

// 方式二
$bulkWrite = new \MongoDB\Driver\BulkWrite(['ordered'=>true]);
$writeConcern = new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY,1000);
$dbCollectionName = 'test.friend';
$extra = array('limit'=>1);
$where = ['name' => '杨幂'];
$bulkWrite->delete($where,$extra);
$cursor = $manager->executeBulkWrite($dbCollectionName,$bulkWrite,$writeConcern);
print_r($cursor);exit;
  1. 修改数据
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
// 方式一
$cmd = [
'update' => 'friend', // collection 表名
'updates' => [
[
'q' => ['Age' => 29], //更新条件array
'u' => ['$set' => ['name' => '修改']], //要更新的内容(这个等同于update()函数的更新的内容)
'upsert' => false, // 没有找到则插入,找到了则更新
'multi' => true // true符合条件的则全部更新,默认false只更新第一条
],
],
'ordered' => false, // 是否依次执行updates语句,true表示执行失败后继续后面的语句,false 表示一旦失败立即返回
];
$command6 = new MongoDB\Driver\Command($cmd);
$cursor = $manager->executeCommand('test', $command6);
print_r($cursor->toArray());

// 方式二
$bulkWrite = new \MongoDB\Driver\BulkWrite(['ordered'=>true]);
$writeConcern = new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY,1000);
$dbCollectionName = 'test.friend';
$where = ['name' => '杨幂'];
$update = ['name' => '大咪咪', 'actor' => '小雪'];
$extra = ['multi' => false, 'upsert'=>false];
$bulkWrite->update(
$where,
$update,
$extra
);
$cursor = $manager->executeBulkWrite($dbCollectionName,$bulkWrite,$writeConcern);
print_r($cursor);
  1. 查询数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 方式一
$command1 = new MongoDB\Driver\Command([//查出某一个
"find" => "friend",
"filter" => ["name" => "羽博"]
]);
$cursor = $manager->executeCommand('test', $command1);
print_r($cursor->toArray());

// 方式二
$command2 = new MongoDB\Driver\Command([
"distinct" => "friend",
"key" => "Age",
"query" => ["Age" => 27]
]);
$cursor = $manager->executeCommand('test', $command2);
print_r($cursor->toArray());exit;
  1. 统计
1
2
3
4
5
6
7
8
$where = ['name'  => '修改'];
$cmd = array(
'count' => 'friend',
'query' => $where
);
$command = new \MongoDB\Driver\Command($cmd);
$cursor = $manager->executeCommand('test',$command);
print_r($cursor->toArray());
  1. 索引
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 添加索引
$command5 = new MongoDB\Driver\Command([
'createIndexes' => 'friend',
'indexes' => [
[
'name' => 'email',
'key' => array('E-Mail' => 1),//1升序0降序
'unique' => false,
]
],
]);
$cursor = $manager->executeCommand('test', $command5);
print_r($cursor->toArray());

// 删除索引
$command6 = new MongoDB\Driver\Command([
'dropIndexes' => 'friend',
'index' => 'email',
]);
$cursor = $manager->executeCommand('test', $command6);
print_r($cursor->toArray());

四、使用PHP对MongoDB进行CURD操作(基于Composer包mongodb/mongodb操作)

  1. 安装依赖包 composer require mongodb/mongodb

五、参考

  1. 参考一
  2. 参考二
  3. 参考三

RPC示例

一、概念

      RPC是一个完整的远程调用方案,它包括了接口规范+序列化反序列化规范+通信协议等,HTTP只是一个工作在OSI第七层通信协议,HTTP+Restful规范+序列化与反序列化整体构成了一个完整的远程调用方案。

RPC

二、示例

  1. server.php
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<?php
class RpcServer
{
private $params = [
'host' => '', // ip地址,列出来的目的是为了友好看出来此变量中存储的信息
'port' => '', // 端口
'path' => '' // 服务目录
];

private $config = [
'real_path' => '',
'max_size' => 2048 // 最大接收数据大小
];

private $server = null;

public function __construct($params) {
$this->check();
$this->init($params);
}

private function check() {
$this->serverPath();
}

private function init($params) {
$this->params = $params;
$this->createServer();
}

private function createServer() {
$this->server = stream_socket_server("tcp://{$this->params['host']}:{$this->params['port']}", $errno, $errstr);
if (!$this->server) exit([
$errno, $errstr
]);
}

/**
* Description: rpc服务目录 通过客户端传值传过来
*/
public function serverPath() {
$path = $this->params['path'];
$realPath = realpath(__DIR__ . $path);
if ($realPath === false || !file_exists($realPath)) {
exit("{$path} error!");
}
$this->config['real_path'] = $realPath;
}

public function run() {
while (true) {
$client = stream_socket_accept($this->server, 100000); //这里的100000设置链接的超时时间
if ($client) {
echo "have a new connection\n";
$buf = fread($client, $this->config['max_size']);
print_r('receive data:' . $buf . "\n");
// 自定义协议目的是拿到类方法和参数(可改成自己定义的)
$infos = $this->parseProtocol($buf);
$this->execMethod($client, $infos["class"], $infos["method"], $infos["params"]);
fclose($client);
echo "close connection\n";
}
}
}

private function execMethod($client, $class, $method, $params) {
if ($class && $method) {
$class = ucfirst($class);
$file = $this->params['path'] . '/' . $class . '.php';
if (file_exists($file)) {
require_once $file;
$obj = new $class();
if (!$params) {
$data = $obj->$method();
} else {
$data = $obj->$method($params);
}
$data = $this->packProtocol($data);
//把运行后的结果返回给客户端
fwrite($client, $data);
}
} else {
fwrite($client, 'class or method error');
}
}

private function parseProtocol($buf) {
$buf = json_decode($buf, true);
return $buf;
}

private function packProtocol($data) {
$data = json_encode($data, JSON_UNESCAPED_UNICODE);
return $data;
}
}

(new RpcServer([
'host' => '127.0.0.1',
'port' => 9999,
'path' => './classes' #指定被调用的程序目录
]))->run();

  1. classes/test.php
1
2
3
4
5
6
7
8
9
10
<?php
class Test {

public function noparams() {
return 'no params';
}
public function myparams($params) {
return $params;
}
}
  1. client.php
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
<?php
/*
* RPC测试--RPC客户端
*/

class RpcClient {

private $urlInfo = array();

public function __construct($url) {
$this->urlInfo = parse_url($url);
print_r($this->urlInfo);
}

public static function instance($url) {
return new self($url);
}

/*
* 关键的魔术方法
*/
public function __call($name, $arguments) {
// TODO: Implement __call() method.
//创建一个客户端
$client = stream_socket_client("tcp://{$this->urlInfo['host']}:{$this->urlInfo['port']}", $errno, $errstr);
if (!$client) {
exit("{$errno} : {$errstr} \n");
}
$data = [
'class' => basename($this->urlInfo['path']),
'method' => $name,
'params' => $arguments
];
//向服务端发送我们自定义的协议数据
fwrite($client, json_encode($data));
//读取服务端传来的数据
$data = fread($client, 2048);
//关闭客户端
fclose($client);
return $data;
}
}
$cli = new RpcClient('tcp://127.0.0.1:9999/test');
echo $cli->noparams()."\n";
echo $cli->myparams(array("school"=>"hahah", "grade"=>3));
  1. 运行server.php
  2. 运行client.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server端:
have a new connection
receive data:{"class":"test","method":"noparams","params":[]}
close connection
have a new connection
receive data:{"class":"test","method":"myparams","params":[{"school":"hahah","grade":3}]}
close connection

client端:
Array
(
[scheme] => tcp
[host] => 127.0.0.1
[port] => 9999
[path] => /test
)
"no params"
[{"school":"hahah","grade":3}]

三、参考

  1. 参考一
  2. 参考二

开发composer包

一、开干

  1. 注册github账号,配置ssh-key等,此处不再赘述
  2. 登录github,新建一个仓库,如my-first-packagist
  3. 回到本机,克隆刚新建的仓库 git clone https://github.com/liusirdotnet/my-first-packagist.git
  4. 切换到仓库目录 cd my-first-packagist
  5. 初始化composer.json文件 composer init
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
58
59
$ composer init

Welcome to the Composer config generator

This command will guide you through creating your composer.json config.

// 1. 输入项目命名空间,默认【用户名/仓库名】
Package name (<vendor>/<name>) [dell/htdocs]: yourname/projectname

// 2. 项目描述
Description []: 这是一个测试

// 3. 输入作者信息,可以直接回车,默认当前git配置的信息
Author [test <123@qq.com>, n to skip]:

// 4. 输入最低稳定版本,stable, RC, beta, alpha, dev
Minimum Stability []: dev

// 5. 输入项目类型,
Package Type (e.g. library, project, metapackage, composer-plugin) []: library

// 6. 输入授权类型
License []: MIT //github建仓库时可指定,不写的话packagist会报错

> Define your dependencies.
// 7. 输入依赖信息
Would you like to define your dependencies (require) interactively [yes]?

// 7.1如果需要依赖,则输入要安装的依赖
Search for a package: php

// 7.2输入版本号,如需多个则重复7.1和7.2
Enter the version constraint to require (or leave blank to use the latest version): >=5.6

// 8. 是否需要require-dev,操作同7
Would you like to define your dev dependencies (require-dev) interactively [yes]?

{
"name": "yourname/projectname",
"description": "这是一个测试",
"type": "library",
"require": {
"php": ">=5.6"
},
"require-dev": {
"php": ">=5.6"
},
"license": "",
"authors": [
{
"name": "test",
"email": "test@qq.com"
}
],
"minimum-stability": "dev"
}

// 9. 是否生成composer.json
Do you confirm generation [yes]? yes
  1. 编辑composer.json文件,添加自动加载
1
2
3
4
5
"autoload": {
"psr-4": {
"namespace_name\\": "src/" //自定义命名空间名称,和类文件命名空间保持一致
}
}
  1. 新建src/Test.php
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
<?php

/**
* This is a test class
*
* @detail null
* @author Name <email@email.com>
* @date 2018-06-17
* @version v1.0
*/

namespace Liusir; // 注意命名空间与composer.json中的一致

class Test
{
/**
* This is a test function
*
* @access public
* @param string $arg1 参数一的说明
* @param int $arg2 参数二的说明
* @param mixed $mixed 这是一个混合类型
* @return array 返回类型
* @author Name <email@email.com>
* @date 2018-06-17
*/
public function testFunc()
{
echo 'This is a test script';
}
}
  1. 生成vendor目录 composer install
  2. 跟src同级目录新建demo.php
1
2
3
4
5
6
7
require './vendor/autoload.php';

use Liusir\Test;

$test = new Test();

$test->testFunc();
  1. 提交并推送到github
1
2
3
git add .
git commit -m 'first commit'
git push
  1. 发布到packagist

    • 注册packagist
    • 点击submit
    • 将github仓库输入到输入框,Check && Submit
  2. 切换到另一个项目目录,安装刚发布的包 composer require liusirdotnet/my-first-packagist,报错

1
2
3
4
  [InvalidArgumentException]                                                                                                                
Could not find a matching version of package liuyulong/second-packagist. Check the package spelling, your version constraint and that th
e package is available in a stability which matches your minimum-stability (stable).
require [--dev] [--prefer-source] [--prefer-dist] [--no-progress] [--no-suggest] [--no-update] [--no-scripts] [--update-no-dev] [--update-with-dependencies] [--update-with-all-dependencies] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [--sort-packages] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--] [<packages>]...
  1. 打一个tag并推送到github
1
2
3
git tag -a v1.0 -m 'first tag'
git push
git push origin v1.0
  1. 回到github后台并进入当前仓库,点击release选项,点击Draft a new release按钮,勾选下面的This is a pre-release,最后点击Publish release按钮

  2. 切换到另一个项目目录,安装刚发布的包 composer require liusirdotnet/my-first-packagist

  3. 新建index.php

1
2
3
4
5
6
<?php
require './vendor/autoload.php';
use RabbitMQ\RabbitMQ;

$ob = new RabbitMQ;
$ob->test();
  1. 设置更新composer包自动更新packagist【github停止此服务了】
    • 在packagist后台获取 API Token
    • 进入当前仓库,点击settings,点击add service,选择packagist,输入表单后添加add service按钮,最后点击Update service

Note: GitHub Services have been deprecated. Please contact your integrator for more information on how to migrate or replace a service with webhooks or GitHub Apps.

二、参考

  1. 如何建立自己的composer包
  2. 开发一个自己的composer包

PHP函数

一、数组函数

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
>>>>>>>>>>>array_chunk
1. 功能:将一个数组分割成多个。
2. 用法:`array_chunk(array $array, int $size[, bool $preserve_keys = false ]):array`
3. 说明: 将一个数组分割成多个数组,其中每个数组的单元数目由 size 决定,最后一个数组的单元数目可能会少于 size 个。
4. 参数
* array:需要操作的数组
* size:每个数组的单元数目
* preserve_keys:设为 TRUE,可以使 PHP 保留输入数组中原来的键名。如果你指定了 FALSE,那每个结果数组将用从零开始的新数字索引。
* 默认值是 FALSE。

#索引数组
$input_array = array('a', 'b', 'c', 'd', 'e');
print_r(array_chunk($input_array, 2));#第三个参数默认
print_r(array_chunk($input_array, 2, true));#第三个参数指定为true

#关联数组
$input_array = array('a'=>'a', 'b'=>'b', 'c'=>'c', 'd'=>'d', 'e'=>'e');
print_r(array_chunk($input_array, 2));
print_r(array_chunk($input_array, 2, true));

>>>>>>>>>>>array_column
1. 功能:返回数组中指定的一列
2. 用法:`array_column(array $input, mixed $column_key[, mixed $index_key = null ]):array`
3. 说明:返回input数组中键值为column_key的列,如果指定了可选参数index_key,那么input数组中的这一列的值将作为返回数组中对应值的键。
4. 参数
* input:需要取出数组列的多维数组。 如果提供的是包含一组对象的数组,只有 public 属性会被直接取出。 为了也能取出 private 和 protected 属性,类必须实现 __get() 和 __isset() 魔术方法。
* column_key:需要返回值的列,它可以是索引数组的列索引,或者是关联数组的列的键,也可以是属性名。**也可以是NULL,此时将返回整个数组(配合index_key参数来重置数组键的时候,非常管用)**
* index_key:作为返回数组的索引/键的列,它可以是该列的整数索引,或者字符串键值。

#取某个字段形成新数组
$records = array(
array(
'id' => 2135,
'first_name' => 'John',
'last_name' => 'Doe',
),
array(
'id' => 3245,
'first_name' => 'Sally',
'last_name' => 'Smith',
),
array(
'id' => 5342,
'first_name' => 'Jane',
'last_name' => 'Jones',
),
array(
'id' => 5623,
'first_name' => 'Peter',
'last_name' => 'Doe',
)
);
$first_names = array_column($records, 'first_name');
print_r($first_names);

#指定数组下标
$id_names = array_column($records, 'first_name', 'id');
print_r($id_names);


#结合array_combine一起使用
$arr = [
[
'id' => 123,
'name' => 'test',
'age' => 10,
'created' => '2019-07-03 12:12:12'
],
[
'id' => 456,
'name' => 'test1',
'age' => 10,
'created' => '2019-07-03 12:12:12'
],
[
'id' => 789,
'name' => 'test2',
'age' => 10,
'created' => '2019-07-03 12:12:12'
],
];

$id = array_column($arr,'id');
$new = array_combine($id, $arr);
print_r($new);

#神来之笔,同demo3

$arr = [
[
'id' => 123,
'name' => 'test',
'age' => 10,
'created' => '2019-07-03 12:12:12'
],
[
'id' => 456,
'name' => 'test1',
'age' => 10,
'created' => '2019-07-03 12:12:12'
],
[
'id' => 789,
'name' => 'test2',
'age' => 10,
'created' => '2019-07-03 12:12:12'
],
];
$tmp = array_column($arr, null, 'id');
print_r($tmp);

>>>>>>>>>>>array_combine
$a = array(
'onekey1' => 'onevalue1',
'onekey2' => 'onevalue2',
'onekey3' => 'onevalue3'
);
$b = array(
'twokey1' => 'twovalue1',
'twokey2' => 'twovalue2',
'twokey3' => 'twovalue3'
);
$c = array_combine($a, $b);
print_r($c);

$season = [
['id' => 'aaa', 'name' => '春季', 'sequence' => 1],
['id' => 'bbb', 'name' => '暑假', 'sequence' => 2],
['id' => 'ccc', 'name' => '秋季', 'sequence' => 3],
['id' => 'ddd', 'name' => '寒假', 'sequence' => 4],
];
print_r($season);
#取$season的id组成新数组$season_id

$season_id = array_column($season, 'id');
print_r($season_id);

$new_season = array_combine($season_id, $season);
print_r($new_season);


>>>>>>>>>>>array_flip(array $array):array
1. 将数组的键值互换并返回反转后的数组
* array中的值需要能够作为合法的键名(例如需要是`integer`或者 `string`)。
* 如果类型不对,将出现一个警告,并且有问题的键/值对将不会出现在结果里。
* 如果同一个值出现多次,则最后一个键名将作为它的值,其它键会被丢弃。

#值为null
$arr = [
'one' => 'a',
'two' => null
];
$res = array_flip($arr);
print_r($res);
输出:Warning: array_flip(): Can only flip STRING and INTEGER values! in xxx

#值为string
$arr = [
'one' => 'a',
'two' => 'null'
];
$res = array_flip($arr);

>>>>>>>>>>>array_map
1. 语法:`array_map(callable $callback, array $array1[, array $... ]):array`
* callback:回调函数,应用到每个数组里的每个元素。
* array1:数组,遍历运行 callback 函数。
2. 作用:为数组的每个元素应用回调函数

#金钱以千分位分隔符隔开,保留小数点后两位
$num = [1, 3, 4.5, 5.4, 8.11111];
$one = array_map('number_format', $num, [2, 2, 2, 2, 2]);
$two = array_map(function ($value) {
return number_format($value, 2);
}, $num);
function test($data,$len = 4) {
return number_format($data, $len);
}
$three = array_map('test', $num);
$len = 2;
$four = array_map(function($value) use ($len) {
return number_format($value, $len);
}, $num);

foreach($num as $k => $v) {
$five[] = number_format($v, 2);
}

#数据类型转换
$data = ['a' => 1, 'b' => 2];
var_dump($data);
array_map(function ($v) {return (string)$v;}, $data);
var_dump($data);
foreach ($data as $k => $v) {
$data[$k] = (string)$v;
}
var_dump($data);

>>>>>>>>>>>array_merge
1. 将一个或多个数组的单元合并起来,一个数组中的值附加在前一个数组的后面,返回作为结果的数组。
* 如果输入的数组中有相同的字符串键名,则该键名后面的值将覆盖前一个值
* 然而如果数组包含数字键名,后面的值将不会覆盖原来的值,而是附加到后面。
* 如果只给了一个数组并且该数组是数字索引的,则键名会以连续方式重新索引。

#合并两个数组,后面数组中的值附加在前面数组的后面
##混合数组
$a = array(1, 'a' => '2', 4);
$b = array(1, 'a' => '3', 5);
print_r(array_merge($a, $b));
输出:
Array
(
[0] => 1
[a] => 3
[1] => 4
[2] => 1
[3] => 5
)
print_r($a + $b);
输出:
Array
(
[0] => 1
[a] => 2
[1] => 4
)

##索引数组
$arr1 = [1,2,3];
$arr2 = [1,4,5];
print_r(array_merge);
输出:
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 1
[4] => 4
[5] => 5
)
print_r($arr1 + $arr2);
输出:
Array
(
[0] => 1
[1] => 2
[2] => 3
)

#重新索引索引数组的下标,关联数组无变化
## 情形一、索引数组重新索引
$a = array(
1 => 3,
'a' => array( 'c' => 3)
);
print_r(array_merge($a));
输出:
Array
(
[0] => 3
[a] => Array
(
[c] => 3
)
)
## 情形二、关联数组无变化
$a = array(
'b' => 3,
'a' => array('c' => 3)
);
print_r(array_merge($a));
输出:
Array
(
[b] => 3
[a] => Array
(
[c] => 3
)
)

## 情形三、索引数组键名相同
$a = array( 1 => 3, 1 => 'aa');
print_r(array_merge($a));
输出:
Array
(
[0] => aa
)

## 情形四、关联数组键名相同
$a = array( 'a' => 3, 'a' => 'aa');
print_r(array_merge($a));
输出:
Array
(
[a] => aa
)

>>>>>>>>>>>array_reduce
1. `array_reduce(array $array, callable $callback[, mixed $initial = NULL ]):mixed`,将回调函数callback迭代地作用到array数组中的每一个单元中,从而将数组简化为单一的值。

#树状结构合并为一维数组
$str = '{"department":{"05f1a4c91ef77c001424f0c587079ff7":["be18576d271e945a42caf90abf9e9917","00f721589af54c0bad38461527b7baa3"],"65fd7303b005f401b2420b54c981bddf":["e24c06ee5eed4124925521304999cabe","88f54bd22293bff0472c8d58823334fe"],"b0c11f49d96671c6cce192db6a71c14a":["c9cc49492421fdab3fc09eab5e2cc458"],"e70f902e33a71bc21281204405fefcdb":["abf1a042b6648ab481a40581619f65a3","d88675f4bdad473db63cd068108059ce","f76128fad970474a8efba2d98d890d72","ad2fa5178a72cead089e50e3b794748e","83f4e6e48882b32531ed8ff0e19cc329","c44ffdd7bce30684c39cf20d6e8be9ca","08951a0cedb9a54a67bed7cfdee597bb","f5932a8aeb99c0582006372cfd64c5f1","20fa40645f5cb64bfe53f5ebdda5ff65","8ac3ac731e0a5bc5ac19bc0af476a9cf","a66fa40b6a7af410877537bf65d27213","2a64ae8188e57ec1c156bb9641fb06e6","fbafc8faf75f19cbd612ea380fcf2bfb","cc500b7657d8d2ce2ded43f9e51c8756","92ae9a79300956a928047977c5f80e9b","d3935bdb80aa134e65e71f48b0385fc2","3af3fb418fbd539b05190a4ba3d46e66","04e8912d5c23ddacbea2b25d65b6402a","dc6d9d20889f210d7ca320f16b6f64b1","05cb588367917b56f892b016a53ee5a3","79e0c11706e748329f4fcc83ed861a38","d52177c64c57d69be7f108be9331f647","36c038780b8be0e4ecf58e161b4cd6b3","26112e2bccdd433f068c264f58ea49ee","3ef8afd46dcfebe8201bb4a6a9dddf5e","81a8e88121384206dcce7832e2ab2005","46c9af7f3fc496cbbb30e9c4c2a6cfac","e54c2d42fb430c695e13f9212eb079e6","32c0eb23f9ff1150f61c81e847590a29"],"eaefdb7f8ed5b8d27b3c7c55e9537e38":["b4f8ba4c3d9c4035be30e3aa3e4332a9","6cee088e8f61457f915698cf2e35bf02","caee10803879471b98081d1569a94caf"]},"category":{"355368f0ad814ba3a8366d15bc42dee0":{"23592bd68289488495014b95b46d7624":["0b894a8c700d43028b10104c0f22ddcf","8c333c275b684419a4523ffdad72e7a8"]},"54a5fc4fee654d5ca1276ebfee2ba533":{"6fe77674e4d74eab87726289e8b8069f":["ced560d54e3142b1a473c060693387c6"]},"68e6de61d7c040ac9d8c22674be683fb":{"472bc6b0a1bf4c27afa9d04f06c51677":["113e35415e8744b687b7552fe094d0e9","1ba6c3d9b20d4cf8b8824ed0e3cc4a7d","47b900f5d6f547a6b5b0bfb0664b2dbe","6949537769674595927c77d5aabb5aaa","78b9e482d4f549e2aacbabb1ca8107cb","804e62e92d4f4d30af38ec8079f18dbc"],"56c3633b2728481981ae790a4de8ee60":["3231d4e30db3456695996cde28229a25","50e3cdb16e3c42779bd0b108c54e0d23","5a2fbd08a3554c40a8774e401059beb5","8344350ecf1648c485f8d1547f500ff5","9f8946b1e5e34574a35510c5a000462b","bdbfbc203e104a62a6e0fc2acfda9c9e"],"6caa57943cc6486aabe8b89f3cc0aa8a":["177a9d2a5f9c4dd991371cf28458d79c","89b5f65c0ad3452a85ce2237bbbfe4e5","bd700b119c744b81a51410e7eb6c48b9","bdf3b16826a243d8b5288460591751a5","c7cc9eb68ba441068c7523aa26fc2a8f","e428da54fcb9405bac6a3781a3263c00"],"cd22cad3245a4b7f951b93a3529e9f19":["2583fa83855648e39b73756eb886e056","52edc4b74d084be484a3b4af9b7d9b63","5fd41cd95db24868a7ee0b31f59a4c52","e83b305707704f13804f7a9872789328","e90a51b2f6bd4b39a03a45c30868da26","ff4eadce871848479aeac2414e96393c"],"d8a387406e56470f90314b6a8b1745a1":["276f4a6b652f4fc097eca87deebc47ef","3cf17e73193140cbb26167ff19088668","ad1b9b8a9757469dbe3caa5b3666e7c8","aef36aa21492421dbe78577c3c8490a8","b4d9e5364bcb4b65a72de5b7c5212fcd","df9bd01c87af4941b90164f0505a2839"],"fa79ebafd195480fa5828c19f3246a44":["0e4d949154af47d2a561c2505f1f9022","2a0d5edab20b40e983bfdb5e689c2570","690fbc43680341df8fc0d5d65d26c685","77d7ad9d9c624846acd67fd1895bfb15","f2af9410efcd4b3baf1d19570582b3c2","f4405453e9f143fcb7f2f843b08c457a"]},"8c44d1e8d0584ce9a2297b844ea96c9f":{"049651f9031449bca76d116e422a2cbf":["0987adeaecb64fc18f0e4a1737c6fddf","19a71573a8fd4de9b5f8dbc3fa51cc95","24349ad5df0f4efaaaa84a34a301a598","39772a3d09d84f7c820434c6c2a68eab","3e29d4194d1741d4adb291706d98feb4","6a331b2cdde845f287db80d7819c1e66","7614213bec6b489d842127500853fa73","7b99640b101c4059982c95a0b6c2fbee","c2512cb0e51d47918d9e9d1ad18926f9","e3a8e8f69abd41b488c3a8b2537397d2","ed1cbafc7f664b73ae2bfa5ce46eda24"],"70d0dd1057c847208871ea1e8c3a67cc":["0e8901d058c548f5ad2fc2346919d040","15b9bd54e6fc46b99d75341673f57515","1b297cbb12ec4bedaf57fa30d3a35373","3f6e7ac306e7481cae8a3cf8d4261d56","44742a5415664f1ab11830d607000e63","6eb649d8ecfc4ba3beb8bd57603cbe42","9a63d939bf16403f901cd8a9aca54863","cb2d7caf8c904f1facb328892b976f28","cc0e0f540a0246de82c3912a456c5ad7","edb8b8ec3fcd42edb71ff3faf9907d40","f50f346a79094d37b7d6360d3964f688"],"9ee5a06ce1324f2994b20b19f072b58b":["0a2d1a5cac8e43cd820a0529e89e45a8","5958bf22316440d0b071ee61215a15f3","5a4c6376c8f7463bae19c737ca009768","5bc74976a03547fc91c6a1834fdc0814","676afcfbfe1c40a993b9f5d68b01c2bb","6cc992d8ed084ebe80e679f6d0f1f63f","90b19d2263b64fc5a06c2c24189c3f66","a78bad51d9624f168f92341b47ff25db","ccd36fe7023644c092dd7d1de494bba9","dc8d31100122455c94383b6386ba1849","f60397f21440423e96aa66ab724fcf91"]},"8d0d9bffe18b48e6bdcb7a98a0e03ee1":{"58d5c2ada9124305a676913a8a03bb3f":["047d5b99412247099cf2b6e25f3cbeca","0d576c7bfb054a5c83b1438dfc27a056","1e4ead73539e4bbca160b2153f8045b8","2086d145185e44f0ba5d69c92eedd8a5","2b137611a1d3477cb7fa442e46d875eb","352df502a82d40529923f83803384a77","4b2bc253e71c4237a02425c7ecda5fa9","56c906d5acdc4de6a520056457af15dd","5da3a89090684e29bd2e9e4cd710adf5","96d85168ce2141f2b230d19e094c170c","9b1c459989714dc2b88cdeed9d3f08f1","9fb2f9ed56804a72b90532ce4a11a8ec","add8e47bb2f248cdbe691d673131b7db","b81aa2c66b7d46e1aa1d444a0f1c3ef7","c556721dc58c43a785a216fa299e85fa","de8141d583d6446e94b20e2e1f6dcaf1"],"97df4652ee0e4d1b8bd886d73890fb61":["1c7c8d3ee2bb4334b36c88b33240754d","8397d5a30cf447e7a390a1ac1eb1ff59","85c77eb145e9449db05bd740880af19a","9505bce89b8c429b9eac083725351b49","96b1cb77cfc74200a0fd5c7735764791"],"bd02671df27643bbafc51069cee1f230":["02c05a3c815a4456abfa18ab2c6c8af7","4435226714864fe8894236660cb27594","459fabcd15de4e279cb19dc7ab316545","54054cb30ab149f3a9480514f4d67911","79298623a63a4f728840aa94fbad4f42","a3cc6d610eb44c37956e0fba1c5615cd","a82f335455eb43dcbc7541ea05261c44","d8849854300542628b00758e23a00f3f","e3b448ce588643d6910f453554fafd2a","eab920f6e9f643f4bac5fdd1fc11f6a6","efd3d1ab80ef4b4bbcee12d5d9bad42c"]},"9f219946df4f4336946579b89cfbfe7e":{"c470eae33bdf4d9a95b492322a9cab88":["4c762ca56977422fab22a15ea3be1bdc"],"df1f542e44744023a44cdc5fc871258f":["03538fc829814f39aa1f67a15263ae85","04d8d325576e4cf585bbe67485554762","16e195e1370e42efa52ab7e2afee9770","2236742961b74ec5b33ea7c2398091fe","38de905eae2449cc9d2d12011366acd7","92daf2b6a17b4493af8cb6bfa0e01270","d72a56bf64dd4bbf8b0b6f7c9b437888"]},"aa59ad1f470742fc99c8acdac5b3acf0":{"7ce3b9b92c5b464aaa219c6772deb3c4":[""],"b5641323a977486ebc94ef97a6b01b67":[""]},"ddce280fb06a461680863ca9b2ca7412":{"2c69cb34dce1469b96773d1e2d0f72ed":["0e57fb961455429799a237b8ab7ad36e","5cced7cffa39449899bfcc5c768ee67d","8c1b2ccf81b14e39b9fe7b69391296ba","dbd6945f6d7c4af0a6cd66101a9d2e27","fd1a8aee5b4846b0853a3ddd355a2089"],"35f3fd424ab84283b172c18c04116888":["0fb3bed41fa24c959937daa40c0591a0","676c7d63f70c4797a6fd053f6ca4483d","7eaf89fb8ff147d0baa635d0780c6d7d","8713d76160a74cc187a3384a25f5a1c4","97d1274bddf746d599e9a916643f58c0","9b330bb14bb149ca8522980c7855aad8","9e15a143006c420481356ff380c5d1ae","ad16ee81d0cc49f2b514ed0cc6224d6b","c5c07d858bdb41a7b36ba8d21e14f69e"],"4b50d1f2c7e94f0c99d5d38b79c1fe73":["3cabed9e4fc0451ea9378405fcc102b9","50e3b78b6f9a48f69b151798b2092378","7c57ea1d124a4b95b401b8a6cc17cbd2","8372c95b3c5f4cf0b32f9560d77d65f9","acba29a1aae543549d0b9e7209939ad3","bb5ce1d966ba450fb2459186c05d8623","da0e715ebc934fcaad6d04e2d51063bd"],"7f8e49afa4b844ce83faf56c7ede6cdf":["92070c70e4e84e42867cab6a995f04d9"],"8a631a6a2f9c461292016f13c3712879":["a9d469b916b64c67b3c3e0c484755dd1","d49dd3018b7349549c0ba830f19fad8b"],"c4969db58b914f548264bda4c3c45c9a":["104cdf825a574f5f8263f261f8429fdb","11d661c6c73047108da2e7f050a3e1ba","1abe94a9de3c405b9951fb3f9c4773fe","21ba7a4a9a3b40c8b3528c34598ddf18","40683b4c82114f26ae6a0776163c9678","42e750525e6943b1bd20a9806fa6ea62","47b061e862d04b5288e177774bc37c92","50aa3cbd206945618b1db7150a3c72fe","561d81fac6bc4daf9154617afd6f22a3","5a8053db41464580ab45679957d9d062","858677536b16480093efd92f54171737","861784fde4774def9e2348df12f37711","f6e3d898d9ad44ec8adf1032200b2312"],"f02e85199e1f4861b2722dbdff079407":["060d68731e5441b69c6a564ceb7dbc31","0681be33003a4b3483a6d29581425338","15ea6e126355407b8e79b6bfc484cc80","1a7af252561049db82d7b277098aa340","60896a10f56b4ee5806cf4988b86bfd4","631cda1cf9134b02865241fd4c730c3f","7fc62475f27142799ab1a2048d7cca08","a504c327ee7140e5ae497dfa40517eac","a55ce4f15a7844c5bda4812db67c4c9b","a9aa2194faa64327bc65bda42d25c8c6","e9b41b85b40e4d01bbec5c0dacb9bf0e"]}},"join_merchant":["e5870db2d6324594a6c2c73b80cf5e73"],"pool_merchant":["4f1dc462a4814991a8145139925e2282","374951c1b7f9488b8ac563a54ee41244"]}';
$array = json_decode($str, 1);
$array = $array['department'];
$arr = array_reduce($array, 'array_merge', []);
print_r($arr);

>>>>>>>>>>>array_slice
1. 功能:从数组中取出一段
2. 用法:`array_slice(array $array , int $offset [, int $length = NULL [, bool $preserve_keys = false ]]):array`
3. 说明:返回根据`offset`和`length`参数所指定的`array`数组中的一段序列。
4. 参数
* array:输入的数组。
* offset:
* 如果`offset`非负,则序列将从`array`中的此偏移量开始。
* 如果`offset`为负,则序列将从`array`中距离末端这么远的地方开始。
* length:
* 如果给出了`length`并且为正,则序列中将具有这么多的单元。
* 如果给出了`length`并且为负,则序列将终止在距离数组末端这么远的地方。
* 如果省略,则序列将从`offset`开始一直到`array`的末端。
* preserve_keys:注意`array_slice()`默认会重新排序并重置数组的数字索引。你可以通过将`preserve_keys`设为`TRUE`来改变此行为。
5. 注意事项
* 如果`offset`参数大于`array`尺寸,就会返回空的`array`

$input = array("a", "b", "c", "d", "e");
$output = array_slice($input, 2); // returns "c", "d", and "e"
$output = array_slice($input, -2, 1); // returns "d"
$output = array_slice($input, 0, 3); // returns "a", "b", and "c"
// note the differences in the array keys
print_r(array_slice($input, 2, -1));
print_r(array_slice($input, 2, -1, true));


>>>>>>>>>>>array_splice
1. 功能: 去掉数组中的某一部分并用其它值取代
2. 用法:`array_splice(array &$input, int $offset[, int $length = count($input)[, mixed $replacement = array()]]):array`
3. 说明: 把`input`数组中由`offset`和`length`指定的单元去掉,如果提供了`replacement`参数,则用其中的单元取代。
* 注意`input`中的数字键名不被保留
* `input`传引用方式
4. 参数
* input:输入的数组,传引用。
* offset:
* 如果`offset`为正,则从`input`数组中该值指定的偏移量开始移除。
* 如果`offset`为负,则从`input`末尾倒数该值指定的偏移量开始移除。
* length:
* 如果省略`length`,则移除数组中从`offset`到结尾的所有部分。
* 如果指定了`length`并且为正值,则移除这么多单元。
* 如果指定了`length`并且为负值,则移除从`offset`到数组末尾倒数`length`为止中间所有的单元。
* 如果设置了`length`为零,不会移除单元。
* 小窍门:当给出了`replacement`时要移除从`offset`到数组末尾所有单元时,用`count($input)`作为`length`。
* replacement
* 如果给出了`replacement`数组,则被移除的单元被此数组中的单元替代。
* 如果`offset`和`length`的组合结果是不会移除任何值,则`replacement`数组中的单元将被插入到`offset`指定的位置。
* 注意替换数组中的键名不保留。
* 如果用来替换`replacement`只有一个单元,那么不需要给它加上 array(),除非该单元本身就是一个数组、一个对象或者 NULL。

$input = array("red", "green", "blue", "yellow");
array_splice($input, 2); // $input is now array("red", "green")

$input = array("red", "green", "blue", "yellow");
array_splice($input, 1, -1); // $input is now array("red", "yellow")

$input = array("red", "green", "blue", "yellow");
array_splice($input, 1, count($input), "orange"); // $input is now array("red", "orange")

$input = array("red", "green", "blue", "yellow");
array_splice($input, -1, 1, array("black", "maroon")); // $input is now array("red", "green","blue", "black", "maroon")

$input = array("red", "green", "blue", "yellow");
array_splice($input, 3, 0, "purple"); // $input is now array("red", "green","blue", "purple", "yellow");

$input = array("red", "green", "blue", "yellow");
array_splice($input, 3, 1, "purple"); // $input is now array("red", "green","blue", "purple");

>>>>>>>>>>>数组和json转换
$arr = array('name'=> 'liuyulong','sex'=>'man','weight'=>'80kg');
$json = json_encode($arr);
print_r($json);

#多维数组
$arr = array(
'first'=>array('name'=>'liuyulong','sex'=>'man','weight' =>'80kg'),
'second'=>array('name'=>'liusirdotme','sex'=>'man','weight'=>'80kg'),
);
$json = json_encode($arr);
print_r($json);

#一维数组
$arr = array('liuyulong','man','80kg');
$json = json_encode($arr);
print_r($json);

#多维数组
$arr = array(
array('liuyulong','man','80kg'),
array('liusirdotme','man','80kg'),
);
$json = json_encode($arr);
print_r($json);


#索引+关联
$arr = array(
array('name'=>'liuyulong','sex'=>'man','weight'=>'80kg'),
array('name'=>'liusirdotme','sex'=>'man','weight'=>'80kg'),
);
$json = json_encode($arr);
print_r($json);

#关联+索引
$arr = array(
"first" => array('liuyulong','man','80kg'),
"second" => array('liusirdotme','man','80kg'),
);
$json = json_encode($arr);
print_r($json);


>>>>>>>>>>>call_user_func
1. 语法 `call_user_func(callable $callback[, mixed $parameter[, mixed $... ]]):mixed
2. 说明:把第一个参数作为回调函数调用
* 第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数

function increment(&$var){
$var++;
}
$a = 0;
$b = &$a;
call_user_func('increment', $b);
echo $a;
输出:0
call_user_func_array('increment', array(&$a));
echo $a;
>传入call_user_func()的参数不能为引用传递。

>>>>>>>>>>>call_user_func_array
1. 语法 `call_user_func_array(callable $callback, array $param_arr):mixed`
2. 说明:调用回调函数,并把一个数组参数作为回调函数的参数
* 参数:第一个参数作为回调函数调用,把参数数组作为回调函数的的参数传入
* 参数数组得是索引数组
* 返回值:正常返回回调函数的结果,出错的话就返回FALSE

#直接调用函数
function func($arg, $arg2, $arg3 = 3) {
echo __FUNCTION__, " 拿到参数 $arg 和 $arg2 和 $arg3 \n";
}
call_user_func_array("func", [1, 2]);

#调用对象的方法
class Test {
function method($arg, $arg2) {
echo __METHOD__, " 拿到参数 $arg 和 $arg2\n";
}
}
//调用类的方法
$test = new Test();
call_user_func_array(array($test, "method"), [3, 4]);

#对比
>call_user_func可以传一个参数,也可传多个,跟回调函数的参数有关,call_user_func_array只有2个参数,必须传2个参数
>call_user_func传的参数是字符串形式,call_user_func_array传的参数是数组形式
>call_user_func不能引用传递传参数,call_user_func_array允许引用传递
>性能上call_user_func比call_user_func_array快

二、字符串函数

  1. addcslashes:以 C 语言风格使用反斜线转义字符串中的字符
  2. addslashes:使用反斜线引用字符串
  3. stripcslashes:反引用一个使用 addcslashes() 转义的字符串
  4. stripslashes:反引用一个引用字符串
  5. bin2hex:函数把包含数据的二进制字符串转换为十六进制值
  6. hex2bin:转换十六进制字符串为二进制字符串
  7. chr:返回指定的字符
  8. chunk_​split:将字符串分割成小块
  9. convert_​cyr_​string:将字符由一种 Cyrillic 字符转换成另一种
  10. convert_​uudecode:解码一个 uuencode 编码的字符串
  11. convert_​uuencode:使用 uuencode 编码一个字符串
  12. count_​chars:返回字符串所用字符的信
  13. crc32:计算一个字符串的 crc32 多项式
  14. crypt:单向字符串散列
  15. echo:输出一个或多个字符串
  16. fprintf:将格式化后的字符串写入到流
  17. get_​html_​translation_​table:返回使用 htmlspecialchars() 和 htmlentities() 后的转换表
  18. hebrev:将逻辑顺序希伯来文(logical-Hebrew)转换为视觉顺序希伯来文(visual-Hebrew)
  19. hebrevc:将逻辑顺序希伯来文(logical-Hebrew)转换为视觉顺序希伯来文(visual-Hebrew),并且转换换行符
  20. html_​entity_​decode:Convert HTML entities to their corresponding characters
  21. htmlentities:将字符转换为 HTML 转义字符
  22. htmlspecialchars_​decode:将特殊的 HTML 实体转换回普通字符
  23. htmlspecialchars:将特殊字符转换为 HTML 实体
  24. explode:使用一个字符串分割另一个字符串
  25. implode:将一个一维数组的值转化为字符串
  26. join:别名 implode()
  27. lcfirst:使一个字符串的第一个字符小写
  28. ucfirst:将字符串的首字母转换为大写
  29. ucwords:将字符串中每个单词的首字母转换为大写
  30. strtolower:将字符串转化为小写
  31. strtoupper:将字符串转化为大写
  32. levenshtein:计算两个字符串之间的编辑距离
  33. localeconv:Get numeric formatting information
  34. trim:去除字符串首尾处的空白字符(或者其他字符)
  35. ltrim:删除字符串开头的空白字符(或其他字符)
  36. rtrim:删除字符串末端的空白字符(或者其他字符)
  37. chop:rtrim() 的别名
  38. md5_​file:计算指定文件的 MD5 散列值
  39. md5:计算字符串的 MD5 散列值
  40. metaphone:Calculate the metaphone key of a string
  41. money_​format:将数字格式化成货币字符串
  42. nl_​langinfo:Query language and locale information
  43. nl2br:在字符串所有新行之前插入 HTML 换行标记
  44. number_​format:以千位分隔符方式格式化一个数字
  45. ord:转换字符串第一个字节为 0-255 之间的值
  46. parse_​str:将字符串解析成多个变量
  47. print:输出字符串
  48. printf:输出格式化字符串
  49. quoted_​printable_​decode:将 quoted-printable 字符串转换为 8-bit 字符串
  50. quoted_​printable_​encode:将 8-bit 字符串转换成 quoted-printable 字符串
  51. quotemeta:转义元字符集
  52. setlocale:设置地区信息
  53. sha1_​file:计算文件的 sha1 散列值
  54. sha1:计算字符串的 sha1 散列值
  55. similar_​text:计算两个字符串的相似度
  56. soundex:Calculate the soundex key of a string
  57. sprintf:Return a formatted string
  58. sscanf:根据指定格式解析输入的字符
  59. str_​getcsv:解析 CSV 字符串为一个数组
  60. str_​replace:子字符串替换
  61. str_​ireplace:str_replace() 的忽略大小写版本
  62. str_​pad:使用另一个字符串填充字符串为指定长度
  63. str_​repeat:重复一个字符串
  64. str_​rot13:对字符串执行 ROT13 转换
  65. str_​shuffle:随机打乱一个字符串
  66. str_​split:将字符串转换为数组
  67. str_​word_​count:返回字符串中单词的使用情况
  68. strcasecmp:二进制安全比较字符串(不区分大小写
  69. strchr:别名 strstr()
  70. strrchr:查找指定字符在字符串中的最后一次出现
  71. strstr:查找字符串的首次出现
  72. stristr:strstr() 函数的忽略大小写版本
  73. strcmp:二进制安全字符串比较
  74. strcoll:基于区域设置的字符串比较
  75. strcspn:获取不匹配遮罩的起始子字符串的长度
  76. strip_​tags:从字符串中去除 HTML 和 PHP 标记
  77. strpos:查找字符串首次出现的位置
  78. stripos:查找字符串首次出现的位置(不区分大小写)
  79. strripos:计算指定字符串在目标字符串中最后一次出现的位置(不区分大小写)
  80. strrpos:计算指定字符串在目标字符串中最后一次出现的位置
  81. strlen:获取字符串长度
  82. strnatcasecmp:使用“自然顺序”算法比较字符串(不区分大小写)
  83. strnatcmp:使用自然排序算法比较字符串
  84. strncasecmp:二进制安全比较字符串开头的若干个字符(不区分大小写)
  85. strncmp:二进制安全比较字符串开头的若干个字符
  86. strpbrk:在字符串中查找一组字符的任何一个字符
  87. strrev:反转字符串
  88. strspn:计算字符串中全部字符都存在于指定字符集合中的第一段子串的长度。
  89. strtok:标记分割字符串
  90. strtr:转换指定字符
  91. substr_​compare:二进制安全比较字符串(从偏移位置比较指定长度)
  92. substr_​count:计算字串出现的次数
  93. substr_​replace:替换字符串的子串
  94. substr:返回字符串的子串
  95. vfprintf:将格式化字符串写入流
  96. vprintf:输出格式化字符串
  97. vsprintf:返回格式化字符串
  98. wordwrap:打断字符串为指定数量的字串

三、ob函数

概念

  1. 当PHP脚本有输出时,输出控制函数可以用这些来控制输出。这在多种不同情况中非常有用,尤其是用来在脚本开始输出数据后,发送http头信息到浏览器。输出控制函数不影响由header()或setcookie()发送的文件头信息,仅影响像echo这样的函数和PHP代码块间的数据。

  2. 常用函数

函数名称 含义
ob_start(); 打开一个输出缓冲区,所有的输出信息不再直接发送到涉猎器,而是保存在输出缓冲区里面
ob_clean(); 删除内部缓冲区的内容,不关闭缓冲区(不输出)
ob_end_clean() 删除内部缓冲区的内容,关闭缓冲区(不输出)
ob_get_clean() 返回内部缓冲区的内容,关闭缓冲区。相当于执行ob_get_contents()和ob_end_clean()
flush(); 将ob_flush释放出来的内容,以及不在PHP缓冲区中的内容,整个输出至涉猎器;刷新内部缓冲区的内容,并输出
ob_flush(); 发送内部缓冲区的内容到涉猎器,删除缓冲区的内容,不关闭缓冲区
ob_end_flush() 发送内部缓冲区的内容到涉猎器,删除缓冲区的内容,关闭缓冲区
ob_get_flush() 返回内部缓冲区的内容,并关闭缓冲区,再释放缓冲区的内容。相当于ob_end_flush()并返回缓冲区内容
ob_get_contents() 返回缓冲区的内容,不输出
  1. 输出控制存在的意义:因为http协议的限制(前几行必须是协议信息,然后一个空行,然后才是用户需要的内容,需要保证header信息在其他内容之前发送,否则浏览器无法解析服务器返回的内容。

  2. demo

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#ob_start
echo "a";
ob_start();
echo 123;
sleep(5);
echo 456;
#结果:先输出a,然后过了5s,输出123456

#ob_end_flush与ob_flush
echo "a";
ob_start();
echo 123;
sleep(5);
ob_end_flush();
sleep(5);
echo 456;
sleep(5);
echo 789;

echo "a";
ob_start();
echo 123;
sleep(5);
ob_flush();
sleep(5);
echo 456;
sleep(5);
echo 789;
#ob_end_flush结果:先输出字符a,5s后输出123并输出缓冲区内容,清空缓冲区,再过5s后输出456,再过5s后输出789
#ob_flush结果:先输出字符a,5秒之后输出123并输出缓冲区内容,10s之后同时输出456 789

function ob_callback($string){
return $string.$string."\n";
}

ob_start("ob_callback");
echo 123;
sleep(5);
ob_flush();
sleep(5);
echo 456;
ob_flush();
sleep(5);
echo 789;
#结果:每隔5s依次输出123123、456456、789789,说明当调用ob_flush()之后会调用ob_callback()方法。

function ob_callback($string){
return $string.$string."\n";
}

ob_start("ob_callback",4);
echo 12345;
sleep(5);
ob_flush();
echo 456;
sleep(5);
ob_end_flush();
sleep(5);
echo 789;

#结果:立即输出1234512345,5s之后输出换行符(空字符),再过5s之后输出456456,再过5s输出789789。
#分析
* ob_start()的chunk_size=4,因为输出字符串12345长度已经超过了4,所以直接输出了,输出的时候调用了ob_callback()方法
* 过了5s后ob_flush()刷新缓冲区,此时缓冲区中的内容为空,所以执行ob_callback()的时候输出一个换行
* 接着执行echo 456;长度小于4,所以执行sleep(5)之后执行ob_end_flush(),刷新缓冲区并且执行ob_callback(),输出456456
* ob_end_flush()已经关闭了缓冲区,所以接下来过了5s之后输出789789

function outer($string){
return "out".$string;
}
function inner($string){
return "in".$string;
}

ob_start("outer");
echo "111";
ob_start("inner");
echo "222";
* 结果:out111in222

echo 'A';
ob_start();
echo 123;
ob_clean();

echo 'A';
ob_start();
echo 123;
ob_end_clean();

echo 'A';
ob_start();
echo 123;
echo ob_get_clean();
#结果:A、A、A123

四、常见函数对比

  1. 安全函数使用

    真题:怎么在代码类的网站存储类似的脚本<script>window.location.href='http://www.baidu.com'</script>

  • html_entity_decode
  • htmlentities
  • addslashes
  • stripslashes
  • htmlspecialchars
  • htmlspecialchars_decode
  • strip_tags
  1. isset-is_null-empty
  • 真题

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    $a = null;
    $b = 0;
    $c = '';

    var_dump(isset($a)); // bool(false)
    var_dump(isset($b)); // bool(true)
    var_dump(isset($c)); // bool(true)
    var_dump(isset($d)); // bool(false)

    var_dump(is_null($a)); // bool(true)
    var_dump(is_null($b)); // bool(false)
    var_dump(is_null($c)); // bool(false)
    var_dump(is_null($d)); // bool(true)

    var_dump(empty($a)); // bool(true)
    var_dump(empty($b)); // bool(true)
    var_dump(empty($c)); // bool(true)
    var_dump(empty($d)); // bool(true)
  • 对比

isset()函数为false的情况

  • 变量的值为null
  • 未定义的变量
  • unset()变量之后

is_null函数为true的情况(和isset相反)

  • 变量的值为null
  • 未定义的变量
  • unset()变量后

empty()函数为true的情况

  • 空字符串
  • false
  • 空数组array()
  • NULL
  • 0
  • 浮点数0.0
    • 字符串0.0为false
  • unset之后的变量
  • 字符串0
  • 未定义的变量$var
  • 没有任何属性的对象

unset()函数,变量的值变为NULL

1
2
3
4
5
6
$a = '123';
var_dump($a);// string(3) "123"
unset($a);
var_dump($a);// NULL
var_dump($a === NULL);// bool(true)
var_dump(empty($a)); //bool(true)
  • 表格
对比项 isset()方法 empty()方法 is_null方法
空字符串”” true ture false
一个空格” “ true false false
FALSE true true false
TRUE true false false
空数组array() true true false
NULL false true true
字符串”0” true true false
整型0 true true false
浮点型0.0 true true false
未定义的变量$a false true true
  • 综合

    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
    $a = "";
    $b = " ";
    $c = false;
    $d = true;
    $e = array();
    $f = null;
    $g = 0;
    $h = 0.0;
    $i = "0";

    var_dump(isset($a)); // bool(true)
    var_dump(isset($b)); // bool(true)
    var_dump(isset($c)); // bool(true)
    var_dump(isset($d)); // bool(true)
    var_dump(isset($e)); // bool(true)
    var_dump(isset($f)); // bool(false)
    var_dump(isset($g)); // bool(true)
    var_dump(isset($h)); // bool(true)
    var_dump(isset($i)); // bool(true)
    var_dump(isset($j)); // bool(false)

    var_dump(is_null($a)); // bool(false)
    var_dump(is_null($b)); // bool(false)
    var_dump(is_null($c)); // bool(false)
    var_dump(is_null($d)); // bool(false)
    var_dump(is_null($e)); // bool(false)
    var_dump(is_null($f)); // bool(true)
    var_dump(is_null($g)); // bool(false)
    var_dump(is_null($h)); // bool(false)
    var_dump(is_null($i)); // bool(false)
    var_dump(is_null($j)); // bool(true)

    var_dump(empty($a)); // bool(true)
    var_dump(empty($b)); // bool(false)
    var_dump(empty($c)); // bool(true)
    var_dump(empty($d)); // bool(false)
    var_dump(empty($e)); // bool(true)
    var_dump(empty($f)); // bool(true)
    var_dump(empty($g)); // bool(true)
    var_dump(empty($h)); // bool(true)
    var_dump(empty($i)); // bool(true)
    var_dump(empty($j)); // bool(true)
  • 特例

    1
    2
    3
    4
    var_dump(empty(0));      // bool(true)
    var_dump(empty('0')); // bool(true)
    var_dump(empty(0.00)); // bool(true)
    var_dump(empty('0.00')); // bool(false) 特殊记忆字符串0.00
  1. func_num_args-func_get_arg-func_get_args

PHP默认支持按值传递参数,通过引用传递参数以及默认参数,也支持可变长度参数列表,其中可变长度参数列表就主要是通过以下几个函数实现的。

  • func_num_args

    • 语法 func_num_args(void):int

    • 说明:返回调用函数的传入参数个数

      • 参数:void,即不需要传参
      • 返回值:int型,为传入函数的参数数量
    • demo

      1
      2
      3
      4
      5
      6
      7
      8
      function test()
      {
      $count = func_num_args();
      echo $count;
      }

      test(1, 2); //2
      test(1, 2, 3); //3
  • func_get_arg

    • 语法 func_get_arg(int $arg_num):mixed

    • 说明:返回参数列表的某一项

      • 参数:arg_num,参数的偏移量,函数的参数从0开始计数
      • 返回值:mixed,返回指定的参数,错误则返回FALSE
    • demo

      1
      2
      3
      4
      5
      6
      7
      8
      9
      function test()
      {
      $one = func_get_arg(0);
      $two = func_get_arg(1);

      echo $one,PHP_EOL,$two,PHP_EOL;
      }

      test(1, 2);

当在自定义函数的外面调用的该函数的时候会发出一个警告,或者是当arg_num比实际传入的参数的数目大的时候也会发出一个警告。

  • func_get_args

    • 语法 func_get_args(void):array

    • 说明:返回一个包含函数参数列表的数组

      • 参数:void,即不需要传参
      • 返回值:array,一个数组,其中每个元素都是目前用户自定义函数的参数列表的相应元素的副本。
    • demo

      1
      2
      3
      4
      5
      6
      7
      function test()
      {
      $arg = func_get_args();
      print_r($arg);
      }

      test(1, 2);

在用户自定义函数外调用则会出现错误警告。

  1. get_object_vars和compact

    get_object_vars ———— 返回由对象属性组成的关联数组,compact ———— 建立一个数组,包括变量名和它们的值。

  2. array_map、array_filter、array_walk

    • 相同点:都是利用回调函数对数组中每个元素进行操作

    • 不同点

      • 返回值
        • array_map 返回数组,遍历数组元素调用函数
        • array_walk 返回true/false
        • array_filter 返回数组,作用是过滤数组中的元素,回调函数返回真才能保存到返回数组中
      • 回调函数的参数及顺序
      • 是否改变数组的值
    • demo

    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
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    $num = array(0, 1, 2); 
    $map = array_map(function ($v) {
    return $v == 0 ? 0 : 10;
    }, $num);
    print_r($map);
    输出:
    Array
    (
    [0] => 0
    [1] => 10
    [2] => 10
    )

    A.不传回调函数
    $filter = array(
    'a' => null,
    'b' => false,
    'c' => 0,
    'e' => '',
    'd' => 1,
    );
    $new = array_filter($filter);
    print_r($new);
    输出:
    Array
    (
    [d] => 1
    )
    B.传回调函数
    $num = array(0,1,2);
    $filter = array_filter($num, function($v){
    return $v == 0 ? '' : $v;
    });
    print_r($filter);
    输出:
    Array
    (
    [1] => 1
    [2] => 2
    )

    A.传值
    $num = array(0, 1, 2);
    array_walk($num, function ($v, $k) {
    $v++;
    });
    print_r($num);
    输出:
    Array
    (
    [0] => 0
    [1] => 1
    [2] => 2
    )

    B.传引用
    array_walk($num, function (&$v, $k) {
    $v++;
    });
    print_r($num);
    输出:
    Array
    (
    [0] => 1
    [1] => 2
    [2] => 3
    )

    C.官网示例
    $fruits = array(
    'a' => 'apple',
    'b' => 'banana',
    );
    function changeValue(&$item, $key, $prefix) {
    $item = "$prefix: $item";
    }
    function printItem($item, $key) {
    echo "$key. $item<br />\n";
    }
    echo "改变值之前打印:\n";
    array_walk($fruits, 'printItem');
    array_walk($fruits, 'changeValue', 'fruit');
    echo "\n改变值之后打印:\n";
    array_walk($fruits, 'printItem');
  1. 魔术方法之call和callStatic

    • 定义

      • __call($method, $arg),调用不可访问或不存在的方法时被调用
      • __callStatic($method, $arg),调用不可访问或不存在的静态方法时被调用
    • demo

    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
    58
    59
    60
    61
    62
    63
    class Person {

    private function getPrivateFunc()
    {
    echo "Private function";
    }

    private static function getPrivateStaticFunc($arg)
    {
    echo "Private static function";
    }

    private static function getProtectedStaticFunc($arg)
    {
    echo "Private static function";
    }

    public function __call($method, $arg)
    {
    return call_user_func_array('print_r', $arg);
    }

    public static function __callStatic($method, $arg)
    {
    return call_user_func('print_r', $arg);
    }
    }
    $p = new Person();

    $p->getMyFunc([1,2,3]);

    $p->getPrivateFunc(1);

    Person::getStaticFunc('a');

    Person::getPrivateStaticFunc('b');

    Person::getProtectedStaticFunc('c');

    输出:
    Array
    (
    [0] => 1
    [1] => 2
    [2] => 3
    )

    1

    Array
    (
    [0] => a
    )

    Array
    (
    [0] => b
    )

    Array
    (
    [0] => c
    )
  2. pack和unpack

五、register_shutdown_function

  1. register_shutdown_function(callable $callback[, mixed $parameter[, mixed $... ]]):void,注册一个callback,它会在脚本执行完成或者exit()后被调用。可以多次调用register_shutdown_function(),这些被注册的回调会按照他们注册时的顺序被依次调用。如果你在注册的方法内部调用 exit(),那么所有处理会被中止,并且其他注册的中止回调也不会再被调用。

  2. Note

    • 在某些web server(如Apache)上,可以在中止函数内对脚本的工作目录进行修改。
    • 如果进程被信号SIGTERM或SIGKILL杀死,那么中止函数将不会被调用。尽管你无法中断SIGKILL,但你可以通过pcntl_signal()来捕获SIGTERM,通过在其中调用exit()来进行一个正常的中止。
  3. 基本用法,和别的注册回调没太大差别

1
2
3
4
5
6
function shutdown()
{
echo 'Script executed with success', PHP_EOL;
}
register_shutdown_function('shutdown');
//call_user_func('shutdown');
  1. 和魔术函数__destruct()对比,
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
ini_set("max_execution_time", "10");

class TestOne
{
function __construct()
{
register_shutdown_function(array(&$this, 'shutdown_func'));
}

function shutdown_func() {
$fp = fopen("/Users/playcrab/Desktop/exec.txt", "w+");
fputs($fp, "执行超时,正常输出");
fclose($fp);
}
};

$obj = new TestOne();
while (true) {
echo 1;
}


class TestTwo
{
function __destruct()
{
$fp = fopen("/Users/playcrab/Desktop/destruct.txt", "w+");
fputs($fp, "执行超时,不能输出");
fclose($fp);
}
};

$obj = new TestTwo();
while (true) {
echo 2;
}

由对比可知,使用register_shutdown_function能保证即使脚本异常中止时,清理函数的代码仍然能够执行。

  1. 结合进程扩展控制
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
declare(ticks = 1);

//信号处理函数
function sig_handler($signo)
{
switch ($signo) {
case SIGTERM:
exit;
break;
case SIGHUP:
break;
case SIGUSR1:
echo "Caught SIGUSR1...\n";
break;
default:
break;
}

}
echo "Installing signal handler...\n";

//安装信号处理器
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGHUP, "sig_handler");

$obj = new T();

// pcntl_signal(SIGUSR1, array($obj, "test"));
pcntl_signal(SIGUSR1, array($obj, '__construct'));

echo "Generating signal SIGTERM to self...\n";

posix_kill(posix_getpid(), SIGUSR1);//向当前进程发送SIGUSR1信号

echo "Done\n";

class T
{
public function __construct()
{
register_shutdown_function(array(&$this, 'handle'));
}

public function test()
{
register_shutdown_function(array(&$this, 'handle'));
}

public function handle()
{
file_put_contents('/Users/playcrab/Desktop/111.txt', 2);
echo '进程挂了但是内容保存了';
}
}
  1. 官网手册

六、debug_backtrace

  1. 用法debug_backtrace([ int$options = DEBUG_BACKTRACE_PROVIDE_OBJECT[, int $limit = 0 ]]):array

    • 返回一个包含众多关联数组的array
  2. 返回值说明

名字 类型 说明
function string 当前的函数名。
line integer 当前的行号。
file string 当前的文件名。
class string 当前 class 的名称。
object object 当前的 object。
type string 当前调用的类型。如果是一个方法,会返回 “->”。如果是一个静态方法,会返回 “::”。 如果是一个函数调用,则返回空。
args array 如果在一个函数里,这会列出函数的参数。 如果是在一个被包含的文件里,会列出包含的文件名。
  1. 测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function func1() {
func2();
}

function func2() {
func3();
}

function func3() {
$debug = debug_backtrace();
print_r($debug);
}
func1();
exit;
  1. 运行
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
Array
(
[0] => Array
(
[file] => your_path
[line] => 8
[function] => func3
[args] => Array
(
)
)
[1] => Array
(
[file] => your_path
[line] => 4
[function] => func2
[args] => Array
(
)
)
[2] => Array
(
[file] => your_path
[line] => 15
[function] => func1
[args] => Array
(
)
)
)
  1. 官网手册

七、str_replace

  1. 功能:子字符串替换

  2. 用法:str_replace(mixed $search, mixed $replace, mixed $subject[, int &$count ]): mixed

  3. 说明:该函数返回一个字符串或者数组。该字符串或数组是将subject中全部的search都被replace替换之后的结果。

    • 如果searchreplace为数组,那么str_replace()将对subject做二者的映射替换,它们的值将会被依次处理。
    • 如果replace的值的个数少于search的个数,多余的替换将使用空字符串来进行。
    • 如果search是一个数组而replace是一个字符串,那么search中每个元素的替换将始终使用这个字符串。
  4. 参数

    • search:查找的目标值,也就是 needle。一个数组可以指定多个目标。
    • replace:search 的替换值。一个数组可以被用来指定多重替换。
    • subject:执行替换的数组或者字符串。也就是 haystack。如果 subject 是一个数组,替换操作将遍历整个 subject,返回值也将是一个数组。
    • count:如果被指定,它的值将被设置为替换发生的次数。
  5. demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
## 1.简单替换并计数
$str = 'AaaaaBbCc';
$search = 'a';
$replace = 'A';
$str = str_replace($search, $replace, $str, $count);
echo $str . '--' .$count; //AAAAABbCc--4

## 2.多个替换
$search = array("a", "e", "i", "o", "u", "A", "E", "I", "O", "U");
$res = str_replace($search, "", "Hello World of PHP");
echo $res; //Hll Wrld f PHP

## 3.多个对应替换
$str = "You should eat aa, bb, and cc every day.";
$search = array("aa", "bb", "cc", "dd");
$replace = array("AA", "BB", "CC");
$str = str_replace($search, $replace, $str);
echo $str;// You should eat AA, BB, and CC every day.

八、函数的参数

  1. 通过参数列表可以传递信息到函数,即以逗号作为分隔符的表达式列表。参数是从左向右求值的。PHP支持按值传递参数(默认),通过引用传递参数以及默认参数,也支持可变长度参数列表。
  2. 不声明参数类型
  3. 强制声明参数类型
参数类型 说明 PHP最小版本支持
class/interface name The parameter must be an instanceof the given class or interface name. PHP 5.0.0
self The parameter must be an instanceof the same class as the one the method is defined on. This can only be used on class and instance methods. PHP 5.0.0
array The parameter must be an array. PHP 5.1.0
callable The parameter must be a valid callable. PHP 5.4.0
bool The parameter must be a boolean value. PHP 7.0.0
float The parameter must be a floating point number. PHP 7.0.0
int The parameter must be an integer. PHP 7.0.0
string The parameter must be a string. PHP 7.0.0
  1. 延伸
    • 可变参数...
1
2
3
4
5
6
7
8
9
10
11
12
13
function add(...$numbers) {  
$sum = 0;
foreach ($numbers as $n) {
$sum += $n;
}
return $sum;
}

echo add(1, 2, 3),PHP_EOL;
echo add(1, 2, 3, 4),PHP_EOL;
echo add(1, 2, 3, 4, 5),PHP_EOL;

exit;
  • func_num_args/func_get_arg/func_get_args
1
2
3
4
5
6
7
8
9
10
11
function test() {
$num = func_num_args();
echo "num of test args:" . $num,PHP_EOL;
$args = func_get_args();
for ($i = 0; $i < $num; $i++) {
echo "The $i arg of func_get_args is:" . $args[$i],PHP_EOL;
echo "The $i arg of func_get_arg is:" . func_get_arg($i), PHP_EOL;
}
}

test(1,2,3);
  1. 参考