PHP7新特性总结

查用PHP命令

1
2
3
4
php -d memory-limit=-1 #  Define INI entry 不限制内存运行PHP 
php -m # 显示编译到内核下的模块
php --ini # 显示配置文件名
php --info # 查看类/函数/扩展信息,和web服务器上php 使用phpinfo()一样

self与static区别

  • static:如果在子类中重写了父类中的 static 方法、属性,父类就会去访问了子类的 static 方法
  • self: 是类内指针,不管子类有没有重写过父类中的方法、属性都指向本类的静态方法、属性

参考文章:https://www.php.cn/php-weizijiaocheng-392754.html

太空船运算符

太空船操作符用于比较两个表达式。当$a小于、等于或大于$b时它分别返回-1、0或1。 比较的原则是沿用 PHP 的常规比较规则进行的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
// 整数
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1

// 浮点数
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1

// 字符串
echo "a" <=> "a"; // 0
echo "a" <=> "b"; // -1
echo "b" <=> "a"; // 1
?>

箭头函数

1
2
3
4
5
6
7
8
9
$posts = [/* … */];

// 之前的写法
$ids = array_map(function ($post) {
return $post->id;
}, $posts);

// 箭头函数的写法
$ids = array_map(fn($post) => $post->id, $posts);

匿名类

现在支持通过new class 来实例化一个匿名类,这可以用来替代一些“用后即焚”的完整类定义。

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
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;
$app->setLogger(new class implements Logger {
public function log(string $msg) {
echo $msg;
}
});

var_dump($app->getLogger());

// 输出
// object(class@anonymous)#2 (0) {
// }
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

// PHP 7 之前的代码
class Logger
{
public function log($msg)
{
echo $msg;
}
}

$util->setLogger(new Logger());

// 使用了 PHP 7+ 后的代码
$util->setLogger(new class {
public function log($msg)
{
echo $msg;
}
});

PHP CSPRNG 伪随机数生成器

CSPRNG(Cryptographically Secure Pseudo-Random Number Generator,伪随机数产生器)。 PHP 7 通过引入几个 CSPRNG 函数提供一种简单的机制来生成密码学上强壮的随机数。

1
2
random_bytes() // 加密生存被保护的伪随机字符串。
random_int() // 加密生存被保护的伪随机整数。

可变参数

PHP5.5 及更早版本

使用以下函数:

  • func_num_args 返回参数的总数量
  • func_get_arg 返回参数列表的某一项
  • func_get_args 返回一个包含函数参数列表的数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function test()
{
echo '参数总数;', func_num_args(), "\n";
echo '第一个参数:', func_get_arg(0), "\n";
echo '全部参数;';
print_r(func_get_args());
}

test(1, 2, 3, 4);

/*
参数总数;4
第一个参数:1
全部参数;Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
*/

PHP5.6 及以上版本

使用 … 语法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function test(...$params)
{
print_r($params);
}

test(1, 2, 3, 4);

/*
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
*/

sprintf

1
2
3
4
5
6
<?php
$number = 2;
$str = "Shanghai";
$txt = sprintf("There are %u million cars in %s.",$number,$str);
echo $txt; // There are 2 million cars in Shanghai.
?>

参考文章:https://www.php.cn/php-weizijiaocheng-456254.html

迭代器yield

迭代器接口:https://www.php.net/manual/zh/class.iterator.php

yield生成器是php5.5之后出现的,官方文档这样解释:yield提供了一种更容易的方法来实现简单的迭代对象,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。

生成器的核心是一个yield关键字,一个生成器函数看起来像一个普通的函数,不同的是:普通函数返回一个值,而一个生成器可以yield生成许多它所需要的值。生成器函数被调用时,返回的是一个可以被遍历的对象。

yield和return有点类似,不过不同的是,return会返回值并且终止代码的执行,而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 对某一数组进行加权处理
$numbers = array(‘nike‘ => 200, ‘jordan‘ => 500, ‘adiads‘ => 800);

//通常方法,如果是百万级别的访问量,这种方法会占用极大内存
function rand_weight($numbers)
{
$total = 0;
foreach ($numbers as $number => $weight) {
$total += $weight;
$distribution[$number] = $total;
}
$rand = mt_rand(0, $total-1);

foreach ($distribution as $num => $weight) {
if ($rand < $weight) return $num;
}
}

//改用yield生成器
function mt_rand_weight($numbers) {
$total = 0;
foreach ($numbers as $number => $weight) {
$total += $weight;
yield $number => $total;
}
}

function mt_rand_generator($numbers)
{
$total = array_sum($numbers);
$rand = mt_rand(0, $total -1);
foreach (mt_rand_weight($numbers) as $num => $weight) {
if ($rand < $weight) return $num;
}
}

反射

PHP的反射机制提供了一套反射API,用来访问和使用类、方法、属性、参数和注释等,比如可以通过一个对象知道这个对象所属的类,这个类包含哪些方法,这些方法需要传入什么参数,每个参数是什么类型等等,不用创建类的实例也可以访问类的成员和方法,就算类成员定义为 private 也可以在外部访问。 官方文档提供了诸如 ReflectionClassReflectionMethodReflectionObjectReflectionExtension 等反射类及相应的API,用得最多的是 ReflectionClass。 为了演示反射效果,首先创建一个类(假设定义了一个类 User),并实例化。基于这个实例,反射类可以访问 User 中的属性和方法。

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
<?php
/**
* 用户相关类
*/
class User {
public $username;
private $password;

public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}

/**
* 获取用户名
* @return string
*/
public function getUsername()
{
return $this->username;
}

/**
* 设置用户名
* @param string $username
*/
public function setUsername($username)
{
$this->username = $username;
}

/**
* 获取密码
* @return string
*/
private function getPassword()
{
return $this->password;
}

/**
* 设置密码
* @param string $password
*/
private function setPassowrd($password)
{
$this->password = $password;
}
}

创建反射类实例

1
2
3
$refClass = new ReflectionClass(new User('liulu', '123456'));
// 也可以写成
$refClass = new ReflectionClass('User'); // 将类名User作为参数,建立User类的反射类

反射属性

1
2
3
4
5
6
7
8
9
$properties = $refClass->getProperties(); // 获取User类的所有属性,返回ReflectionProperty的数组
$property = $refClass->getProperty('password'); // 获取User类的password属性
//$properties 结果如下:
Array (
[0] => ReflectionProperty Object ( [name] => username [class] => User )
[1] => ReflectionProperty Object ( [name] => password [class] => User )
)
//$property 结果如下:
ReflectionProperty Object ( [name] => password [class] => User )

反射方法

1
2
3
4
5
6
7
8
9
10
11
12
$methods = $refClass->getMethods(); // 获取User类的所有方法,返回ReflectionMethod数组
$method = $refClass->getMethod('getUsername'); // 获取User类的getUsername方法
//$methods 结果如下:
Array (
[0] => ReflectionMethod Object ( [name] => __construct [class] => User )
[1] => ReflectionMethod Object ( [name] => getUsername [class] => User )
[2] => ReflectionMethod Object ( [name] => setUsername [class] => User )
[3] => ReflectionMethod Object ( [name] => getPassword [class] => User )
[4] => ReflectionMethod Object ( [name] => setPassowrd [class] => User )
)
//$method 结果如下:
ReflectionMethod Object ( [name] => getUsername [class] => User )

反射注释

1
2
3
4
5
6
$classComment = $refClass->getDocComment();  // 获取User类的注释文档,即定义在类之前的注释
$methodComment = $refClass->getMethod('setPassowrd')->getDocComment(); // 获取User类中setPassowrd方法的注释
//$classComment 结果如下:
/** * 用户相关类 */
//$methodComment 结果如下:
/** * 设置密码 * @param string $password */

反射实例化

1
2
3
4
5
6
7
8
$instance = $refClass->newInstance('admin', 123, '***');  // 从指定的参数创建一个新的类实例
//$instance 结果如下:
User Object ( [username] => admin [password:User:private] => 123 )
注:虽然构造函数中是两个参数,但是newInstance方法接受可变数目的参数,用于传递到类的构造函数。
$params = ['xiaoming', 'asdfg'];
$instance = $refClass->newInstanceArgs($params); // 从给出的参数创建一个新的类实例
//$instance 结果如下:
User Object ( [username] => xiaoming [password:User:private] => asdfg )

访问、执行类的公有方法——public

1
2
3
4
5
6
7
8
9
10
11
$instance->setUsername('admin_1'); // 调用User类的实例调用setUsername方法设置用户名
$username = $instance->getUsername(); // 用过User类的实例调用getUsername方法获取用户名
echo $username . "\n"; // 输出 admin_1
// 也可以写成
$refClass->getProperty('username')->setValue($instance, 'admin_2'); // 通过反射类ReflectionProperty设置指定实例的username属性值
$username = $refClass->getProperty('username')->getValue($instance); // 通过反射类ReflectionProperty获取username的属性值
echo $username . "\n"; // 输出 admin_2
// 还可以写成
$refClass->getMethod('setUsername')->invoke($instance, 'admin_3'); // 通过反射类ReflectionMethod调用指定实例的方法,并且传送参数
$value = $refClass->getMethod('getUsername')->invoke($instance); // 通过反射类ReflectionMethod调用指定实例的方法
echo $value . "\n"; // 输出 admin_3

访问、执行类的非公有方法——private、protected

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
try {
// 正确写法
$property = $refClass->getProperty('password'); // ReflectionProperty Object ( [name] => password [class] => User )
$property->setAccessible(true); // 修改 $property 对象的可访问性
$property->setValue($instance, '987654321'); // 可以执行
$value = $property->getValue($instance); // 可以执行
echo $value . "\n"; // 输出 987654321
// 错误写法
$refClass->getProperty('password')->setAccessible(true); // 临时修改ReflectionProperty对象的可访问性
$refClass->getProperty('password')->setValue($instance, 'password'); // 不能执行,抛出不能访问异常
$refClass = $refClass->getProperty('password')->getValue($instance); // 不能执行,抛出不能访问异常
$refClass = $instance->password; // 不能执行,类本身的属性没有被修改,仍然是private
} catch (Exception $e){
echo $e;
}
// 错误写法 结果如下:
ReflectionException: Cannot access non-public member User::password in xxx.php

小结

  • 不管反射类中定义的属性、方法是否为 public,都可以获取到。
  • 直接访问 protected 或则 private 的属性、方法,会抛出异常。
  • 访问非公有成员需要调用指定的 ReflectionPropertyReflectionMethod 对象 setAccessible(true)方法。

??与?:区别

1
2
3
4
$a=$c??$b;
等同于 $a=isset($c)?$c:$b;
$a=$c?:$b;
等同于 $a=$c?$c:$b;

clone

1
2
3
4
5
6
7
8
9
10
11
12
13
(clone $tableQuery)
->select('date')
->addSelect(Db::raw('sum(coin_amount) as coin_amount'))
->addSelect(Db::raw('sum(amount) as amount'))
->where('status', 8)
->groupBy('date')
->orderByDesc('date')
->get()
->keyBy('date')
->map(function ($item) use (&$info) {
$info[$item->date]['coin_amount'] = $item->coin_amount;
$info[$item->date]['amount'] = $item->amount;
});

魔法方法

1
2
3
4
5
6
7
8
9
10
11
__call() 对象调用某个方法,若方法存在,则直接调用;若不存在,则会去调用__call函数。(此方法工作中很常见,很常用)
__get() 读取一个对象的属性时,若属性存在,则直接返回属性值;若不存在,则会调用__get函数。
__set() 设置一个对象的属性时,若属性存在,则直接赋值;若不存在,则会调用__set函数。
__toString() 打印一个对象的时被调用。如echo $obj;或print $ob
__clone() 克隆对象时被调用。如:$t=new Test();$t1=clone $t;
__sleep() serialize(序列化)之前被调用。若对象比较大,想删减一点东东再序列化,可考虑一下此函数。
__wakeup() unserialize(反序列化)时被调用,做些对象的初始化工作。
__isset() 检测对象一个不存在的属性时被调用。如:isset($c->name)。
__unset() 删除对象一个不存在的属性时被触发。如:unset($c->name)。
__set_state() 调用var_export时,被调用。用__set_state的返回值做为var_export的返回值。
__autoload() 实例化一个对象时,如果对应的类不存在,则该方法被调用。(此方法,工作中很常用,尤其自己封一个框架用的时候,避免需要引入很多类文件,可以使用此方法)

高级方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
set_error_handler(error_function,error_types); // 设置用户自定义的错误处理函数。
set_exception_handler(exception_function); // 用户自定义的异常处理函数,只存在一个回调函数
register_shutdown_function('_shutdown_handler'); // 注册一个会在php中止时执行的函数,注册一个 callback ,它会在脚本执行完成或者 exit() 后被调用,可以多次调用 register_shutdown_function() ,这些被注册的回调会按照他们注册时的顺序被依次调用。 如果你在注册的方法内部调用 exit(), 那么所有处理会被中止,并且其他注册的中止回调也不会再被调用。
debug_backtrace(); // 生成 backtrace
ucfirst(string $str): string // 将字符串的首字母转换为大写
extension_loaded(string $name): bool // 检查一个扩展是否已经加载
ini_set(string $varname , string $newvalue):string //设置指定配置选项的值。这个选项会在脚本运行时保持新的值,并在脚本结束时恢复.不是所有有效的选项都能够用 ini_set() 来改变的
number_format(float $number , int $decimals = 0): string // 以千位分隔符方式格式化一个数字
version_compare() // 对比两个「PHP 规范化」的版本数字字符串
str_pad(string $input, int $pad_length, string $pad_string = " ", int $pad_type = STR_PAD_RIGHT):string // 使用另一个字符串填充字符串为指定长度
memory_get_usage() // 返回分配给 PHP 的内存量
getrusage(int $who = 0): array // 取当前资源使用状况
is_callable(); // 检测参数是否为合法的可调用结构
instanceof()
glob(string $pattern, int $flags = 0):array // 寻找与模式匹配的文件路径
ord(); // 转换字符串第一个字节为 0-255 之间的值,ASCI码
pack(format,args+); // 把数据装入一个二进制字符串
unpack(format,data); // 从二进制字符串对数据进行解包

# 常量
PHP_VERSION
DIRECTORY_SEPARATOR

PHP单引号和双引号的区别是什么?

  • 双引号可以解析变量,单引号不能解析变量
  • 双引号和单引号可以互相嵌套
  • 双引号当中的变量可以使用特殊字符分隔开,但是特殊 字符会原样输出,使用{}不会输出
  • 双引号当中包含单引号,单引号当中包含变量,变量会被解析,单引号会被原样输出
  • 双引号可以解析转义字符,单引号不会解析转义字符,单引号只会解析\本身和单引号本身的转义
  • 单引号当中嵌套单引号,双引号当中嵌套双引号,当中的单引号和双引号需要使用\转义符合
  • 单引号效率要高于双引号

password_hash

password_hash() 函数用于创建密码的散列(hash) PHP 版本要求: PHP 5 >= 5.5.0, PHP 7

语法

1
string password_hash(string $password, int $algo [, array $options])

参考文章:https://www.php.cn/php-weizijiaocheng-451018.html

引用传递

  • php中,数组是当一个普通变量,值传递是要一个实参的一个拷贝副本,跟实参无关,引用传递后可以改变实参的值
  • 类的对象是无论值传递和引用传递都是引用传递,是对对象的引用,都会改变实参的值

set_exception_handler

set_exception_handler() 函数设置用户自定义的异常处理函数。

该函数用于创建运行期间的用户自己的异常处理方法。

该函数返回旧的异常处理程序,如果失败则返回 NULL。

语法

1
set_exception_handler(exception_function)
参数 描述
exception_function 必需。规定未捕获的异常发生时调用的函数。该函数必须在调用 set_exception_handler() 函数之前定义。这个异常处理函数需要需要一个参数,即抛出的 exception 对象。

提示和注释 提示:在这个异常处理程序被调用后,脚本会停止执行。

curl_multi

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
<?php

$srart_time = microtime(TRUE);

$chArr=[];

//创建多个cURL资源
for($i=0; $i<10; $i++){
$chArr[$i]=curl_init();
curl_setopt($chArr[$i], CURLOPT_URL, "http://www.baidu.com/");
curl_setopt($chArr[$i], CURLOPT_RETURNTRANSFER, 1);
curl_setopt($chArr[$i], CURLOPT_TIMEOUT, 1);
}

$mh = curl_multi_init(); //1 创建批处理cURL句柄

foreach($chArr as $k => $ch){
curl_multi_add_handle($mh, $ch); //2 增加句柄
}

// active表示一个用来判断操作是否仍在执行的标识的引用,只有全部url数据接受完毕才变成false
$active = null;


$active = null;

do{
$mrc = curl_multi_exec($mh, $active); //3 执行批处理句柄
}while ($mrc == CURLM_CALL_MULTI_PERFORM); //4


//$active 为true,即$mh批处理之中还有$ch句柄正待处理,$mrc==CURLM_OK,即上一次$ch句柄的读取或写入已经执行完毕。
while ($active && $mrc == CURLM_OK) {
if (curl_multi_select($mh) != -1) {//$mh批处理中还有可执行的$ch句柄,curl_multi_select($mh) != -1程序退出阻塞状态。
do {
$mrc = curl_multi_exec($mh, $active);//继续执行需要处理的$ch句柄。
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}

foreach($chArr as $k => $ch){
$result[$k]= curl_multi_getcontent($ch); //5 获取句柄的返回值
curl_multi_remove_handle($mh, $ch);//6 将$mh中的句柄移除
}

curl_multi_close($mh); //7 关闭全部句柄

// print_r($result);

$end_time = microtime(TRUE);
echo sprintf("use time:%.3f s", $end_time - $srart_time), PHP_EOL;

SPL

SplFixedArray

Posix

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
# posix: 可移植操作信息接口(portable Operating System Interface of Unix)

require '../common.php';

//获取当前的进程id
$curr_pid = posix_getpid();
print_log("当前进程PID: $curr_pid");

// 当前运行进程的用户
$p_uname = posix_getlogin();
print_log("当前进程运行的用户:$p_uname");

// 获取当前运行程序的用户信息
$userinfo = posix_getpwnam($p_uname);
print_log(json_encode($userinfo));

// 当前运行的目录
$cwd = posix_getcwd();
print_log("当前程序运行目录:$cwd");

// 获取当前系统信息
print_log(json_encode(posix_uname()));

# 杀死自己
posix_kill($curr_pid, SIGKILL);

# 前面杀死了自己,所以下面的程序不会窒息
print_log('当前进程已经杀死');

// 2021-04-11 22:08:58 当前进程PID: 1487
// 2021-04-11 22:08:58 当前进程运行的用户:zhoufei
// 2021-04-11 22:08:58 {"name":"zhoufei","passwd":"********","uid":501,"gid":20,"gecos":"\u5468\u98de","dir":"\/Users\/zhoufei","shell":"\/bin\/zsh"}
// 2021-04-11 22:08:58 当前程序运行目录:/Users/zhoufei/Documents/practise/php/pcntl
// 2021-04-11 22:08:58 {"sysname":"Darwin","nodename":"zhoufeideMacBook-Pro.local","release":"20.3.0","version":"Darwin Kernel Version 20.3.0: Thu Jan 21 00:07:06 PST 2021; root:xnu-7195.81.3~1\/RELEASE_X86_64","machine":"x86_64"}
// zsh: killed php posix.test.php
有用就打赏一下作者吧!