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. php5和php7

  3. tp3和tp5

  4. 抽象类和接口

    抽象类:一种特殊的,不能被实例化的类,只能作为其他类的父类使用,使用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:使用
    • 参考

二、资料

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