【ArchSummit】如何通过AIOps推动可量化的业务价值增长和效率提升?>>> 了解详情
写点什么

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:002862

评论 1 条评论

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

经验分享|企业该怎样利用SaaS进行企业知识管理

Baklib

易观分析《2022年中国数据安全市场数据监测报告》正式启动

易观分析

技术

“万物互联,使能千行百业”,2022 开放原子全球开源峰会 OpenAtom OpenHarmony 分论坛即将开幕

kk-OSC

开源 开放原子全球开源峰会

学习大数据技术之前做好这些准备

小谷哥

DistSQL 深度解析:打造动态化的分布式数据库

SphereEx

数据库 开源社区 ShardingSphere SphereEx #开源

java程序员培训班怎么选?

小谷哥

智能运维场景解析:如何通过异常检测发现业务系统状态异常

云智慧AIOps社区

人工智能 机器学习 异常检测 智能运维 状态管理

接口文档进化图鉴,有些古早接口文档工具,你可能都没用过

Liam

Postman 接口文档 API swagger API文档

这样优化Spring Boot,启动速度快到飞起!

艾小仙

Java 微服务 springboot Eureka 微服务治理

8个方法管理 GitHub 用户权限

SEAL安全

git GitHub 安全 软件安全 软件供应链安全

【干货】知识共享的障碍及解决方法

Geek_da0866

Tapdata 与优炫数据库完成产品兼容性互认证

tapdata

数据库 Tapdata 实时数据 交互式 优炫数据库

极客星球丨字节跳动一站式数据治理解决方案及平台架构

MobTech袤博科技

架构 运维 数据治理 全链路

跟我读论文丨Multi-Model Text Recognition Network

华为云开发者联盟

人工智能 文字识别 语言模型 视觉特征

Review 后台管理系统实战:请求参数的 2 种封装风格

掘金安东尼

前端 编程范式 7月月更

个人实战经验:数据建模 “账户数据是属于维度还是账户域 ”

松子(李博源)

数据仓库 数据建模 数据中台场景实践

tsconfig.json在配置文件中找不到任何输入,怎么办?

华为云开发者联盟

JavaScript 前端

李宏毅《机器学习》丨5. Tips for neural network design(神经网络设计技巧)

AXYZdong

机器学习 7月月更

学习java开发技术有用吗?

小谷哥

如何快速开发一个简单实用的MES系统?

优秀

MES系统

知识分享|分享一些提升企业文档管理水平的方法

Baklib

你离「TDengine 开发者大会」只差一条 SQL 语句!

TDengine

tdengine 开源 时序数据库

算法题每日一练---第4天:图像模糊问题

知心宝贝

算法 前端 后端 7月月更

为Python打包创建一个世外桃源,解决打包太大且启动慢的问题

迷彩

pyinstaller 7月月更 Python打包

wallys/new product/DR7915/MT7915+MT7975/WiFi6 MiniPCIe Module 2T2R

wallys-wifi6

阿里云技术专家郝晨栋:云上可观测能力——问题的发现与定位实践

阿里云弹性计算

DevOps 运维 可观测性

学好Web前端开发能找到好工作吗

小谷哥

活动报名:如何零基础快速上手开源的 Tapdata Live Data Platform?

tapdata

开源 开源社区 Tapdata 实时数据

鼓励企业知识共享的好处,你知道多少?

Geek_da0866

还在用 ListView?使用 AnimatedList 让列表元素动起来

岛上码农

flutter ios 安卓开发 跨平台开发 7月月更

大数据培训机构如何选择

小谷哥

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