PHP 8.1 新口味品尝记录 (一)

951 阅读2分钟

PHP 8.1 新口味品尝记录 (一)

前言

PHP 官方在 2021年11月25日发布了 PHP 8.1 正式版本, 带来了很多有意思的新语法和修改, 具体可以看官方文档, 今天开始我们来一个个尝试这些新东西.

新语法与特性较多, 将分开多篇讲述, 找了一圈目前没有代码高亮支持 8.1 语法, 以下代码块请耐心查看

枚举类 enum

1. 基础语法

enum OrderStatus
{
    case created;
    case payed;
    case sent;
    case finish;
    case Finish;
    // ... case 不限量, 大小写敏感
}

// enum 与 class, interface, trait 同级, 也就命名不可重复
class OrderStatus {} // PHP Fatal error:  Cannot declare class OrderStatus, because the name is already in use

// 静态清单方法 cases()
OrderStatus::cases(); // [OrderStatus::created, OrderStatus::payed, OrderStatus::sent, OrderStatus::finish, OrderStatus::Finish]

function validStatus(OrderStatus $status)
{
    var_dump($status);
    return true;
}
$r = validStatus(OrderStatus::payed); // enum(OrderStatus::payed)
var_dump($r); // true

validStatus(OrderStatus::foo); // Error : Undefined constant OrderStatus::foo

// 值是一种新数据类型, 枚举类型, 不是字符串, 有属性 name 就是自身名称字符串
OrderStatus::payed == 'payed'; // false
$s1 = OrderStatus::sent;
$s2 = OrderStatus::sent;
$s1 === $s2; // true
OrderStatus::Finish == OrderStatus::finish; // false
echo OrderStatus::finish->name; // finish

2. Backed 枚举

官方翻译回退枚举, 支持标量形式, 转魔术值与魔术字很好用

// 仅能 int 或者 string, 不允许联合 int|string
// 实际为实现了内置接口 BackedEnum interface
enum OrderStatus: int
{
    case created = 0;
    case payed = 1;
    case sent = 2;
    case finish = 3;

    // from() 和 tryFrom() 为 Backed Enum 保留方法
    public static function from() {} // Error: Cannot redeclare OrderStatus::from()
}

var_dump(OrderStatus::created->value); // int(0)
// 标量 value 不影响 cases() 结果
OrderStatus::cases(); // [OrderStatus::created, OrderStatus::payed, OrderStatus::sent, OrderStatus::finish]

// 只读属性, 不允许赋值
OrderStatus::created->value = 1; // PHP Fatal error:  Cannot use temporary expression in write context

// from() 和 tryFrom() 方法主要区别是对于不存在的 value 处理
OrderStatus::from(1); // enum(OrderStatus::payed)
OrderStatus::from(-1); // ValueError:  -1 is not a valid backing value for enum "OrderStatus"
OrderStatus::tryFrom(1); // enum(OrderStatus::payed)
OrderStatus::tryFrom(-1); // NULL

// 值比对查找遵循弱类型原则, 转换为定义类型再查找
OrderStatus::from(2.0); // enum(OrderStatus::sent)
OrderStatus::from('2.0'); // enum(OrderStatus::sent)
// !!特别注意特殊值的转换问题
OrderStatus::from('');
OrderStatus::from(false); // enum(OrderStatus::created)
OrderStatus::from(true); // enum(OrderStatus::payed)
OrderStatus::from(null); // TypeError: Passing null to parameter #1 ($value) of type string|int is deprecated
OrderStatus::from(''); // TypeError: Argument #1 ($value) must be of type int, string given

3. interface, function, const

枚举允许实现 interface 与定义 function, 回退枚举枚举例子提到实际为实现 BackedEnum interface


interface Status {
    public function hadPay(): bool;
    public static function getDefault(): static;
}


enum OrderStatus implements Status
{
    case created;
    case payed;
    case sent;
    case finish;
    
    public const PAYED = self::payed;

    public function hadPay(): bool
    {
        return match($this) {
            OrderStatus::created => false,
            OrderStatus::payed, OrderStatus::sent, OrderStatus::finish => true,
        };
    }

    public static function getDefault(): static
    {
        return OrderStatus::created;
    }

    private function _func(): bool
    {
        return false;
    }

}

OrderStatus::PAYED; // enum(OrderStatus::payed)
OrderStatus::created->hadPay(); // false
OrderStatus::created->_func(); // Error: Call to private method OrderStatus::_func() from global scope
OrderStatus::getDefault(); // enum(OrderStatus::created)

readonly

1. 基础说明

  • 增加 readonly 修饰符定义类成员变量为真正的只读, 只允许在初始化__construct时候赋值
  • 之前只读做法是设置成员变量为 private, 但是可以被class 中 function 修改, "假只读"
  • 和常量 const 区别是可以初始化时候赋值

2. 示例

class Human {

    public readonly string $name; // 设置 readonly
    private string $_sex; // 旧版做法

    public function __construct(string $name, string $sex)
    {
        $this->name = $name;
        $this->_sex = $sex;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $newName): never
    {
        $this->name = $newName;
    }

    public function getSex(): string
    {
        return $this->_sex;
    }

    public function setSex(string $newSex): never
    {
        $this->_sex = $newSex;
    }

}

$coder = new Human('hammer', 'man');
echo $coder->name; // hammer
echo $coder->getName(); // hammer
$coder->setName('nobody'); // Error: Cannot modify readonly property Human::$name

echo $coder->getSex(); // man
$coder->setSex('women');
echo $coder->getSex(); // women

总结

本篇主要示范 enum 与 readonly, 更多内容后续推出 (咕咕咕, 一定一定), 新人发文, 欢迎大家提建议, 是否喜欢代码加注释的讲解风格文章

可以看出 PHP 发展趋势是往强类型靠拢, 提供更多更严谨语法, 优化性能与结构, 我们更应拥抱变化, 与时俱进, 终身学习

如有错误或者疑问, 欢迎回复讨论

souyisou.png.png