ブログ
🦸🏿♂️ 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);
}