【PHP8.0】PHP8.0的新功能
PHP8.2 / PHP8.1 / PHP8.0 / PHP7.4 可供选择的 PHP 版本为:
2020年11月26日发布。
2020年8月4日,PHP 8.0的功能凍結已經完成。
這意味著與語言功能相關的功能添加和修改已經截止。
未來將在不斷進行調試的過程中提高完整性,預計於2020年11月26日發布PHP 8.0。
让我们来看一下决定在PHP8.0中增加支持的RFC。
RFC 计划
即时编译
以50票赞成,2票反对通过。
PHP8的亮点是JIT。
将PHP编译为本机代码,并可以重用编译结果于下一个请求。
速度方面,相较于启用Opcache的情况下,平均可以提高1.3-1.5倍,且对于CPU密集型处理,速度提升可达3倍以上。
若没有使用Opcache,处理时间从1分钟减少为8秒。
如果与PHP7.4引入的预加载结合使用,这难道不已经成为一种编译语言了吗?
命名参数
以57票赞成、18票反对予计通过。
这是指定命名参数。
// これまで
array_fill(0, 100, 50);
// 名前付き引数
array_fill(start_index: 0, num: 100, value: 50);
// 同じ
htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
htmlspecialchars($string, double_encode: false);
这个Python的东西和大致相同。
在参数较多的函数中,当只想指定最后一个参数时非常有用。
此外,以前无法知道参数的顺序和代表的含义,但通过使用参数名称来写,可以使得代码更易读。
匹配表达式 v2
以43票赞成,2票反对,该议案被接受。
这是一个匹配模式。
echo match ("1") {
true => 'Foo',
1 => 'Bar',
"1" => 'Baz',
}; // Baz
这是一种很好的语法结构,几乎解决了现有switch语句的大部分缺点,例如严谨的比较、不会漏掉分支、具有返回值的表达式。
相反,由于分支内部只能写一个表达式,所以无法进行复杂的处理。
除非必须使用switch,否则将来使用match来编写会更好。
空安全运算符
在56人赞成2人反对的情况下,被接受。
我是零安全操作员。
$country = $session?->user?->getAddress()?->country;
可以更容易地编写可能中途出现null的方法链。
当遇到?时,执行会被中断,并且之后的代码都不会被执行。
// bar()、baz()は実行されない
null?->foo(bar())->baz();
联合类型 v2
以61票赞成,5票反对通过。
这是一个UNION类型的RFC。
function setNumber(int|float $number): void {
// $numberはintかfloat
}
可以接受或返回多种类型,但过度使用会使类型系统失去意义,所以建议适度使用。
混合类型v2
以50票赞成、11票反对通过。
是一种接受任何事物的类型。
class A
{
public function foo(mixed $value): mixed {
return $value;
}
}
如果想要接收任何类型的参数,可以使用它作为类似于var_dump()的函数。在TypeScript中被称为”any”。在重构时会发挥作用。基本上,最好不要在新项目中主动使用它。
特性 (版本2)
通过51票赞成,1票反对,这一提案得到了受理。
这是一个属性。
通过 <<>> 可以嵌入元数据。
<<WithoutArgument>>
<<SingleArgument(0)>>
<<FewArguments('Hello', 'World')>>
function foo() {}
在过去,只能通过注释来书写PHPDoc或PSR-5,并且缺乏强制力。
属性将成为一种以PHP语法具有实际效力的方式来书写元数据。
属性修改
有多个投票,但全部都已被接受。
这是关于属性详细添加规范的RFC。
这个规格添加允许在逗号分隔的形式下写入多个属性,将之前名为PhpAttribute的类名改为Attribute等。好像并没有对属性本身做出大的变更。
简化属性语法
这是关于属性语法更改的RFC。
在属性语法中存在一些问题。
<<Bar(2 * (3 + 3)>>Baz, (4 + 5) * 2)>>
<<JoinTable(
"User_Group",
<<JoinColumn("User_id", "id")>>,
<<JoinColumn("Group_id", "id")>>,
)>>
在嵌套以及与位运算符混合使用时会变得非常难懂,而且将来如果引入泛型的话会更加混乱。因此,引入了替代语法@@・#[]・<<>> 之间的争议,无论是在投票还是在 Reddit 上,@@ 都得到了最多的支持。
@@JoinTable(
"User_Group",
@@JoinColumn("User_id", "id"),
@@JoinColumn("Group_id", "id"),
)
尽管已经作出了决定,但由于语法上出现了各种问题,因此又提交了进一步的RFC。
简化属性语法更改
以下是用中国大陆的汉语来表述的同义句:
@@这是为了解决句法问题而再次进行的属性句法修改的RFC文档。
该RFC文档在特殊情况下,在功能冻结后开始投票,并于2020年9月2日结束。
構文的变更本身被接受,赞成50票,反对11票。
在修改后的属性语法中,可从@@Attr、#[Attr]、@[Attr]、<>、@:Attr、@{Attr}中选择。
投票结果中,#[Attr]获得32票,位居首位。
经历了一系列曲折,最终我们决定采用以下的结构。
#[
ORM\Entity,
ORM\Table("user")
]
class User
{
#[ORM\Id, ORM\Column("integer"), ORM\GeneratedValue]
private $id;
#[ORM\Column("string", ORM\Column::UNIQUE)]
#[Assert\Email(["message" => "The email '{{ value }}' is not a valid email."])]
private $email;
}
重分类引擎警告
54人賛成、3人反対の結果、可決されました。
警戒レベルが厳しくなるでしょう。
$a = 1;
$a->b++;
在PHP7中,即使是这种难以理解的代码,也只会产生E_WARNING级别的警告,但是以后将会变成ErrorException。
对于不正确的20多种格式,之前是E_NOTICE级别的警告的部分将变成E_WARNING级别,而之前是E_WARNING级别的警告的部分将变成异常,变得更加严格。
算术/位运算符的类型检查更为严格
這個RFC的接受率是57贊成0反對。
這是關於非原始值算術運算的RFC。
var_dump( [] % [42] );
var_dump( new stdClass >> tmpfile() );
这是一个完全没有意义的计算,但在PHP7中它至少可以运行。
然而,即使这样的事情发生了,也没有任何用处,因此自 PHP 8.0 版本开始,对数组、资源和对象进行算术运算都会导致类型错误。
如果是对象的情况下,具有重载操作符的一些类(如GMP)仍然可以进行操作。
同时,对于诸如string和int等原始类型,本RFC不会产生影响。
內部函數的類型錯誤一直存在。
通过50票赞成、2票反对,该提案被接受。
这是关于内部函数类型参数的RFC。
function foo(int $bar) {}
foo("not an int"); // TypeError
strlen(new stdClass); // Warning: strlen() expects parameter 1 to be string, object given
var_dump(DateTime::createFromFormat(new stdClass, "foobar")); // E_WARNING
var_dump(DateTime::createFromFormat("foobar", "foobar", new stdClass)); // TypeError
如果传递了错误类型的参数,则用户定义的函数将始终会引发TypeError错误。而对于内部函数,则可能会引发TypeError或E_WARNING类型的错误。
这不好,因此将其统一为TypeError。
因此,如果你编写了抑制传统E_WARNING的代码,那么你将在这里遇到困难。
据说PHP本体的测试代码中,大约有1500个相关代码(有意产生错误的测试代码)会受到这个变更的影响。
更明智的字符串至数字比较
通过44票赞成、1票反对,该提案被接受。
这个RFC旨在改变非严格的比较运算符==的行为。
"true" == 0; // true PHP7まで
"true" == 0; // false PHP8
如果要比较数字和不是数值类型的字符串,以前我们会将字符串转换为数字然后进行比较,但今后我们将转换数字为字符串再进行比较。对于数字和数值类型的字符串的比较,以及其他非数字的比较,仍然采用过去的方法。
这个变更看起来非常危险,但是我认为这个变更不会对具有异常行为的质量程序造成实际问题,因为在这个变更之前,它们很可能已经被Reclassifying engine warnings、Consistent type errors for internal functions、以及Stricter type checks for arithmetic/bitwise operators这些环节发现并不能正常运行了。所以,实际上并没有什么实质性的影响。
更简洁的数字字符串
该RFC被以30票赞成、4票反对的结果通过。
这是一份修改数字类型字符串定义的RFC。
在PHP7之前,数值类型字符串有多种定义方法。
・正规的数字类型字符串:0个或多个空格+数字
・非规的数字类型字符串:正规的数字类型字符串+任意的字符串
・其他所有情况都不是数字类型字符串。
function foo(int $i) { var_dump($i); }
foo("123"); // int(123)
foo(" 123"); // int(123)
foo("123 "); // int(123) with E_NOTICE "A non well formed numeric value encountered"
foo("123abc"); // int(123) with E_NOTICE "A non well formed numeric value encountered"
foo("string"); // TypeError
因为这个原因导致了许多麻烦的事情。
特别是123和123不同的地方让人感到非常不自然。
所以,在PHP8中,将合并数字类型和字符串类型的定义。
– 数值型字符串:0个或多个空格+数字+0个或多个空格
– 其他情况均不属于数值型字符串。
由此,123和123会逐渐变为相同的值。
同时,123abc将不再是数字类型字符串。
function foo(int $i) { var_dump($i); }
foo("123"); // int(123)
foo(" 123"); // int(123)
foo("123 "); // int(123)
foo("123abc"); // TypeError
foo("string"); // TypeError
坦白说,相比于Saner结构与数字的比较,这边的影响范围要大得多。
尽管不再是数值型字符串,但是如果(int)”123abc”变成0的话,后果太过严重了,所以似乎确实会将其改为123。
将具有命名空间的名称视为单个标记
这项RFC已被38人赞成、4人反对通过。
这是一项关于更改命名空间解析器令牌的RFC。
在过去,命名空间Foo\Bar会在词法分析中被分解为T_STRING,T_NS_SEPARATOR,T_STRING,而现在将被统一为T_NAME_QUALIFIED。
作为使用PHP的一方,除了那些使用token_get_all的异常脚本外,与这完全无关。
然而惊讶的是,其中还提到了禁止使用命名空间的空白。
值得一提的是,违规脚本只在Composer的前2000个软件包中的5个地方出现。
在参数列表中允许使用尾逗号。
在投票中,有58人赞成,1人反对,该提案被接受。
这是关于函数参数末尾逗号的RFC。
在PHP中,从以前开始就可以在数组中使用最后一个逗号,在PHP 7.3之后,函数调用和大多数列表中也可以使用末尾逗号。
尽管函数调用方已经进行了相应的处理,但是函数参数却不知何故没有进行相应的处理,因此需要进行补充相应的处理。
function hoge(
$foo,
$bar, // PHP8.0以降OK
){}
hoge(1, 2, ); // PHP7.3以降OK
允许在闭包使用列表中使用尾逗号
以49票赞成、0票反对被接受。
这是在RFC(请求评论)中的末尾逗号。
$longArgs_longVars = function (
$longArgument,
$longerArgument,
$muchLongerArgument, // ↑の引数末尾カンマでOKになった
) use (
$longVar1,
$longerVar2,
$muchLongerVar3, // このRFC
) {
};
由于无法在”use”的末尾放置逗号,因此进行了相应的补充处理。
通过这个缺失的补充,可能实现了在列举的所有地方都可以使用末尾逗号。
确保魔法方法的签名正确。
以45票赞成、2票反对被接受。
这是关于魔术方法参数的RFC。
在魔法方法中,以前可以写入不正确的类型。
class Foo {
public function __get(array $name): void{}
}
未来将会禁止此举,并只允许写入正确格式;或者干脆什么都不写。
class Foo {
public function __get(string $name): mixed{}
// ↓でもOK
public function __get($name){}
}
像往常一样,Nikita检查了Composer前1000个包,发现只有7个违反脚本规则。
在getTraceAsString()中可配置的字符串长度
通过36票赞成和2票反对被接纳。
这是一项改变例外字符串展开字符数量的RFC。
在使用Throwable::getTraceAsString()等方法将错误展开为字符串时,堆栈跟踪中的字符串会被截断为15个字节。
因此,在需要进行深入调查等情况下,这是一个困难的状态。
因此,在ini配置文件中添加zend.exception_string_param_max_len选项,并允许将设置的字节数展开到堆栈跟踪中。默认值为现有值15。
移除私有方法上的不恰当继承签名检查。
根据24人赞成和11人反对的结果,此RFC已被接受。
此RFC涉及私有方法的继承。
class A
{
final private function finalPrivate() {
echo __METHOD__ . PHP_EOL;
}
}
class B extends A
{
private function finalPrivate() {
echo __METHOD__ . PHP_EOL;
}
}
私有方法并不会被继承,但以这种方式编写会导致“无法覆盖最终方法”致命错误。
在某些情况下,继承时会进行私有方法的重写检查。
因此,PHP 8.0及更高版本不会在继承时执行对私有方法的重写检查。
验证抽象特征方法
以賛成52票、反対0票通过。
这是关于Trait的抽象方法行为的RFC(请求评论)。
Note: I have used simplified Chinese characters for the translation. If you require traditional Chinese characters, please let me know.
如果在特征中写了抽象方法,继承时的规则就会出现问题。
trait MyTrait {
abstract public function publicFunction(): string;
abstract private function privateFunction(): string; // cannot be declared private
}
class TraitUser {
use MyTrait;
// 何故かOK
public function publicFunction(): stdClass { }
}
有时候即使签名不同也可以通过,因此我们会像普通的类继承一样对其进行修正。
同时,我们还可以定义抽象私有方法。
trait MyTrait {
abstract private function neededByTheTrait(): string;
public function doSomething() {
return strlen($this->neededByTheTrait());
}
}
class TraitUser {
use MyTrait;
// OK
private function neededByTheTrait(): string { }
// 返り値の型が異なるのでNG
private function neededByTheTrait(): stdClass { }
// staticとnonstaticなのでNG
private static function neededByTheTrait(): string { }
}
使用trait的抽象私有方法在使用类中必须进行实现。虽然不太愿意强制要求私有方法的实现。
使排序变得稳定
在24票赞成和11票反对的情况下,被接受。
这是一个将排序变为稳定排序的RFC。
$array = [
'c' => 1,
'd' => 1,
'a' => 0,
'b' => 0,
];
asort($array);
由于以前的PHP排序是不稳定的,所以无法保证排序后a和b的顺序,以及c和d的顺序。
自PHP8.0开始,排序后的顺序将保证为[‘a’ => 0, ‘b’ => 0, ‘c’ => 1, ‘d’ => 1]。
构造函数属性推广
以46票赞成,10票反对通过。
这是对象初始化器。
class Point {
// プロパティx,y,zが生える
public function __construct(
public float $x = 0.0,
public float $y = 0.0,
public float $z = 0.0,
) {}
}
只需要在构造函数中指定参数可见性,就会自动将其注册为属性。
以前,初始化对象时需要大量的模板代码,但是现在可以更轻松地编写。
始终可用的JSON扩展
以56票赞成、0票反对通过。
这是一项使JSON始终有效的RFC。
事实上,以前我们可以通过–disable-json选项来避免安装JSON扩展,但现在无法再这么做了。
因为它是一个甚至在手册中都没有提及的隐藏功能,所以即使它消失了,完全没有问题。
拆分ext/xmlrpc
以50票支持、无反对而通过。
这是一项将移除ext/xmlrpc的RFC。
由于xmlrpc-epi已经被放弃,xmlrpc在内部不再使用xmlrpc-epi,并将其从支持中移除。这意味着xmlrpc_xxx函数将无法使用。
由于这个模块一直被告知是实验性的,所以受到影响的人将非常少。
非捕捉捕获
以48票赞成、1票反对通过。
该RFC不接受任何例外。
try {
changeImportantData();
} catch (PermissionException) {
echo "You don't have permission to do this";
}
如果不使用捕获的异常,您可以从一开始就不接收它。这绝不是用于压制异常的功能。
将不受区域限制的浮点数转换为字符串。
42人同意,1人反对,通过。
这是关于在某些编程语言中进行(string)转换的RFC。
如果将语言环境设置为法语,字符串”3.14″将变成”3,14″,因为在法语中使用逗号作为小数点。
setlocale(LC_ALL, "de_DE");
$f = 3.14; // float(3,14)
$s = (string) $f; // string(4) "3,14"
$f = (float) $s; // float(3)
然而,无法将“3.14”转化为小数,而是成为3。
为了解决这个矛盾,而且由于在字符串表达不同之时变得难以理解,我们决定无论地区如何,string转换始终为“3.14”。
中文和日语等语言中的小数点点”.”在本地环境中没有任何影响。
修改默认的PDO错误模式
以49票赞成、2票反对通过。
这是关于PDO默认错误模式的RFC。
在PHP7之前,PDO::ATTR_ERRMODE默认的设置是PDO::ERRMODE_SILENT。这意味着即使出现错误,也不会有任何提示,因此这个设置非常不理想。
从PHP8.0开始,默认情况下PDO::ERRMODE_EXCEPTION被设置为默认模式。
太好了。
将 str_starts_with 和 str_ends_with 添加到 PHP。
以51票赞成和4票反对的结果通过。
这个结果以开始为startsWith并以结束为endsWith。
echo str_starts_with ( 'abcdef' , 'abc' );
echo str_ends_with ( 'abcdef' , 'def' );
在用户界面上看起来很容易实现,但实际上这个函数的实现有很多陷阱,如果写得不好,很快就会变得低效。
如果语言本身能够提供这样的功能,那当然最好不过了。
字符串包含
43人同意,9人反对,通过。
包含字符串。
str_contains('放課後アトリエといろ', '放課後'); // true
为什么直到现在,我们一直强制使用神秘的格式strpos($haystack, $needle)!==false来表示”包含XX”,但现在终于可以以简洁的方式表达了。
由于实现了str_contains和str_starts_with/str_ends_with函数,可以说PHP的文本处理函数已经齐全了。
投掷表情
46人赞成,3人反对,通过。throw语句将变为throw表达式。
// PHP7
if(!$nullableValue){
throw new InvalidArgumentException();
}
$value = $nullableValue;
// 概ね同じ
$value = $nullableValue ?? throw new InvalidArgumentException();
由于throw在过去一直是作为句子存在的,所以无法包含在表达式之间。
未来将能够更轻松地进行throw。
面向对象的token_get_all()的替代方案
提案者获得了47票赞成和0票反对,该提案被接受。
这是有关token_get_all替代语法的请求评论。
token_get_all函数返回的是解析结果的数组。
这个RFC引入了新的PhpToken类,并将解析结果以对象形式接收。这样一来,代码变得更易读,同时也减少了内存消耗。
为了兼容性,保持现有的token_get_all不变。
Stringable (可轉換為字符串的)
在29人赞成和9人反对的情况下通过。
这是关于Stringable接口的RFC。
实现Countable接口可以使对象可计数,实现Traversable接口可以使对象可遍历。
这是一个提案,旨在实现一个名为Stringable的接口,以便能够类似地使用__toString()函数。
interface Stringable
{
public function __toString(): string;
}
因为强行执行这个操作会导致所有实现都坏掉,所以实现了__toString方法的类会隐式地实现Stringable接口。
因此,作为使用者,你不需要特别担心。
这是类似于标记接口的东西,用于在库中接受string|Stringable时。
实际用例可以在Symfony中找到。
允许在对象上使用 ::class
以60票赞成、0票反对通过。
这是获取课程名称的RFC。
在PHP中,您可以使用stdClass::class来获取类名”stdClass”,但是一旦对其进行对象化,就无法使用::class来获取类名了。
您需要使用get_class函数来达到目的。
在PHP8中,可以写成$object::class。
echo stdClass::class; // class
echo (new stdClass)::class; // PHP8.0以降OK
静态返回类型
54票赞成,0票反对,通过接受。
此RFC的目的是允许在返回值中使用static。
Note: RFC(Request for Comments)是指Internet Engineering Task Force(IETF)的文件,用于描述Internet协议、传输机制等的规范。
在PHP7中,可以作为返回值写入parent或self。
<?php
class FUGA{}
class HOGE extends FUGA{
public function a(): parent{
return new parent();
}
}
(new HOGE())->a(); // FUGAになる
让我们也能写static。这是延迟静态绑定中的static。
// PHP7までsyntax error
class Test {
public function createFromWhatever($whatever): static {
return new static($whatever);
}
}
就我个人而言,我从未使用过延迟静态绑定,所以我真的不知道它有什么好处。
变量语法调整
以47:0的支持, 被接受。
是关于变量语法的RFC。
在PHP7.0中,对变量语法进行了大规模改进,但是一些被忽视的语法在此次改进中也得到了相应的支持。尽管被忽视了,但这些变化非常细微以至于我们可能都没有察觉到。另外,这次改动使得之前会产生语法错误的语法也变得有效,因此没有不兼容的变更。
$bar = "bar";
echo "foobar"[0]; // OK
echo "foo$bar"[0]; // PHP7までsyntax error、8からOK
echo __FILE__[0]; // PHP7までsyntax error、8からOK
DOM的生活标准API
以37票赞成、0票反对通过。
这是关于DOM更新的RFC。
PHP的DOM是根据W3C Group在很久以前制定的DOM Level 3标准开发的。
之后DOM被纳入Living Standard,由WHATWG进行管理。
由于对其进行了不少改进,所以我们会引入这些变化。
嗯,因为操作DOM很麻烦,所以我并不经常使用它。
始终为不兼容的方法签名生成致命错误。
39人赞成,3人反对通过。
这是关于方法签名的RFC。
如果在继承时违反了LSP原则的编写方式,那么在PHP7中将变得致命错误或E_WARNING的其中一种。
// こっちは致命的エラー
interface I {
public function method(array $a);
}
class C implements I {
public function method(int $a) {}
}
// こっちはE_WARNING
class C1 {
public function method(array $a) {}
}
class C2 extends C1 {
public function method(int $a) {}
}
将此错误统一为所有情况下的致命错误。
为什么这个问题直到现在才变成致命错误的案例?
以负索引开始的数组
以17票赞成,2票反对被接受。
负数作为数组的索引可以被使用。
$arr = [
-10 => 'a'
];
$arr[] = 'b'; // PHP7.4まで0、PHP8から-9
我完全搞不清楚到底谁会从中受益。
弱映射
以25票赞成、0票反对,被接受通过。
这个地图相对较弱。
根据实际情况,在PHP7.4中引入了弱引用,但更常用的实际上是使用弱映射,因此似乎要对其进行引入。
我无法想到一个使用这个地图,而不是普通地图的场景。
获取调试类型
以42票赞成、3票反对的结果被接受。
这是get_debug_type。
总结起来,可以使用 `gettype()` 和 `get_class()` 来获取对象的类名或者是原始类型的名称。
gettype(1); // integer
get_class(1); // TypeError
get_debug_type(1); // int
gettype(new stdClass); // object
get_class(new stdClass); // stdClass
get_debug_type(new stdClass); // stdClass
不需要分别使用,也不会出现不正确的类型名称(如integer),所以非常方便。
不要在getMetadata()之外自动反序列化Phar元数据。
通过25票赞成、0票反对通过。
这是关于处理Phar元数据的RFC。
在PHP7中,当查找Phar文件时,例如file_exists(“phar://path/to/phar.ext”),将自动展开该文件的元数据。因此,这可能导致所谓的不安全的反序列化攻击成为可能。
因此,我将对其进行修改,除非手动调用Phar :: getMetadata,否则不会展开元数据。
另外,getMetadata使用unserialize来展开元数据。
所以我们添加参数$unserialize_options,允许任意更改反序列化方法。
然而,我总觉得只要服务器上存放的Phar文件被污染,而不是像Cookie那样的外部可污染数据,就会有点不妙。你觉得呢?
为内容管理系统增加支持
没有投票。
这是关于CMS支持的RFC。
即使被称为CMS,也并不是指内容管理系统,而是指RFC5652中的加密消息语法(Cryptographic Message Syntax)。实现了用于加密和解密的函数,如openssl_cms_encrypt等相当于openssl_pkcs7_encrypt。
虽然提出了RFC,但由于OpenSSL的基本功能并不需要进行投票,所以似乎被采用而无需进行投票。
印象
嘿,那个说PHP8没什么了不起的人是谁啊。
真的变得相当惊人了。
尽管属性的使用方式有些不透明,但除此之外,它应该会被顺利地实现。
使用这些功能,PHP8 可以让编写变得更加容易、可读性更高,并且能够创建更快的程序。
因此,之前使用一些随意编写的代码可能会变得更加困难。
另外,此列表只包含了RFC通过投票被接受和决定实施的内容。
除此之外,还应包括一些修复错误和RFC提出数量并不多的小改动等。
由于PHP 8的推出,PHP已经非常接近完美的感觉了。
还能期待有比这更大的需求吗?也许就只有泛型了吧。
如果说了这样的话,下一次也可能会被揪出很多问题来。
前不久,Microsoft作为一家企业宣布不支持PHP8,这是一个令人遗憾的消息,但目前有志者已迅速提供了Windows版本的构建。对于PHP8.0来说,影响不会太大。虽然不清楚未来会怎样(可能会涉及Windows版特有的错误等情况),可能是说要赶快转向WSL2吧。但是对于本地开发来说,XAMPP足够了,不想麻烦地一个个敲命令,希望能更简化一些。