Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
7cea5c8b26 | |||
5418c27788 | |||
6a3d3fec57 | |||
e3409513c3 | |||
d22df4bd9c | |||
0b572094f7 |
@ -2,7 +2,6 @@
|
||||
|
||||
$config = new Amp\CodeStyle\Config();
|
||||
$config->getFinder()
|
||||
->in(__DIR__ . '/examples')
|
||||
->in(__DIR__ . '/src')
|
||||
->in(__DIR__ . '/test');
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "phabel/phabel",
|
||||
"description": "Write and deploy modern PHP 8 code, today.",
|
||||
"type": "project",
|
||||
"type": "composer-plugin",
|
||||
"require": {
|
||||
"nikic/php-parser": "^4.7",
|
||||
"phabel/phabel-plugin": "*"
|
||||
"composer-plugin-api": "^2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7 | ^8 | ^9",
|
||||
@ -23,6 +23,9 @@
|
||||
"Phabel\\": "src"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"class": "Phabel\\Composer\\Plugin"
|
||||
},
|
||||
"scripts": {
|
||||
"check": [
|
||||
"@cs",
|
||||
@ -32,4 +35,4 @@
|
||||
"cs-fix": "php-cs-fixer fix -v --diff",
|
||||
"test": "@php -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
src/Composer/PhabelConstraintInterface.php
Normal file
11
src/Composer/PhabelConstraintInterface.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Phabel\Composer;
|
||||
|
||||
use Composer\Semver\Constraint\ConstraintInterface;
|
||||
|
||||
interface PhabelConstraintInterface extends ConstraintInterface
|
||||
{
|
||||
public function getConfig(): array;
|
||||
public function setConfig(array $config);
|
||||
}
|
7
src/Composer/PhabelRepositoryInterface.php
Normal file
7
src/Composer/PhabelRepositoryInterface.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Phabel\Composer;
|
||||
|
||||
interface PhabelRepositoryInterface
|
||||
{
|
||||
}
|
@ -5,11 +5,13 @@ namespace Phabel\Composer;
|
||||
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;
|
||||
use Phabel\Composer\Traits\Repository;
|
||||
use Phabel\Tools;
|
||||
use ReflectionObject;
|
||||
|
||||
/**
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
@ -31,9 +33,19 @@ class Plugin implements PluginInterface, EventSubscriberInterface
|
||||
*/
|
||||
public function activate(Composer $composer, IOInterface $io): void
|
||||
{
|
||||
$rootPackage = $composer->getPackage();
|
||||
Repository::preparePackage($rootPackage, null);
|
||||
var_dump($rootPackage->getRequires());
|
||||
|
||||
$repoManager = $composer->getRepositoryManager();
|
||||
$repos = $repoManager->getRepositories();
|
||||
$repoManager->prependRepository(new Repository($repos[0]));
|
||||
$reflect = (new ReflectionObject($repoManager))->getProperty('repositories');
|
||||
$reflect->setAccessible(true);
|
||||
$reflect->setValue($repoManager, []);
|
||||
foreach ($repos as $repo) {
|
||||
$repoManager->prependRepository(Tools::cloneWithTrait($repo, Repository::class, PhabelRepositoryInterface::class));
|
||||
}
|
||||
//\var_dump(\array_map('get_class', $repoManager->getRepositories()));
|
||||
$this->io = $io;
|
||||
}
|
||||
|
||||
@ -69,8 +81,6 @@ class Plugin implements PluginInterface, EventSubscriberInterface
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
InstallerEvents::PRE_DEPENDENCIES_SOLVING =>
|
||||
['onDependencySolve', 100000],
|
||||
PackageEvents::POST_PACKAGE_INSTALL =>
|
||||
['onInstall', 100000],
|
||||
];
|
||||
@ -78,7 +88,6 @@ class Plugin implements PluginInterface, EventSubscriberInterface
|
||||
|
||||
public function onInstall(PackageEvent $event): void
|
||||
{
|
||||
\var_dumP($event);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,6 +99,5 @@ class Plugin implements PluginInterface, EventSubscriberInterface
|
||||
*/
|
||||
public function onDependencySolve(InstallerEvent $event): void
|
||||
{
|
||||
\var_dump($event);
|
||||
}
|
||||
}
|
||||
|
@ -1,226 +0,0 @@
|
||||
<?php
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @license MIT
|
||||
*/
|
||||
class Repository extends ComposerRepository
|
||||
{
|
||||
/**
|
||||
* Configuration prefix.
|
||||
*/
|
||||
private const CONFIG_PREFIX = 'phabel-config';
|
||||
/**
|
||||
* Previous repository .
|
||||
*/
|
||||
private ComposerRepository $repository;
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param RepositoryInterface[] $repositories Previous repositories
|
||||
*/
|
||||
public function __construct(ComposerRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->packages = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get repository configuration.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRepoConfig()
|
||||
{
|
||||
return $this->repository->getRepoConfig();
|
||||
}
|
||||
/**
|
||||
* Set root aliases.
|
||||
*
|
||||
* @param array $rootAliases Root aliases
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setRootAliases(array $rootAliases): void
|
||||
{
|
||||
$this->repository->setRootAliases($rootAliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if specified package registered (installed).
|
||||
*
|
||||
* @param PackageInterface $package package instance
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPackage(PackageInterface $package): bool
|
||||
{
|
||||
return $this->repository->hasPackage($package);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for phabel configuration parameters in constraint.
|
||||
*
|
||||
* @param string|\Composer\Semver\Constraint\ConstraintInterface|null &$constraint package version or version constraint to match against
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function prepareConstraint(&$constraint): array
|
||||
{
|
||||
if (!$constraint instanceof ConstraintInterface && !\is_string($constraint)) {
|
||||
return [];
|
||||
}
|
||||
$constraint = (string) $constraint;
|
||||
if (!str_starts_with($constraint, self::CONFIG_PREFIX)) {
|
||||
return [];
|
||||
}
|
||||
[$config, $constraint] = \explode("\n", $constraint, 2);
|
||||
return \json_decode(\substr($config, 0, \strlen(self::CONFIG_PREFIX)), true) ?: [];
|
||||
}
|
||||
/**
|
||||
* Prepare package.
|
||||
*
|
||||
* @param PackageInterface $package Package
|
||||
* @param array $config Configuration inherited from constraint
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function preparePackage(PackageInterface $package, array $config): void
|
||||
{
|
||||
/**
|
||||
* Phabel configuration of current package.
|
||||
* @var array
|
||||
*/
|
||||
$myConfig = $package->getExtra()['phabel'] ?? [];
|
||||
$havePhabel = false;
|
||||
foreach ($package->getRequires() as $link) {
|
||||
if ($link->getTarget() === 'phabel/phabel') {
|
||||
$havePhabel = true;
|
||||
}
|
||||
if ($link->getTarget() === 'php') {
|
||||
$myConfig['target'] = $link->getConstraint();
|
||||
if ($havePhabel) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$havePhabel) {
|
||||
return;
|
||||
}
|
||||
// Config merging logic here...
|
||||
$links = [];
|
||||
foreach ($package->getRequires() as $link) {
|
||||
$version = self::CONFIG_PREFIX.\json_encode($config)."\n".($link->getConstraint() ?? '');
|
||||
$links []= new Link($link->getSource(), $link->getTarget(), new Constraint('>=', $version), $link->getDescription());
|
||||
}
|
||||
$package->setRequires($links);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the first match of a package by name and version.
|
||||
*
|
||||
* @param string $name package name
|
||||
* @param string|\Composer\Semver\Constraint\ConstraintInterface $constraint package version or version constraint to match against
|
||||
*
|
||||
* @return PackageInterface|null
|
||||
*/
|
||||
public function findPackage(string $name, $constraint): ?PackageInterface
|
||||
{
|
||||
$config = self::prepareConstraint($constraint);
|
||||
if (!$package = $this->repository->findPackage($name, $constraint)) {
|
||||
return null;
|
||||
}
|
||||
return self::preparePackage($package, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for all packages matching a name and optionally a version.
|
||||
*
|
||||
* @param string $name package name
|
||||
* @param string|\Composer\Semver\Constraint\ConstraintInterface $constraint package version or version constraint to match against
|
||||
*
|
||||
* @return PackageInterface[]
|
||||
*/
|
||||
public function findPackages($name, $constraint = null)
|
||||
{
|
||||
$config = self::prepareConstraint($constraint);
|
||||
foreach ($packages = $this->repository->findPackages($name, $constraint) as $package) {
|
||||
self::preparePackage($package, $config);
|
||||
}
|
||||
return $packages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of registered packages.
|
||||
*
|
||||
* @return PackageInterface[]
|
||||
*/
|
||||
public function getPackages()
|
||||
{
|
||||
$packages = $this->repository->getPackages();
|
||||
foreach ($packages as $package) {
|
||||
self::preparePackage($package, []);
|
||||
}
|
||||
return $packages;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function search($query, $mode = 0, $type = null)
|
||||
{
|
||||
return $this->repository->search($query, $mode, $type);
|
||||
}
|
||||
|
||||
public function getProviderNames()
|
||||
{
|
||||
return $this->repository->getProviderNames();
|
||||
}
|
||||
|
||||
public function hasProviders()
|
||||
{
|
||||
return $this->repository->hasProviders();
|
||||
}
|
||||
public function resetPackageIds()
|
||||
{
|
||||
return $this->repository->resetPackageIds();
|
||||
}
|
||||
public function addPackage(PackageInterface $package)
|
||||
{
|
||||
$this->repository->addPackage($package);
|
||||
}
|
||||
/**
|
||||
* @param Pool $pool
|
||||
* @param string $name package name
|
||||
* @param bool $bypassFilters If set to true, this bypasses the stability filtering, and forces a recompute without cache
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function whatProvides(Pool $pool, $name, $bypassFilters = false)
|
||||
{
|
||||
$whatProvides = $this->repository->whatProvides($pool, $name, $bypassFilters);
|
||||
foreach ($whatProvides as $package => &$versions) {
|
||||
foreach ($versions as &$version) {
|
||||
if (!isset($version['require']['phabel/phabel'])) {
|
||||
continue;
|
||||
}
|
||||
$config = $version['extra']['phabel'] ?? [];
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $whatProvides;
|
||||
}
|
||||
}
|
35
src/Composer/Traits/Constraint.php
Normal file
35
src/Composer/Traits/Constraint.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Phabel\Composer\Traits;
|
||||
|
||||
trait Constraint
|
||||
{
|
||||
/**
|
||||
* Phabel config.
|
||||
*/
|
||||
private array $config;
|
||||
|
||||
/**
|
||||
* Get phabel config.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getConfig(): array
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set phabel config.
|
||||
*
|
||||
* @param array $config Phabel config
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setConfig($config): self
|
||||
{
|
||||
$this->config = is_string($config) ? json_decode($config, true) : $config;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
215
src/Composer/Traits/Repository.php
Normal file
215
src/Composer/Traits/Repository.php
Normal file
@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
namespace Phabel\Composer\Traits;
|
||||
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Package\Link;
|
||||
use Composer\Package\Package;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Repository\PlatformRepository;
|
||||
use Composer\Semver\Constraint\Constraint as ComposerConstraint;
|
||||
use Composer\Semver\Constraint\ConstraintInterface;
|
||||
use Composer\Semver\Constraint\MultiConstraint;
|
||||
use Phabel\Composer\PhabelConstraintInterface;
|
||||
use Phabel\Target\Php;
|
||||
use Phabel\Tools;
|
||||
|
||||
/**
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @license MIT
|
||||
*/
|
||||
trait Repository
|
||||
{
|
||||
/**
|
||||
* TODO v3 should make this private once we can drop PHP 5.3 support.
|
||||
*
|
||||
* @param string $name package name (must be lowercased already)
|
||||
* @private
|
||||
*/
|
||||
public function isVersionAcceptable($constraint, $name, $versionData, array $acceptableStabilities = null, array $stabilityFlags = null)
|
||||
{
|
||||
self::extractConfig($constraint);
|
||||
return parent::isVersionAcceptable($constraint, $name, $versionData, $acceptableStabilities, $stabilityFlags);
|
||||
}
|
||||
public function loadPackages(array $packageNameMap, array $acceptableStabilities, array $stabilityFlags, array $alreadyLoaded = [])
|
||||
{
|
||||
$configs = [];
|
||||
foreach ($packageNameMap as $key => &$constraint) {
|
||||
$configs[$key] = self::extractConfig($constraint);
|
||||
}
|
||||
|
||||
$packages = parent::loadPackages($packageNameMap, $acceptableStabilities, $stabilityFlags, $alreadyLoaded);
|
||||
foreach ($packages['packages'] as &$package) {
|
||||
self::preparePackage($package, $configs[$package->getName()] ?? null);
|
||||
}
|
||||
return $packages;
|
||||
}
|
||||
|
||||
private static function trickleMergeConfig(?array $prev, ?array $new): ?array
|
||||
{
|
||||
if ($prev === null || empty($prev)) {
|
||||
return $new;
|
||||
}
|
||||
return $prev;
|
||||
}
|
||||
/**
|
||||
* Prepare package.
|
||||
*
|
||||
* @param PackageInterface $package Package
|
||||
* @param ?array $config Configuration inherited from constraint
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function preparePackage(PackageInterface $package, ?array $config): void
|
||||
{
|
||||
/**
|
||||
* Phabel configuration of current package.
|
||||
* @var array
|
||||
*/
|
||||
$myConfig = $package->getExtra()['phabel'] ?? [];
|
||||
$havePhabel = false;
|
||||
foreach ($package->getRequires() as $link) {
|
||||
if ($link->getTarget() === 'phabel/phabel') {
|
||||
$havePhabel = true;
|
||||
}
|
||||
if ($link->getTarget() === 'php') {
|
||||
$myConfig['target'] = Php::normalizeVersion($link->getConstraint()->getLowerBound()->getVersion());
|
||||
if ($havePhabel) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$havePhabel && $config === null) {
|
||||
\var_dump("Skipping ".$package->getName());
|
||||
return;
|
||||
}
|
||||
\var_dump("Applying ".$package->getName());
|
||||
$config = self::trickleMergeConfig($config, $myConfig);
|
||||
self::processRequires($package, \json_encode($config));
|
||||
}
|
||||
|
||||
private static function processRequires(PackageInterface $package, string $config)
|
||||
{
|
||||
$links = [];
|
||||
foreach ($package->getRequires() as $link) {
|
||||
if (PlatformRepository::isPlatformPackage($link->getTarget())) {
|
||||
continue;
|
||||
}
|
||||
//var_dumP($link->getTarget(), (string) $link->getConstraint());
|
||||
$links []= new Link(
|
||||
$link->getSource(),
|
||||
$link->getTarget(),
|
||||
self::injectConfig($link->getConstraint(), $config),
|
||||
$link->getDescription(),
|
||||
$link->getPrettyConstraint()
|
||||
);
|
||||
}
|
||||
if ($package instanceof Package) {
|
||||
$package->setRequires($links);
|
||||
} elseif ($package instanceof AliasPackage) {
|
||||
Tools::setVar($package, 'requires', $links);
|
||||
self::processRequires($package->getAliasOf(), $config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject config into constraint.
|
||||
*
|
||||
* @param ConstraintInterface $constraint
|
||||
* @param string $config
|
||||
* @return ConstraintInterface
|
||||
*/
|
||||
private static function injectConfig(ConstraintInterface $constraint, string $config): ConstraintInterface
|
||||
{
|
||||
if ($constraint instanceof ComposerConstraint) {
|
||||
$version = $constraint->getVersion();
|
||||
$version = "phabel$version\0$config";
|
||||
return new ComposerConstraint($constraint->getOperator(), $version);
|
||||
} elseif ($constraint instanceof MultiConstraint) {
|
||||
$constraints = $constraint->getConstraints();
|
||||
foreach ($constraints as &$cur) {
|
||||
$cur = self::injectConfig($cur, $config);
|
||||
}
|
||||
return new MultiConstraint($constraints, $constraint->isConjunctive());
|
||||
}
|
||||
return $constraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for phabel configuration parameters in constraint.
|
||||
*
|
||||
* @param \Composer\Semver\Constraint\ConstraintInterface $constraint package version or version constraint to match against
|
||||
*
|
||||
* @return ?array
|
||||
*/
|
||||
public static function extractConfig(ConstraintInterface &$constraint): ?array
|
||||
{
|
||||
$config = null;
|
||||
if ($constraint instanceof ComposerConstraint) {
|
||||
$version = $constraint->getVersion();
|
||||
if (str_starts_with($version, 'phabel')) {
|
||||
[$version, $config] = \explode("\0", \substr($version, 6));
|
||||
$config = \json_decode($config, true);
|
||||
if ($config === false) {
|
||||
$config = null;
|
||||
}
|
||||
$constraint = new ComposerConstraint($constraint->getOperator(), $version);
|
||||
}
|
||||
} elseif ($constraint instanceof MultiConstraint) {
|
||||
$constraints = $constraint->getConstraints() ;
|
||||
foreach ($constraints as &$cur) {
|
||||
$config = self::trickleMergeConfig($config, self::extractConfig($cur));
|
||||
}
|
||||
$constraint = new MultiConstraint($constraints, $constraint->isConjunctive());
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
/**
|
||||
* Searches for the first match of a package by name and version.
|
||||
*
|
||||
* @param string $name package name
|
||||
* @param string|\Composer\Semver\Constraint\ConstraintInterface $constraint package version or version constraint to match against
|
||||
*
|
||||
* @return PackageInterface|null
|
||||
*/
|
||||
public function findPackage($name, $constraint)
|
||||
{
|
||||
$config = self::extractConfig($constraint);
|
||||
if (!$package = parent::findPackage($name, $constraint)) {
|
||||
return null;
|
||||
}
|
||||
return self::preparePackage($package, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for all packages matching a name and optionally a version.
|
||||
*
|
||||
* @param string $name package name
|
||||
* @param string|\Composer\Semver\Constraint\ConstraintInterface $constraint package version or version constraint to match against
|
||||
*
|
||||
* @return PackageInterface[]
|
||||
*/
|
||||
public function findPackages($name, $constraint = null)
|
||||
{
|
||||
$config = self::extractConfig($constraint);
|
||||
foreach ($packages = parent::findPackages($name, $constraint) as $package) {
|
||||
self::preparePackage($package, $config);
|
||||
}
|
||||
return $packages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of registered packages.
|
||||
*
|
||||
* @return PackageInterface[]
|
||||
*/
|
||||
public function getPackages()
|
||||
{
|
||||
$packages = parent::getPackages();
|
||||
foreach ($packages as $package) {
|
||||
self::preparePackage($package, null);
|
||||
}
|
||||
return $packages;
|
||||
}
|
||||
}
|
@ -308,7 +308,7 @@ class Context
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets name context
|
||||
* Gets name context.
|
||||
*
|
||||
* @return NameContext
|
||||
*/
|
||||
|
@ -119,35 +119,35 @@ abstract class Plugin extends Tools implements PluginInterface
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function composerRequires(): array
|
||||
public static function composerRequires(array $config): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function runAfter(): array
|
||||
public static function runAfter(array $config): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function runBefore(): array
|
||||
public static function runBefore(array $config): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function runWithBefore(): array
|
||||
public static function runWithBefore(array $config): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function runWithAfter(): array
|
||||
public static function runWithAfter(array $config): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ class GraphInternal
|
||||
* @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
|
||||
|
@ -24,7 +24,7 @@ interface PluginInterface
|
||||
*
|
||||
* @psalm-return class-string<PluginInterface>[]|array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
public static function runAfter(): array;
|
||||
public static function runAfter(array $config): array;
|
||||
/**
|
||||
* Specify which plugins should run before this plugin.
|
||||
*
|
||||
@ -32,7 +32,7 @@ interface PluginInterface
|
||||
*
|
||||
* @psalm-return class-string<PluginInterface>[]|array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
public static function runBefore(): array;
|
||||
public static function runBefore(array $config): array;
|
||||
/**
|
||||
* Specify which plugins does this plugin extend.
|
||||
*
|
||||
@ -44,21 +44,21 @@ interface PluginInterface
|
||||
*
|
||||
* @psalm-return class-string<PluginInterface>[]|array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
public static function runWithBefore(): array;
|
||||
public static function runWithBefore(array $config): array;
|
||||
/**
|
||||
*
|
||||
* @return array Plugin name(s)
|
||||
*
|
||||
* @psalm-return class-string<PluginInterface>[]|array<class-string<PluginInterface>, array>
|
||||
*/
|
||||
public static function runWithAfter(): array;
|
||||
public static function runWithAfter(array $config): array;
|
||||
|
||||
/**
|
||||
* Specify a list of composer dependencies.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function composerRequires(): array;
|
||||
public static function composerRequires(array $config): array;
|
||||
/**
|
||||
* Set configuration array.
|
||||
*
|
||||
|
@ -3,7 +3,6 @@
|
||||
namespace Phabel\Target;
|
||||
|
||||
use Phabel\Plugin;
|
||||
use Phabel\Target\Php55\YieldDetector;
|
||||
|
||||
/**
|
||||
* Makes changes necessary to polyfill syntaxes of various PHP versions.
|
||||
@ -30,6 +29,14 @@ class Php extends Plugin
|
||||
* Default target.
|
||||
*/
|
||||
private const DEFAULT_TARGET = '70';
|
||||
public static function normalizeVersion(string $target): string
|
||||
{
|
||||
if (\preg_match(":^\D*(\d+\.\d+)\..*:", $target, $matches)) {
|
||||
$target = $matches[1];
|
||||
}
|
||||
$target = \str_replace('.', '', $target);
|
||||
return in_array($target, self::VERSIONS) ? $target : self::DEFAULT_TARGET;
|
||||
}
|
||||
/**
|
||||
* Get PHP version range to target.
|
||||
*
|
||||
@ -39,10 +46,10 @@ class Php extends Plugin
|
||||
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)) {
|
||||
if (\preg_match(":^\D*(\d+\.\d+)\..*:", $config['target'], $matches)) {
|
||||
$target = $matches[1];
|
||||
}
|
||||
$key = \array_search(str_replace('.', '', $target), self::VERSIONS);
|
||||
$key = \array_search(\str_replace('.', '', $target), self::VERSIONS);
|
||||
return \array_slice(
|
||||
self::VERSIONS,
|
||||
$key === false ? self::DEFAULT_TARGET : $key
|
||||
@ -59,12 +66,42 @@ class Php extends Plugin
|
||||
{
|
||||
$classes = [];
|
||||
foreach (self::getRange($config) as $version) {
|
||||
foreach (scandir(__DIR__."/Php$version") as $file) {
|
||||
if (substr($file, -4) !== '.php') continue;
|
||||
$class = basename($version, '.php');
|
||||
foreach (\scandir(__DIR__."/Php$version") as $file) {
|
||||
if (\substr($file, -4) !== '.php') {
|
||||
continue;
|
||||
}
|
||||
$class = \basename($version, '.php');
|
||||
$classes[$class] = $config[$class] ?? [];
|
||||
}
|
||||
}
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
public static function mergeConfigs(array ...$configs): array
|
||||
{
|
||||
$configsByTarget = [];
|
||||
foreach ($configs as $config) {
|
||||
$configsByTarget[$config['target'] ?? PHP_MAJOR_VERSION.PHP_MINOR_VERSION] = [
|
||||
$config
|
||||
];
|
||||
}
|
||||
|
||||
return $configs;
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
public static function splitConfig(array $config): array
|
||||
{
|
||||
$target = $config['target'] = $config['target'] ?? PHP_MAJOR_VERSION.PHP_MINOR_VERSION;
|
||||
unset($config['target']);
|
||||
$chunks = array_chunk($target, 1, true);
|
||||
foreach ($chunks as $k => $chunk) {
|
||||
$chunks[$k]['target'] = $target;
|
||||
}
|
||||
return $chunks;
|
||||
}*/
|
||||
}
|
||||
|
116
src/Tools.php
116
src/Tools.php
@ -63,10 +63,10 @@ abstract class Tools
|
||||
$nodeNew->setAttributes($node->getAttributes());
|
||||
return $nodeNew;
|
||||
}
|
||||
return new $class(
|
||||
return new $class(...[
|
||||
...\array_map(fn (string $name) => $node->{$name}, $node->getSubNodeNames()),
|
||||
$node->getAttributes()
|
||||
);
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Replace type in-place.
|
||||
@ -216,4 +216,116 @@ abstract class Tools
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new object extended from this object, with the specified additional trait + interface
|
||||
*
|
||||
* @param object $obj
|
||||
* @param string $trait
|
||||
* @param string $interface
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function cloneWithTrait(object $obj, string $trait, string $interface): object
|
||||
{
|
||||
$reflect = new ReflectionClass($obj);
|
||||
|
||||
|
||||
$r = $reflect;
|
||||
while ($r && $r->isAnonymous()) {
|
||||
$r = $r->getParentClass();
|
||||
}
|
||||
|
||||
$extend = "extends \\".$r->getName();
|
||||
$eval = "\$newObj = new class $extend implements \\$interface {
|
||||
use \\$trait;
|
||||
|
||||
public function __construct() {}
|
||||
};";
|
||||
eval($eval);
|
||||
|
||||
$reflectNew = new ReflectionClass($newObj);
|
||||
|
||||
do {
|
||||
if ($tmp = $reflectNew->getParentClass()) {
|
||||
$reflectNew = $tmp;
|
||||
}
|
||||
foreach ($reflect->getProperties() as $prop) {
|
||||
if ($reflectNew->hasProperty($prop->getName())) {
|
||||
$propNew = $reflectNew->getProperty($prop->getName());
|
||||
$propNew->setAccessible(true);
|
||||
$prop->setAccessible(true);
|
||||
$propNew->setValue($newObj, $prop->getValue($obj));
|
||||
}
|
||||
}
|
||||
} while ($reflect = $reflect->getParentClass());
|
||||
|
||||
return $newObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks private property exists in an object.
|
||||
*
|
||||
* @param object $obj Object
|
||||
* @param string $var Attribute name
|
||||
*
|
||||
* @psalm-suppress InvalidScope
|
||||
*
|
||||
* @return bool
|
||||
* @access public
|
||||
*/
|
||||
public static function hasVar($obj, string $var): bool
|
||||
{
|
||||
return \Closure::bind(
|
||||
function () use ($var) {
|
||||
return isset($this->{$var});
|
||||
},
|
||||
$obj,
|
||||
\get_class($obj)
|
||||
)->__invoke();
|
||||
}
|
||||
/**
|
||||
* Accesses a private variable from an object.
|
||||
*
|
||||
* @param object $obj Object
|
||||
* @param string $var Attribute name
|
||||
*
|
||||
* @psalm-suppress InvalidScope
|
||||
*
|
||||
* @return mixed
|
||||
* @access public
|
||||
*/
|
||||
public static function &getVar($obj, string $var)
|
||||
{
|
||||
return \Closure::bind(
|
||||
function & () use ($var) {
|
||||
return $this->{$var};
|
||||
},
|
||||
$obj,
|
||||
\get_class($obj)
|
||||
)->__invoke();
|
||||
}
|
||||
/**
|
||||
* Sets a private variable in an object.
|
||||
*
|
||||
* @param object $obj Object
|
||||
* @param string $var Attribute name
|
||||
* @param mixed $val Attribute value
|
||||
*
|
||||
* @psalm-suppress InvalidScope
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public static function setVar($obj, string $var, &$val): void
|
||||
{
|
||||
\Closure::bind(
|
||||
function () use ($var, &$val) {
|
||||
$this->{$var} =& $val;
|
||||
},
|
||||
$obj,
|
||||
\get_class($obj)
|
||||
)->__invoke();
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
namespace Phabel;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\NodeTraverser;
|
||||
use PhpParser\Parser;
|
||||
use PhpParser\ParserFactory;
|
||||
use SplQueue;
|
||||
|
Loading…
Reference in New Issue
Block a user