π¦ΈπΏββοΈ Gato GraphQL is now transpiled from PHP 8.0 to 7.1

Some time ago, I wrote about the art of transpiling PHP code:
- 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
Transpiling PHP code enables to use the latest PHP features for development, yet release the plugin with its code converted to an older PHP version for production, as to target a bigger user base.
I spent the past few weeks further tuning this process for the Gato GraphQL plugin.
I'm happy to announce that, from now on, it's required PHP version has been upgraded, to PHP 8.0:
Since the plugin can now count on PHP 8.0, I've been able to complete adding a type to all properties for all PHP classes across the code base, now also including union types.
Awesome!
Here is the summary of all the new PHP 8.0 features available when developing the plugin.
New PHP 8.0 features
When developing Gato GraphQL, the following PHP 8.0 features are now available:
- Union types
mixed
pseudo typestatic
return type::class
magic constant on objectsmatch
expressionscatch
exceptions only by type- Null-safe operator
- Class constructor property promotion
- Trailing commas in parameter lists and closure
use
lists
Let's see an example of each, how they are used in the plugin for development, and what they get transpiled down to when generating graphql-api.zip
.
Union types
interface CustomPostTypeAPIInterface
{
public function createCustomPost(array $data): string | int | null | Error;
}
Transpiled to:
interface CustomPostTypeAPIInterface
{
public function createCustomPost(array $data)
}
mixed
pseudo type
interface CMSServiceInterface
{
public function getOption(string $option, mixed $default = false): mixed;
}
Transpiled to:
interface CMSServiceInterface
{
public function getOption(string $option, $default = false);
}
::class
magic constant on objects
foreach ($directiveResolvers as $directiveResolver) {
$directiveResolverName = $directiveResolver->getDirectiveName();
$this->directiveNameClasses[$directiveResolverName][] = $directiveResolver::class;
}
Transpiled to:
foreach ($directiveResolvers as $directiveResolver) {
$directiveResolverName = $directiveResolver->getDirectiveName();
$this->directiveNameClasses[$directiveResolverName][] = get_class($directiveResolver);
}
match
expressions
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;
}
Transpiled to:
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
exceptions only by type
try {
// ...
} catch (InvalidArgumentException) {
return sprintf(
'<p>%s</p>',
\__('Oops, the documentation for this module is not available', 'graphql-api')
);
}
Transpiled to:
try {
// ...
} catch (InvalidArgumentException $exception) {
return sprintf(
'<p>%s</p>',
\__('Oops, the documentation for this module is not available', 'graphql-api')
);
}
Null-safe operator
public function getSchemaDirectiveDeprecationDescription(TypeResolverInterface $typeResolver): ?string
{
return $this->getSchemaDefinitionResolver($typeResolver)?->getSchemaDirectiveDeprecationDescription($typeResolver);
}
Transpiled to:
public function getSchemaDirectiveDeprecationDescription(TypeResolverInterface $typeResolver): ?string
{
return $this->getSchemaDefinitionResolver($typeResolver) ? $this->getSchemaDefinitionResolver($typeResolver)->getSchemaDirectiveDeprecationDescription($typeResolver) : null;
}
Class constructor property promotion
abstract class AbstractEndpointResolver
{
function __construct(protected EndpointHelpers $endpointHelpers)
{
}
}
Transpiled to:
abstract class AbstractEndpointResolver
{
/**
* @var \GraphQLAPI\GraphQLAPI\Services\Helpers\EndpointHelpers
*/
protected $endpointHelpers;
function __construct(EndpointHelpers $endpointHelpers)
{
$this->endpointHelpers = $endpointHelpers;
}
}
use
lists Trailing commas in parameter lists and closure
public function resolveFieldTypeResolverClass(TypeResolverInterface $typeResolver, string $fieldName): ?string
{
switch ($fieldName) {
case 'accessControlLists':
return CustomPostTypeResolver::class;
}
return parent::resolveFieldTypeResolverClass(
$typeResolver,
$fieldName,
);
}
Transpiled to:
public function resolveFieldTypeResolverClass(TypeResolverInterface $typeResolver, string $fieldName): ?string
{
switch ($fieldName) {
case 'accessControlLists':
return CustomPostTypeResolver::class;
}
return parent::resolveFieldTypeResolverClass($typeResolver, $fieldName);
}