枚举(Enumerations)
PHP 8.1使用enum枚举类型来取代一组常量,并获得开箱即用的验证。
<?php
enum UserRole: string
{
case ADMIN = "1";
case GUEST = "2";
case WRITER = "3";
case EDITOR = "4";
}
fprintf(STDOUT, "我的名字:%s\n", UserRole::WRITER->name);
fprintf(STDOUT, "我的只:%s\n", UserRole::WRITER->value);
if(UserRole::WRITER instanceof UserRole){
echo "我属于 \n";
}
if(!"1" instanceof UserRole){
echo "我不属于 \n";
}
function test(UserRole $role): void
{
echo "参数限制";
}
只读属性(Readonly Properties)
在PHP 8.1中,只读属性不能在初始化后更改,需先给它们进行赋值,可用于对值对象和数据传输对象建模。如果尝试强行更新该值,应用程序将抛出错误。
<?php
/**
* User Class
*/
class User
{
/**
* Declare a variable with readonly property.
* @var int $authUserID
* @access public
*/
public readonly int $authUserID;
/**
* Constructor of the class.
* @param integer $userID
* @return void
*/
public function __construct(int $userID)
{
/**
* Change the value of the property as specified.
* Updating the value of readonly properties are
* allowed only through the constructor.
*/
$this->authUserID = $userID;
}
/**
* Update Auth User ID
* This function tries to update the readonly property (which is not allowed).
* @method updateAuthUserID()
* @param integer $userID
* @return void
*@author Tara Prasad Routray <someemailaddress@example.com>
* @access public
*/
public function updateAuthUserID(int $userID): void
{
/**
* Change the value of the property as specified.
* Executing this function will throw the following error;
* PHP Fatal error: Uncaught Error: Cannot modify readonly property User::$authUserID
*/
$this->authUserID = $userID;
}
}
/**
* Initialize the class and update the value of the readonly property.
*/
$user = new User(30);
/**
* Print the readonly property value.
* This will print 30.
*/
echo $user->authUserID;
/**
* Call another function inside the class and try to update the class property.
*/
$user->updateAuthUserID(50);
/**
* Print the readonly property value.
*/
echo $user->authUserID;
纤程(Fibers)
PHP 8.1 添加了对 Fiber 的支持,这是一个低级组件,允许在 PHP 中执行并发代码。Fiber 是一个代码块,它包含自己的变量和状态堆栈。这些 Fiber 可以被视为应用程序线程,可以从主程序启动。
一旦启动,主程序将无法挂起或终止 Fiber。它只能从 Fiber 代码块内部暂停<或终止。在 Fiber 挂起后,控制权再次返回到主程序,它可以从挂起的点继续执行 Fiber。
Fiber 本身不允许同时执行多个 Fiber 或主线程和一个 Fiber。但是,对于 PHP 框架来说,高效管理执行堆栈并允许异步执行是一个巨大的优势。
<?php
/**
* Initialize the Fiber.
*/
$fiber = new Fiber(function (): void {
/**
* Print some message from inside the Fiber.
* Before the Fiber gets suspended.
*/
echo "Welcome to Fiber!\n";
/**
* 暂停 the Fiber.
*/
Fiber::suspend();
/**
* Print some message from inside the Fiber.
* After the Fiber gets resumed.
*/
echo "Welcome back to Fiber!\n";
});
/**
* Print a message before starting a Fiber.
*/
echo "Starting a Fiber\n";
/**
* Start the Fiber.
*/
$fiber->start();
/**
* Fiber has been suspened from the inside.
* Print some message, and then resume the Fiber.
*/
echo "Fiber has been suspended\n";
echo "Resuming the Fiber\n";
/**
* 恢复 the Fiber.
*/
$fiber->resume();
/**
* End of the example.
*/
echo "Fiber completed execution\n";
Never返回类型(Never return type)
PHP 8.1 添加了名为 never 的返回类型。该 never 类型可用于指示函数将在执行一组指定的任务后终止程序执行。这可以通过抛出异常、调用 exit() 或 die() 函数来完成。
never 返回类型类似于 void 返回类型。但是,void 返回类型在函数完成一组指定的任务后继续执行。
<?php
/**
* Route Class
*/
class Route
{
/**
* Constructor of the class
* @return void
*/
public function __construct()
{
}
/**
* Redirect To a Page
* This function redirects to an URL specified by the user.
* @method redirect()
* @param string $url
* @param integer $httpCode
* @author Tara Prasad Routray <someemailaddress@example.com>
* @access public
* @return never
*/
public static function redirect(string $url, int $httpCode = 301): never
{
/**
* Redirect to the URL specified.
*/
header("Location: {$url}", true, $httpCode);
die;
}
}
Route::redirect('https://www.google.com');
Final类常量(Final class constants)
PHP 8.1 添加了对名为 final 的类常量的支持。最终类常量不能被修改,即使是通过继承,这意味着它们不能被子类扩展或覆盖。
这个标志不能用于私有常量,因为它不能在类之外被访问。声明 final 和 private 常量将导致致命错误。
<?php
/**
* UserIde Class
*/
class UserIde
{
/**
* Declare a final class constant with a value.
*/
final public const ADMIN = '1';
}
/**
* UserAct Class extending the UserIde Class
*/
class UserAct extends UserIde
{
/**
* Declare another constant with the same name
* as of the parent class to override the value.
*
* Note: Overriding the value will throw the following error:
* PHP Fatal error: User::ADMIN cannot override final constant UserRole::ADMIN
*/
public const ADMIN = '2';
}
echo UserIde::ADMIN;
echo UserAct::ADMIN;
array_is_list() 函数
PHP 8.1 添加了名为 array_is_list() 的数组函数。它标识指定的数组是否具有从 0 开始的所有连续整数。如果数组是值的语义列表(一个数组,其键从 0 开始,都是整数,并且之间没有间隙),则此函数返回 true。对于空数组,它也返回 true。
<?php
/**
* Returns true for empty array.
*/
array_is_list([]);
/**
* Returns true for sequential set of keys.
*/
array_is_list([1, 2, 3]);
/**
* Returns true as the first key is zero, and keys are in sequential order.
* It is same as [0 => 'apple', 1 => 2, 2 => 3]
*/
array_is_list(['apple', 2, 3]);
/**
* Returns true as the first key is zero, and keys are in sequential order.
* It is same as [0 => 'apple', 1 => 'scissor']
*/
array_is_list(['apple', 'orange']);
/**
* Returns true as the first key is zero, and keys are in sequential order.
* It is same as [0 => 'apple', 1 => 'scissor']
*/
array_is_list([0 => 'apple', 'orange']);
/**
* Returns true as the first key is zero, and keys are in sequential order.
*/
array_is_list([0 => 'rock', 1 => 'scissor']);
/**
* Returns false
*/
array_is_list([1 => 'rock', 2 => 'scissor']);
fsync() 和 fdatasync() 函数
PHP 8.1 添加了对 fsync() 和 fdatasync() 函数的支持。两者都与现有 fflush() 函数有相似之处,该函数当前用于将缓冲区刷新到操作系统中。
然而,fsync() 和 fdatasync() 刷新该缓冲区到物理存储。它们之间的唯一区别是该 fsync() 函数在同步文件更改时包含元数据,而该 fdatasync() 函数不包含元数据。
fsync() 函数将采用文件指针并尝试将更改提交到磁盘。成功时返回 true,失败时返回 false,如果资源不是文件,则会发出警告。fdatasync() 函数的工作方式相同,但速度稍快一些,因为 fsync () 将尝试完全同步文件的数据更改和有关文件的元数据(上次修改时间等),这在技术上是两次磁盘写入。
<?php
/**
* Declare a variable and assign a filename.
*/
$fileName = 'notes.txt';
/**
* Create the file with read and write permission.
*/
$file = fopen($fileName, 'w+');
/**
* Add some text into the file.
*/
fwrite($file, 'Paragraph 1');
/**
* Add a line break into the file.
*/
fwrite($file, "\r\n");
/**
* Add some more text into the file.
*/
fwrite($file, 'Paragraph 2');
/**
* You can use both the fsync() or fdatasync() functions
* to commit changs to disk.
*/
fsync($file); // or fdatasync($file).
/**
* Close the open file pointer.
*/
fclose($file);
数组解包
PHP 8.1 添加了对字符串键数组解包的支持。为了解压数组,PHP 使用展开 (…) 运算符。PHP 7.4 中引入了这个运算符来合并两个或多个数组,但语法更简洁。但在 PHP 8.1 之前,展开运算符仅支持带数字键的数组。请参阅以下代码片以了解如何将展开运算符用于字符串键控数组。
<?php
/**
* Declare an array
*/
$fruits1 = ['Jonathan Apples', 'Sapote'];
/**
* Declare another array
*/
$fruits2 = ['Pomelo', 'Jackfruit'];
/**
* Merge above two arrays using array unpacking.
*/
$unpackedFruits = [...$fruits1, ...$fruits2, ...['Red Delicious']];
/**
* Print the above unpacked array.
* This will print:
* array(5) {
* [0]=>
* string(15) "Jonathan Apples"
* [1]=>
* string(6) "Sapote"
* [2]=>
* string(6) "Pomelo"
* [3]=>
* string(9) "Jackfruit"
* [4]=>
* string(13) "Red Delicious"
* }
*/
var_dump($unpackedFruits);
IntlDatePatternGenerator 类
PHP 8.1 添加了对新 IntlDatePatternGenerator 类的支持。在 PHP 8.1 之前,只能使用 IntlDateFormatter 。虽然它支持昨天、今天和明天使用的八种预定义格式,但是这些格式和 IntlDatePatternGenerator 不太一样。
这个类允许指定日期、月份和时间的格式,并且顺序将由类自动处理。请参阅以下代码片段以了解如何使用 IntlDatePatternGenerator 类。
<?php
/**
* Define a default date format.
*/
$skeleton = "YYYY-MM-dd";
/**
* Parse a time string (for today) according to a specified format.
*/
$today = \DateTimeImmutable::createFromFormat('Y-m-d', date('Y-m-d'));
/**
* ===========================
* PRINTING DATE IN USA FORMAT
* ===========================
* Initiate an instance for the IntlDatePatternGenerator class
* and provide the locale information.
* In the below example, I've used locale: en_US.
*/
$intlDatePatternGenerator = new \IntlDatePatternGenerator("en_US");
/**
* Get the correct date format for the locale: en_US.
* Following function "getBestPattern" will return:
* MM/dd/YYYY
*/
$enUSDatePattern = $intlDatePatternGenerator->getBestPattern($skeleton);
/**
* Use the "formatObject" function of IntlDateFormatter to print as per specified pattern.
* This will print the following:
* Date in en-US: 12/03/2021
*/
echo "Date in en-US: " . \IntlDateFormatter::formatObject($today, $enUSDatePattern, "en_US") . "\n";
/**
* =============================
* PRINTING DATE IN INDIA FORMAT
* =============================
* Initiate an instance for the IntlDatePatternGenerator class
* and provide the locale information.
* In the below example, I've used locale: en_IN.
*/
$intlDatePatternGenerator = new \IntlDatePatternGenerator("en_IN");
/**
* Get the correct date format for the locale: en_IN.
* Following function "getBestPattern" will return:
* dd/MM/YYYY
*/
$enINDatePattern = $intlDatePatternGenerator->getBestPattern($skeleton);
/**
* Use the "formatObject" function of IntlDateFormatter to print as per specified pattern.
* This will print the following:
* Date in en-IN: 03/12/2021
*/
echo "Date in en-IN: " . \IntlDateFormatter::formatObject($today, $enINDatePattern, "en_IN") . "\n";
纯交集类型
PHP 8.1添加了对交集类型的支持。它类似于PHP 8.0 中引入的联合类型,但它们的预期用途恰恰相反。
为了更好地理解它的用法,让我们回顾一下PHP中类型声明的工作原理。
本质上,您可以向函数参数、返回值和类属性添加类型声明。这种分配称为类型提示,并确保值在调用时是正确的类型。否则,它会立即抛出一个TypeError。反过来,这可以帮助您更好地调试代码。
但是,声明单一类型有其局限性。联合类型通过允许您声明具有多种类型的值来帮助您克服这个问题,并且输入必须至少满足声明的类型之一。
请注意使用&(AND) 运算符来声明交集类型。相反,我们使用|(OR) 运算符来声明联合类型。
在交集类型中使用大多数标准类型将导致永远无法满足的类型(例如整数和字符串)。因此,交集类型只能包括类类型(即接口和类名)。
<?php
class A {
private Traversable&Countable $countableIterator;
public function setIterator(Traversable&Countable $countableIterator): void {
$this->countableIterator = $countableIterator;
}
/**
* 交集类型
* @return Countable&Traversable
*/
public function getIterator(): Traversable&Countable {
return $this->countableIterator;
}
/**
* 联合类型
* @return int|float
*/
public function getIteratorTow(): int|float {
return "one";
}
}
使用继承缓存提高性能
继承缓存(Inheritance Cache )是opcache的新增功能,可消除PHP类继承开销。
PHP类由opcache单独编译和缓存。但是,它们已经在运行时针对每个请求进行了链接。这个过程可能涉及几个兼容性检查和从父类和特征借用方法/属性/常量。
因此,即使每个请求的结果都相同,这仍需要相当长的时间来执行。
继承缓存链接所有唯一的依赖类(父类、接口、特征、属性类型、方法)并将结果存储在opcache共享内存中。由于这种情况现在只发生一次,因此继承需要较少的指令。
此外,它消除了对不可变类的限制,例如未解析的常量、类型化属性和协变类型检查。因此,存储在opcache中的所有类都是不可变的,进一步减少了所需的指令数量。
First-Class可调用语法
PHP 8.1添加了First-Class可调用语法来取代使用字符串和数组的现有编码。除了创建更干净的Closure 之外,静态分析工具也可以访问这种新语法并尊重声明的范围。
php8.1之前版本
<?php
$closure = Closure::fromCallable('array_merge');
print_r($closure([1,2], [3,4]));
// Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 )
php8.1版本
<?php
$closure = array_merge(...);
print_r($closure([1,2], [3,4]));
// Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 )