博客
🦸🏿♂️ Gato GraphQL 现已从 PHP 8.0 转译至 7.1
前不久,我写了几篇关于 PHP 代码转译技术的文章:
- Transpiling PHP code from 8.0 to 7.x via Rector
- Coding in PHP 7.4 and deploying to 7.1 via Rector and GitHub Actions
转译 PHP 代码使我们能够在开发时使用最新的 PHP 特性,同时在发布插件时将代码转换为更旧的 PHP 版本用于生产环境,从而覆盖更广泛的用户群体。
过去几周,我一直在为 Gato GraphQL 插件进一步优化这一流程。
我很高兴地宣布,从现在起,所需的 PHP 版本已升级至 PHP 8.0:

由于插件现在可以依赖 PHP 8.0,我已经能够完成为整个代码库中所有 PHP 类的所有属性添加类型的工作,现在也包括联合类型。
太棒了!
以下是开发插件时所有可用的 PHP 8.0 新特性汇总。
PHP 8.0 新特性
在开发 Gato GraphQL 时,以下 PHP 8.0 特性现已可用:
- 联合类型
mixed伪类型static返回类型- 对象上的
::class魔术常量 match表达式- 仅按类型捕获
catch异常 - Null 安全运算符
- 类构造函数属性提升
- 参数列表和闭包
use列表中的尾逗号
让我们来看看每个特性的示例,它们在插件开发中如何使用,以及在生成 graphql-api.zip 时如何被转译。
联合类型
代码示例:
interface CustomPostTypeAPIInterface
{
public function createCustomPost(array $data): string | int | null | Error;
}转译后:
interface CustomPostTypeAPIInterface
{
public function createCustomPost(array $data)
}mixed 伪类型
代码示例:
interface CMSServiceInterface
{
public function getOption(string $option, mixed $default = false): mixed;
}转译后:
interface CMSServiceInterface
{
public function getOption(string $option, $default = false);
}对象上的 ::class 魔术常量
代码示例:
foreach ($directiveResolvers as $directiveResolver) {
$directiveResolverName = $directiveResolver->getDirectiveName();
$this->directiveNameClasses[$directiveResolverName][] = $directiveResolver::class;
}转译后:
foreach ($directiveResolvers as $directiveResolver) {
$directiveResolverName = $directiveResolver->getDirectiveName();
$this->directiveNameClasses[$directiveResolverName][] = get_class($directiveResolver);
}match 表达式
代码示例:
public function getSchemaFieldType(TypeResolverInterface $typeResolver, string $fieldName): ?string
{
$ret = match($fieldName) {
'accessControlLists' => TypeCastingHelpers::makeArray(SchemaDefinition::TYPE_ID),
'cacheControlLists' => TypeCastingHelpers::makeArray(SchemaDefinition::TYPE_ID),
'fieldDeprecationLists' => TypeCastingHelpers::makeArray(SchemaDefinition::TYPE_ID),
'schemaConfigurations' => TypeCastingHelpers::makeArray(SchemaDefinition::TYPE_ID),
default => parent::getSchemaFieldType($typeResolver, $fieldName),
};
return $ret;
}转译后:
public function getSchemaFieldType(TypeResolverInterface $typeResolver, string $fieldName): ?string
{
switch ($fieldName) {
case 'accessControlLists':
$ret = TypeCastingHelpers::makeArray(SchemaDefinition::TYPE_ID);
break;
case 'cacheControlLists':
$ret = TypeCastingHelpers::makeArray(SchemaDefinition::TYPE_ID);
break;
case 'fieldDeprecationLists':
$ret = TypeCastingHelpers::makeArray(SchemaDefinition::TYPE_ID);
break;
case 'schemaConfigurations':
$ret = TypeCastingHelpers::makeArray(SchemaDefinition::TYPE_ID);
break;
default:
$ret = parent::getSchemaFieldType($typeResolver, $fieldName);
break;
}
return $ret;
}仅按类型捕获 catch 异常
代码示例:
try {
// ...
} catch (InvalidArgumentException) {
return sprintf(
'<p>%s</p>',
\__('Oops, the documentation for this module is not available', 'graphql-api')
);
}转译后:
try {
// ...
} catch (InvalidArgumentException $exception) {
return sprintf(
'<p>%s</p>',
\__('Oops, the documentation for this module is not available', 'graphql-api')
);
}Null 安全运算符
代码示例:
public function getSchemaDirectiveDeprecationDescription(TypeResolverInterface $typeResolver): ?string
{
return $this->getSchemaDefinitionResolver($typeResolver)?->getSchemaDirectiveDeprecationDescription($typeResolver);
}转译后:
public function getSchemaDirectiveDeprecationDescription(TypeResolverInterface $typeResolver): ?string
{
return $this->getSchemaDefinitionResolver($typeResolver) ? $this->getSchemaDefinitionResolver($typeResolver)->getSchemaDirectiveDeprecationDescription($typeResolver) : null;
}类构造函数属性提升
代码示例:
abstract class AbstractEndpointResolver
{
function __construct(protected EndpointHelpers $endpointHelpers)
{
}
}转译后:
abstract class AbstractEndpointResolver
{
/**
* @var \GraphQLAPI\GraphQLAPI\Services\Helpers\EndpointHelpers
*/
protected $endpointHelpers;
function __construct(EndpointHelpers $endpointHelpers)
{
$this->endpointHelpers = $endpointHelpers;
}
}参数列表和闭包 use 列表中的尾逗号
代码示例:
public function resolveFieldTypeResolverClass(TypeResolverInterface $typeResolver, string $fieldName): ?string
{
switch ($fieldName) {
case 'accessControlLists':
return CustomPostTypeResolver::class;
}
return parent::resolveFieldTypeResolverClass(
$typeResolver,
$fieldName,
);
}转译后:
public function resolveFieldTypeResolverClass(TypeResolverInterface $typeResolver, string $fieldName): ?string
{
switch ($fieldName) {
case 'accessControlLists':
return CustomPostTypeResolver::class;
}
return parent::resolveFieldTypeResolverClass($typeResolver, $fieldName);
}