Commence writing resolution logic
This commit is contained in:
parent
c43eac2b19
commit
70710fb26d
|
@ -113,9 +113,9 @@ abstract class Plugin implements PluginInterface
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getConfig(string $key)
|
||||
public function getConfig(string $key, $default)
|
||||
{
|
||||
return $this->config[$key];
|
||||
return $this->config[$key] ?? $default;
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
|
@ -127,14 +127,28 @@ abstract class Plugin implements PluginInterface
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function needs()
|
||||
public static function mergeConfigs(array ...$configs): array
|
||||
{
|
||||
return $configs;
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function composerRequires(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function extends()
|
||||
public static function needs(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function extends(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -26,13 +26,12 @@ class TypeHintStripper extends Plugin
|
|||
if (!$type || $type instanceof UnionType) {
|
||||
return;
|
||||
}
|
||||
if ($type instanceof NullableType && $this->getConfig('nullable')) {
|
||||
if ($type instanceof NullableType && $this->getConfig('nullable', false)) {
|
||||
$type = null;
|
||||
return;
|
||||
}
|
||||
$throwableType = $type instanceof NullableType ? $type->type : $type;
|
||||
// Make this less ugly when we implement a namespace context
|
||||
if (\in_array($throwableType->toString(), $this->getConfig('types'))) {
|
||||
if (\in_array($throwableType->toString(), $this->getConfig('types', []))) {
|
||||
$type = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Phabel\PluginGraph;
|
||||
|
||||
use Phabel\PluginInterface;
|
||||
|
||||
class Graph
|
||||
{
|
||||
/**
|
||||
* Plugin nodes, indexed by plugin name+config.
|
||||
*
|
||||
* @var array<class-string<PluginInterface>, array<string, Node>>
|
||||
*/
|
||||
private array $plugins = [];
|
||||
|
||||
/**
|
||||
* Package contexts.
|
||||
*
|
||||
* @var PackageContext[]
|
||||
*/
|
||||
private array $packageContexts = [];
|
||||
|
||||
/**
|
||||
* Get new package context.
|
||||
*
|
||||
* @return PackageContext
|
||||
*/
|
||||
public function getPackageContext(): PackageContext
|
||||
{
|
||||
$packageContext = new PackageContext;
|
||||
$this->packageContexts []= $packageContext;
|
||||
return $packageContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add plugin.
|
||||
*
|
||||
* @param string $plugin Plugin to add
|
||||
* @param array $config Plugin configuration
|
||||
* @param PackageContext $ctx Package context
|
||||
*
|
||||
* @psalm-param class-string<PluginInterface> $plugin Plugin name
|
||||
*
|
||||
* @return Node[]
|
||||
*/
|
||||
public function addPlugin(string $plugin, array $config, PackageContext $ctx): array
|
||||
{
|
||||
$configs = $plugin::splitConfig($config);
|
||||
$nodes = [];
|
||||
foreach ($configs as $config) {
|
||||
$nodes []= $this->addPluginInternal($plugin, $config, $ctx);
|
||||
}
|
||||
return $nodes;
|
||||
}
|
||||
/**
|
||||
* Add plugin.
|
||||
*
|
||||
* @param string $plugin Plugin to add
|
||||
* @param array $config Plugin configuration
|
||||
* @param PackageContext $ctx Package context
|
||||
*
|
||||
* @psalm-param class-string<PluginInterface> $plugin Plugin name
|
||||
*
|
||||
* @return Node
|
||||
*/
|
||||
private function addPluginInternal(string $plugin, array $config, PackageContext $ctx): Node
|
||||
{
|
||||
$configStr = \var_export($config, true);
|
||||
if (isset($this->plugins[$plugin][$configStr])) {
|
||||
return $this->plugins[$plugin][$configStr]->addPackageContext($ctx);
|
||||
}
|
||||
$this->plugins[$plugin][$configStr] = $node = new Node;
|
||||
return $node->init($this, $plugin, $config, $ctx);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
|
||||
namespace Phabel\PluginGraph;
|
||||
|
||||
use Phabel\PluginInterface;
|
||||
use SplObjectStorage;
|
||||
|
||||
/**
|
||||
* Represents a plugin with a certain configuration.
|
||||
*/
|
||||
class Node
|
||||
{
|
||||
/**
|
||||
* Plugin name.
|
||||
*/
|
||||
private string $plugin = '';
|
||||
/**
|
||||
* Plugin configuration.
|
||||
*/
|
||||
private array $config = [];
|
||||
|
||||
/**
|
||||
* Associated package contexts.
|
||||
*/
|
||||
private SplObjectStorage $packageContexts;
|
||||
/**
|
||||
* Nodes that this node requires.
|
||||
*
|
||||
* @var Node[]
|
||||
*/
|
||||
private array $requires = [];
|
||||
|
||||
/**
|
||||
* Nodes that this node extends.
|
||||
*
|
||||
* @var Node[]
|
||||
*/
|
||||
private array $extends = [];
|
||||
|
||||
/**
|
||||
* Nodes that require this node.
|
||||
*
|
||||
* @var Node[]
|
||||
*/
|
||||
private array $requiredBy = [];
|
||||
|
||||
/**
|
||||
* Nodes that extend this node.
|
||||
*
|
||||
* @var Node[]
|
||||
*/
|
||||
private array $extendedBy = [];
|
||||
|
||||
/**
|
||||
* Whether this node was visited when looking for circular requirement references.
|
||||
*/
|
||||
private bool $visitedRequires = false;
|
||||
/**
|
||||
* Whether this node was visited when looking for circular requirement references.
|
||||
*/
|
||||
private bool $visitedExtends = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->packageContexts = new SplObjectStorage;
|
||||
}
|
||||
/**
|
||||
* Initialization function.
|
||||
*
|
||||
* @param Graph $graph Graph instance
|
||||
* @param string $plugin Plugin name
|
||||
* @param array $config Plugin configuration
|
||||
* @param PackageContext $ctx Context
|
||||
*
|
||||
* @psalm-param class-string<PluginInterface> $plugin Plugin name
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function init(Graph $graph, string $plugin, array $config, PackageContext $ctx): self
|
||||
{
|
||||
$this->plugin = $plugin;
|
||||
$this->config = $config;
|
||||
$this->packageContexts->attach($ctx);
|
||||
|
||||
$requirements = self::simplify($plugin::needs());
|
||||
$extends = self::simplify($plugin::extends());
|
||||
|
||||
foreach ($requirements as $class => $config) {
|
||||
foreach ($graph->addPlugin($class, $config, $ctx) as $node) {
|
||||
$this->requires []= $node;
|
||||
$node->requiredBy []= $this;
|
||||
}
|
||||
}
|
||||
foreach ($extends as $class => $config) {
|
||||
foreach ($graph->addPlugin($class, $config, $ctx) as $node) {
|
||||
$this->extends []= $node;
|
||||
$node->extendedBy []= $this;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplify requirements.
|
||||
*
|
||||
* @param (array<class-string<PluginInterface>, array>|class-string<PluginInterface>[]) $requirements Requirements
|
||||
*
|
||||
* @return array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
private static function simplify(array $requirements): array
|
||||
{
|
||||
return isset($requirements[0]) ? \array_fill_keys($requirements, []) : $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add package context.
|
||||
*
|
||||
* @param PackageContext $ctx Context
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addPackageContext(PackageContext $ctx): self
|
||||
{
|
||||
if ($this->packageContexts->contains($ctx)) {
|
||||
return $this;
|
||||
}
|
||||
$this->packageContexts->attach($ctx);
|
||||
foreach ($this->requires as $node) {
|
||||
$node->addPackageContext($ctx);
|
||||
}
|
||||
foreach ($this->extends as $node) {
|
||||
$node->addPackageContext($ctx);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this node requires only one other node.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function oneRequire(): bool
|
||||
{
|
||||
return \count($this->requires) === 1 && empty($this->extends);
|
||||
}
|
||||
/**
|
||||
* Check if this node extends only one other node.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function oneExtend(): bool
|
||||
{
|
||||
return \count($this->extends) === 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace Phabel\PluginGraph;
|
||||
|
||||
/**
|
||||
* List of packages associated with plugin.
|
||||
*/
|
||||
class PackageContext
|
||||
{
|
||||
/**
|
||||
* Package list.
|
||||
*
|
||||
* @var array<string, null>
|
||||
*/
|
||||
private array $packages = [];
|
||||
/**
|
||||
* Add package.
|
||||
*
|
||||
* @param string $package Package
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPackage(string $package): void
|
||||
{
|
||||
$this->packages[$package] = null;
|
||||
}
|
||||
/**
|
||||
* Merge two contexts
|
||||
*
|
||||
* @param self $other Other context
|
||||
*
|
||||
* @return self New context
|
||||
*/
|
||||
public function merge(self $other): self
|
||||
{
|
||||
$this->packages += $other->packages;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get package list
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPackages(): array
|
||||
{
|
||||
return array_values($this->packages);
|
||||
}
|
||||
}
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace Phabel;
|
||||
|
||||
use PhpParser\NodeVisitor;
|
||||
|
||||
interface PluginInterface
|
||||
{
|
||||
/**
|
||||
|
@ -14,40 +12,67 @@ interface PluginInterface
|
|||
*
|
||||
* When possible, use the extends method to reduce complexity.
|
||||
*
|
||||
* @return array|string Plugin name(s)
|
||||
* @return array Plugin name(s)
|
||||
*
|
||||
* @psalm-return array<class-string<Plugin|NodeVisitor>>|class-string<Plugin|NodeVisitor>
|
||||
* @psalm-return class-string<PluginInterface>[]|array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
public function needs();
|
||||
public static function needs(): array;
|
||||
/**
|
||||
* Specify which plugins does this plugin extends.
|
||||
* Specify which plugins does this plugin extend.
|
||||
*
|
||||
* At each depth level, the traverser will first execute the enter|leave methods of the specified plugins, then immediately execute the enter|leave methods of the current plugin.
|
||||
*
|
||||
* This is preferred, and allows only a single traversal of the AST.
|
||||
*
|
||||
* @return array|string Plugin name(s)
|
||||
* @return array Plugin name(s)
|
||||
*
|
||||
* @psalm-return array<class-string<Plugin|NodeVisitor>>|class-string<Plugin|NodeVisitor>
|
||||
* @psalm-return class-string<PluginInterface>[]|array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
public function extends();
|
||||
public static function extends(): array;
|
||||
|
||||
/**
|
||||
* Get configuration key
|
||||
* Specify a list of composer dependencies.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function composerRequires(): array;
|
||||
/**
|
||||
* Get configuration key.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @param mixed $default Default value, if key is not present
|
||||
*
|
||||
* @param string $key Key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $key);
|
||||
public function getConfig(string $key, $default);
|
||||
|
||||
/**
|
||||
* Set configuration key
|
||||
* Set configuration key.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @param mixed $value Value
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setConfig(string $key, $value): void;
|
||||
|
||||
/**
|
||||
* Merge multiple configurations into one (or more).
|
||||
*
|
||||
* @param array ...$configs Configurations
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public static function mergeConfigs(array ...$configs): array;
|
||||
/**
|
||||
* Split configuration.
|
||||
*
|
||||
* For example, if you have a configuration that enables feature A, B and C, return three configuration arrays each enabling ONLY A, only B and only C.
|
||||
* This is used for optimizing the AST traversing process during resolution of the plugin graph.
|
||||
*
|
||||
* @param array $config Configuration
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public static function splitConfig(array $config): array;
|
||||
}
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Spatie\Php7to5\NodeVisitors;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
|
||||
class MethodCallReplacer extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if (!$node instanceof Node\Expr\MethodCall) {
|
||||
return;
|
||||
}
|
||||
$value = &$node->var;
|
||||
if (!$value instanceof Node\Expr\Clone_ &&
|
||||
!$value instanceof Node\Expr\Yield_ &&
|
||||
!$value instanceof Node\Expr\Closure
|
||||
) {
|
||||
return;
|
||||
}
|
||||
$value = new Node\Expr\FuncCall(new Node\Name('\\returnMe'), [$value]);
|
||||
return $node;
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ use PhpParser\Node;
|
|||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\Isset_;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\Yield_;
|
||||
|
||||
/**
|
||||
|
@ -37,7 +38,7 @@ class CompoundAccess extends Plugin
|
|||
* Fix yield array access.
|
||||
*
|
||||
* @param ArrayDimFetch $node Node
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enterArrayYield(ArrayDimFetch $node): void
|
||||
|
@ -48,10 +49,10 @@ class CompoundAccess extends Plugin
|
|||
$node->var = self::callPoly('returnMe', $node->var);
|
||||
}
|
||||
/**
|
||||
* Fix yield array access
|
||||
* Fix yield array access.
|
||||
*
|
||||
* @param Yield_ $node Yield
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enterYield(Yield_ $node): void
|
||||
|
@ -61,10 +62,28 @@ class CompoundAccess extends Plugin
|
|||
return;
|
||||
}
|
||||
if ($value instanceof Node\Expr\FuncCall ||
|
||||
$value instanceof Node\Expr\MethodCall ||
|
||||
$value instanceof Node\Expr\StaticCall ||
|
||||
$value instanceof Node\Scalar
|
||||
) {
|
||||
$value instanceof Node\Expr\MethodCall ||
|
||||
$value instanceof Node\Expr\StaticCall ||
|
||||
$value instanceof Node\Scalar
|
||||
) {
|
||||
return;
|
||||
}
|
||||
$value = self::callPoly('returnMe', $value);
|
||||
}
|
||||
/**
|
||||
* Replace method call on yielded|cloned|closure object.
|
||||
*
|
||||
* @param MethodCall $node Method call
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enterMethodCall(MethodCall $node): void
|
||||
{
|
||||
$value = &$node->var;
|
||||
if (!$value instanceof Node\Expr\Clone_ &&
|
||||
!$value instanceof Node\Expr\Yield_ &&
|
||||
!$value instanceof Node\Expr\Closure
|
||||
) {
|
||||
return;
|
||||
}
|
||||
$value = self::callPoly('returnMe', $value);
|
||||
|
|
|
@ -10,10 +10,10 @@ use PhpParser\Node\Expr\StaticCall;
|
|||
class NullCoalesceReplacer extends Plugin
|
||||
{
|
||||
/**
|
||||
* Replace null coalesce
|
||||
* Replace null coalesce.
|
||||
*
|
||||
* @param Coalesce $node Coalesce
|
||||
*
|
||||
*
|
||||
* @return StaticCall
|
||||
*/
|
||||
public function enter(Coalesce $node): StaticCall
|
||||
|
@ -33,6 +33,6 @@ class NullCoalesceReplacer extends Plugin
|
|||
*/
|
||||
public static function coalesce($ifNotNull, $then)
|
||||
{
|
||||
return isset($ifNotNull) ?: $then;
|
||||
return isset($ifNotNull) ? $ifNotNull : $then;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ class ScalarTypeHintsRemover extends Plugin
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public function needs(): array
|
||||
public static function needs(): array
|
||||
{
|
||||
return [
|
||||
TypeHintStripper::class => [
|
||||
|
|
|
@ -78,7 +78,7 @@ class ThrowableReplacer extends Plugin
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public function extends(): array
|
||||
public static function extends(): array
|
||||
{
|
||||
return [
|
||||
TypeHintStripper::class => [
|
||||
|
|
|
@ -3,10 +3,7 @@
|
|||
namespace Phabel\Target\Php71;
|
||||
|
||||
use Phabel\Plugin;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Stmt\ClassConst;
|
||||
use PhpParser\NodeVisitor\NameResolver;
|
||||
|
||||
/**
|
||||
* Removes the class constant visibility modifiers (PHP 7.1).
|
||||
|
@ -17,19 +14,11 @@ class ClassConstantVisibilityModifiersRemover extends Plugin
|
|||
* Makes public private and protected class constants.
|
||||
*
|
||||
* @param ClassConst $node Constant
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enter(ClassConst $node): void
|
||||
{
|
||||
$node->flags = 0; // Remove constant modifier
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function needs(): string
|
||||
{
|
||||
return NameResolver::class;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,8 +110,8 @@ class ListKey extends Plugin
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function needs(): string
|
||||
public static function needs(): array
|
||||
{
|
||||
return ArrayList::class;
|
||||
return [ArrayList::class];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,8 +42,8 @@ class MultipleCatchReplacer extends Plugin
|
|||
*
|
||||
* @psalm-return class-string
|
||||
*/
|
||||
public function extends(): string
|
||||
public static function extends(): string
|
||||
{
|
||||
return ThrowableReplacer::class;
|
||||
return [ThrowableReplacer::class];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ class NullableTypeRemover extends Plugin
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public function needs(): array
|
||||
public static function needs(): array
|
||||
{
|
||||
return [
|
||||
TypeHintStripper::class => [
|
||||
|
|
|
@ -4,5 +4,126 @@ namespace Phabel;
|
|||
|
||||
class Traverser
|
||||
{
|
||||
|
||||
}
|
||||
/**
|
||||
* Plugins by level.
|
||||
*
|
||||
* @var array<int, array<class-string<PluginInterface>, array>>
|
||||
*/
|
||||
private array $plugins = [];
|
||||
/**
|
||||
* Excluded plugins by level.
|
||||
*
|
||||
* @var array<int, array<class-string<PluginInterface>, bool>>
|
||||
*/
|
||||
private array $excludedPlugins = [];
|
||||
/**
|
||||
* Files indexed by level.
|
||||
*
|
||||
* @var array<int, string[]>
|
||||
*/
|
||||
private array $files = [];
|
||||
/**
|
||||
* Add plugin at a certain dependency level.
|
||||
*
|
||||
* @param class-string<PluginInterface> $plugin Plugin to add
|
||||
* @param array $config Plugin configuration
|
||||
* @param integer $level Dependency level
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPlugin(string $plugin, array $config, int $level): void
|
||||
{
|
||||
$this->plugins[$level][$plugin] = $config;
|
||||
}
|
||||
/**
|
||||
* Exclude plugin at a certain dependency level.
|
||||
*
|
||||
* @param class-string<PluginInterface> $plugin Plugin to exclude
|
||||
* @param bool $excludeNextLevels Whether to exclude plugin from next levels, too
|
||||
* @param integer $level Dependency level
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function excludePlugin(string $plugin, bool $excludeNextLevels, int $level): void
|
||||
{
|
||||
$this->excludedPlugins[$level][$plugin] = $excludeNextLevels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param integer $level
|
||||
* @return void
|
||||
*/
|
||||
public function addFile(string $path, int $level): void
|
||||
{
|
||||
if (\in_array($path, $this->files[$level])) {
|
||||
return;
|
||||
}
|
||||
$this->files[$level][] = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start traversing files.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function traverse(): void
|
||||
{
|
||||
}
|
||||
|
||||
private function resolveCycle(string $class, bool $need, array &$stack): void
|
||||
{
|
||||
|
||||
$allPlugins = [];
|
||||
foreach ($this->plugins as $level => $plugins) {
|
||||
foreach ($plugins as $plugin => $config) {
|
||||
$needs = self::simpleNeeds($plugin);
|
||||
foreach ($needs as $class => $config) {
|
||||
if ()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplify need requirements
|
||||
*
|
||||
* @param class-string<PluginInterface> $class Class to resolve
|
||||
*
|
||||
* @return array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
private static function simpleNeeds(string $class): array
|
||||
{
|
||||
/**
|
||||
* @var array<class-string<PluginInterface>, array>[]
|
||||
*/
|
||||
static $cache = [];
|
||||
if (isset($cache[$class])) {
|
||||
return $cache[$class];
|
||||
}
|
||||
$needs = $class::needs();
|
||||
return $cache[$class] = isset($needs[0]) ? array_fill_keys($needs, []) : $needs
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplify extend requirements
|
||||
*
|
||||
* @param class-string<PluginInterface> $class Class to resolve
|
||||
*
|
||||
* @return array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
private static function simpleExtends(string $class): array
|
||||
{
|
||||
/**
|
||||
* @var array<class-string<PluginInterface>, array>[]
|
||||
*/
|
||||
static $cache = [];
|
||||
if (isset($cache[$class])) {
|
||||
return $cache[$class];
|
||||
}
|
||||
$needs = $class::extends();
|
||||
return $cache[$class] = isset($needs[0]) ? array_fill_keys($needs, []) : $needs
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
namespace Phabel;
|
||||
|
||||
class TraverserConfig
|
||||
{
|
||||
/**
|
||||
* Plugin configurations, indexed by level.
|
||||
*
|
||||
* @var array<int, array<class-string<PluginInterface>, array<0: bool, 1: array>[]>>
|
||||
*/
|
||||
private array $plugins = [];
|
||||
/**
|
||||
* Excluded plugins by level.
|
||||
*
|
||||
* @var array<int, array<class-string<PluginInterface>, array[]>>
|
||||
*/
|
||||
private array $excludedPlugins = [];
|
||||
/**
|
||||
* Files indexed by level.
|
||||
*
|
||||
* @var array<int, string[]>
|
||||
*/
|
||||
private array $files = [];
|
||||
/**
|
||||
* Add plugin at a certain dependency level.
|
||||
*
|
||||
* @param class-string<PluginInterface> $plugin Plugin to add
|
||||
* @param array $config Plugin configuration
|
||||
* @param integer $level Dependency level
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPlugin(string $plugin, array $config, int $level): void
|
||||
{
|
||||
$this->plugins[$level][$plugin] []= $config;
|
||||
}
|
||||
/**
|
||||
* Get all plugins at a certain level.
|
||||
*
|
||||
* @param integer $level Level
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPlugins(int $level): array
|
||||
{
|
||||
return $this->plugins[$level];
|
||||
}
|
||||
/**
|
||||
* Exclude plugin at a certain dependency level.
|
||||
*
|
||||
* @param class-string<PluginInterface> $plugin Plugin to exclude
|
||||
* @param bool $excludeNextLevels Whether to exclude plugin from next levels, too
|
||||
* @param integer $level Dependency level
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function excludePlugin(string $plugin, bool $excludeNextLevels, int $level): void
|
||||
{
|
||||
$this->excludedPlugins[$level][$plugin][] = $excludeNextLevels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get final plugin array.
|
||||
*
|
||||
* @return array<int, array<class-string<PluginInterface, array>>>
|
||||
*/
|
||||
public function final(): array
|
||||
{
|
||||
\ksort($this->plugins);
|
||||
\ksort($this->excludedPlugins);
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Trickle plugins down the dependency tree.
|
||||
*
|
||||
* @return void Insert 1% joke
|
||||
*/
|
||||
private function trickleDown(): void
|
||||
{
|
||||
$met = [];
|
||||
$maxLevel = \array_key_last($this->plugins);
|
||||
foreach ($this->plugins as $level => $plugins) {
|
||||
foreach ($plugins as $plugin => $config) {
|
||||
$found = false;
|
||||
for ($checkLevel = $level + 1; $checkLevel <= $maxLevel; $checkLevel++) {
|
||||
if (isset($this->plugins[$checkLevel][$plugin])
|
||||
&& $this->plugins[$checkLevel][$plugin] !== $config) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
$this->plugins[$checkLevel][$plugin] = $config;
|
||||
}
|
||||
$checkLevel--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue