Start finalizing composer integration

This commit is contained in:
Daniil Gentili 2020-11-01 20:44:11 +01:00
parent 12a33c4a33
commit 409c6c2f1f
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
19 changed files with 221 additions and 281 deletions

View File

@ -6,6 +6,8 @@ use Composer\Composer;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\Installer\InstallerEvent;
use Composer\Installer\InstallerEvents;
use Composer\Installer\PackageEvent;
use Composer\Installer\PackageEvents;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;
@ -31,7 +33,7 @@ class Plugin implements PluginInterface, EventSubscriberInterface
{
$repoManager = $composer->getRepositoryManager();
$repos = $repoManager->getRepositories();
$repoManager->prependRepository();
$repoManager->prependRepository(new Repository($repos[0]));
$this->io = $io;
}
@ -43,9 +45,15 @@ class Plugin implements PluginInterface, EventSubscriberInterface
return [
InstallerEvents::PRE_DEPENDENCIES_SOLVING =>
['onDependencySolve', 100000],
PackageEvents::POST_PACKAGE_INSTALL =>
['onInstall', 100000],
];
}
public function onInstall(PackageEvent $event): void
{
var_dumP($event);
}
/**
* Emitted before composer solves dependencies.

View File

@ -2,9 +2,11 @@
namespace Phabel\Composer;
use Composer\DependencyResolver\Pool;
use Composer\Package\Link;
use Composer\Package\PackageInterface;
use Composer\Repository\ComposerRepository;
use Composer\Semver\Constraint\Constraint;
use Composer\Semver\Constraint\ConstraintInterface;
/**
@ -100,8 +102,6 @@ class Repository extends ComposerRepository
*/
$myConfig = $package->getExtra()['phabel'] ?? [];
$havePhabel = false;
$myConfig['target'] ??= '7.0';
/** @var array */
foreach ($package->getRequires() as $link) {
if ($link->getTarget() === 'phabel/phabel') {
$havePhabel = true;
@ -120,7 +120,7 @@ class Repository extends ComposerRepository
$links = [];
foreach ($package->getRequires() as $link) {
$version = self::CONFIG_PREFIX.\json_encode($config)."\n".($link->getConstraint() ?? '');
$links []= new Link($link->getSource(), $link->getTarget(), $version, $link->getDescription());
$links []= new Link($link->getSource(), $link->getTarget(), new Constraint('>=', $version), $link->getDescription());
}
$package->setRequires($links);
}
@ -213,12 +213,8 @@ class Repository extends ComposerRepository
continue;
}
$config = $version['extra']['phabel'] ?? [];
if (!isset($config['target'])) {
if (isset($version['require']['php'])) {
$config['target'] = $version['require']['php'];
} else {
$config['target'] = '7.0';
}
if (!isset($config['target']) && isset($version['require']['php'])) {
$config['target'] = $version['require']['php'];
}
foreach ($version['require'] as $package => &$version) {
$version = self::CONFIG_PREFIX.\json_encode($config)."\n".$version;

View File

@ -3,6 +3,8 @@
namespace Phabel;
use PhpParser\BuilderHelpers;
use PhpParser\ErrorHandler\Throwing;
use PhpParser\NameContext;
use PhpParser\Node;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\ArrowFunction;
@ -23,6 +25,7 @@ use PhpParser\Node\Expr\Variable;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\If_;
use PhpParser\NodeVisitor\NameResolver;
use SplStack;
/**
@ -45,6 +48,12 @@ class Context
* @var SplStack<VariableContext>
*/
public SplStack $variables;
/**
* Name resolver.
*
* @var NameResolver
*/
public NameResolver $nameResolver;
/**
* Constructor.
*/
@ -54,6 +63,8 @@ class Context
$this->parents = new SplStack;
/** @var SplStack<VariableContext> */
$this->variables = new SplStack;
$this->nameResolver = new NameResolver(new Throwing, ['replaceNodes' => false]);
$this->nameResolver->beforeTraverse([]);
}
/**
* Push node.
@ -67,6 +78,8 @@ class Context
$this->parents->push($node);
if ($node instanceof RootNode) {
$this->variables->push(new VariableContext);
} else {
$this->nameResolver->enterNode($node);
}
if ($node instanceof FunctionLike) {
$variables = \array_fill_keys(
@ -293,4 +306,14 @@ class Context
$subNodeIndex = $parent->getAttribute('currentNodeIndex');
\array_splice($parent->{$subNode}, $subNodeIndex+1, 0, $nodes);
}
/**
* Gets name context
*
* @return NameContext
*/
public function getNameContext(): NameContext
{
return $this->nameResolver->getNameContext();
}
}

View File

@ -92,72 +92,81 @@ class PluginCache
* Get runBefore requirements.
*
* @param class-string<PluginInterface> $plugin Plugin
* @param array $config Config
*
* @return array<class-string<PluginInterface>, array>
* @return array<string, array>
* @psalm-return array<class-string<PluginInterface>, array>
*/
public static function runBefore(string $plugin): array
public static function runBefore(string $plugin, array $config): 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());
return $cache[$plugin] = self::simplify($plugin::runBefore($config));
}
/**
* Get runAfter requirements.
*
* @param class-string<PluginInterface> $plugin Plugin
* @param array $config Config
*
* @return array<class-string<PluginInterface>, array>
* @return array<string, array>
* @psalm-return array<class-string<PluginInterface>, array>
*/
public static function runAfter(string $plugin): array
public static function runAfter(string $plugin, array $config): 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());
return $cache[$plugin] = self::simplify($plugin::runAfter($config));
}
/**
* Get runWithBefore requirements.
*
* @param class-string<PluginInterface> $plugin Plugin
* @param array $config Config
*
* @return array<class-string<PluginInterface>, array>
* @return array<string, array>
* @psalm-return array<class-string<PluginInterface>, array>
*/
public static function runWithBefore(string $plugin): array
public static function runWithBefore(string $plugin, array $config): 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());
return $cache[$plugin] = self::simplify($plugin::runWithBefore($config));
}
/**
* Get runWithAfter requirements.
*
* @param class-string<PluginInterface> $plugin Plugin
* @param array $config Config
*
* @return array<class-string<PluginInterface>, array>
* @return array<string, array>
* @psalm-return array<class-string<PluginInterface>, array>
*/
public static function runWithAfter(string $plugin): array
public static function runWithAfter(string $plugin, array $config): 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());
return $cache[$plugin] = self::simplify($plugin::runWithAfter($config));
}
/**
* Simplify requirements.
*
* @param (array<class-string<PluginInterface>, array>|class-string<PluginInterface>[]) $requirements Requirements
*
* @return array<class-string<PluginInterface>, array>
* @return array<string, array>
* @psalm-return array<class-string<PluginInterface>, array>
*/
private static function simplify(array $requirements): array
{

View File

@ -57,10 +57,12 @@ class GraphInternal
/**
* Add plugin.
*
* @param class-string<PluginInterface> $plugin Plugin to add
* @param array $config Plugin configuration
* @param PackageContext $ctx Package context
* @param string $plugin Plugin to add
* @param array $config Plugin configuration
* @param PackageContext $ctx Package context
*
* @psalm-param class-string<PluginInterface> $plugin Plugin to add
*
* @return Node[]
*/
public function addPlugin(string $plugin, array $config, PackageContext $ctx): array

View File

@ -38,28 +38,28 @@ class Node
/**
* Nodes that this node requires.
*
* @var SplObjectStorage<Node, void>
* @var SplObjectStorage<Node>
*/
private SplObjectStorage $requires;
/**
* Nodes that this node extends.
*
* @var SplObjectStorage<Node, void>
* @var SplObjectStorage<Node>
*/
private SplObjectStorage $extends;
/**
* Nodes that require this node.
*
* @var SplObjectStorage<Node, void>
* @var SplObjectStorage<Node>
*/
private SplObjectStorage $requiredBy;
/**
* Nodes that extend this node.
*
* @var SplObjectStorage<Node, void>
* @var SplObjectStorage<Node>
*/
private SplObjectStorage $extendedBy;
@ -109,22 +109,22 @@ class Node
$this->plugin = new Plugins($plugin, $config);
$this->canBeRequired = PluginCache::canBeRequired($plugin);
foreach (PluginCache::runAfter($plugin) as $class => $config) {
foreach (PluginCache::runAfter($plugin, $config) as $class => $config) {
foreach ($this->graph->addPlugin($class, $config, $this->packageContext) as $node) {
$this->require($node);
}
}
foreach (PluginCache::runBefore($plugin) as $class => $config) {
foreach (PluginCache::runBefore($plugin, $config) as $class => $config) {
foreach ($this->graph->addPlugin($class, $config, $this->packageContext) as $node) {
$node->require($this);
}
}
foreach (PluginCache::runWithAfter($plugin) as $class => $config) {
foreach (PluginCache::runWithAfter($plugin, $config) as $class => $config) {
foreach ($this->graph->addPlugin($class, $config, $this->packageContext) as $node) {
$this->extend($node);
}
}
foreach (PluginCache::runWithBefore($plugin) as $class => $config) {
foreach (PluginCache::runWithBefore($plugin, $config) as $class => $config) {
foreach ($this->graph->addPlugin($class, $config, $this->packageContext) as $node) {
$node->extend($this);
}

70
src/Target/Php.php Normal file
View File

@ -0,0 +1,70 @@
<?php
namespace Phabel\Target;
use Phabel\Plugin;
use Phabel\Target\Php55\YieldDetector;
/**
* Makes changes necessary to polyfill syntaxes of various PHP versions.
*
* @author Daniil Gentili <daniil@daniil.it>
* @license MIT
*/
class Php extends Plugin
{
/**
* PHP versions.
*/
private const VERSIONS = [
'55',
'56',
'70',
'71',
'72',
'73',
'74',
'80',
];
/**
* Default target.
*/
private const DEFAULT_TARGET = '70';
/**
* Get PHP version range to target.
*
* @param array $config
* @return array
*/
private static function getRange(array $config): array
{
$target = $config['target'] ?? PHP_MAJOR_VERSION.PHP_MINOR_VERSION;
if (preg_match(":^\D*(\d+\.\d+)\..*:", $config['target'], $matches)) {
$target = $matches[1];
}
$key = \array_search(str_replace('.', '', $target), self::VERSIONS);
return \array_slice(
self::VERSIONS,
$key === false ? self::DEFAULT_TARGET : $key
);
}
public static function composerRequires(array $config): array
{
return \array_fill_keys(
\array_map(fn (string $version): string => "symfony/polyfill-$version", self::getRange($config)),
'*'
);
}
public static function runWithAfter(array $config): array
{
$classes = [];
foreach (self::getRange($config) as $version) {
foreach (scandir(__DIR__."/Php$version") as $file) {
if (substr($file, -4) !== '.php') continue;
$class = basename($version, '.php');
$classes[$class] = $config[$class] ?? [];
}
}
return $classes;
}
}

View File

@ -1,26 +0,0 @@
<?php
namespace Phabel\Target;
use Phabel\Plugin;
use Phabel\Target\Php55\YieldDetector;
/**
* Makes changes necessary to polyfill PHP 5.5 and run on PHP 5.4 and below.
*
* @author Daniil Gentili <daniil@daniil.it>
* @license MIT
*/
class Php55 extends Plugin
{
public static function composerRequires(): array
{
return ['symfony/polyfill-php55' => '*'];
}
public static function runWithAfter(): array
{
return [
YieldDetector::class
];
}
}

View File

@ -1,52 +0,0 @@
<?php
namespace Phabel\Target;
use Phabel\Plugin;
use Phabel\Target\Php70\AnonymousClassReplacer;
use Phabel\Target\Php70\ClosureCallReplacer;
use Phabel\Target\Php70\CompoundAccess;
use Phabel\Target\Php70\DefineArrayReplacer;
use Phabel\Target\Php70\GroupUseReplacer;
use Phabel\Target\Php70\IssetExpressionFixer;
use Phabel\Target\Php70\NestedExpressionFixer;
use Phabel\Target\Php70\NullCoalesceReplacer;
use Phabel\Target\Php70\ReservedNameReplacer;
use Phabel\Target\Php70\ScalarTypeHints;
use Phabel\Target\Php70\SpaceshipOperatorReplacer;
use Phabel\Target\Php70\StrictTypesDeclareStatementRemover;
use Phabel\Target\Php70\ThrowableReplacer;
use Phabel\Target\Php70\YieldFromReturnDetector;
/**
* Makes changes necessary to polyfill PHP 7.0 and run on PHP 5.6 and below.
*
* @author Daniil Gentili <daniil@daniil.it>
* @license MIT
*/
class Php70 extends Plugin
{
public static function composerRequires(): array
{
return ['symfony/polyfill-php70' => '*'];
}
public static function runWithAfter(): array
{
return [
IssetExpressionFixer::class,
NestedExpressionFixer::class,
AnonymousClassReplacer::class,
ClosureCallReplacer::class,
CompoundAccess::class,
DefineArrayReplacer::class,
GroupUseReplacer::class,
NullCoalesceReplacer::class,
ReservedNameReplacer::class,
ScalarTypeHints::class,
SpaceshipOperatorReplacer::class,
StrictTypesDeclareStatementRemover::class,
ThrowableReplacer::class,
YieldFromReturnDetector::class
];
}
}

View File

@ -1,42 +0,0 @@
<?php
namespace Phabel\Target;
use Phabel\Plugin;
use Phabel\Target\Php71\ArrayList;
use Phabel\Target\Php71\ClassConstantVisibilityModifiersRemover;
use Phabel\Target\Php71\IssetExpressionFixer;
use Phabel\Target\Php71\IterableHint;
use Phabel\Target\Php71\ListKey;
use Phabel\Target\Php71\MultipleCatchReplacer;
use Phabel\Target\Php71\NestedExpressionFixer;
use Phabel\Target\Php71\NullableType;
use Phabel\Target\Php71\VoidReturnType;
/**
* Makes changes necessary to polyfill PHP 7.1 and run on PHP 7.0 and below.
*
* @author Daniil Gentili <daniil@daniil.it>
* @license MIT
*/
class Php71 extends Plugin
{
public static function composerRequires(): array
{
return ['symfony/polyfill-php71' => '*'];
}
public static function runWithAfter(): array
{
return [
IssetExpressionFixer::class,
NestedExpressionFixer::class,
ArrayList::class,
ClassConstantVisibilityModifiersRemover::class,
ListKey::class,
IterableHint::class,
MultipleCatchReplacer::class,
VoidReturnType::class,
NullableType::class
];
}
}

View File

@ -4,6 +4,7 @@ namespace Phabel\Target\Php71;
use Phabel\Context;
use Phabel\Plugin;
use Phabel\Target\Php73\ListReference;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\List_;

View File

@ -1,31 +0,0 @@
<?php
namespace Phabel\Target;
use Phabel\Plugin;
use Phabel\Target\Php72\IssetExpressionFixer;
use Phabel\Target\Php72\NestedExpressionFixer;
use Phabel\Target\Php72\ObjectTypeHintReplacer;
/**
* Makes changes necessary to polyfill PHP 7.2 and run on PHP 7.1 and below.
*
* @author Daniil Gentili <daniil@daniil.it>
* @license MIT
*/
class Php72 extends Plugin
{
public static function composerRequires(): array
{
return ['symfony/polyfill-php72' => '*'];
}
public static function runWithAfter(): array
{
return [
IssetExpressionFixer::class,
NestedExpressionFixer::class,
ObjectTypeHintReplacer::class
];
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace Phabel\Target;
use Phabel\Plugin;
use Phabel\Target\Php73\IssetExpressionFixer;
use Phabel\Target\Php73\NestedExpressionFixer;
/**
* Makes changes necessary to polyfill PHP 7.3 and run on PHP 7.2 and below.
*
* @author Daniil Gentili <daniil@daniil.it>
* @license MIT
*/
class Php73 extends Plugin
{
public static function composerRequires(): array
{
return ['symfony/polyfill-php73' => '*'];
}
public static function runWithAfter(): array
{
return [
IssetExpressionFixer::class,
NestedExpressionFixer::class,
];
}
}

View File

@ -1,36 +0,0 @@
<?php
namespace Phabel\Target;
use Phabel\Plugin;
use Phabel\Target\Php74\ArrayUnpack;
use Phabel\Target\Php74\ArrowClosure;
use Phabel\Target\Php74\IssetExpressionFixer;
use Phabel\Target\Php74\NestedExpressionFixer;
use Phabel\Target\Php74\NullCoalesceAssignment;
use Phabel\Target\Php74\TypedProperty;
/**
* Makes changes necessary to polyfill PHP 7.4 and run on PHP 7.3 and below.
*
* @author Daniil Gentili <daniil@daniil.it>
* @license MIT
*/
class Php74 extends Plugin
{
public static function composerRequires(): array
{
return ['symfony/polyfill-php74' => '*'];
}
public static function runWithAfter(): array
{
return [
IssetExpressionFixer::class,
NestedExpressionFixer::class,
ArrayUnpack::class,
ArrowClosure::class,
NullCoalesceAssignment::class,
TypedProperty::class
];
}
}

View File

@ -3,6 +3,7 @@
namespace Phabel\Target\Php74;
use Phabel\Plugin;
use Phabel\Target\Php70\NullCoalesceReplacer;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\AssignOp\Coalesce;
use PhpParser\Node\Expr\BinaryOp\Coalesce as BinaryOpCoalesce;
@ -17,4 +18,8 @@ class NullCoalesceAssignment extends Plugin
{
return new Assign($coalesce->var, new BinaryOpCoalesce($coalesce->var, $coalesce->expr), $coalesce->getAttributes());
}
public static function runWithBefore(): array
{
return [NullCoalesceReplacer::class];
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace Phabel\Target\Php74;
use Phabel\Context;
use Phabel\Plugin;
use Phabel\Tools;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Serializable as GlobalSerializable;
/**
* Implement __serialize and __unserialize.
*/
class Serializable extends Plugin
{
public function enter(Class_ $class, Context $context): void
{
/** @var array<string, &ClassMethod> */
$methods = [];
foreach ($class->stmts as $stmt) {
if ($stmt instanceof ClassMethod) {
$name = $stmt->name->toLowerString();
$methods[$name] = $stmt;
}
}
if (!isset($methods['__serialize']) && !isset($methods['__unserialize'])) {
return;
}
foreach ($class->implements as $name) {
$resolved = $context->getNameContext()->getResolvedClassName($name);
if ($resolved->toLowerString() === 'serializable' || $name->toLowerString() === 'serializable') {
return; // Already implements
}
}
if (isset($methods['__sleep'])) {
$methods['__sleep']->name = new Identifier('__phabelSleep');
}
if (isset($methods['__wakeup'])) {
$methods['__wakeup']->name = new Identifier('__phabelWakeup');
}
$class->implements []= new FullyQualified(GlobalSerializable::class);
$methods['serialize'] = new ClassM
}
}

View File

@ -2,11 +2,33 @@
namespace Phabel\Target\Php74;
use Phabel\Context;
use Phabel\Plugin;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
/**
*
* Implement typed properties.
*/
class TypedProperty extends Plugin
{
public function enter(Class_ $class, Context $context): void
{
/** @var Property[] */
$typed = [];
foreach ($class->stmts as $stmt) {
if ($stmt instanceof Property && $stmt->type) {
$typed []= $stmt;
}
}
if (empty($typed)) {
return;
}
if
foreach ($typed as $property) {
}
}
}

View File

@ -1,31 +0,0 @@
<?php
namespace Phabel\Target;
use Phabel\Plugin;
use Phabel\Target\Php80\IssetExpressionFixer;
use Phabel\Target\Php80\NestedExpressionFixer;
use Phabel\UnionTypeStripper;
/**
* Makes changes necessary to polyfill PHP 8.0 and run on PHP 7.4 and below.
*
* @author Daniil Gentili <daniil@daniil.it>
* @license MIT
*/
class Php80 extends Plugin
{
public static function composerRequires(): array
{
return ['symfony/polyfill-php80' => '*'];
}
public static function runWithAfter(): array
{
return [
IssetExpressionFixer::class,
NestedExpressionFixer::class,
UnionTypeStripper::class,
];
}
}

View File

@ -3,6 +3,7 @@
namespace Phabel;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\Parser;
use PhpParser\ParserFactory;
use SplQueue;