TypePHP 编译器
https://swoole.com/aot/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
102 lines
3.6 KiB
102 lines
3.6 KiB
<?php
|
|
/**
|
|
* This file is part of TypePHP.
|
|
*
|
|
* @link https://www.swoole.com/
|
|
* @contact service@swoole.com
|
|
*/
|
|
|
|
namespace TypePhp\Transform;
|
|
|
|
use PhpParser\Modifiers;
|
|
use PhpParser\Node;
|
|
use PhpParser\Node\Expr;
|
|
use PhpParser\Node\Param;
|
|
use PhpParser\Node\Stmt;
|
|
use TypePhp\Exception\SyntaxError;
|
|
use TypePhp\Exception\CompileTimeAttributeError;
|
|
use TypePhp\Diagnostics\CompileTimeAttributeDiagnostic;
|
|
|
|
final class ConstructorLowering
|
|
{
|
|
public const GENERATED_ATTRIBUTE = 'typephpConstructorGenerated';
|
|
|
|
public static function validateTarget(Node $node): void
|
|
{
|
|
if (!CompileTimeAttribute::has($node, 'Constructor')) {
|
|
return;
|
|
}
|
|
if (!$node instanceof Stmt\Property || $node->isStatic()) {
|
|
throw new SyntaxError('Constructor can only be applied to instance properties');
|
|
}
|
|
}
|
|
|
|
public static function lowerClassLike(Stmt\Class_|Stmt\Trait_|Stmt\Enum_ $class): void
|
|
{
|
|
$properties = [];
|
|
$target = null;
|
|
foreach ($class->stmts as $stmt) {
|
|
if (!$stmt instanceof Stmt\Property || !CompileTimeAttribute::has($stmt, 'Constructor')) {
|
|
continue;
|
|
}
|
|
if (!$class instanceof Stmt\Class_) {
|
|
throw new SyntaxError('Constructor properties can only be declared in classes');
|
|
}
|
|
$attribute = CompileTimeAttribute::find($stmt, 'Constructor');
|
|
CompileTimeAttribute::consume($stmt, 'Constructor');
|
|
$target ??= $stmt;
|
|
foreach ($stmt->props as $property) {
|
|
$properties[] = [
|
|
$property->name->toString(),
|
|
$stmt->type,
|
|
$property->default,
|
|
$stmt->getAttributes(),
|
|
$stmt,
|
|
$attribute,
|
|
];
|
|
}
|
|
}
|
|
if ($properties === []) {
|
|
return;
|
|
}
|
|
$params = [];
|
|
$stmts = [];
|
|
$optionalSeen = false;
|
|
$optionalTarget = null;
|
|
$optionalAttribute = null;
|
|
foreach ($properties as [$name, $type, $default, $attributes, $propertyTarget, $attributeSource]) {
|
|
if ($default !== null) {
|
|
$optionalSeen = true;
|
|
$optionalTarget ??= $propertyTarget;
|
|
$optionalAttribute ??= $attributeSource;
|
|
} elseif ($optionalSeen) {
|
|
throw new CompileTimeAttributeError(
|
|
'Constructor required properties cannot follow properties with default values',
|
|
$propertyTarget,
|
|
'Constructor',
|
|
$attributeSource,
|
|
'Constructor',
|
|
$optionalAttribute ?? $optionalTarget,
|
|
);
|
|
}
|
|
$params[] = new Param(
|
|
new Expr\Variable($name),
|
|
default: $default === null ? null : clone $default,
|
|
type: $type === null ? null : clone $type,
|
|
attributes: $attributes,
|
|
);
|
|
$stmts[] = new Stmt\Expression(new Expr\Assign(
|
|
new Expr\PropertyFetch(new Expr\Variable('this'), $name),
|
|
new Expr\Variable($name),
|
|
));
|
|
}
|
|
$constructor = new Stmt\ClassMethod('__construct', [
|
|
'flags' => Modifiers::PUBLIC,
|
|
'params' => $params,
|
|
'stmts' => $stmts,
|
|
]);
|
|
$constructor->setAttribute(self::GENERATED_ATTRIBUTE, true);
|
|
CompileTimeAttributeDiagnostic::markGenerated($constructor, 'Constructor', $target ?? $class);
|
|
$class->stmts[] = $constructor;
|
|
}
|
|
}
|
|
|