写点什么

PHP 8:类型系统改进

  • 2023-07-25
    北京
  • 本文字数:8834 字

    阅读完需:约 29 分钟

PHP 8:类型系统改进

本文属于专题文章《深入浅出 PHP 8》


根据w3tech的数据,PHP 仍然是 Web 上使用最广泛的脚本语言之一,77.3%的网站使用 PHP 进行服务器端编程。PHP 8 带来了许多新特性和其他改进,我们将在本系列文章中进行探讨。PHP 8.0 添加了对多个函数和方法相关特性的支持,其中一些是对现有特性的改进,而另一些则是全新的特性。PHP 8.1 中增强的可调用语法可用于通过可调用对象创建匿名函数。命名函数参数可以与位置参数一起使用,另外还有一个好处,即命名参数没有顺序,可以通过它们的名称来传达含义。纤程(Fiber)是可中断的函数,增加了对多任务的支持。


在本文中,我们将讨论 PHP 8、8.1 和 8.2 对 PHP 类型系统的扩展,其中包括联合类型、交集类型和mixed 类型,以及返回类型staticnever


此外,PHP 8 还支持独立类型truenullfalse

一些定义


在 PHP 中,类型声明与类属性、函数参数和函数返回类型一起使用。我们经常使用各种定义从类型系统方面描述一种语言:强/弱,动态/静态。


PHP 是一种动态类型语言。所谓动态类型是指类型检查是在运行时进行的,与之相对的是在静态编译时进行类型检查。PHP 默认是弱类型的,这意味着它在运行时支持的隐式类型转换规则比较少。不过,在 PHP 中可以启用强类型。


PHP 会在不同的上下文中使用类型:


  • 独立类型:可以在类型声明中使用的类型,如intstringarray

  • 字面量类型:除了值的类型之外,还对值本身进行检查的类型。PHP 支持两种字面量类型:truefalse

  • 单元类型:保存单个值的类型,如null。除了简单类型之外,PHP 8 还引入了复合类型,如联合类型交集类型联合类型是多个简单类型的并集。其值只需匹配联合类型中的一种类型。联合类型可用于指定类属性、函数形参的类型或函数返回类型。新增类型mixed 是联合类型的一种特殊类型。


PHP 8.1 还增加了交集类型,用于说明那种是多个类类型的交集的类类型。它还增加了两个新的返回类型。如果函数无返回值,则使用返回类型never 。例如,当函数抛出异常或调用exit()时,可能出现这种情况。返回类型static意味着返回值必须是调用该方法的类的实例。

联合类型


如果你熟悉文氏图,那么你可能还记得集合的并集和交集。为了支持简单类型的并集,PHP 8 引入了联合类型。用于声明联合类型的语法如下:


Type1|Type2|....|TypeN
复制代码


我们首先看个例子。在下面的脚本中,$var1属于联合类型int|string|array 。它被初始化为一个整数值,然后它被设置为联合类型声明中包含的其他类型的值。


<?php
class A{
public int|string|array $var1=1; }
$a= new A();echo $a->var1;$a->var1="hello";echo $a->var1;$a->var1=array( "1" => "a", "2" => "b",);var_dump($a->var1);
复制代码


上述脚本输出如下:


1helloarray(2) { [1]=> string(1) "a" [2]=> string(1) "b" }
复制代码


由于 PHP 是弱类型语言,如果将$var1的值设置为float1.0,就会执行隐式转换。下面的脚本将输出1


<?php
//declare(strict_types = 1);class A{
public int|string|array $var1=1; }
$a= new A(); $a->var1=1.0;echo $a->var1;
复制代码


然而,如果在声明时启用了强类型declare(strict_types = 1) ,那么就不能将$var1 设置为1.0 ,否则会报下面这个错:


Uncaught TypeError:无法将浮点数赋给array|string|int类型的属性A::$var1
复制代码


有时候,在弱类型的情况下,可以将值转换为密切相关的类型,但这种转换并非总能执行。例如,我们不能像下面的脚本那样,给联合类型(int|array )的变量赋字符串值:


<?php
class A{
public int|array $var1=2; }
$a= new A(); $a->var1="hello";echo $a->var1;
复制代码


上述脚本会报如下错误:


Uncaught TypeError: 无法将字符串赋给array|int类型的属性A::$var1
复制代码


我们看一个稍微复杂一点的例子。下面的脚本在类属性声明、函数参数和函数返回类型中使用了联合类型。


<?php
class A{
public string|int|bool $var1=true;
function fn1(string|int|array $a, object|string $b):
string|bool|int { return $a;}
}$a=new A();echo $a->var1;echo $a->fn1("hello","php");
复制代码


输出如下:


1hello
复制代码

联合类型中的 null


联合类型可以为空,在这种情况下,null是联合类型声明中的类型之一。在下面的脚本中,类属性、函数参数和函数返回类型都声明为可空的联合类型。


<?php
class A{
public string|null $var1=null;
function fn1(string|int|null $a=null, object|false|null $b=null):
string|bool|null { return null;}
}$a=new A();echo $a->var1;echo $a->fn1();
复制代码

联合类型中的 false


伪类型false可用于联合类型。在下面的示例中,false类型用于类属性声明、函数参数和函数返回类型,它们全都声明为联合类型。


<?php
class A{
public string|int|false $var1=1;
function fn1(string|int|false $a, false|string $b):
string|false|int { return $a;}
}$a=new A();echo $a->var1;echo $a->fn1("hello",false);
复制代码


输出如下:


1hello
复制代码


如果在联合类型中使用了bool,则不能再使用false,那会被认为是重复声明。考虑下面的脚本,其中一个函数在声明参数时使用了包含falsebool的联合类型。


<?phpfunction fn1(string $a, bool|string|false  $b): object {    return $b;} 
复制代码


执行上述脚本会显示如下错误信息:


重复类型false是多余的
复制代码

联合类型中的 class 类型


Class 类型可以用于联合类型。如下所示,在联合类型中使用 class 类型A


<?php
class A{} function fn1(string|int|A $a, array|A $b): A|string { return $a;}$a=new A();var_dump(fn1($a,$a));
复制代码


输出如下:


object(A)#1 (0) { }
复制代码


但是,如果联合类型中使用了object 类型,就不能再使用 class 类型了。下面的脚本在联合类型中同时使用了 class 类型和object


<?php
class A{} function fn1(object|A $a, A $b): A { return $a;}
复制代码


执行上述脚本会显示如下错误信息:


类型A|object中同时包含object和class类型,这是多余的
复制代码


如果联合类型中使用了iterable,则不能再使用arrayTraversable。下面的脚本在联合类型中同时使用了arrayiterable


<?php
function fn1(object $a, iterable|array $b): iterable { }
复制代码


执行上述脚本会显示如下错误信息:


类型iterable|array中同时包含iterable和array类型,这是多余的
复制代码

联合类型与类继承

如果一个类继承了另一个类,那么联合类型可以单独声明两个类,或者只声明超类。例如,在下面的脚本中,类C继承了类B,类B又继承了类A。然后,在声明联合类型的函数参数时把类ABC都包含了进去。


<?php
class A {function fn1(){ return "Class A object";}}
class B extends A {function fn1(){ return "Class B object";}}
class C extends B {function fn1(){ return "Class C object"; }}
$c = new C();
function fn1(A|B|C $a, A|B|C $b): string { return $a->fn1();}
echo fn1($c,$c);
复制代码


输出如下:


Class C object
复制代码


或者,在声明fn1 的参数时可以只使用A 类型,输出不变:


  function fn1(A $a, A $b): string {         return $a->fn1();}
复制代码

联合类型中的 void


不能在联合类型的返回类型中使用void 。执行如下脚本:


<?php
function fn1(int|string $a, int|string $b): void|string { return $a;}
复制代码


将显示如下错误信息:


Void只能作为独立类型使用
复制代码

联合类型的隐式类型转换


前面我们提到过,如果不启用强类型,那么当一个值与联合类型中的任何类型都无法匹配时,它将转换为与之密切相关的类型。但是,哪些是密切相关的类型呢?隐式转换会按以下优先顺序进行:


  1. 整型

  2. 浮点型

  3. 字符串型

  4. 布尔型例如,在下面的脚本中,字符串值"1"将被转换成一个浮点数:


<?php
class A{
public float|bool $var1=true; }
$a= new A(); $a->var1="1";var_dump($a->var1);?>
复制代码


输出如下:


float(1)
复制代码


但是,如果联合类型中包含int,则输出为int(1)。在下面的脚本中,联合类型(int|float)变量被赋值为字符串"1.0"


<?php
class A{
public int|float $var1=1; }
$a= new A(); $a->var1="1.0";var_dump($a->var1);?>
复制代码


输出如下:


float(1)
复制代码


在下面的脚本中,字符串值"true"会被解释成string 值,因为联合类型中包含string


<?php
class A{
public float|bool|string $var1=1; }
$a= new A(); $a->var1="true";var_dump($a->var1);?>
复制代码


输出如下:


string(4) "true"
复制代码


但是,在下面的脚本中,字符串值"true"会被转换成bool 值,因为联合类型中不包含string


<?php
class A{
public float|bool $var1=1; }
$a= new A(); $a->var1="true";var_dump($a->var1);?>
复制代码


输出如下:


bool(true)
复制代码


下面这个例子的输出难以预测。这个脚本将一个字符串值赋给联合类型为int|bool|float的变量。


<?php
class A{
public int|bool|float $var1=1; }
$a= new A(); $a->var1="hello";var_dump($a->var1);?>
复制代码


输出如下:


bool(true)
复制代码


string被转换为bool值,因为它无法转换为intfloat

新增的 mixed 类型


PHP 8 引入了一个名为mixed的新类型,它相当于联合类型object |resource|array|string|int|float|bool|null。例如,在下面的脚本中,mixed被用作类属性类型、函数参数类型和函数返回类型。启用强类型是为了证明mixed不受强类型所影响。


<?phpdeclare(strict_types = 1);
class A{
public mixed $var1=1; function fn1(mixed $a):mixed{ return $a;}}
$a= new A(); var_dump($a->fn1(true));var_dump($a->var1);$a->var1="hello";var_dump($a->var1);
复制代码


显然,mixed非常灵活,可以输出不同类型:


bool(true) int(1) string(5) "hello"
复制代码


在联合类型中将其他标量类型与混合类型一起使用是多余的,因为mixed类型是所有其他标量类型的联合类型。请看下面的脚本,在一个联合类型中同时使用intmixed类型。


<?php class A{
function fn1(int|mixed $a):mixed{ return $a;}}
复制代码


执行脚本会显示如下错误信息:


类型mixed只能作为独立类型使用
复制代码


类似地,mixed 也不能和任何类类型一起使用。下面的脚本会显示同样的错误信息:


<?php class A{}class B{
function fn1(A|mixed $a):mixed{ return $a;}}
复制代码


子类的方法可以缩小返回类型mixed。例如,子类中的函数fn1将返回类型mixed缩小为array


<?php class A{public function fn1(mixed $a):mixed{ return $a;}}class B extends A{
public function fn1(mixed $a):array{ return $a;
}
复制代码

新增的独立类型 null、false 和 true


在 PHP 8.2 之前,null类型是 PHP 的单元类型,即保存单个值的类型。类似地,false类型是bool类型的字面量类型。不过,nullfalse类型只能在联合类型中使用,而不能作为独立类型使用。要证明这一点,可在 PHP 8.1 及更早的版本中运行如下脚本:


<?php
class A{
public null $var1=null;
}$a=new A();echo $a->var1;
复制代码


在 PHP 8.1 中,该脚本会输出以下错误信息:


Null不能作为独立类型使用
复制代码


类似地,为了证明在 PHP 8.1 或更早的版本中,false类型不能作为独立类型使用,可运行以下脚本:


<?php
class A{
public false $var1=false;
}
复制代码


在 PHP 8.1 中,该脚本会生成如下错误信息:


False不能作为独立类型使用
复制代码


PHP 8.2 支持将nullfalse作为独立类型来使用。下面的脚本使用null作为方法参数类型和方法返回类型。


<?php
class NullExample { public null $nil = null; public function fn1(null $v): null { return null; }}
复制代码


null不能使用?null 显式标记为可空。要证明这一点,可运行以下脚本:


<?php class NullExample {    public null $nil = null;     public function fn1(?null $v): null { return null;  }}
复制代码


该脚本会生成以下错误信息:


null不能标记为可空
复制代码


以下脚本将false 作为独立类型来使用:


<?php class FalseExample {    public false $false = false;     public function fn1(false $f): false { return false;}}
复制代码


nullfalse 可以用于联合类型,如下所示:


<?php class NullUnionExample {    public null $nil = null;     public function fn1(null $v): null|false { return null;  }}
复制代码


此外,PHP 8.2 还新增了一个类型true,它可以用作独立类型使用。下面的脚本使用true作为类属性类型、方法参数类型和方法返回类型。


<?php
class TrueExample { public true $true = true; public function f1(true $v): true { return true;}}
复制代码


true 类型不能和false一起用于联合类型,如下所示:


<?php class TrueExample {     public function f1(true $v): true|false { return true;}}
复制代码


该脚本会生成以下错误信息:


类型同时包含true和false,应该用bool来替代它们
复制代码


类似地,true 也不能和bool 一起用于联合类型,如下所示:


class TrueExample {        public function f1(true $v): true|bool { return true;}}
复制代码


该脚本会生成以下错误信息:


重复类型true是多余的
复制代码

交集类型


PHP 8.1 将交集类型作为复合类型引入。交集类型可以与类和接口类型一起使用。交集类型用于表示多个类和接口类型的类型,而不是单个类或接口类型的类型。交集类型的语法如下:


Type1&Type2...TypeN
复制代码


何时使用交集类型,何时使用联合类型?如果一个类型表示多个类型中的一个,则使用联合类型。如果一个类型要同时表示多个类型,则使用交集类型。下面这个例子很好地说明了这种差异。ABC是 3 个相互之间没有关系的类。如果一个类型要表示这些类型中的任何一个,则要使用联合类型,如下所示:


<?php
class A{ function fn1(){ return "Class A object"; }}
class B { function fn1(){ return "Class B object"; }}
class C { function fn1(){ return "Class C object"; }}
$c = new C();
function fn1(A|B|C $a, A|B|C $b): string { return $a->fn1();}
echo fn1($c,$c); ?>
复制代码


输出如下:


Class C object
复制代码


这里如果使用交集类型,就会产生错误。修改这个函数,使用交集类型:


function fn1(A&B&C $a, A&B&C $b): string {         return $a->fn1();}
复制代码


执行上述脚本会产生以下错误信息:


Uncaught TypeError: fn1(): 参数 #1 ($a)的类型必须是A&B&C,但提供了C
复制代码


如果C 继承了BB 继承了A ,就可以使用交集类型了,如下所示:


<?php
class A{ function fn1(){ return "Class A object";}}
class B extends A{ function fn1(){ return "Class B object";}}
class C extends B{ function fn1(){ return "Class C object"; }}
$c = new C();
function fn1(A&B&C $a, A&B&C $b): string { return $a->fn1();}
echo fn1($c,$c); ?>
复制代码


输出如下:


Class C object
复制代码

标量类型与交集类型


交集类型只能与类和接口类型一起使用,但不能与标量类型一起使用。为了证明这一点,修改前述脚本中的fn1函数,如下所示:


function fn1(A&B&C&string $a, A&B&C $b): string {     }
复制代码


执行上述脚本会产生以下错误信息:


string类型不能作为交集类型的一部分
复制代码

交集类型与联合类型


交集类型不能与联合类型组合使用。具体来说,在同一类型声明中,交集类型表示法不能与联合类型表示法组合使用。为了证明这一点,修改fn1函数如下:


function fn1(A&B|C $a, A&B|C $b): string {    }
复制代码


上述脚本会导致如下解析错误信息:


Parse error: 语法错误,需要变量,但意外遇到符号“|”
复制代码


在同一个函数声明中,交集类型可以与联合类型一起使用,如下所示:


function fn1(A&B&C $a, A|B|C $b): string {       }
复制代码

返回类型 static 与 never


PHP 8.0 引入了一个新的返回类型static ,PHP 8.1 引入了一个新的返回类型 never

返回类型 static


如果返回类型指定为static ,则返回值的类型必须是定义方法的类的类型。例如,类A中定义的fn1方法返回类型为static,因此,该方法必须返回类型为A的值,即声明该函数的类。


<?php
class A { public int $var1=1; public function fn1(): static { return new A(); }}
$a=new A();echo $a->fn1()->var1;
复制代码


输出如下:


1
复制代码


返回类型声明为static 的函数必须属于某个类。为了证明这一点,声明一个返回类型为static 的全局函数:


<?php
function fn1(): static { }
复制代码


上述脚本会导致如下错误:


当没有处于活动状态的类作用域时,不能使用“static”
复制代码


返回的类对象必须是外围类。下面的脚本会生成一个错误,因为返回值是类类型B,而返回类型static 要求返回类型为类型A


<?php
class B{}class A { public int $var1=1; public function fn1(): static { return new B(); }}
复制代码


上述脚本会产生以下错误:


Uncaught TypeError: A::fn1(): 返回类型必须为类型A,但返回了B 
复制代码


如果类B 继承了类A ,那么上述脚本就不会有什么问题,将输出1


class B extends A{}
复制代码


返回类型static 可用于联合类型。如果在联合类型中使用static ,则返回值不一定是类类型。例如,以下脚本在联合类型中使用了static


<?php
class A { public int $var1=1; public function fn1(): static|int { return 1; }}
$a=new A(); echo $a->fn1();
复制代码


输出如下:


 1
复制代码


类型static 不能用于交集类型。为了证明这一点,请看下面的脚本:


<?php
class B extends A{}class A { public function fn1(): static&B { return new B(); }}
复制代码


该脚本会导致以下错误:


类型static不能作为交集类型的一部分
复制代码

返回类型 never


如果返回类型为never,则函数必须不返回值,或者根本不返回,即函数不终止。返回类型never 是其他所有返回类型的子类型。也就是说,在继承一个类时,never 可以在重写方法中替换任何其他返回类型。返回never 的函数必须执行以下操作之一:


  1. 抛出一个异常

  2. 调用 exit()

  3. 启动一个无限循环如果返回never 的函数永远不会被调用,那么这个函数可以为空,如下所示:


<?php
class A{function fn1(): never { } }
复制代码


A中的函数fn1()不能被调用,因为该函数隐式返回NULL 。为了证明这一点,我们将上述脚本修改为:


<?php
class A {function fn1(): never { } }
$a=new A();$a->fn1();
复制代码


执行该脚本将生成以下错误信息:


Uncaught TypeError: A::fn1(): 返回never的函数不能隐式返回
复制代码


下面的脚本将生成同样的错误消息,因为if条件永远无法满足,而函数隐式返回NULL


<?php
function fn1(): never{ if (false) { exit(); }}fn1();
复制代码


static 返回类型不同,never可以用作不属于类作用域的函数的返回类型,例如:


<?php
class A{ } function fn1(): never { }
复制代码


返回类型为never的函数一定不能返回值。为了证明这一点,下面的脚本声明了一个函数,该函数试图返回值,尽管它的返回类型为never


<?php
function fn1(): never { return 1; }
复制代码


执行上述脚本会生成以下错误信息:


返回never的函数必须没有返回值
复制代码


如果返回类型为never,则函数即使是隐式返回也不行。例如,下述脚本中的fn1函数不返回值,而是在其作用域结束时隐式返回。


<?php
function fn1(): never { }
fn1();
复制代码


上述脚本会导致以下错误:


Uncaught TypeError: fn1(): 返回never的函数不能隐式返回
复制代码


声明返回类型为never且不会终止的函数有什么用?返回类型never 可以在开发、测试和调试期间使用。返回never类型的函数可以通过调用exit()退出。这样的函数甚至可以被调用,如下面的脚本所示:


<?php
function fn1(): never { exit(); }
fn1();
复制代码


返回never类型的函数可以抛出异常,如下所示:


<?php
function fn1(): never { throw new Exception('Exception thrown'); }
复制代码


包含无限循环的函数可以将返回类型声明为never ,如下所示:


<?php
function fn1(): never { while (1){}}
复制代码


返回类型never 可以覆盖派生类中的任何类型,如下所示:


<?phpclass A{     function fn1(): int {        }}class B extends A{function fn1(): never {      }}
复制代码


返回类型never 不能用于联合类型。为了证明这一点,下面的脚本在联合类型中使用了never


<?php class A{  function fn1(): never|int {      }}
复制代码


该脚本将导致以下错误:


never只能作为独立类型使用
复制代码


never 类型不能用于交集类型。为了证明这一点,请运行以下将never和类类型B 一起使用的脚本。


<?php class B{}class A{  function fn1(): never&B {      }}
复制代码


该脚本将导致以下错误:


类型never不能作为交集类型的一部分
复制代码

标量类型不支持别名


从 PHP 8 开始,如果使用标量类型别名,就会生成警告信息。例如,如果使用boolean代替bool,则生成一条消息,说明boolean 将被解释成类名。为了证明这一点,考虑下面的脚本,函数声明将integer作为参数类型。


<?php    function fn1(integer $param) {}    fn1(1);?>
复制代码


如下所示,该脚本的输出中将包含一条警告信息:


警告:“integer”将被解释为类名。你是指“int”吗?输入“\integer”来消除该警告
Fatal error: Uncaught TypeError: fn1():参数#1 ($param)必须是integer类型,但提供的是int
复制代码

不再支持从 void 函数通过引用返回


从 PHP 8.1 开始,不再支持从void函数通过引用返回,因为只有变量引用可以通过引用返回,而void返回类型不返回值。为了证明这一点,可运行下面的脚本:


<?phpfunction &fn1(): void {}?>
复制代码


该脚本将输出一条弃用提示信息:


弃用:不再支持从void函数通过引用返回
复制代码

小结


在本文中,我们讨论了 PHP 8 中引入的与类型相关的新特性,包括联合类型、交集类型和mixed 类型,以及返回类型staticnever 。在下一篇文章中,我们将介绍与 PHP 数组、变量、运算符和异常处理相关的新特性。


原文链接:

https://www.infoq.com/articles/php-8-type-system-improvements/


相关阅读:

PHP 8:注解、match 表达式及其他改进

PHP 8:类和枚举

2023-07-25 08:003024

评论 1 条评论

发布
用户头像
当年的屠龙的勇士,最终成为了龙
2023-07-26 14:52 · 北京
回复
没有更多了
发现更多内容

从狗狗币说起 看区块链的未来发展方向

CECBC

狗狗币

Tars Java 客户端源码分析

vivo互联网技术

Java TARS RPC架构

关于安卓设备声音远程传输的解决方案

行者AI

云平台

马丁格尔策略交易软件源码,量化策略系统开发

现代电信企业:极低延迟与复杂决策如何兼得?

VoltDB

数据分析 5G 数据平台 低延迟

腾讯T6!万字长文体系化讲解Spring源码,码农:太透彻了,学会了

牛哄哄的java大师

Java

Hive解析Json数组超全讲解

五分钟学大数据

hive 5月日更

如何在苹果M1上安装使用FL Studio

奈奈的杂社

编曲 教程分享 编曲软件

【HDC.Cloud 2021】边云协同,打通AI最后一公里

华为云原生团队

人工智能 开源 云原生 边缘计算 华为云

如何快速排查发现redis的bigkey?4种方案一次性给到你!

观测云

redis 云计算 云服务

模块2作业 微信朋友圈高性能复杂度

TH

架构实战营

智能会话机器人:SaaS 平台的设计与思考

极客志

自然语言处理 chatbot 聊天机器人 智能会话机器人

看完阿里P7技术大牛的JVM知识点总结,竟帮我斩获了3份大厂Offer

飞飞JAva

Java JVM

架构实战营 模块三作业

netspecial

架构实战营

纯干货!看了10多篇Thread详解,只有阿里P7大佬的这份才是王者

牛哄哄的java大师

Java Thread

模块2学习总结

TH

架构实战营

如何快速制作短视频?拥有这个神器,轻松搞定!

奈奈的杂社

短视频 视频剪辑 自媒体

“红黑树”详解丨红黑树的应用场景

Linux服务器开发

后端 红黑树 Linux服务器开发 Linux内核 红黑树应用场景

超详细教程:SpringBoot整合MybatisPlus

华为云开发者联盟

Java spring springboot 代码 MyBatisPlus

Offer收割机!阿里P7大神甩出JSP实战笔记,网友:信息量过大

飞飞JAva

Java

Jmeter下载与mysql简单操作

InfoQ_Springup

工具软件

运维五一不加班,从一套On-Call响应机制开始!

睿象云

运维 告警 智能运维 告警设置 告警管理

“服务可达的数据链DNA” ,打通从代码到用户的“任督二脉”

博睿数据

数据链DNA

为何“低代码”频频引发业界热议?

优秀

低代码

架构实战模块三作业

Geek_649372

架构实战营

打破思维定式(三)

Changing Lin

爬虫实战教程:采集微信公众号文章

前嗅大数据

大数据 爬虫 数据采集 爬虫教程

编程风格漫谈

顿晓

编程风格 5月日更

nmon和nmon analyser的网盘下载安装与使用

InfoQ_Springup

工具

一周信创舆情观察(4.26~5.5)

统小信uos

全新 Jira 系列,适用于所有团队!

Atlassian

DevOps 敏捷 Atlassian Jira

PHP 8:类型系统改进_编程语言_Deepak Vohra_InfoQ精选文章