【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足够了,不想麻烦地一个个敲命令,希望能更简化一些。

广告
将在 10 秒后关闭
bannerAds