MINI Sh3ll
<?php
namespace UpdateHelper;
use Composer\Composer;
use Composer\EventDispatcher\Event;
use Composer\Installer\PackageEvent;
use Composer\IO\IOInterface;
use Composer\Json\JsonFile;
use Composer\Script\Event as ScriptEvent;
use Composer\Semver\Semver;
use Exception;
use InvalidArgumentException;
use RuntimeException;
use Throwable;
class UpdateHelper
{
/** @var Event */
private $event;
/** @var IOInterface|null */
private $io;
/** @var Composer */
private $composer;
/** @var array */
private $dependencies = array();
/** @var string */
private $composerFilePath;
/** @var JsonFile */
private $file;
protected static function appendConfig(&$classes, $directory, $key = null)
{
$file = $directory.DIRECTORY_SEPARATOR.'composer.json';
$json = new JsonFile($file);
$key = $key ? $key : 'update-helper';
try {
$dependencyConfig = $json->read();
} catch (Exception $e) {
$dependencyConfig = null;
}
if (is_array($dependencyConfig) && isset($dependencyConfig['extra'], $dependencyConfig['extra'][$key])) {
$classes[$file] = $dependencyConfig['extra'][$key];
}
}
protected static function getUpdateHelperConfig(Composer $composer, $key = null)
{
$vendorDir = $composer->getConfig()->get('vendor-dir');
$npm = array();
foreach (scandir($vendorDir) as $namespace) {
if ($namespace === '.' || $namespace === '..' || !is_dir($directory = $vendorDir.DIRECTORY_SEPARATOR.$namespace)) {
continue;
}
foreach (scandir($directory) as $dependency) {
if ($dependency === '.' || $dependency === '..' || !is_dir($subDirectory = $directory.DIRECTORY_SEPARATOR.$dependency)) {
continue;
}
static::appendConfig($npm, $subDirectory, $key);
}
}
static::appendConfig($npm, dirname($vendorDir), $key);
return $npm;
}
/**
* @param Event $event
* @param IOInterface $io
* @param Composer $composer
* @param string[] $subClasses
*/
protected static function checkHelper($event, IOInterface $io, $composer, $class)
{
if (!is_string($class) || !class_exists($class)) {
throw new NotUpdateInterfaceInstanceException();
}
try {
$helper = new $class();
} catch (Exception $e) {
throw new InvalidArgumentException($e->getMessage(), 1000, $e);
} catch (Throwable $e) {
throw new InvalidArgumentException($e->getMessage(), 1000, $e);
}
if (!($helper instanceof UpdateHelperInterface)) {
throw new NotUpdateInterfaceInstanceException();
}
$helper->check(new static($event, $io, $composer));
}
/**
* @param string $file
* @param Event $event
* @param IOInterface $io
* @param Composer $composer
* @param string[] $subClasses
*/
protected static function checkFileHelpers($file, $event, IOInterface $io, $composer, array $subClasses)
{
foreach ($subClasses as $class) {
try {
static::checkHelper($event, $io, $composer, $class);
} catch (InvalidArgumentException $exception) {
$io->writeError(static::getErrorMessage($exception, $file, $class));
continue;
}
}
}
protected static function getErrorMessage(InvalidArgumentException $exception, $file, $class)
{
if ($exception instanceof NotUpdateInterfaceInstanceException) {
return 'UpdateHelper error in '.$file.":\n".JsonFile::encode($class).' is not an instance of UpdateHelperInterface.';
}
return 'UpdateHelper error: '.$exception->getPrevious()->getMessage().
"\nFile: ".$exception->getPrevious()->getFile().
"\nLine:".$exception->getPrevious()->getLine().
"\n\n".$exception->getPrevious()->getTraceAsString();
}
public static function check(Event $event)
{
if (!($event instanceof ScriptEvent) && !($event instanceof PackageEvent)) {
return;
}
$io = $event->getIO();
$composer = $event->getComposer();
$autoload = $composer->getConfig()->get('vendor-dir').'/autoload.php';
if (file_exists($autoload)) {
include_once $autoload;
}
$classes = static::getUpdateHelperConfig($composer);
foreach ($classes as $file => $subClasses) {
static::checkFileHelpers($file, $event, $io, $composer, (array) $subClasses);
}
}
public function __construct(Event $event, IOInterface $io = null, Composer $composer = null)
{
$this->event = $event;
$this->io = $io ?: (method_exists($event, 'getIO') ? $event->getIO() : null);
$this->composer = $composer ?: (method_exists($event, 'getComposer') ? $event->getComposer() : null);
if ($this->composer &&
($directory = $this->composer->getConfig()->get('archive-dir')) &&
file_exists($file = $directory.'/composer.json')
) {
$this->composerFilePath = $file;
$this->file = new JsonFile($this->composerFilePath);
$this->dependencies = $this->file->read();
}
}
/**
* @return JsonFile
*/
public function getFile()
{
return $this->file;
}
/**
* @return string
*/
public function getComposerFilePath()
{
return $this->composerFilePath;
}
/**
* @return Composer
*/
public function getComposer()
{
return $this->composer;
}
/**
* @return Event
*/
public function getEvent()
{
return $this->event;
}
/**
* @return IOInterface|null
*/
public function getIo()
{
return $this->io;
}
/**
* @return array
*/
public function getDependencies()
{
return $this->dependencies;
}
/**
* @return array
*/
public function getDevDependencies()
{
return isset($this->dependencies['require-dev']) ? $this->dependencies['require-dev'] : array();
}
/**
* @return array
*/
public function getProdDependencies()
{
return isset($this->dependencies['require']) ? $this->dependencies['require'] : array();
}
/**
* @return array
*/
public function getFlattenDependencies()
{
return array_merge($this->getDevDependencies(), $this->getProdDependencies());
}
/**
* @param string $dependency
*
* @return bool
*/
public function hasAsDevDependency($dependency)
{
return isset($this->dependencies['require-dev'][$dependency]);
}
/**
* @param string $dependency
*
* @return bool
*/
public function hasAsProdDependency($dependency)
{
return isset($this->dependencies['require'][$dependency]);
}
/**
* @param string $dependency
*
* @return bool
*/
public function hasAsDependency($dependency)
{
return $this->hasAsDevDependency($dependency) || $this->hasAsProdDependency($dependency);
}
/**
* @param string $dependency
* @param string $version
*
* @return bool
*/
public function isDependencyAtLeast($dependency, $version)
{
if ($this->hasAsProdDependency($dependency)) {
return Semver::satisfies($version, $this->dependencies['require'][$dependency]);
}
if ($this->hasAsDevDependency($dependency)) {
return Semver::satisfies($version, $this->dependencies['require-dev'][$dependency]);
}
return false;
}
/**
* @param string $dependency
* @param string $version
*
* @return bool
*/
public function isDependencyLesserThan($dependency, $version)
{
return !$this->isDependencyAtLeast($dependency, $version);
}
/**
* @param string $dependency
* @param string $version
* @param array $environments
*
* @throws Exception
*
* @return $this
*/
public function setDependencyVersion($dependency, $version, $environments = array('require', 'require-dev'))
{
return $this->setDependencyVersions(array($dependency => $version), $environments);
}
/**
* @param array $dependencies
* @param array $environments
*
* @throws Exception
*
* @return $this
*/
public function setDependencyVersions($dependencies, $environments = array('require', 'require-dev'))
{
if (!$this->composerFilePath) {
throw new RuntimeException('No composer instance detected.');
}
$touched = false;
foreach ($environments as $environment) {
foreach ($dependencies as $dependency => $version) {
if (isset($this->dependencies[$environment], $this->dependencies[$environment][$dependency])) {
$this->dependencies[$environment][$dependency] = $version;
$touched = true;
}
}
}
if ($touched) {
if (!$this->composerFilePath) {
throw new RuntimeException('composer.json not found (custom vendor-dir are not yet supported).');
}
$file = new JsonFile($this->composerFilePath);
$file->write($this->dependencies);
}
return $this;
}
/**
* @return $this
*/
public function update()
{
$output = shell_exec('composer update --no-scripts');
if (!empty($output)) {
$this->write($output);
}
return $this;
}
/**
* @param string|array $text
*/
public function write($text)
{
if ($this->io) {
$this->io->write($text);
return;
}
if (is_array($text)) {
$text = implode("\n", $text);
}
echo $text;
}
/**
* @return bool
*/
public function isInteractive()
{
return $this->io && $this->io->isInteractive();
}
}
OHA YOOOO