PHP8 已经不是你记忆中的那个样子了

以往的经验

PHP 是一种非常弱的动态类型化语言,这在以前是有其好处的,可以更快的开发,技术人员的的培训和上手也变的更简单。但自从人们开始在更大的项目中使用 PHP,其类型系统的缺点就变得很明显,无法与JAVA等语言相比。项目管理工作和代码质量都不能很好的完成。

PHP8

现在,PHP是一种奇特的语言:它仍然允许你写出完全动态的、弱类型化的代码,但也有一个更强大的、可选择的类型系统。结合静态分析、例如Psalm、Phan和PHPStan等工具,你可以写出安全、强类型化和静态分析的代码。

7.x + 的类型校验模式给 PHP 带来了新的可能,它即可以是以前快速开发的若类型语言,也可以是与其它强类型定义语言相媲美的语言。

PHP8 引入了一些很好的特性,也改进了内存效率和运行效率,但是因为老版本中存在的问题没有得到解决导致了其它开发语言的迅速超越。缺少在大型应用中的广泛使用,很难形成强大的社区力量,给语言发展赋能。

在中小型应用和个人应用中的占有率非常高,这给 PHP 的未来提供了可能性。是否能抓住中小型应用的未来和互联网的未来也是 PHP 的未来。

特点

JIT

PHP实现了一个虚拟机,一种虚拟处理器 Zend VM。PHP将人类可读的脚本编译成虚拟机能够理解的指令(操作码),这个执行阶段就是我们所说的“编译时”。在执行的“运行时”阶段,虚拟机(Zend VM)执行代码的指令(操作码)。

这一切工作得很好,像OPCache这样的工具可以缓存代码的指令(操作码),以便“编译时”只在必须的时候发生。

Just-in-time 是一种编译器策略,它接受代码的中间表示形式,并在运行时将其转换为依赖于体系结构的机器代码,以便及时执行。

在PHP中,这意味着JIT将为Zend VM生成的指令作为中间表示,并发出依赖于体系结构的机器代码,因此代码的宿主不再是ZendVM,而是CPU。

但是 JIT 在 I/O绑定 的代码中效果不明显,在 CPU绑定 的代码上才能发挥出优势,意味着能够更好的支持数学计算型的应用。

PHP 类型声明

PHP7 引入了两个合适类型:标量类型和返回值类型。

<?php
declare(strict_types=1);

function isBooleanFunc():bool
{
    return 'abcde';
}
echo isBooleanFunc();

运行这段代码,你将会得到一个致命的错误:

Fatal error: Uncaught TypeError: Return value of isBooleanFunc() must be of the type boolean, string returned!

未捕获的类型错误:isBooleanFunc()返回值必须是布尔类型,返回了字符串!

<?php
<br/>
// 严格模式
declare(strict_types=1);
<br/>
function sum(int ...$ints){
   return array_sum($ints);
}

print(sum(2, '3', 4.1));
?>

执行输出结果为:

PHP Fatal error: Uncaught TypeError: Argument 2 passedto sum() must be of the typeinteger, string given, calledin……

以上程序由于采用了严格模式,所以如果参数中出现不适整数的类型会报错。 strict_types =0 的话,运行程序会返回 9,4.1 会被先转换为整形后再相加。

返回值类型和标量声明类型:

  • array 数组
  • bool 布尔
  • callable 必须是有效的回调类型,不能用于类属性的类型声明。
  • int  整型数字
  • string 字符串
  • float 浮点型数字
  • void 无返回值,仅能用于返回值类型声明
  • object 必须是一个对象
  • resource 资源类型,保存了到外部资源的一个引用,不能用于类属性的类型声明
  • null 被复制为 null 或变量没有赋值,类属性的类型声明中使用 ? 符号代替
  • self 必须是所在方法的类的一个 instanceof。 只能在类的内部使用。
  • mixed 前面的所有类型,当我们期望的类型在 PHP 中无法被类型提示才使用

注意:代码中通过指定 strict_types的值(1或者0),1表示严格类型校验模式,作用于函数调用和返回语句;0表示弱类型校验模式。目前若类型校验模式对一些类型的转换提示不友好。建议全部使用严格模式。

 

联合类型

考虑到 PHP 动态语言类型的特性,现在很多情况下,联合类型都是很有用的。联合类型是两个或者多个类型的集合,表示可以使用其中任何一个类型。

<?php
public function price(Product|Sku $input): int|float;

请注意,联合类型中不包含 void,因为void 表示的含义是“根本没有返回值”。 另外,可以使用 |null 或者现有的 ? 表示法来表示包含 nullable 的联合体 :

<?php
public function price(Product|Sku $input): void;
public function setPrice(?Product $product): void;

null 合并运算符

<?php

//获取$_GET['user']的值并返回'nobody'。
// 如果它不存在返回'nobody'。
$username = $_GET['user'] ??'nobody';
//上面的标但是这相当于:
$username =isset($_GET['user']) ? $_GET['用户'] :'nobody';
//可以用链式连接:返回第一个有效的值
$username = $_GET['user'] ? $_POST['user'] ?'无名氏'。

如果变量存在且值不为NULL, 它就会返回自身的值,否则返回它的第二个操作数。

太空船操作符(组合比较符) ¶

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

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

通过 define() 定义常量数组 

<?php
define('ANIMALS', [
    'dog',
    'cat',
    'bird'
]);
echo ANIMALS[1]; // 输出 "cat"

Array 类型的常量现在可以通过 define() 来定义。在 PHP5.6 中仅能通过 const 定义。

匿名类 ¶

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

dd($app->getLogger());

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

生成器

生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。

<?php

function gen_one_to_three() {
    for ($i = 1; $i<= 3; $i++) {
        //注意变量$i的值在不同的yield之间是保持传递的。
        yield $i;
    }
}

$generator = gen_one_to_three();
foreach ($generator as $value) {
    echo "$value\n";
}

上例输出:

123

PHP7 新增了 生成器委托 (yield from) 功能

<?php
function count_to_ten() {
    yield 1;
    yield 2;
    yield from [3, 4];
    yield from new ArrayIterator([5, 6]);
    yield from seven_eight();
    yield 9;
    yield 10;
}
function seven_eight() {
    yield 7;
    yield from eight();
}
function eight() {
    yield 8;
}
foreach (count_to_ten() as $num) {
    echo "$num ";
}

上例输出:

12345678910

Weak maps

WeakMap保存对对象的引用,这不会阻止这些对象被垃圾回收。

RFC 中 Weak maps 的示例:

<?php
class Foo  
{ 
    private WeakMap $cache; 
  
    public function getSomethingWithCaching(object $obj): object 
    { 
        return $this->cache[$obj] 
           ??= $this->computeSomethingExpensive($obj); 
    } 
}

在对象上允许::class

在可以在对象上使用::class,而不必使用get_class()。它的工作方式与get_class()相同。

$foo = new Foo(); 
var_dump($foo::class);

构造属性

class Money  
{ 
    public function __construct( 
        public Currency $currency, 
        public int $amount, 
    ) {} 
} 
// 原来的写法
class Money  
{ 
    public Currency $currency; 
    public int $amount; 
  
    public function __construct( 
        Currency $currency, 
        int $amount, 
    ) { 
        $this->currency = $currency; 
        $this->amount = $amount; 
    } 
}

发表评论