PHP5.5到PHP7的新特性
作者:悟尘
一、PHP5.5
官方手册:http://php.net/manual/zh/migration55.new-features.php
PHP 5.5.x 中废弃的特性
废弃 ext/mysql
原始的 MySQL 扩展 现在被废弃,当连接到数据库时会产生一个 E_DEPRECATED 错误。可使用 MySQLi 或PDO_MySQL 扩展作为替代。
preg_replace() 中的 /e 修饰符 eval
preg_replace() 函数中用到的 /e 修饰符现在被弃用。可以使用 preg_replace_callback() 函数来替代。
但是如果你非要用,它还可以支持的,最高支持以php5.6.x,不过会报Deprecated,PHP7中就不可以使用了.
PHP 5.5.x 中新特性:
1、生成器[yield]
关键点 : [yield]它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并终止函数的执行,而yield会返回一个值给调用此生成器的代码,并且只是执行生成器函数 循环
$html = '
hello world
';$html = preg_replace(
'(
'"
$html = '
hello world
';$html = preg_replace_callback('(
echo '
';function fun(){
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
// 我们可以使用echo来进行输出
//echo 'ok';
}
$ret = fun();
foreach($ret as $item){
echo $item.'
';}
// 传统的循环数据中的数据
$arr = range(1,10);
foreach(range(1,10) as $val){
echo $val.'
';}
// 运用生成器来生成1到10的记录数
function xrange(){
for($i = 1; $ <= 10; $i++){
yield $i;
}
}
// 循环读取出生成器中的内容
foreach(xrange() as $item){echo $item.'
';}// 数据库操作
$dsn = 'mysql:host=localhost;dbname=a67_web64;charset=utf8';
$user = 'root';
$pwd = 'root';
try{
$pdo = new PDO($dsn, $user, $pwd);
}catch (PDOException $e){
throw new Exception($e->getMessage());
}
$sql = "select * from a67_addon_dianying";
echo '
';foreach(findAll($sql) as $item){
extract($item);
echo $aid.'----'.$daoyan.'
';}
2、finally关键字的使用
不管抛出异常是否被抛出,程序代码块都会被执行
注: finally区块中一般执行一些如关闭资源和连接等操作,此代码块不管程序是否有异常都必须要执行。
3、foreach 现在支持 list()
foreach 控制结构现在支持通过 list() 构造将嵌套数组分离到单独的变量
function findAll($sql){
global $pdo;
$stmt = $pdo->prepare($sql);
$stmt->execute();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
yield $row;
}
}
try {
// 抛出异常消息
throw new Exception('打开文件失败');
} catch (Exception $e) {
// 处理异常错误
echo '
';var_dump($e);
// 模拟可以处理异常的时候出现了别的异常情况的情况
throw new Exception('处理异常失败');
}finally{
// 无论如何都要执行此代码块中的内容
echo '
文件我关闭了';}
echo '
看我执行了没有了';
// 定义一个数组
$arr = [
[1,2],
[3,4],
[5,6],
];
// 需求,把上面的数组打印出来
// 以前的写法
foreach($arr as $val){
echo $val[0].' -- '.$val[1].'
';}
// php5.5以后的写法 foreach中支持 list写法 支持索引数组
foreach($arr as list($a,$b)){
echo $a.' -- '.$b.'
';}
#注: 关联数组暂不支持
4、empty() 支持任意表达式
empty() 现在支持传入一个任意表达式,而不仅是一个变量,对以前的功能的升级
echo '
';// 定义一个函数
function fun(){
return true;
}
// 空字符串
var_dump(empty('')); # true
// 支持表达式
var_dump(empty(1 + 2)); # false
var_dump(empty(1 + 1 > 3)); # true
// 支持调用函数
var_dump(empty(fun())); # false
5、新的密码加密函数-password_hash()
简单的md5加密很容易通过字典的方式进行破解,随便找个md5解密的网站就能获取原始密码,所以PHP官方推出了一个新的加密的函数,password_hash。
password_hash加密
优点:就是更加的安全可靠,每次运行加密的结果都不一致。
缺点:互操作性差,如和其它语言进行交互验证加密起来就不行了,只能通过password_verify来验证。
string password_hash ( string $password , integer $algo [, array $options ] )
当前支持的算法:
PASSWORD_DEFAULT - 使用 bcrypt 算法 (PHP 5.5.0 默认)。 注意,该常量会随着 PHP 加入更新更高强度的算法而改变。 所以,使用此常量生成结果的长度将在未来有变化。 因此,数据库里储存结果的列可超过60个字符(最好是255个字符)。
PASSWORD_BCRYPT - 使用 CRYPT_BLOWFISH 算法创建哈希。 这会产生兼容使用 " 2y" 的 crypt()。结果将会是 60 个字符的字符串, 或者在失败时返回 FALSE。
①、加密 password_hash
// 明文密码
$pwd = 'admin888';
// 加密密码
echo password_hash($pwd,PASSWORD_DEFAULT);
②、验证 password_verify
password_verify() 验证密码是否和哈希匹配
boolean password_verify ( string $password , string $hash )
$password 明文的密码
$hash 加密的密码
// 明文密码
$pwd = 'admin888';
// 加密密码
$pass = password_hash($pwd,PASSWORD_DEFAULT);
echo '加密后的密码:'.$pass.'
';// 验证密码是否正确
// 不正确
$plainPwd = '111';
if (password_verify($plainPwd,$pass)) {
echo '密码一致
';}else{
echo '密码不一致
';}
// 正确
if (password_verify($pwd,$pass)) {
echo '密码一致
';}else{
echo '密码不一致
';}
二、PHP5.6
官方手册:http://php.net/manual/zh/migration56.new-features.php
PHP 5.6.x 中已废弃的特性
$HTTP_RAW_POST_DATA 和 always_populate_raw_post_data
使用 always_populate_raw_post_data 会导致在填充 HTTP_RAW_POST_DATA 时产生 E_DEPRECATED 错误。
请使用 php://input 替代 HTTP_RAW_POST_DATA, 因为它可能在后续的 PHP 版本中被移除。
设置 always_populate_raw_post_data 为 -1 (这样会强制 $HTTP_RAW_POST_DATA 未定义,所以也不会导致E_DEPRECATED 的错误) 来体验新的行为。
1、使用表达式定义常量 const
http://blog.csdn.net/cscrazybing/article/details/46989749
// 1、const不能放在循环或条件语句中,只放在命名空间或头档的顶级中,则可以
// 2、在类中定义常量,只能用const
// 3、define在php7之前不能定义数组而const则可以
// 4、const在php5.6之前是不可定义表达式常量,define则可以
const ONE = 1;
const TWO = ONE * 2;
class C {
const THREE = TWO + 1;
const ONE_THIRD = ONE / self::THREE;
const SENTENCE = 'The value of THREE is '.self::THREE;
public function f($a = ONE + self::THREE) {
return $a;
}
}
echo (new C)->f()."
";echo C::SENTENCE;
2、使用 ... 运算符定义变长参数函数和参数展开
echo '
';// 把第三个以一的参数全部在函数中接收
function test($a,$b,...$c){
// 前两个参数给了$a和$b
echo $a.'---'.$b.'
';// 后面的全部以一维数组的形式给了$c
print_r($c);
}
test(1,2,3,4,5,6,7);
echo '
';// 使用 ... 运算符进行参数展开
$arr = [1,2,3,4,5];
test(...$arr);
3、导入命令空间中的常量和函数
对use运算符进行了扩展以支持在类中导入外部的函数和常量。对应的结构为 use function 和 use const
// 申明一个A命名空间
namespace A {
const AGE = 20;
function fun(){
echo "
你好!";}
}
// 申明一个B命名空间
namespace B {
// 调用 命名空间A中的常量和函数 使用use
use const A\AGE;
use function A\fun;
echo '
';var_dump(AGE);
fun();
}
三、PHP7.0
官网手册:http://php.net/manual/zh/migration70.new-features.php
解析顺序的变化
表达式 PHP 5 的解析方式 PHP 7 的解析方式
$$foo['bar']['baz'] ${$foo['bar']['baz']} ($$foo)['bar']['baz']
$foo->$bar['baz'] $foo->{$bar['baz']} ($foo->$bar)['baz']
$foo->$bar['baz']() $foo->{$bar['baz']}() ($foo->$bar)['baz']()
Foo::$bar['baz']() Foo::{$bar['baz']}() (Foo::$bar)['baz']()
所有 ext/mysql 函数已被删掉了
移除掉的 INI 配置指令
always_populate_raw_post_data 在php.ini文件中已经移除
always_populate_raw_post_data 只能用file_get_content('php://input')
要想在php5.6之前生效,还需要打开工php.ini中的配置文件 asp_tags = Off修改为 asp_tags = On 然后重启apache,此选项在php7之后被移除
asp_tags <%=$name;%>
1、标量类型声明[严格类型]
标量的类型的申明有两种模式:强制(默认)和严格模式。
字符串 string
布尔值 bool
整数 int
浮点 float
类[class]、接口[interface]、回调[callable]
①、强制模式[默认]
function test(array $arr){
echo '
';var_dump($arr);
}
#test(1); // 报错
test([1,2,3]);
②、严格模式
默认情况下,如果能做到的话,PHP将会强迫错误类型的值转为函数期望的标量类型。 例如,一个函数的一个参数期望是string,但传入的是integer,最终函数得到的将会是一个string类型的值。
可以基于每一个文件开启严格模式。在严格模式中,只有一个与类型声明完全相符的变量才会被接受,否则将会抛出一个TypeError。 唯一的一个例外是可以将integer传给一个期望float的函数。
开启严格模式
使用 declare 语句和strict_types 声明来启用严格模式,声明指令必须放在文件的顶部,意味着严格声明标量是基于文件可配的, 这个指令不仅影响参数的类型声明,也影响到函数的返回值声明。
# 开启严格模式
declare(strict_types=1);
function test2(string $username,int $age){
echo '名称:'.$username;
echo '
';echo '年龄:'.$age;
}
#test(1,20); # 报错
test2('小明',20);
2、返回值类型声明
返回类型声明指明了函数返回值的类型,类型有class,interface,array,int,callable,bool,float,int,string
# 开启严格模式
declare(strict_types=1);
// 返回值类型申明
function test2(int $age) : bool{
if ($age >20) {
return true;
}else{
return false;
}
}
if (test2(19)) {
echo '年龄有点的小';
}else{
echo '成年了';
}
# 使用它的时候,一定要有memcache扩展
function sum2(int $a, int $b): Memcache {
return new Memcache();
}
开启类型限定,可以极大的提升PHP在编译阶段对程序的执行速度的优化提升。
3、null合并运算符[重要]
简化了之前的三元表达式的写法
// php7.0 之前的写法
$username = $_GET['username'] ? $_GET['username'] : '没有名称';
echo $username;
echo '
';// php7.0之后
$uname = $_GET['uname'] ?? '没有姓名';
echo $uname;
echo '
';// 嵌套来运用
$age = $_GET['age'] ?? $_POST['age'] ?? '没有值';
echo $age;
4、太空船操作符(组合比较符)
太空船操作符用于比较两个表达式。当 $a 小于、等于或大于$b时它分别返回-1、0或1
// 比较两个变量的大小,如果a变量大于b变量,返回1
// 如果a变量等于b变量,返回0
// 如果a变量小于b变量,返回-1
function compare($a,$b){
if ($a > $b) {
return 1;
}elseif ($a == $b) {
return 0;
}elseif ($a < $b) {
return -1;
}
}
$a = 1;
$b = 2;
var_dump(compare($a,$b));
echo '
';$a = 2;
$b = 2;
var_dump(compare($a,$b));
echo '
';$a = 2;
$b = 1;
var_dump(compare($a,$b));
echo '
';// php7.0之后来完成这样的需求
$a = 1;
$b = 2;
var_dump($a <=> $b); # -1
echo '
';$a = 2;
$b = 2;
var_dump($a <=> $b); # 0
echo '
';$a = 2;
$b = 1;
var_dump($a <=> $b); # 1
echo '
';注:一定使用的场景多在一些算法场景中,如我们的排序,像【usort】可带回定义回调规则排序中,就可以运用。
$a = array(3, 2, 5, 6, 1);
usort($a,function ($v1,$v2){
return $v1 <=> $v2;
});
echo '
';print_r($a);
5、通过 define() 定义常量数组
# 定义一个常量的数组
define('ADMINS',[
'admin',
'zhangsan',
'lisi',
[1,2,3]
]);
echo '
';print_r(ADMINS);
echo ADMINS[1];
6、匿名类
现在支持通过new class 来实例化一个匿名类,这可以用来替代一些“用后即焚”的完整类定义。
// 匿名类
$obj = new class {
public $username = '小明
';public function __construct(){
echo '这是一个匿名的类
';}
};
echo $obj->username;
# 日志接口
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) {
return $msg;
}
});
var_dump($app->getLogger());
echo '
';var_dump($app->getLogger()->log('111'));
7、Unicode 转译语法
每个汉字都有对应的unicode编码
PHP是世界上最好的语言
\u50\u48\u50\u662f\u4e16\u754c\u4e0a\u6700\u597d\u7684\u8bed\u8a00
像这样的编码在php7.0之后可以直接转化为汉字,人可识别的,php7.0之前的PHP的转化可以参考此地址来使用:http://blog.csdn.net/friendan/article/details/52900529
/**
* PHP是世界上最好的语言
* \u50\u48\u50\u662f\u4e16\u754c\u4e0a\u6700\u597d\u7684\u8bed\u8a00
*/
$unicodeStr =
"\u{50}\u{48}\u{50}\u{662f}\u{4e16}\u{754c}\u{4e0a}\u{6700}\u{597d}\u{7684}\u{8bed}\u{8a00}";
echo $unicodeStr;
注:定义的字符必须用双引号且还必须要用{}括起来不带\u的部份,应用的场景如emoji图标:
https://unicode-table.com/cn/sets/hearts-symbols/
8、Closure::call()
Closure::call() 现在有着更好的性能,简短干练的暂时绑定一个方法到对象上闭包并调用它。
# 匿名函数
$fun2 = function(){
$this->say();
};
class Test {
private function say(){
echo 'Hi.
';}
}
$fun2->call(new Test);
9、预期 assert
预期是向后兼用并增强之前的 assert() 的方法。 它使得在生产环境中启用断言为零成本,并且提供当断言失败时抛出特定异常的能力。它允许第一个参数是一个表达式,在调试测试中用。
// assert(true|false)
// 如果为true 得到了预期的结果,后面的结果不能执行
// 如果为false 没有得到预期的结果,执行后面
//assert(true,'1111');
assert(1+1>3 , new Exception('预期失败,异常抛出,条件不成立。'));
10、批量导入命名空间
从同一 namespace 导入的类、函数和常量现在可以通过单个 use 语句 一次性导入。
namespace A {
class C1{}
class C2{}
function fun1(){echo 'fun1';}
function fun2(){echo 'fun2';}
const AGE1 = 20;
const AGE2 = 21;
}
// PHP 7 之前的代码
namespace B {
use A\C1;
use A\C2;
use function A\fun1;
use function A\fun2;
use const A\AGE1;
use const A\AGE2;
}
// PHP 7+ 及更高版本的代码
namespace C {
use A\{C1,C2};
use function A\{fun1,fun2};
use const A\{AGE1,AGE2};
var_dump(new C1());
echo '
';var_dump(new C2());
echo '
';fun1();
echo '
';fun2();
echo '
';echo AGE1;
echo '
';echo AGE2;
echo '
';}
11、生成器[yield]可以返回表达式
此特性基于 PHP 5.5 版本中引入的生成器特性构建的。 可以通过调用 Generator::getReturn() 方法来获取生成器的返回值, 但是这个方法只能在生成器完成产生工作以后调用一次。
function test(){
yield '1
';yield '2
';yield '3
';return '我是返回值:4
';}
$gen = test();
#echo $gen->getReturn(); # 报错,必须是生成循环完毕后才能执行它
foreach ($gen as $val) {
echo $val;
}
echo $gen->getReturn();
## 类JS的写法
$gen2 = (function() {
yield '张三';
yield '李四';
return '返回值';
})();
foreach ($gen2 as $val) {
echo $val, '
';}
echo $gen2->getReturn(), '
';四、PHP7.x
官网手册:http://php.net/manual/zh/migration71.new-features.php
1、可为空(Nullable)类型
类型现在允许为空,当启用这个特性时,传入的参数或者函数返回的结果要么是给定的类型,要么是 null 。可以通过在类型前面加上一个问号【?】来使之成为可为空的。
function test(?string $username,int $age){
echo "姓名:{$username} -- 年龄:{$age}
";}
test('小明',20);
test(null,22);
2、 Void函数
对于 void 函数来说,null 不是一个合法的返回值
# 可以不写return 或 return;就可以,返回null会报错
function test() : void {
return; //表示返回void
echo 111;
//return null; # 报错,因为return null不能替换return;
}
test();
3、array赋值变量取出
短数组语法([])现在可以用于将数组的值赋给一些变量(包括在foreach中)。 这种方式使从数组中提取值变得更为容易。
$arr = [1,2];
[$a,$b] = $arr;
echo $a.' -- '.$b;
echo '
';// 互换两个变量的值
$aa = 1;
$bb = 2;
[$aa,$bb] = [$bb,$aa];
echo $aa.' -- '.$bb;
echo '
';// 定义数组
$data = [
['id' => 1,'name' => 'tom'],
['id' => 2,'name' => 'jack']
];
// 取出数组中的元素给指定的变量
['id' => $id,'name' => $name] = ['id' => 1,'name' => 'tom'];
echo $id.' --- '.$name.'
';echo '
';// 循环中取出数组中的元素给指定的变量
foreach($data as ['id' => $id,'name' => $name]){
echo $id.' --- '.$name.'
';}
4、多异常捕获
一个catch语句块现在可以通过管道字符(|)来实现多个异常的捕获。 这对于需要同时处理来自不同类的不同异常时很有用。
class Exception1 extends Exception {
}
class Exception2 extends Exception {
}
class Exception3 extends Exception {
}
try {
throw new Exception('这里有异常了');
} catch (Exception1|Exception2|Exception3 $e) {
var_dump($e);
}
5、list()支持关联数组
现在list()支持在它内部去指定键名。这意味着它可以将任意类型的数组 都赋值给一些变量(与短数组语法类似)
$arr = ['username' => '张三', 'age' => 20];
list('username' => $username, 'age' => $age) = $arr;
echo $username . '----' . $age . '
';$data = [
['id' => 1, 'name' => 'Tom'],
['id' => 2, 'name' => 'jack'],
];
echo '
';// php7.1写法
foreach($data as list('id' => $id, 'name' => $name)){
echo $id . '----' . $name . '
';}