Finish graph logic
This commit is contained in:
parent
70710fb26d
commit
0fb5cf8d88
|
@ -1,2 +1,7 @@
|
|||
/vendor/
|
||||
.vscode
|
||||
build
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
vendor
|
||||
.php_cs.cache
|
||||
coverage
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
$config = new Amp\CodeStyle\Config();
|
||||
$config->getFinder()
|
||||
->in(__DIR__ . '/examples')
|
||||
->in(__DIR__ . '/src')
|
||||
->in(__DIR__ . '/test');
|
||||
|
||||
$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;
|
||||
|
||||
$config->setCacheFile($cacheDir . '/.php_cs.cache');
|
||||
|
||||
return $config;
|
|
@ -5,11 +5,22 @@
|
|||
"require": {
|
||||
"nikic/php-parser": "^4.7"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7 | ^8 | ^9",
|
||||
"amphp/php-cs-fixer-config": "dev-master"
|
||||
},
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Daniil Gentili",
|
||||
"email": "daniil@daniil.it"
|
||||
}
|
||||
]
|
||||
}
|
||||
"authors": [{
|
||||
"name": "Daniil Gentili",
|
||||
"email": "daniil@daniil.it"
|
||||
}],
|
||||
"scripts": {
|
||||
"check": [
|
||||
"@cs",
|
||||
"@test"
|
||||
],
|
||||
"cs": "php-cs-fixer fix -v --diff --dry-run",
|
||||
"cs-fix": "php-cs-fixer fix -v --diff",
|
||||
"test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
|
||||
}
|
||||
}
|
|
@ -4,5 +4,4 @@ namespace Phabel;
|
|||
|
||||
class Context
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,5 +6,4 @@ use Phabel\Plugin;
|
|||
|
||||
class UniqueId extends Plugin
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,16 @@ abstract class Plugin implements PluginInterface
|
|||
* Configuration array.
|
||||
*/
|
||||
private array $config = [];
|
||||
/**
|
||||
* Set configuration array.
|
||||
*
|
||||
* @param array $config
|
||||
* @return void
|
||||
*/
|
||||
public function setConfigArray(array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
/**
|
||||
* Replace node of one type with another.
|
||||
*
|
||||
|
@ -141,14 +151,28 @@ abstract class Plugin implements PluginInterface
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function needs(): array
|
||||
public static function runAfter(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function extends(): array
|
||||
public static function runBefore(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function runWithBefore(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function runWithAfter(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ namespace Spatie\Php7to5\NodeVisitors;
|
|||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use PhpParser\ParserFactory;
|
||||
|
||||
class YieldFromReplacer extends NodeVisitorAbstract
|
||||
{
|
||||
|
|
|
@ -3,13 +3,7 @@
|
|||
namespace Spatie\Php7to5\NodeVisitors;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\NodeTraverser;
|
||||
use PhpParser\Node\Stmt\Declare_;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\Node\Stmt\Use_;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use Spatie\Php7to5\Converter;
|
||||
use Spatie\Php7to5\Exceptions\InvalidPhpCode;
|
||||
|
||||
class YieldReturnDetector extends NodeVisitorAbstract
|
||||
{
|
||||
|
@ -26,13 +20,13 @@ class YieldReturnDetector extends NodeVisitorAbstract
|
|||
if ($node instanceof Node\Expr\Yield_ ||
|
||||
$node instanceof Node\Expr\YieldFrom
|
||||
) {
|
||||
end($this->hasYield)->hasYield = true;
|
||||
\end($this->hasYield)->hasYield = true;
|
||||
}
|
||||
}
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Node\FunctionLike) {
|
||||
array_pop($this->hasYield);
|
||||
\array_pop($this->hasYield);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class YieldReturnReplacer extends NodeVisitorAbstract
|
|||
public function leaveNode(Node $node)
|
||||
{
|
||||
if ($node instanceof Node\FunctionLike) {
|
||||
array_pop($this->functions);
|
||||
\array_pop($this->functions);
|
||||
return;
|
||||
}
|
||||
if (!$node instanceof Node\Stmt\Return_) {
|
||||
|
@ -34,7 +34,7 @@ class YieldReturnReplacer extends NodeVisitorAbstract
|
|||
return new Node\Stmt\Return_();
|
||||
}
|
||||
|
||||
if (!(end($this->functions)->hasYield ?? false)) {
|
||||
if (!(\end($this->functions)->hasYield ?? false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
namespace Phabel;
|
||||
|
||||
/**
|
||||
* Caches plugin information.
|
||||
*/
|
||||
class PluginCache
|
||||
{
|
||||
/**
|
||||
* Enter method names for each plugin.
|
||||
*
|
||||
* @var array<class-string<PluginInterface>, string[]>
|
||||
*/
|
||||
private static array $enterMethods = [];
|
||||
/**
|
||||
* Leave method names.
|
||||
*
|
||||
* @var array<class-string<PluginInterface>, string[]>
|
||||
*/
|
||||
private static array $leaveMethods = [];
|
||||
|
||||
/**
|
||||
* Cache method information.
|
||||
*
|
||||
* @param class-string<PluginInterface> $plugin Plugin
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function cacheMethods(string $plugin): void
|
||||
{
|
||||
if (!isset(self::$enterMethods[$plugin])) {
|
||||
self::$enterMethods[$plugin] = [];
|
||||
self::$leaveMethods[$plugin] = [];
|
||||
foreach (\get_class_methods($plugin) as $method) {
|
||||
if (\str_starts_with($method, 'enter')) {
|
||||
self::$enterMethods[$plugin] []= $method;
|
||||
} elseif (\str_starts_with($method, 'leave')) {
|
||||
self::$leaveMethods[$plugin] []= $method;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Return whether this plugin can be required by another plugin.
|
||||
*
|
||||
* If false, this plugin should only be extended by other plugins, to reduce complexity.
|
||||
*
|
||||
* @param class-string<PluginInterface> $plugin Plugin
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function canBeRequired(string $plugin): bool
|
||||
{
|
||||
self::cacheMethods($plugin);
|
||||
return empty(self::$leaveMethods[$plugin]);
|
||||
}
|
||||
/**
|
||||
* Get enter methods array.
|
||||
*
|
||||
* @param class-string<PluginInterface> $plugin Plugin name
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function enterMethods(string $plugin): array
|
||||
{
|
||||
self::cacheMethods($plugin);
|
||||
return self::$enterMethods[$plugin];
|
||||
}
|
||||
/**
|
||||
* Get leave methods array.
|
||||
*
|
||||
* @param class-string<PluginInterface> $plugin Plugin name
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function leaveMethods(string $plugin): array
|
||||
{
|
||||
self::cacheMethods($plugin);
|
||||
return self::$leaveMethods[$plugin];
|
||||
}
|
||||
/**
|
||||
* Get runBefore requirements.
|
||||
*
|
||||
* @param class-string<PluginInterface> $plugin Plugin
|
||||
*
|
||||
* @return array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
public static function runBefore(string $plugin): array
|
||||
{
|
||||
/** @var array<class-string<PluginInterface>, array<class-string<PluginInterface>, array>> */
|
||||
static $cache = [];
|
||||
if (isset($cache[$plugin])) {
|
||||
return $cache[$plugin];
|
||||
}
|
||||
return $cache[$plugin] = self::simplify($plugin::runBefore());
|
||||
}
|
||||
/**
|
||||
* Get runAfter requirements.
|
||||
*
|
||||
* @param class-string<PluginInterface> $plugin Plugin
|
||||
*
|
||||
* @return array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
public static function runAfter(string $plugin): array
|
||||
{
|
||||
/** @var array<class-string<PluginInterface>, array<class-string<PluginInterface>, array>> */
|
||||
static $cache = [];
|
||||
if (isset($cache[$plugin])) {
|
||||
return $cache[$plugin];
|
||||
}
|
||||
return $cache[$plugin] = self::simplify($plugin::runAfter());
|
||||
}
|
||||
/**
|
||||
* Get runWithBefore requirements.
|
||||
*
|
||||
* @param class-string<PluginInterface> $plugin Plugin
|
||||
*
|
||||
* @return array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
public static function runWithBefore(string $plugin): array
|
||||
{
|
||||
/** @var array<class-string<PluginInterface>, array<class-string<PluginInterface>, array>> */
|
||||
static $cache = [];
|
||||
if (isset($cache[$plugin])) {
|
||||
return $cache[$plugin];
|
||||
}
|
||||
return $cache[$plugin] = self::simplify($plugin::runWithBefore());
|
||||
}
|
||||
/**
|
||||
* Get runWithAfter requirements.
|
||||
*
|
||||
* @param class-string<PluginInterface> $plugin Plugin
|
||||
*
|
||||
* @return array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
public static function runWithAfter(string $plugin): array
|
||||
{
|
||||
/** @var array<class-string<PluginInterface>, array<class-string<PluginInterface>, array>> */
|
||||
static $cache = [];
|
||||
if (isset($cache[$plugin])) {
|
||||
return $cache[$plugin];
|
||||
}
|
||||
return $cache[$plugin] = self::simplify($plugin::runWithAfter());
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Phabel\PluginGraph;
|
||||
|
||||
use Phabel\PluginInterface;
|
||||
|
||||
class CircularException extends \Exception
|
||||
{
|
||||
/**
|
||||
* Plugin array.
|
||||
*
|
||||
* @var class-string<PluginInterface>[]
|
||||
*/
|
||||
private array $plugins = [];
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param class-string<PluginInterface>[] $plugins Plugin array
|
||||
* @param \Throwable $previous Previous exception
|
||||
*/
|
||||
public function __construct(array $plugins, \Throwable $previous = null)
|
||||
{
|
||||
$this->plugins = $plugins;
|
||||
parent::__construct("Detected circular reference: ".\implode(" => ", $plugins), 0, $previous);
|
||||
}
|
||||
/**
|
||||
* Get plugins.
|
||||
*
|
||||
* @return class-string<PluginInterface>[]
|
||||
*/
|
||||
public function getPlugins(): array
|
||||
{
|
||||
return $this->plugins;
|
||||
}
|
||||
}
|
|
@ -2,24 +2,22 @@
|
|||
|
||||
namespace Phabel\PluginGraph;
|
||||
|
||||
use Phabel\PluginInterface;
|
||||
|
||||
/**
|
||||
* Graph API wrapper.
|
||||
*/
|
||||
class Graph
|
||||
{
|
||||
/**
|
||||
* Plugin nodes, indexed by plugin name+config.
|
||||
*
|
||||
* @var array<class-string<PluginInterface>, array<string, Node>>
|
||||
* Graph instance.
|
||||
*/
|
||||
private array $plugins = [];
|
||||
|
||||
private GraphInternal $graph;
|
||||
/**
|
||||
* Package contexts.
|
||||
*
|
||||
* @var PackageContext[]
|
||||
* Constructr.
|
||||
*/
|
||||
private array $packageContexts = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->graph = new GraphInternal;
|
||||
}
|
||||
/**
|
||||
* Get new package context.
|
||||
*
|
||||
|
@ -27,9 +25,7 @@ class Graph
|
|||
*/
|
||||
public function getPackageContext(): PackageContext
|
||||
{
|
||||
$packageContext = new PackageContext;
|
||||
$this->packageContexts []= $packageContext;
|
||||
return $packageContext;
|
||||
return $this->graph->getPackageContext();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,31 +41,15 @@ class Graph
|
|||
*/
|
||||
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;
|
||||
return $this->graph->addPlugin($plugin, $config, $ctx);
|
||||
}
|
||||
/**
|
||||
* Add plugin.
|
||||
* Flatten graph.
|
||||
*
|
||||
* @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
|
||||
* @return SplQueue<SplQueue<Plugin>>
|
||||
*/
|
||||
private function addPluginInternal(string $plugin, array $config, PackageContext $ctx): Node
|
||||
public function flatten(): \SplQueue
|
||||
{
|
||||
$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);
|
||||
return $this->graph->flatten();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
|
||||
namespace Phabel\PluginGraph;
|
||||
|
||||
use Phabel\PluginInterface;
|
||||
use SplObjectStorage;
|
||||
use SplQueue;
|
||||
|
||||
class GraphInternal
|
||||
{
|
||||
/**
|
||||
* 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 = [];
|
||||
|
||||
/**
|
||||
* Stores list of Nodes that are not required by any other node.
|
||||
*
|
||||
* @var SplObjectStorage<Node, void>
|
||||
*/
|
||||
private SplObjectStorage $unlinkedNodes;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->unlinkedNodes = new SplObjectStorage;
|
||||
}
|
||||
/**
|
||||
* Get new package context.
|
||||
*
|
||||
* @return PackageContext
|
||||
*/
|
||||
public function getPackageContext(): PackageContext
|
||||
{
|
||||
$packageContext = new PackageContext;
|
||||
$this->packageContexts []= $packageContext;
|
||||
return $packageContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add plugin.
|
||||
*
|
||||
* @param class-string<PluginInterface> $plugin Plugin to add
|
||||
* @param array $config Plugin configuration
|
||||
* @param PackageContext $ctx Package context
|
||||
*
|
||||
* @return Node[]
|
||||
*/
|
||||
public function addPlugin(string $plugin, array $config, PackageContext $ctx): array
|
||||
{
|
||||
if ($config === ['*']) {
|
||||
if (isset($this->plugins[$plugin])) {
|
||||
return \array_map(fn (Node $node) => $node->addPackageContext($ctx), $this->plugins[$plugin]);
|
||||
}
|
||||
$config = [];
|
||||
}
|
||||
$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($this);
|
||||
$this->unlinkedNodes->attach($node);
|
||||
return $node->init($plugin, $config, $ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set unlinked node as linked.
|
||||
*
|
||||
* @param Node $node
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function linkNode(Node $node): void
|
||||
{
|
||||
$this->unlinkedNodes->detach($node);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Flatten graph.
|
||||
*
|
||||
* @return SplQueue<SplQueue<Plugin>>
|
||||
*/
|
||||
public function flatten(): \SplQueue
|
||||
{
|
||||
if (!$this->plugins) {
|
||||
return new \SplQueue;
|
||||
}
|
||||
if ($this->unlinkedNodes->count()) {
|
||||
foreach ($this->unlinkedNodes as $node) {
|
||||
if (isset($initNode)) {
|
||||
$node = $initNode->merge($node);
|
||||
}
|
||||
/** @var Node */
|
||||
$initNode = $node;
|
||||
}
|
||||
return $initNode->circular()->flatten();
|
||||
}
|
||||
return \array_values(\array_values($this->plugins)[0])[0]->circular()->flatten();
|
||||
}
|
||||
}
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
namespace Phabel\PluginGraph;
|
||||
|
||||
use Phabel\PluginCache;
|
||||
use Phabel\PluginInterface;
|
||||
use SplObjectStorage;
|
||||
use SplQueue;
|
||||
|
||||
/**
|
||||
* Represents a plugin with a certain configuration.
|
||||
|
@ -11,66 +13,85 @@ use SplObjectStorage;
|
|||
class Node
|
||||
{
|
||||
/**
|
||||
* Plugin name.
|
||||
* Plugins and configs.
|
||||
*
|
||||
* @var Plugin
|
||||
*/
|
||||
private string $plugin = '';
|
||||
private Plugin $plugin;
|
||||
|
||||
/**
|
||||
* Plugin configuration.
|
||||
* Original plugin name.
|
||||
*
|
||||
* @var class-string<PluginInterface>
|
||||
*/
|
||||
private array $config = [];
|
||||
private string $name;
|
||||
|
||||
/**
|
||||
* Associated package contexts.
|
||||
*
|
||||
* @var SplObjectStorage<PackageContext, void>
|
||||
*/
|
||||
private SplObjectStorage $packageContexts;
|
||||
/**
|
||||
* Nodes that this node requires.
|
||||
*
|
||||
* @var Node[]
|
||||
* @var SplObjectStorage<Node, void>
|
||||
*/
|
||||
private array $requires = [];
|
||||
private SplObjectStorage $requires;
|
||||
|
||||
/**
|
||||
* Nodes that this node extends.
|
||||
*
|
||||
* @var Node[]
|
||||
* @var SplObjectStorage<Node, void>
|
||||
*/
|
||||
private array $extends = [];
|
||||
private SplObjectStorage $extends;
|
||||
|
||||
/**
|
||||
* Nodes that require this node.
|
||||
*
|
||||
* @var Node[]
|
||||
* @var SplObjectStorage<Node, void>
|
||||
*/
|
||||
private array $requiredBy = [];
|
||||
private SplObjectStorage $requiredBy;
|
||||
|
||||
/**
|
||||
* Nodes that extend this node.
|
||||
*
|
||||
* @var Node[]
|
||||
* @var SplObjectStorage<Node, void>
|
||||
*/
|
||||
private array $extendedBy = [];
|
||||
private SplObjectStorage $extendedBy;
|
||||
|
||||
/**
|
||||
* Whether this node was visited when looking for circular requirement references.
|
||||
* Graph instance.
|
||||
*/
|
||||
private bool $visitedRequires = false;
|
||||
private GraphInternal $graph;
|
||||
|
||||
/**
|
||||
* Whether this node was visited when looking for circular requirement references.
|
||||
* Whether this node was visited when looking for circular requirements.
|
||||
*/
|
||||
private bool $visitedExtends = false;
|
||||
private bool $visitedCircular = false;
|
||||
|
||||
/**
|
||||
* Whether this node can be required, or only extended.
|
||||
*/
|
||||
private bool $canBeRequired = true;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param GraphInternal $graph Graph instance
|
||||
*/
|
||||
public function __construct()
|
||||
public function __construct(GraphInternal $graph)
|
||||
{
|
||||
$this->graph = $graph;
|
||||
$this->packageContexts = new SplObjectStorage;
|
||||
$this->requiredBy = new SplObjectStorage;
|
||||
$this->extendedBy = new SplObjectStorage;
|
||||
$this->requires = new SplObjectStorage;
|
||||
$this->extends = new SplObjectStorage;
|
||||
}
|
||||
/**
|
||||
* Initialization function.
|
||||
*
|
||||
* @param Graph $graph Graph instance
|
||||
* @param string $plugin Plugin name
|
||||
* @param array $config Plugin configuration
|
||||
* @param PackageContext $ctx Context
|
||||
|
@ -79,25 +100,31 @@ class Node
|
|||
*
|
||||
* @return self
|
||||
*/
|
||||
public function init(Graph $graph, string $plugin, array $config, PackageContext $ctx): self
|
||||
public function init(string $plugin, array $config, PackageContext $ctx): self
|
||||
{
|
||||
$this->plugin = $plugin;
|
||||
$this->config = $config;
|
||||
$this->name = $plugin;
|
||||
$this->plugin = new Plugin($plugin, $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;
|
||||
$this->canBeRequired = PluginCache::canBeRequired($plugin);
|
||||
foreach (PluginCache::runAfter($plugin) as $class => $config) {
|
||||
foreach ($this->graph->addPlugin($class, $config, $ctx) as $node) {
|
||||
$this->require($node);
|
||||
}
|
||||
}
|
||||
foreach ($extends as $class => $config) {
|
||||
foreach ($graph->addPlugin($class, $config, $ctx) as $node) {
|
||||
$this->extends []= $node;
|
||||
$node->extendedBy []= $this;
|
||||
foreach (PluginCache::runBefore($plugin) as $class => $config) {
|
||||
foreach ($this->graph->addPlugin($class, $config, $ctx) as $node) {
|
||||
$node->require($this);
|
||||
}
|
||||
}
|
||||
foreach (PluginCache::runWithAfter($plugin) as $class => $config) {
|
||||
foreach ($this->graph->addPlugin($class, $config, $ctx) as $node) {
|
||||
$this->extend($node);
|
||||
}
|
||||
}
|
||||
foreach (PluginCache::runWithBefore($plugin) as $class => $config) {
|
||||
foreach ($this->graph->addPlugin($class, $config, $ctx) as $node) {
|
||||
$node->extend($this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,15 +132,43 @@ class Node
|
|||
}
|
||||
|
||||
/**
|
||||
* Simplify requirements.
|
||||
* Make node require another node.
|
||||
*
|
||||
* @param (array<class-string<PluginInterface>, array>|class-string<PluginInterface>[]) $requirements Requirements
|
||||
* @param self $node Node
|
||||
*
|
||||
* @return array<class-string<PluginInterface>, array>
|
||||
* @return void
|
||||
*/
|
||||
private static function simplify(array $requirements): array
|
||||
private function require(self $node): void
|
||||
{
|
||||
return isset($requirements[0]) ? \array_fill_keys($requirements, []) : $requirements;
|
||||
if (!$node->canBeRequired) {
|
||||
$this->extend($node);
|
||||
return;
|
||||
}
|
||||
if ($this->extends->contains($node) || $node->extendedBy->contains($this)) {
|
||||
$this->extends->detach($node);
|
||||
$node->extendedBy->detach($this);
|
||||
}
|
||||
$this->requires->attach($node);
|
||||
$node->requiredBy->attach($this);
|
||||
|
||||
$this->graph->linkNode($this);
|
||||
}
|
||||
/**
|
||||
* Make node extend another node.
|
||||
*
|
||||
* @param self $node Node
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function extend(self $node): void
|
||||
{
|
||||
if ($this->requires->contains($node) || $node->requiredBy->contains($this)) {
|
||||
return;
|
||||
}
|
||||
$this->extends->attach($node);
|
||||
$node->extendedBy->attach($this);
|
||||
|
||||
$this->graph->linkNode($this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,21 +194,133 @@ class Node
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if this node requires only one other node.
|
||||
* Merge node with another node.
|
||||
*
|
||||
* @return boolean
|
||||
* @param self $other Other node
|
||||
*
|
||||
* @return Node
|
||||
*/
|
||||
private function oneRequire(): bool
|
||||
public function merge(self $other): Node
|
||||
{
|
||||
return \count($this->requires) === 1 && empty($this->extends);
|
||||
$this->plugin->merge($other->plugin);
|
||||
foreach ($other->requiredBy as $node) {
|
||||
$node->require($this);
|
||||
$node->requires->detach($other);
|
||||
}
|
||||
foreach ($other->extendedBy as $node) {
|
||||
$node->extend($this);
|
||||
$node->extends->detach($other);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten tree.
|
||||
*
|
||||
* @return SplQueue<SplQueue<PluginInterface>>
|
||||
*/
|
||||
public function flatten(): SplQueue
|
||||
{
|
||||
/** @var SplQueue<PluginInterface> */
|
||||
$initQueue = new SplQueue;
|
||||
|
||||
/** @var SplQueue<SplQueue<PluginInterface>> */
|
||||
$queue = new SplQueue;
|
||||
$queue->enqueue($initQueue);
|
||||
|
||||
$this->flattenInternal($queue);
|
||||
|
||||
return $queue;
|
||||
}
|
||||
/**
|
||||
* Check if this node extends only one other node.
|
||||
* Look for circular references.
|
||||
*
|
||||
* @return boolean
|
||||
* @return self
|
||||
*/
|
||||
private function oneExtend(): bool
|
||||
public function circular(): self
|
||||
{
|
||||
return \count($this->extends) === 1;
|
||||
if ($this->visitedCircular) {
|
||||
$plugins = [$this->name];
|
||||
foreach (\debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, DEBUG_BACKTRACE_PROVIDE_OBJECT) as $frame) {
|
||||
$plugins []= $frame['object']->name;
|
||||
if ($frame['object'] === $this) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new CircularException($plugins);
|
||||
}
|
||||
$this->visitedCircular = true;
|
||||
|
||||
foreach ($this->requiredBy as $node) {
|
||||
$node->circular();
|
||||
}
|
||||
foreach ($this->extendedBy as $node) {
|
||||
$node->circular();
|
||||
}
|
||||
|
||||
$this->visitedCircular = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Internal flattening.
|
||||
*
|
||||
* @param SplQueue<SplQueue<PluginInterface>> $splQueue Queue
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function flattenInternal(SplQueue $splQueue): void
|
||||
{
|
||||
$queue = $splQueue->top();
|
||||
$this->plugin->enqueue($queue);
|
||||
|
||||
/** @var SplQueue<Node> */
|
||||
$extendedBy = new SplQueue;
|
||||
$prevNode = null;
|
||||
foreach ($this->extendedBy as $node) {
|
||||
if (\count($node->requires) + \count($node->extends) === 1) {
|
||||
if ($prevNode instanceof self) {
|
||||
$node->merge($prevNode);
|
||||
}
|
||||
$prevNode = $node;
|
||||
} else {
|
||||
$extendedBy->enqueue($node);
|
||||
}
|
||||
}
|
||||
if ($prevNode) {
|
||||
$extendedBy->enqueue($prevNode);
|
||||
}
|
||||
|
||||
/** @var SplQueue<Node> */
|
||||
$requiredBy = new SplQueue;
|
||||
$prevNode = null;
|
||||
foreach ($this->requiredBy as $node) {
|
||||
if (\count($node->requires) + \count($node->extends) === 1) {
|
||||
if ($prevNode instanceof self) {
|
||||
$node->merge($prevNode);
|
||||
}
|
||||
$prevNode = $node;
|
||||
} else {
|
||||
$requiredBy->enqueue($node);
|
||||
}
|
||||
}
|
||||
if ($prevNode) {
|
||||
$requiredBy->enqueue($prevNode);
|
||||
}
|
||||
|
||||
foreach ($extendedBy as $node) {
|
||||
$node->extends->detach($this);
|
||||
if (\count($node->extends) + \count($node->requires) === 0) {
|
||||
$node->flattenInternal($splQueue);
|
||||
}
|
||||
}
|
||||
foreach ($requiredBy as $node) {
|
||||
$node->requires->detach($this);
|
||||
if (\count($node->extends) + \count($node->requires) === 0) {
|
||||
$splQueue->enqueue(new SplQueue);
|
||||
$node->flattenInternal($splQueue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,10 +25,10 @@ class PackageContext
|
|||
$this->packages[$package] = null;
|
||||
}
|
||||
/**
|
||||
* Merge two contexts
|
||||
* Merge two contexts.
|
||||
*
|
||||
* @param self $other Other context
|
||||
*
|
||||
*
|
||||
* @return self New context
|
||||
*/
|
||||
public function merge(self $other): self
|
||||
|
@ -38,12 +38,12 @@ class PackageContext
|
|||
}
|
||||
|
||||
/**
|
||||
* Get package list
|
||||
* Get package list.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPackages(): array
|
||||
{
|
||||
return array_values($this->packages);
|
||||
return \array_values($this->packages);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Phabel\PluginGraph;
|
||||
|
||||
use Phabel\PluginInterface;
|
||||
use SplQueue;
|
||||
|
||||
/**
|
||||
* Representation of multiple plugins+configs.
|
||||
*/
|
||||
class Plugin
|
||||
{
|
||||
/**
|
||||
* Plugin configs, indexed by plugin name.
|
||||
*
|
||||
* @var array<class-string<PluginInterface>, array[]>
|
||||
*/
|
||||
private array $plugins = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param class-string<PluginInterface> $plugin Plugin
|
||||
* @param array $config Config
|
||||
*/
|
||||
public function __construct(string $plugin, array $config)
|
||||
{
|
||||
$this->plugins[$plugin] = [$config];
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge with other plugins.
|
||||
*
|
||||
* @param self $other Plugins
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function merge(self $other): void
|
||||
{
|
||||
foreach ($other->plugins as $plugin => $configs) {
|
||||
if (isset($this->plugins[$plugin])) {
|
||||
$this->plugins[$plugin] = \array_merge($this->plugins[$plugin], $configs);
|
||||
} else {
|
||||
$this->plugins[$plugin] = $configs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue plugins.
|
||||
*
|
||||
* @param SplQueue<PluginInterface> $queue
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue(SplQueue $queue): void
|
||||
{
|
||||
foreach ($this->plugins as $plugin => $configs) {
|
||||
foreach ($plugin::mergeConfigs(...$configs) as $config) {
|
||||
$pluginObj = new $plugin;
|
||||
$pluginObj->setConfigArray($config);
|
||||
$queue->enqueue($pluginObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,15 @@ interface PluginInterface
|
|||
*
|
||||
* @psalm-return class-string<PluginInterface>[]|array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
public static function needs(): array;
|
||||
public static function runAfter(): array;
|
||||
/**
|
||||
* Specify which plugins should run before this plugin.
|
||||
*
|
||||
* @return array Plugin name(s)
|
||||
*
|
||||
* @psalm-return class-string<PluginInterface>[]|array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
public static function runBefore(): array;
|
||||
/**
|
||||
* Specify which plugins does this plugin extend.
|
||||
*
|
||||
|
@ -28,7 +36,14 @@ interface PluginInterface
|
|||
*
|
||||
* @psalm-return class-string<PluginInterface>[]|array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
public static function extends(): array;
|
||||
public static function runWithBefore(): array;
|
||||
/**
|
||||
*
|
||||
* @return array Plugin name(s)
|
||||
*
|
||||
* @psalm-return class-string<PluginInterface>[]|array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
public static function runWithAfter(): array;
|
||||
|
||||
/**
|
||||
* Specify a list of composer dependencies.
|
||||
|
@ -36,6 +51,14 @@ interface PluginInterface
|
|||
* @return array
|
||||
*/
|
||||
public static function composerRequires(): array;
|
||||
/**
|
||||
* Set configuration array.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setConfigArray(array $config): void;
|
||||
/**
|
||||
* Get configuration key.
|
||||
*
|
||||
|
@ -66,7 +89,7 @@ interface PluginInterface
|
|||
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.
|
||||
*
|
||||
|
|
|
@ -53,7 +53,7 @@ class AnonymousClassReplacer extends NodeVisitorAbstract
|
|||
*/
|
||||
public function afterTraverse(array $nodes)
|
||||
{
|
||||
if (count($this->anonymousClassNodes) === 0) {
|
||||
if (\count($this->anonymousClassNodes) === 0) {
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
|
@ -101,15 +101,15 @@ class AnonymousClassReplacer extends NodeVisitorAbstract
|
|||
* @param array $nodes
|
||||
* @param $hookIndex
|
||||
* @param $anonymousClassStatements
|
||||
*
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function moveAnonymousClassesToHook(array $nodes, $hookIndex, $anonymousClassStatements)
|
||||
{
|
||||
$preStatements = array_slice($nodes, 0, $hookIndex);
|
||||
$postStatements = array_slice($nodes, $hookIndex);
|
||||
$preStatements = \array_slice($nodes, 0, $hookIndex);
|
||||
$postStatements = \array_slice($nodes, $hookIndex);
|
||||
|
||||
return array_merge($preStatements, $anonymousClassStatements, $postStatements);
|
||||
return \array_merge($preStatements, $anonymousClassStatements, $postStatements);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,10 +12,10 @@ use PhpParser\Node\Name;
|
|||
class ClosureCallReplacer extends Plugin
|
||||
{
|
||||
/**
|
||||
* Replace composite function calls
|
||||
* Replace composite function calls.
|
||||
*
|
||||
* @param FuncCall $node Function call
|
||||
*
|
||||
*
|
||||
* @return StaticCall|null
|
||||
*/
|
||||
public function enter(FuncCall $node): ?StaticCall
|
||||
|
|
|
@ -14,10 +14,10 @@ use PhpParser\Node\Stmt\Const_;
|
|||
class DefineArrayReplacer extends Plugin
|
||||
{
|
||||
/**
|
||||
* Convert define() arrays into const arrays
|
||||
* Convert define() arrays into const arrays.
|
||||
*
|
||||
* @param FuncCall $node Node
|
||||
*
|
||||
*
|
||||
* @return Const_|null
|
||||
*/
|
||||
public function enter(FuncCall $node): ?Const_
|
||||
|
|
|
@ -11,10 +11,10 @@ use PhpParser\Node\Stmt\UseUse;
|
|||
class GroupUseReplacer extends Plugin
|
||||
{
|
||||
/**
|
||||
* Replace group use with multiple use statements
|
||||
* Replace group use with multiple use statements.
|
||||
*
|
||||
* @param GroupUse $node Group use statement
|
||||
*
|
||||
*
|
||||
* @return Use_[]
|
||||
*/
|
||||
public function leave(GroupUse $node): array
|
||||
|
|
|
@ -12,7 +12,7 @@ class ScalarTypeHintsRemover extends Plugin
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function needs(): array
|
||||
public static function runAfter(): array
|
||||
{
|
||||
return [
|
||||
TypeHintStripper::class => [
|
||||
|
|
|
@ -56,18 +56,18 @@ class ThrowableReplacer extends Plugin
|
|||
{
|
||||
foreach ($node->catches as $catch) {
|
||||
$alreadyHasError = false;
|
||||
$needs = false;
|
||||
$runAfter = false;
|
||||
foreach ($catch->types as &$type) {
|
||||
if ($type instanceof FullyQualified &&
|
||||
$type->getLast() === "Error") {
|
||||
$alreadyHasError = true;
|
||||
}
|
||||
if ($this->isThrowable($type)) {
|
||||
$needs = true;
|
||||
$runAfter = true;
|
||||
$type = new FullyQualified('Exception');
|
||||
}
|
||||
}
|
||||
if ($needs && !$alreadyHasError) {
|
||||
if ($runAfter && !$alreadyHasError) {
|
||||
$catch->types[] = new FullyQualified('Error');
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ class ThrowableReplacer extends Plugin
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function extends(): array
|
||||
public static function runWithBefore(): array
|
||||
{
|
||||
return [
|
||||
TypeHintStripper::class => [
|
||||
|
|
|
@ -4,7 +4,6 @@ namespace Phabel\Target\Php71;
|
|||
|
||||
use Phabel\Plugin;
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Expr\ArrayItem;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\List_;
|
||||
use PhpParser\Node\Stmt\Foreach_;
|
||||
|
@ -110,7 +109,7 @@ class ListKey extends Plugin
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function needs(): array
|
||||
public static function runAfter(): array
|
||||
{
|
||||
return [ArrayList::class];
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ class MultipleCatchReplacer extends Plugin
|
|||
{
|
||||
/**
|
||||
* Replace compound catches.
|
||||
*
|
||||
*
|
||||
* Do this while leaving to avoid re-iterating uselessly on duplicated code.
|
||||
*
|
||||
* @param TryCatch $node Catch stmt
|
||||
|
@ -36,13 +36,13 @@ class MultipleCatchReplacer extends Plugin
|
|||
$node->catches = $catches;
|
||||
}
|
||||
/**
|
||||
* Extends throwable replacer
|
||||
* Extends throwable replacer.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
*
|
||||
* @psalm-return class-string
|
||||
*/
|
||||
public static function extends(): string
|
||||
public static function runWithBefore(): string
|
||||
{
|
||||
return [ThrowableReplacer::class];
|
||||
}
|
||||
|
|
|
@ -8,11 +8,11 @@ use Phabel\Plugin\TypeHintStripper;
|
|||
class NullableTypeRemover extends Plugin
|
||||
{
|
||||
/**
|
||||
* Remove nullable typehint
|
||||
* Remove nullable typehint.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function needs(): array
|
||||
public static function runAfter(): array
|
||||
{
|
||||
return [
|
||||
TypeHintStripper::class => [
|
||||
|
|
|
@ -4,126 +4,4 @@ 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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
<?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