phabel/src/Plugin.php

278 lines
7.1 KiB
PHP
Raw Normal View History

2020-08-03 21:31:32 +02:00
<?php
namespace Phabel;
2020-08-09 16:23:01 +02:00
use Phabel\PluginGraph\PackageContext;
2020-08-03 21:31:32 +02:00
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
2020-08-09 15:14:32 +02:00
use PhpParser\Node\Expr\Assign;
2020-08-03 21:31:32 +02:00
use PhpParser\Node\Expr\FuncCall;
2020-08-23 20:54:56 +02:00
use PhpParser\Node\Expr\MethodCall;
2020-08-03 21:31:32 +02:00
use PhpParser\Node\Expr\StaticCall;
2020-08-09 15:14:32 +02:00
use PhpParser\Node\Expr\Variable;
2020-08-03 21:31:32 +02:00
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Expression;
use PhpParser\ParserFactory;
use ReflectionClass;
2020-08-13 18:30:12 +02:00
/**
* @author Daniil Gentili <daniil@daniil.it>
*/
2020-08-03 21:31:32 +02:00
abstract class Plugin implements PluginInterface
{
/**
* Configuration array.
*/
private array $config = [];
2020-08-09 16:23:01 +02:00
/**
2020-08-13 21:34:59 +02:00
* Package context.
2020-08-09 16:23:01 +02:00
*/
private PackageContext $ctx;
2020-08-09 13:49:19 +02:00
/**
* Set configuration array.
*
* @param array $config
* @return void
*/
2020-08-13 18:30:12 +02:00
public function setConfigArray(array $config): void
2020-08-09 13:49:19 +02:00
{
$this->config = $config;
}
2020-08-09 16:23:01 +02:00
/**
2020-08-13 21:34:59 +02:00
* Set package context.
2020-08-09 16:23:01 +02:00
*
* @param PackageContext $ctx Ctx
2020-08-13 21:34:59 +02:00
*
2020-08-09 16:23:01 +02:00
* @return void
*/
public function setPackageContext(PackageContext $ctx): void
{
$this->ctx = $ctx;
}
/**
2020-08-13 21:34:59 +02:00
* Get package context.
2020-08-09 16:23:01 +02:00
*
* @return PackageContext
*/
public function getPackageContext(): PackageContext
{
return $this->ctx;
}
/**
2020-08-13 21:34:59 +02:00
* Check if plugin should run.
2020-08-09 16:23:01 +02:00
*
* @param string $package Package name
2020-08-13 21:34:59 +02:00
*
2020-08-09 16:23:01 +02:00
* @return boolean
*/
public function shouldRun(string $package): bool
{
return $this->ctx->has($package);
}
/**
2020-08-13 21:34:59 +02:00
* Check if plugin should run.
2020-08-09 16:23:01 +02:00
*
* @param string $file File name
2020-08-13 21:34:59 +02:00
*
2020-08-09 16:23:01 +02:00
* @return boolean
*/
public function shouldRunFile(string $file): bool
{
return true;
}
2020-08-03 21:31:32 +02:00
/**
* Replace node of one type with another.
*
* @param Node $node Original node
* @param string $class Class of new node
* @param array $propertyMap Property map between old and new objects
*
* @psalm-param class-string<Node> $class Class of new node
* @psalm-param array<string, string> $propertyMap Property map between old and new objects
*
* @return Node
*/
public static function replaceType(Node $node, string $class, array $propertyMap = []): Node
{
if ($propertyMap) {
/** @var Node */
$nodeNew = (new ReflectionClass($class))->newInstanceWithoutConstructor();
foreach ($propertyMap as $old => $new) {
$nodeNew->{$new} = $node->{$old};
}
$nodeNew->setAttributes($node->getAttributes());
return $nodeNew;
}
return new $class(
...\array_map(fn (string $name) => $node->{$name}, $node->getSubNodeNames()),
$node->getAttributes()
);
}
/**
* Replace type in-place.
*
* @param Node &$node Original node
* @param string $class Class of new node
* @param array $propertyMap Property map between old and new objects
*
* @psalm-param class-string<Node> $class Class of new node
* @psalm-param array<string, string> $propertyMap Property map between old and new objects
*
* @param-out Node &$node
*
* @return void
*/
public static function replaceTypeInPlace(Node &$node, string $class, array $propertyMap = []): void
{
$node = self::replaceType($node, $class, $propertyMap);
}
2020-08-09 15:14:32 +02:00
/**
2020-08-13 21:34:59 +02:00
* Create variable assignment.
2020-08-09 15:14:32 +02:00
*
* @param Variable $name Variable
* @param Expr $expression Expression
2020-08-13 21:34:59 +02:00
*
2020-08-09 15:14:32 +02:00
* @return Expression
*/
public static function assign(Variable $name, Expr $expression): Expression
{
return new Expression(
new Assign(
$name,
$expression
)
);
}
2020-08-03 21:31:32 +02:00
/**
* Call function.
*
2020-08-23 20:54:56 +02:00
* @param class-string|array{0: class-string, 1: string}|callable-string $name Function name
* @param Expr|Arg ...$parameters Parameters
2020-08-03 21:31:32 +02:00
*
* @return FuncCall|StaticCall
*/
public static function call($name, ...$parameters)
{
$parameters = \array_map(fn ($data) => $data instanceof Arg ? $data : new Arg($data), $parameters);
return \is_array($name) ? new StaticCall(new Name($name[0]), $name[1], $parameters) : new FuncCall(new Name($name), $parameters);
}
/**
* Call polyfill function from current plugin.
*
* @param string $name Function name
* @param Expr|Arg ...$parameters Parameters
*
* @return StaticCall
*/
protected static function callPoly(string $name, ...$parameters): StaticCall
{
return self::call([static::class, $name], ...$parameters);
}
2020-08-23 20:54:56 +02:00
/**
* Call method of object.
*
* @param Expr $name Object name
* @param string $method Method
* @param Expr|Arg ...$parameters Parameters
*
* @return MethodCall
*/
public static function callMethod(Expr $name, string $method, ...$parameters): MethodCall
{
$parameters = \array_map(fn ($data) => $data instanceof Arg ? $data : new Arg($data), $parameters);
return new MethodCall($name, $method, $parameters);
}
2020-08-03 21:31:32 +02:00
/**
* Convert array, int or other literal to node.
*
* @param mixed $data Data to convert
*
* @return Node
*/
public static function toLiteral($data): Node
{
2020-08-14 20:40:01 +02:00
return self::toNode(\var_export($data, true));
}
/**
* Convert code to node.
*
* @param string $code Code
*
* @memoize $code
*
* @return Node
*/
public static function toNode(string $code): Node
{
$res = (new ParserFactory)->create(ParserFactory::PREFER_PHP7)->parse('<?php '.$code);
2020-08-03 21:31:32 +02:00
if ($res === null || empty($res) || !$res[0] instanceof Expression || !isset($res[0]->expr)) {
2020-08-14 20:40:01 +02:00
throw new \RuntimeException('Invalid code was provided!');
2020-08-03 21:31:32 +02:00
}
2020-08-14 20:40:01 +02:00
return $res[0]->expr;
2020-08-03 21:31:32 +02:00
}
/**
* {@inheritDoc}
*/
2020-08-05 14:52:49 +02:00
public function getConfig(string $key, $default)
2020-08-03 21:31:32 +02:00
{
2020-08-05 14:52:49 +02:00
return $this->config[$key] ?? $default;
2020-08-03 21:31:32 +02:00
}
/**
* {@inheritDoc}
*/
public function setConfig(string $key, $value): void
{
$this->config[$key] = $value;
}
/**
* {@inheritDoc}
*/
2020-08-05 14:52:49 +02:00
public static function mergeConfigs(array ...$configs): array
{
return $configs;
}
2020-08-13 18:30:12 +02:00
/**
* {@inheritDoc}
*/
public static function splitConfig(array $config): array
{
return [$config];
}
2020-08-05 14:52:49 +02:00
/**
* {@inheritDoc}
*/
public static function composerRequires(): array
{
return [];
}
/**
* {@inheritDoc}
*/
2020-08-09 13:49:19 +02:00
public static function runAfter(): array
{
return [];
}
/**
* {@inheritDoc}
*/
public static function runBefore(): array
{
return [];
}
/**
* {@inheritDoc}
*/
public static function runWithBefore(): array
2020-08-03 21:31:32 +02:00
{
return [];
}
/**
* {@inheritDoc}
*/
2020-08-09 13:49:19 +02:00
public static function runWithAfter(): array
2020-08-03 21:31:32 +02:00
{
return [];
}
}