Merge remote-tracking branch 'upstream/master'
Conflicts: src/Composer/Downloader/FileDownloader.phpmain
commit
0f2c0ab389
@ -0,0 +1,150 @@
|
|||||||
|
<!--
|
||||||
|
tagline: Modify and extend Composer's functionality
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Setting up and using plugins
|
||||||
|
|
||||||
|
## Synopsis
|
||||||
|
|
||||||
|
You may wish to alter or expand Composer's functionality with your own. For
|
||||||
|
example if your environment poses special requirements on the behaviour of
|
||||||
|
Composer which do not apply to the majority of its users or if you wish to
|
||||||
|
accomplish something with composer in a way that is not desired by most users.
|
||||||
|
|
||||||
|
In these cases you could consider creating a plugin to handle your
|
||||||
|
specific logic.
|
||||||
|
|
||||||
|
## Creating a Plugin
|
||||||
|
|
||||||
|
A plugin is a regular composer package which ships its code as part of the
|
||||||
|
package and may also depend on further packages.
|
||||||
|
|
||||||
|
### Plugin Package
|
||||||
|
|
||||||
|
The package file is the same as any other package file but with the following
|
||||||
|
requirements:
|
||||||
|
|
||||||
|
1. the [type][1] attribute must be `composer-plugin`.
|
||||||
|
2. the [extra][2] attribute must contain an element `class` defining the
|
||||||
|
class name of the plugin (including namespace). If a package contains
|
||||||
|
multiple plugins this can be array of class names.
|
||||||
|
|
||||||
|
Additionally you must require the special package called `composer-plugin-api`
|
||||||
|
to define which composer API versions your plugin is compatible with. The
|
||||||
|
current composer plugin API version is 1.0.0.
|
||||||
|
|
||||||
|
For example
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "my/plugin-package",
|
||||||
|
"type": "composer-plugin",
|
||||||
|
"require": {
|
||||||
|
"composer-plugin-api": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
### Plugin Class
|
||||||
|
|
||||||
|
Every plugin has to supply a class which implements the
|
||||||
|
[`Composer\Plugin\PluginInterface`][3]. The `activate()` method of the plugin
|
||||||
|
is called after the plugin is loaded and receives an instance of
|
||||||
|
[`Composer\Composer`][4] as well as an instance of
|
||||||
|
[`Composer\IO\IOInterface`][5]. Using these two objects all configuration can
|
||||||
|
be read and all internal objects and state can be manipulated as desired.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
namespace phpDocumentor\Composer;
|
||||||
|
|
||||||
|
use Composer\Composer;
|
||||||
|
use Composer\IO\IOInterface;
|
||||||
|
use Composer\Plugin\PluginInterface;
|
||||||
|
|
||||||
|
class TemplateInstallerPlugin implements PluginInterface
|
||||||
|
{
|
||||||
|
public function activate(Composer $composer, IOInterface $io)
|
||||||
|
{
|
||||||
|
$installer = new TemplateInstaller($io, $composer);
|
||||||
|
$composer->getInstallationManager()->addInstaller($installer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
## Event Handler
|
||||||
|
|
||||||
|
Furthermore plugins may implement the
|
||||||
|
[`Composer\EventDispatcher\EventSubscriberInterface`][6] in order to have its
|
||||||
|
event handlers automatically registered with the `EventDispatcher` when the
|
||||||
|
plugin is loaded.
|
||||||
|
|
||||||
|
The events available for plugins are:
|
||||||
|
|
||||||
|
* **COMMAND**, is called at the beginning of all commands that load plugins.
|
||||||
|
It provides you with access to the input and output objects of the program.
|
||||||
|
* **PRE_FILE_DOWNLOAD**, is triggered before files are downloaded and allows
|
||||||
|
you to manipulate the `RemoteFilesystem` object prior to downloading files
|
||||||
|
based on the URL to be downloaded.
|
||||||
|
|
||||||
|
> A plugin can also subscribe to [script events][7].
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
namespace Naderman\Composer\AWS;
|
||||||
|
|
||||||
|
use Composer\Composer;
|
||||||
|
use Composer\EventDispatcher\EventSubscriberInterface;
|
||||||
|
use Composer\IO\IOInterface;
|
||||||
|
use Composer\Plugin\PluginInterface;
|
||||||
|
use Composer\Plugin\PluginEvents;
|
||||||
|
use Composer\Plugin\PreFileDownloadEvent;
|
||||||
|
|
||||||
|
class AwsPlugin implements PluginInterface, EventSubscriberInterface
|
||||||
|
{
|
||||||
|
protected $composer;
|
||||||
|
protected $io;
|
||||||
|
|
||||||
|
public function activate(Composer $composer, IOInterface $io)
|
||||||
|
{
|
||||||
|
$this->composer = $composer;
|
||||||
|
$this->io = $io;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getSubscribedEvents()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
PluginEvents::PRE_FILE_DOWNLOAD => array(
|
||||||
|
array('onPreFileDownload', 0)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onPreFileDownload(PreFileDownloadEvent $event)
|
||||||
|
{
|
||||||
|
$protocol = parse_url($event->getProcessedUrl(), PHP_URL_SCHEME);
|
||||||
|
|
||||||
|
if ($protocol === 's3') {
|
||||||
|
$awsClient = new AwsClient($this->io, $this->composer->getConfig());
|
||||||
|
$s3RemoteFilesystem = new S3RemoteFilesystem($this->io, $event->getRemoteFilesystem()->getOptions(), $awsClient);
|
||||||
|
$event->setRemoteFilesystem($s3RemoteFilesystem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
## Using Plugins
|
||||||
|
|
||||||
|
Plugin packages are automatically loaded as soon as they are installed and will
|
||||||
|
be loaded when composer starts up if they are found in the current project's
|
||||||
|
list of installed packages. Additionally all plugin packages installed in the
|
||||||
|
`COMPOSER_HOME` directory using the composer global command are loaded before
|
||||||
|
local project plugins are loaded.
|
||||||
|
|
||||||
|
> You may pass the `--no-plugins` option to composer commands to disable all
|
||||||
|
> installed commands. This may be particularly helpful if any of the plugins
|
||||||
|
> causes errors and you wish to update or uninstall it.
|
||||||
|
|
||||||
|
[1]: ../04-schema.md#type
|
||||||
|
[2]: ../04-schema.md#extra
|
||||||
|
[3]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/PluginInterface.php
|
||||||
|
[4]: https://github.com/composer/composer/blob/master/src/Composer/Composer.php
|
||||||
|
[5]: https://github.com/composer/composer/blob/master/src/Composer/IO/IOInterface.php
|
||||||
|
[6]: https://github.com/composer/composer/blob/master/src/Composer/EventDispatcher/EventSubscriberInterface.php
|
||||||
|
[7]: ./scripts.md#event-names
|
@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Downloader;
|
||||||
|
|
||||||
|
use Composer\Package\PackageInterface;
|
||||||
|
use Composer\Repository\VcsRepository;
|
||||||
|
use Composer\Util\Perforce;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Matt Whittom <Matt.Whittom@veteransunited.com>
|
||||||
|
*/
|
||||||
|
class PerforceDownloader extends VcsDownloader
|
||||||
|
{
|
||||||
|
protected $perforce;
|
||||||
|
protected $perforceInjected = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function doDownload(PackageInterface $package, $path)
|
||||||
|
{
|
||||||
|
$ref = $package->getSourceReference();
|
||||||
|
$label = $package->getPrettyVersion();
|
||||||
|
|
||||||
|
$this->io->write(' Cloning ' . $ref);
|
||||||
|
$this->initPerforce($package, $path);
|
||||||
|
$this->perforce->setStream($ref);
|
||||||
|
$this->perforce->p4Login($this->io);
|
||||||
|
$this->perforce->writeP4ClientSpec();
|
||||||
|
$this->perforce->connectClient();
|
||||||
|
$this->perforce->syncCodeBase($label);
|
||||||
|
$this->perforce->cleanupClientSpec();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initPerforce($package, $path)
|
||||||
|
{
|
||||||
|
if ($this->perforce) {
|
||||||
|
$this->perforce->initializePath($path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$repository = $package->getRepository();
|
||||||
|
$repoConfig = null;
|
||||||
|
if ($repository instanceof VcsRepository) {
|
||||||
|
$repoConfig = $this->getRepoConfig($repository);
|
||||||
|
}
|
||||||
|
$this->perforce = Perforce::create($repoConfig, $package->getSourceUrl(), $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getRepoConfig(VcsRepository $repository)
|
||||||
|
{
|
||||||
|
return $repository->getRepoConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path)
|
||||||
|
{
|
||||||
|
$this->doDownload($target, $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getLocalChanges(PackageInterface $package, $path)
|
||||||
|
{
|
||||||
|
$this->io->write('Perforce driver does not check for local changes before overriding', true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function getCommitLogs($fromReference, $toReference, $path)
|
||||||
|
{
|
||||||
|
$commitLogs = $this->perforce->getCommitLogs($fromReference, $toReference);
|
||||||
|
|
||||||
|
return $commitLogs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPerforce($perforce)
|
||||||
|
{
|
||||||
|
$this->perforce = $perforce;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Downloader;
|
||||||
|
|
||||||
|
use Composer\Config;
|
||||||
|
use Composer\Cache;
|
||||||
|
use Composer\EventDispatcher\EventDispatcher;
|
||||||
|
use Composer\Util\ProcessExecutor;
|
||||||
|
use Composer\IO\IOInterface;
|
||||||
|
use RarArchive;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RAR archive downloader.
|
||||||
|
*
|
||||||
|
* Based on previous work by Jordi Boggiano ({@see ZipDownloader}).
|
||||||
|
*
|
||||||
|
* @author Derrick Nelson <drrcknlsn@gmail.com>
|
||||||
|
*/
|
||||||
|
class RarDownloader extends ArchiveDownloader
|
||||||
|
{
|
||||||
|
protected $process;
|
||||||
|
|
||||||
|
public function __construct(IOInterface $io, Config $config, EventDispatcher $eventDispatcher = null, Cache $cache = null, ProcessExecutor $process = null)
|
||||||
|
{
|
||||||
|
$this->process = $process ?: new ProcessExecutor($io);
|
||||||
|
parent::__construct($io, $config, $eventDispatcher, $cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function extract($file, $path)
|
||||||
|
{
|
||||||
|
$processError = null;
|
||||||
|
|
||||||
|
// Try to use unrar on *nix
|
||||||
|
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||||
|
$command = 'unrar x ' . escapeshellarg($file) . ' ' . escapeshellarg($path) . ' && chmod -R u+w ' . escapeshellarg($path);
|
||||||
|
|
||||||
|
if (0 === $this->process->execute($command, $ignoredOutput)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$processError = 'Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!class_exists('RarArchive')) {
|
||||||
|
// php.ini path is added to the error message to help users find the correct file
|
||||||
|
$iniPath = php_ini_loaded_file();
|
||||||
|
|
||||||
|
if ($iniPath) {
|
||||||
|
$iniMessage = 'The php.ini used by your command-line PHP is: ' . $iniPath;
|
||||||
|
} else {
|
||||||
|
$iniMessage = 'A php.ini file does not exist. You will have to create one.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$error = "Could not decompress the archive, enable the PHP rar extension or install unrar.\n"
|
||||||
|
. $iniMessage . "\n" . $processError;
|
||||||
|
|
||||||
|
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||||
|
$error = "Could not decompress the archive, enable the PHP rar extension.\n" . $iniMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \RuntimeException($error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rarArchive = RarArchive::open($file);
|
||||||
|
|
||||||
|
if (false === $rarArchive) {
|
||||||
|
throw new \UnexpectedValueException('Could not open RAR archive: ' . $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
$entries = $rarArchive->getEntries();
|
||||||
|
|
||||||
|
if (false === $entries) {
|
||||||
|
throw new \RuntimeException('Could not retrieve RAR archive entries');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
if (false === $entry->extract($path)) {
|
||||||
|
throw new \RuntimeException('Could not extract entry');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rarArchive->close();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\EventDispatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base event class
|
||||||
|
*
|
||||||
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
|
*/
|
||||||
|
class Event
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string This event's name
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean Whether the event should not be passed to more listeners
|
||||||
|
*/
|
||||||
|
private $propagationStopped = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param string $name The event name
|
||||||
|
*/
|
||||||
|
public function __construct($name)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the event's name.
|
||||||
|
*
|
||||||
|
* @return string The event name
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if stopPropagation has been called
|
||||||
|
*
|
||||||
|
* @return boolean Whether propagation has been stopped
|
||||||
|
*/
|
||||||
|
public function isPropagationStopped()
|
||||||
|
{
|
||||||
|
return $this->propagationStopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents the event from being passed to further listeners
|
||||||
|
*/
|
||||||
|
public function stopPropagation()
|
||||||
|
{
|
||||||
|
$this->propagationStopped = true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\EventDispatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An EventSubscriber knows which events it is interested in.
|
||||||
|
*
|
||||||
|
* If an EventSubscriber is added to an EventDispatcher, the manager invokes
|
||||||
|
* {@link getSubscribedEvents} and registers the subscriber as a listener for all
|
||||||
|
* returned events.
|
||||||
|
*
|
||||||
|
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||||
|
* @author Jonathan Wage <jonwage@gmail.com>
|
||||||
|
* @author Roman Borschel <roman@code-factory.org>
|
||||||
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
|
*/
|
||||||
|
interface EventSubscriberInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns an array of event names this subscriber wants to listen to.
|
||||||
|
*
|
||||||
|
* The array keys are event names and the value can be:
|
||||||
|
*
|
||||||
|
* * The method name to call (priority defaults to 0)
|
||||||
|
* * An array composed of the method name to call and the priority
|
||||||
|
* * An array of arrays composed of the method names to call and respective
|
||||||
|
* priorities, or 0 if unset
|
||||||
|
*
|
||||||
|
* For instance:
|
||||||
|
*
|
||||||
|
* * array('eventName' => 'methodName')
|
||||||
|
* * array('eventName' => array('methodName', $priority))
|
||||||
|
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
|
||||||
|
*
|
||||||
|
* @return array The event names to listen to
|
||||||
|
*/
|
||||||
|
public static function getSubscribedEvents();
|
||||||
|
}
|
@ -1,104 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Composer.
|
|
||||||
*
|
|
||||||
* (c) Nils Adermann <naderman@naderman.de>
|
|
||||||
* Jordi Boggiano <j.boggiano@seld.be>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Composer\Installer;
|
|
||||||
|
|
||||||
use Composer\Composer;
|
|
||||||
use Composer\Package\Package;
|
|
||||||
use Composer\IO\IOInterface;
|
|
||||||
use Composer\Repository\InstalledRepositoryInterface;
|
|
||||||
use Composer\Package\PackageInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Installer installation manager.
|
|
||||||
*
|
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
|
||||||
*/
|
|
||||||
class InstallerInstaller extends LibraryInstaller
|
|
||||||
{
|
|
||||||
private $installationManager;
|
|
||||||
private static $classCounter = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes Installer installer.
|
|
||||||
*
|
|
||||||
* @param IOInterface $io
|
|
||||||
* @param Composer $composer
|
|
||||||
* @param string $type
|
|
||||||
*/
|
|
||||||
public function __construct(IOInterface $io, Composer $composer, $type = 'library')
|
|
||||||
{
|
|
||||||
parent::__construct($io, $composer, 'composer-installer');
|
|
||||||
$this->installationManager = $composer->getInstallationManager();
|
|
||||||
|
|
||||||
$repo = $composer->getRepositoryManager()->getLocalRepository();
|
|
||||||
foreach ($repo->getPackages() as $package) {
|
|
||||||
if ('composer-installer' === $package->getType()) {
|
|
||||||
$this->registerInstaller($package);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
|
|
||||||
{
|
|
||||||
$extra = $package->getExtra();
|
|
||||||
if (empty($extra['class'])) {
|
|
||||||
throw new \UnexpectedValueException('Error while installing '.$package->getPrettyName().', composer-installer packages should have a class defined in their extra key to be usable.');
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::install($repo, $package);
|
|
||||||
$this->registerInstaller($package);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
|
|
||||||
{
|
|
||||||
$extra = $target->getExtra();
|
|
||||||
if (empty($extra['class'])) {
|
|
||||||
throw new \UnexpectedValueException('Error while installing '.$target->getPrettyName().', composer-installer packages should have a class defined in their extra key to be usable.');
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::update($repo, $initial, $target);
|
|
||||||
$this->registerInstaller($target);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function registerInstaller(PackageInterface $package)
|
|
||||||
{
|
|
||||||
$downloadPath = $this->getInstallPath($package);
|
|
||||||
|
|
||||||
$extra = $package->getExtra();
|
|
||||||
$classes = is_array($extra['class']) ? $extra['class'] : array($extra['class']);
|
|
||||||
|
|
||||||
$generator = $this->composer->getAutoloadGenerator();
|
|
||||||
$map = $generator->parseAutoloads(array(array($package, $downloadPath)), new Package('dummy', '1.0.0.0', '1.0.0'));
|
|
||||||
$classLoader = $generator->createLoader($map);
|
|
||||||
$classLoader->register();
|
|
||||||
|
|
||||||
foreach ($classes as $class) {
|
|
||||||
if (class_exists($class, false)) {
|
|
||||||
$code = file_get_contents($classLoader->findFile($class));
|
|
||||||
$code = preg_replace('{^(\s*)class\s+(\S+)}mi', '$1class $2_composer_tmp'.self::$classCounter, $code);
|
|
||||||
eval('?>'.$code);
|
|
||||||
$class .= '_composer_tmp'.self::$classCounter;
|
|
||||||
self::$classCounter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
$installer = new $class($this->io, $this->composer);
|
|
||||||
$this->installationManager->addInstaller($installer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Installer;
|
||||||
|
|
||||||
|
use Composer\Composer;
|
||||||
|
use Composer\Package\Package;
|
||||||
|
use Composer\IO\IOInterface;
|
||||||
|
use Composer\Repository\InstalledRepositoryInterface;
|
||||||
|
use Composer\Package\PackageInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installer for plugin packages
|
||||||
|
*
|
||||||
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
|
*/
|
||||||
|
class PluginInstaller extends LibraryInstaller
|
||||||
|
{
|
||||||
|
private $installationManager;
|
||||||
|
private static $classCounter = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes Plugin installer.
|
||||||
|
*
|
||||||
|
* @param IOInterface $io
|
||||||
|
* @param Composer $composer
|
||||||
|
* @param string $type
|
||||||
|
*/
|
||||||
|
public function __construct(IOInterface $io, Composer $composer, $type = 'library')
|
||||||
|
{
|
||||||
|
parent::__construct($io, $composer, 'composer-plugin');
|
||||||
|
$this->installationManager = $composer->getInstallationManager();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function supports($packageType)
|
||||||
|
{
|
||||||
|
return $packageType === 'composer-plugin' || $packageType === 'composer-installer';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
|
||||||
|
{
|
||||||
|
$extra = $package->getExtra();
|
||||||
|
if (empty($extra['class'])) {
|
||||||
|
throw new \UnexpectedValueException('Error while installing '.$package->getPrettyName().', composer-plugin packages should have a class defined in their extra key to be usable.');
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::install($repo, $package);
|
||||||
|
$this->composer->getPluginManager()->registerPackage($package);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
|
||||||
|
{
|
||||||
|
$extra = $target->getExtra();
|
||||||
|
if (empty($extra['class'])) {
|
||||||
|
throw new \UnexpectedValueException('Error while installing '.$target->getPrettyName().', composer-plugin packages should have a class defined in their extra key to be usable.');
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::update($repo, $initial, $target);
|
||||||
|
$this->composer->getPluginManager()->registerPackage($target);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Plugin;
|
||||||
|
|
||||||
|
use Composer\EventDispatcher\Event;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event for all commands.
|
||||||
|
*
|
||||||
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
|
*/
|
||||||
|
class CommandEvent extends Event
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $commandName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var InputInterface
|
||||||
|
*/
|
||||||
|
private $input;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var OutputInterface
|
||||||
|
*/
|
||||||
|
private $output;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param string $name The event name
|
||||||
|
* @param string $commandName The command name
|
||||||
|
* @param InputInterface $input
|
||||||
|
* @param OutputInterface $output
|
||||||
|
*/
|
||||||
|
public function __construct($name, $commandName, $input, $output)
|
||||||
|
{
|
||||||
|
parent::__construct($name);
|
||||||
|
$this->commandName = $commandName;
|
||||||
|
$this->input = $input;
|
||||||
|
$this->output = $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the command input interface
|
||||||
|
*
|
||||||
|
* @return InputInterface
|
||||||
|
*/
|
||||||
|
public function getInput()
|
||||||
|
{
|
||||||
|
return $this->input;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the command output interface
|
||||||
|
*
|
||||||
|
* @return OutputInterface
|
||||||
|
*/
|
||||||
|
public function getOutput()
|
||||||
|
{
|
||||||
|
return $this->output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the name of the command being run
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getCommandName()
|
||||||
|
{
|
||||||
|
return $this->commandName;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Plugin Events.
|
||||||
|
*
|
||||||
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
|
*/
|
||||||
|
class PluginEvents
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The COMMAND event occurs as a command begins
|
||||||
|
*
|
||||||
|
* The event listener method receives a
|
||||||
|
* Composer\Plugin\CommandEvent instance.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const COMMAND = 'command';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The PRE_FILE_DOWNLOAD event occurs before downloading a file
|
||||||
|
*
|
||||||
|
* The event listener method receives a
|
||||||
|
* Composer\Plugin\PreFileDownloadEvent instance.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const PRE_FILE_DOWNLOAD = 'pre-file-download';
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Plugin;
|
||||||
|
|
||||||
|
use Composer\Composer;
|
||||||
|
use Composer\IO\IOInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin interface
|
||||||
|
*
|
||||||
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
|
*/
|
||||||
|
interface PluginInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Version number of the fake composer-plugin-api package
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const PLUGIN_API_VERSION = '1.0.0';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply plugin modifications to composer
|
||||||
|
*
|
||||||
|
* @param Composer $composer
|
||||||
|
* @param IOInterface $io
|
||||||
|
*/
|
||||||
|
public function activate(Composer $composer, IOInterface $io);
|
||||||
|
}
|
@ -0,0 +1,259 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Plugin;
|
||||||
|
|
||||||
|
use Composer\Composer;
|
||||||
|
use Composer\EventDispatcher\EventSubscriberInterface;
|
||||||
|
use Composer\IO\IOInterface;
|
||||||
|
use Composer\Package\Package;
|
||||||
|
use Composer\Package\Version\VersionParser;
|
||||||
|
use Composer\Repository\RepositoryInterface;
|
||||||
|
use Composer\Package\AliasPackage;
|
||||||
|
use Composer\Package\PackageInterface;
|
||||||
|
use Composer\Package\Link;
|
||||||
|
use Composer\Package\LinkConstraint\VersionConstraint;
|
||||||
|
use Composer\DependencyResolver\Pool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin manager
|
||||||
|
*
|
||||||
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
|
*/
|
||||||
|
class PluginManager
|
||||||
|
{
|
||||||
|
protected $composer;
|
||||||
|
protected $io;
|
||||||
|
protected $globalRepository;
|
||||||
|
protected $versionParser;
|
||||||
|
|
||||||
|
protected $plugins = array();
|
||||||
|
|
||||||
|
private static $classCounter = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes plugin manager
|
||||||
|
*
|
||||||
|
* @param Composer $composer
|
||||||
|
* @param IOInterface $io
|
||||||
|
* @param RepositoryInterface $globalRepository
|
||||||
|
*/
|
||||||
|
public function __construct(Composer $composer, IOInterface $io, RepositoryInterface $globalRepository = null)
|
||||||
|
{
|
||||||
|
$this->composer = $composer;
|
||||||
|
$this->io = $io;
|
||||||
|
$this->globalRepository = $globalRepository;
|
||||||
|
$this->versionParser = new VersionParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all plugins from currently installed plugin packages
|
||||||
|
*/
|
||||||
|
public function loadInstalledPlugins()
|
||||||
|
{
|
||||||
|
$repo = $this->composer->getRepositoryManager()->getLocalRepository();
|
||||||
|
|
||||||
|
if ($repo) {
|
||||||
|
$this->loadRepository($repo);
|
||||||
|
}
|
||||||
|
if ($this->globalRepository) {
|
||||||
|
$this->loadRepository($this->globalRepository);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a plugin, activates it and registers it with the event dispatcher
|
||||||
|
*
|
||||||
|
* @param PluginInterface $plugin plugin instance
|
||||||
|
*/
|
||||||
|
public function addPlugin(PluginInterface $plugin)
|
||||||
|
{
|
||||||
|
$this->plugins[] = $plugin;
|
||||||
|
$plugin->activate($this->composer, $this->io);
|
||||||
|
|
||||||
|
if ($plugin instanceof EventSubscriberInterface) {
|
||||||
|
$this->composer->getEventDispatcher()->addSubscriber($plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all currently active plugin instances
|
||||||
|
*
|
||||||
|
* @return array plugins
|
||||||
|
*/
|
||||||
|
public function getPlugins()
|
||||||
|
{
|
||||||
|
return $this->plugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all plugins and installers from a repository
|
||||||
|
*
|
||||||
|
* Note that plugins in the specified repository that rely on events that
|
||||||
|
* have fired prior to loading will be missed. This means you likely want to
|
||||||
|
* call this method as early as possible.
|
||||||
|
*
|
||||||
|
* @param RepositoryInterface $repo Repository to scan for plugins to install
|
||||||
|
*/
|
||||||
|
public function loadRepository(RepositoryInterface $repo)
|
||||||
|
{
|
||||||
|
foreach ($repo->getPackages() as $package) {
|
||||||
|
if ($package instanceof AliasPackage) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ('composer-plugin' === $package->getType()) {
|
||||||
|
$requiresComposer = null;
|
||||||
|
foreach ($package->getRequires() as $link) {
|
||||||
|
if ($link->getTarget() == 'composer-plugin-api') {
|
||||||
|
$requiresComposer = $link->getConstraint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$requiresComposer) {
|
||||||
|
throw new \RuntimeException("Plugin ".$package->getName()." is missing a require statement for a version of the composer-plugin-api package.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$requiresComposer->matches(new VersionConstraint('==', $this->versionParser->normalize(PluginInterface::PLUGIN_API_VERSION)))) {
|
||||||
|
$this->io->write("<warning>The plugin ".$package->getName()." requires a version of composer-plugin-api that does not match your composer installation. You may need to run composer update with the '--no-plugins' option.</warning>");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->registerPackage($package);
|
||||||
|
}
|
||||||
|
// Backward compatibility
|
||||||
|
if ('composer-installer' === $package->getType()) {
|
||||||
|
$this->registerPackage($package);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively generates a map of package names to packages for all deps
|
||||||
|
*
|
||||||
|
* @param Pool $pool Package pool of installed packages
|
||||||
|
* @param array $collected Current state of the map for recursion
|
||||||
|
* @param PackageInterface $package The package to analyze
|
||||||
|
*
|
||||||
|
* @return array Map of package names to packages
|
||||||
|
*/
|
||||||
|
protected function collectDependencies(Pool $pool, array $collected, PackageInterface $package)
|
||||||
|
{
|
||||||
|
$requires = array_merge(
|
||||||
|
$package->getRequires(),
|
||||||
|
$package->getDevRequires()
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($requires as $requireLink) {
|
||||||
|
$requiredPackage = $this->lookupInstalledPackage($pool, $requireLink);
|
||||||
|
if ($requiredPackage && !isset($collected[$requiredPackage->getName()])) {
|
||||||
|
$collected[$requiredPackage->getName()] = $requiredPackage;
|
||||||
|
$collected = $this->collectDependencies($pool, $collected, $requiredPackage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collected;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a package link to a package in the installed pool
|
||||||
|
*
|
||||||
|
* Since dependencies are already installed this should always find one.
|
||||||
|
*
|
||||||
|
* @param Pool $pool Pool of installed packages only
|
||||||
|
* @param Link $link Package link to look up
|
||||||
|
*
|
||||||
|
* @return PackageInterface|null The found package
|
||||||
|
*/
|
||||||
|
protected function lookupInstalledPackage(Pool $pool, Link $link)
|
||||||
|
{
|
||||||
|
$packages = $pool->whatProvides($link->getTarget(), $link->getConstraint());
|
||||||
|
|
||||||
|
return (!empty($packages)) ? $packages[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a plugin package, activate it etc.
|
||||||
|
*
|
||||||
|
* If it's of type composer-installer it is registered as an installer
|
||||||
|
* instead for BC
|
||||||
|
*
|
||||||
|
* @param PackageInterface $package
|
||||||
|
*/
|
||||||
|
public function registerPackage(PackageInterface $package)
|
||||||
|
{
|
||||||
|
$oldInstallerPlugin = ($package->getType() === 'composer-installer');
|
||||||
|
|
||||||
|
$extra = $package->getExtra();
|
||||||
|
if (empty($extra['class'])) {
|
||||||
|
throw new \UnexpectedValueException('Error while installing '.$package->getPrettyName().', composer-plugin packages should have a class defined in their extra key to be usable.');
|
||||||
|
}
|
||||||
|
$classes = is_array($extra['class']) ? $extra['class'] : array($extra['class']);
|
||||||
|
|
||||||
|
$pool = new Pool('dev');
|
||||||
|
$localRepo = $this->composer->getRepositoryManager()->getLocalRepository();
|
||||||
|
$pool->addRepository($localRepo);
|
||||||
|
if ($this->globalRepository) {
|
||||||
|
$pool->addRepository($this->globalRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
$autoloadPackages = array($package->getName() => $package);
|
||||||
|
$autoloadPackages = $this->collectDependencies($pool, $autoloadPackages, $package);
|
||||||
|
|
||||||
|
$generator = $this->composer->getAutoloadGenerator();
|
||||||
|
$autoloads = array();
|
||||||
|
foreach ($autoloadPackages as $autoloadPackage) {
|
||||||
|
$downloadPath = $this->getInstallPath($autoloadPackage, ($this->globalRepository && $this->globalRepository->hasPackage($autoloadPackage)));
|
||||||
|
$autoloads[] = array($autoloadPackage, $downloadPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
$map = $generator->parseAutoloads($autoloads, new Package('dummy', '1.0.0.0', '1.0.0'));
|
||||||
|
$classLoader = $generator->createLoader($map);
|
||||||
|
$classLoader->register();
|
||||||
|
|
||||||
|
foreach ($classes as $class) {
|
||||||
|
if (class_exists($class, false)) {
|
||||||
|
$code = file_get_contents($classLoader->findFile($class));
|
||||||
|
$code = preg_replace('{^(\s*)class\s+(\S+)}mi', '$1class $2_composer_tmp'.self::$classCounter, $code);
|
||||||
|
eval('?>'.$code);
|
||||||
|
$class .= '_composer_tmp'.self::$classCounter;
|
||||||
|
self::$classCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($oldInstallerPlugin) {
|
||||||
|
$installer = new $class($this->io, $this->composer);
|
||||||
|
$this->composer->getInstallationManager()->addInstaller($installer);
|
||||||
|
} else {
|
||||||
|
$plugin = new $class();
|
||||||
|
$this->addPlugin($plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the path a package is installed to.
|
||||||
|
*
|
||||||
|
* @param PackageInterface $package
|
||||||
|
* @param bool $global Whether this is a global package
|
||||||
|
*
|
||||||
|
* @return string Install path
|
||||||
|
*/
|
||||||
|
public function getInstallPath(PackageInterface $package, $global = false)
|
||||||
|
{
|
||||||
|
if (!$global) {
|
||||||
|
return $this->composer->getInstallationManager()->getInstallPath($package);
|
||||||
|
}
|
||||||
|
|
||||||
|
$targetDir = $package->getTargetDir();
|
||||||
|
$vendorDir = $this->composer->getConfig()->get('home').'/vendor';
|
||||||
|
|
||||||
|
return ($vendorDir ? $vendorDir.'/' : '').$package->getPrettyName().($targetDir ? '/'.$targetDir : '');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Plugin;
|
||||||
|
|
||||||
|
use Composer\EventDispatcher\Event;
|
||||||
|
use Composer\Util\RemoteFilesystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pre file download event.
|
||||||
|
*
|
||||||
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
|
*/
|
||||||
|
class PreFileDownloadEvent extends Event
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var RemoteFilesystem
|
||||||
|
*/
|
||||||
|
private $rfs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $processedUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param string $name The event name
|
||||||
|
* @param RemoteFilesystem $rfs
|
||||||
|
* @param string $processedUrl
|
||||||
|
*/
|
||||||
|
public function __construct($name, RemoteFilesystem $rfs, $processedUrl)
|
||||||
|
{
|
||||||
|
parent::__construct($name);
|
||||||
|
$this->rfs = $rfs;
|
||||||
|
$this->processedUrl = $processedUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the remote filesystem
|
||||||
|
*
|
||||||
|
* @return RemoteFilesystem
|
||||||
|
*/
|
||||||
|
public function getRemoteFilesystem()
|
||||||
|
{
|
||||||
|
return $this->rfs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the remote filesystem
|
||||||
|
*
|
||||||
|
* @param RemoteFilesystem $rfs
|
||||||
|
*/
|
||||||
|
public function setRemoteFilesystem(RemoteFilesystem $rfs)
|
||||||
|
{
|
||||||
|
$this->rfs = $rfs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the processed URL this remote filesystem will be used for
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getProcessedUrl()
|
||||||
|
{
|
||||||
|
return $this->processedUrl;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,194 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Repository\Vcs;
|
||||||
|
|
||||||
|
use Composer\Config;
|
||||||
|
use Composer\IO\IOInterface;
|
||||||
|
use Composer\Util\ProcessExecutor;
|
||||||
|
use Composer\Util\Perforce;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Matt Whittom <Matt.Whittom@veteransunited.com>
|
||||||
|
*/
|
||||||
|
class PerforceDriver extends VcsDriver
|
||||||
|
{
|
||||||
|
protected $depot;
|
||||||
|
protected $branch;
|
||||||
|
protected $perforce;
|
||||||
|
protected $composerInfo;
|
||||||
|
protected $composerInfoIdentifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function initialize()
|
||||||
|
{
|
||||||
|
$this->depot = $this->repoConfig['depot'];
|
||||||
|
$this->branch = '';
|
||||||
|
if (isset($this->repoConfig['branch'])) {
|
||||||
|
$this->branch = $this->repoConfig['branch'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->initPerforce($this->repoConfig);
|
||||||
|
$this->perforce->p4Login($this->io);
|
||||||
|
$this->perforce->checkStream($this->depot);
|
||||||
|
|
||||||
|
$this->perforce->writeP4ClientSpec();
|
||||||
|
$this->perforce->connectClient();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function initPerforce($repoConfig)
|
||||||
|
{
|
||||||
|
if (isset($this->perforce)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$repoDir = $this->config->get('cache-vcs-dir') . '/' . $this->depot;
|
||||||
|
$this->perforce = Perforce::create($repoConfig, $this->getUrl(), $repoDir, $this->process);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getComposerInformation($identifier)
|
||||||
|
{
|
||||||
|
if (isset($this->composerInfoIdentifier)) {
|
||||||
|
if (strcmp($identifier, $this->composerInfoIdentifier) === 0) {
|
||||||
|
return $this->composerInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$composer_info = $this->perforce->getComposerInformation($identifier);
|
||||||
|
|
||||||
|
return $composer_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getRootIdentifier()
|
||||||
|
{
|
||||||
|
return $this->branch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getBranches()
|
||||||
|
{
|
||||||
|
$branches = $this->perforce->getBranches();
|
||||||
|
|
||||||
|
return $branches;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getTags()
|
||||||
|
{
|
||||||
|
$tags = $this->perforce->getTags();
|
||||||
|
|
||||||
|
return $tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getDist($identifier)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getSource($identifier)
|
||||||
|
{
|
||||||
|
$source = array(
|
||||||
|
'type' => 'perforce',
|
||||||
|
'url' => $this->repoConfig['url'],
|
||||||
|
'reference' => $identifier,
|
||||||
|
'p4user' => $this->perforce->getUser()
|
||||||
|
);
|
||||||
|
|
||||||
|
return $source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getUrl()
|
||||||
|
{
|
||||||
|
return $this->url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function hasComposerFile($identifier)
|
||||||
|
{
|
||||||
|
$this->composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier);
|
||||||
|
$this->composerInfoIdentifier = $identifier;
|
||||||
|
$result = false;
|
||||||
|
if (isset($this->composerInfo)) {
|
||||||
|
$result = count($this->composerInfo) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function getContents($url)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public static function supports(IOInterface $io, Config $config, $url, $deep = false)
|
||||||
|
{
|
||||||
|
if ($deep || preg_match('#\b(perforce|p4)\b#i', $url)) {
|
||||||
|
return Perforce::checkServerExists($url, new ProcessExecutor($io));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function cleanup()
|
||||||
|
{
|
||||||
|
$this->perforce->cleanupClientSpec();
|
||||||
|
$this->perforce = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDepot()
|
||||||
|
{
|
||||||
|
return $this->depot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBranch()
|
||||||
|
{
|
||||||
|
return $this->branch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPerforce(Perforce $perforce)
|
||||||
|
{
|
||||||
|
$this->perforce = $perforce;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,544 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Util;
|
||||||
|
|
||||||
|
use Composer\IO\IOInterface;
|
||||||
|
use Symfony\Component\Process\Process;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Matt Whittom <Matt.Whittom@veteransunited.com>
|
||||||
|
*/
|
||||||
|
class Perforce
|
||||||
|
{
|
||||||
|
protected $path;
|
||||||
|
protected $p4Depot;
|
||||||
|
protected $p4Client;
|
||||||
|
protected $p4User;
|
||||||
|
protected $p4Password;
|
||||||
|
protected $p4Port;
|
||||||
|
protected $p4Stream;
|
||||||
|
protected $p4ClientSpec;
|
||||||
|
protected $p4DepotType;
|
||||||
|
protected $p4Branch;
|
||||||
|
protected $process;
|
||||||
|
protected $uniquePerforceClientName;
|
||||||
|
protected $windowsFlag;
|
||||||
|
protected $commandResult;
|
||||||
|
|
||||||
|
public function __construct($repoConfig, $port, $path, ProcessExecutor $process, $isWindows)
|
||||||
|
{
|
||||||
|
$this->windowsFlag = $isWindows;
|
||||||
|
$this->p4Port = $port;
|
||||||
|
$this->initializePath($path);
|
||||||
|
$this->process = $process;
|
||||||
|
$this->initialize($repoConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function create($repoConfig, $port, $path, ProcessExecutor $process = null)
|
||||||
|
{
|
||||||
|
if (!isset($process)) {
|
||||||
|
$process = new ProcessExecutor;
|
||||||
|
}
|
||||||
|
$isWindows = defined('PHP_WINDOWS_VERSION_BUILD');
|
||||||
|
|
||||||
|
$perforce = new Perforce($repoConfig, $port, $path, $process, $isWindows);
|
||||||
|
|
||||||
|
return $perforce;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initialize($repoConfig)
|
||||||
|
{
|
||||||
|
$this->uniquePerforceClientName = $this->generateUniquePerforceClientName();
|
||||||
|
if (null == $repoConfig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isset($repoConfig['unique_perforce_client_name'])) {
|
||||||
|
$this->uniquePerforceClientName = $repoConfig['unique_perforce_client_name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($repoConfig['depot'])) {
|
||||||
|
$this->p4Depot = $repoConfig['depot'];
|
||||||
|
}
|
||||||
|
if (isset($repoConfig['branch'])) {
|
||||||
|
$this->p4Branch = $repoConfig['branch'];
|
||||||
|
}
|
||||||
|
if (isset($repoConfig['p4user'])) {
|
||||||
|
$this->p4User = $repoConfig['p4user'];
|
||||||
|
} else {
|
||||||
|
$this->p4User = $this->getP4variable('P4USER');
|
||||||
|
}
|
||||||
|
if (isset($repoConfig['p4password'])) {
|
||||||
|
$this->p4Password = $repoConfig['p4password'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initializeDepotAndBranch($depot, $branch)
|
||||||
|
{
|
||||||
|
if (isset($depot)) {
|
||||||
|
$this->p4Depot = $depot;
|
||||||
|
}
|
||||||
|
if (isset($branch)) {
|
||||||
|
$this->p4Branch = $branch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateUniquePerforceClientName()
|
||||||
|
{
|
||||||
|
return gethostname() . "_" . time();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cleanupClientSpec()
|
||||||
|
{
|
||||||
|
$client = $this->getClient();
|
||||||
|
$command = 'p4 client -d $client';
|
||||||
|
$this->executeCommand($command);
|
||||||
|
$clientSpec = $this->getP4ClientSpec();
|
||||||
|
$fileSystem = new FileSystem($this->process);
|
||||||
|
$fileSystem->remove($clientSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function executeCommand($command)
|
||||||
|
{
|
||||||
|
$this->commandResult = "";
|
||||||
|
$exit_code = $this->process->execute($command, $this->commandResult);
|
||||||
|
return $exit_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClient()
|
||||||
|
{
|
||||||
|
if (!isset($this->p4Client)) {
|
||||||
|
$cleanStreamName = str_replace('@', '', str_replace('/', '_', str_replace('//', '', $this->getStream())));
|
||||||
|
$this->p4Client = 'composer_perforce_' . $this->uniquePerforceClientName . '_' . $cleanStreamName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->p4Client;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPath()
|
||||||
|
{
|
||||||
|
return $this->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initializePath($path)
|
||||||
|
{
|
||||||
|
$this->path = $path;
|
||||||
|
$fs = new Filesystem();
|
||||||
|
$fs->ensureDirectoryExists($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPort()
|
||||||
|
{
|
||||||
|
return $this->p4Port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStream($stream)
|
||||||
|
{
|
||||||
|
$this->p4Stream = $stream;
|
||||||
|
$index = strrpos($stream, '/');
|
||||||
|
//Stream format is //depot/stream, while non-streaming depot is //depot
|
||||||
|
if ($index > 2) {
|
||||||
|
$this->p4DepotType = 'stream';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isStream()
|
||||||
|
{
|
||||||
|
return (strcmp($this->p4DepotType, 'stream') === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStream()
|
||||||
|
{
|
||||||
|
if (!isset($this->p4Stream)) {
|
||||||
|
if ($this->isStream()) {
|
||||||
|
$this->p4Stream = '//' . $this->p4Depot . '/' . $this->p4Branch;
|
||||||
|
} else {
|
||||||
|
$this->p4Stream = '//' . $this->p4Depot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->p4Stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStreamWithoutLabel($stream)
|
||||||
|
{
|
||||||
|
$index = strpos($stream, '@');
|
||||||
|
if ($index === false) {
|
||||||
|
return $stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
return substr($stream, 0, $index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getP4ClientSpec()
|
||||||
|
{
|
||||||
|
$p4clientSpec = $this->path . '/' . $this->getClient() . '.p4.spec';
|
||||||
|
|
||||||
|
return $p4clientSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUser()
|
||||||
|
{
|
||||||
|
return $this->p4User;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function queryP4User(IOInterface $io)
|
||||||
|
{
|
||||||
|
$this->getUser();
|
||||||
|
if (strlen($this->p4User) > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->p4User = $this->getP4variable('P4USER');
|
||||||
|
if (strlen($this->p4User) > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->p4User = $io->ask('Enter P4 User:');
|
||||||
|
if ($this->windowsFlag) {
|
||||||
|
$command = 'p4 set P4USER=' . $this->p4User;
|
||||||
|
} else {
|
||||||
|
$command = 'export P4USER=' . $this->p4User;
|
||||||
|
}
|
||||||
|
$this->executeCommand($command);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getP4variable($name)
|
||||||
|
{
|
||||||
|
if ($this->windowsFlag) {
|
||||||
|
$command = 'p4 set';
|
||||||
|
$this->executeCommand($command);
|
||||||
|
$result = trim($this->commandResult);
|
||||||
|
$resArray = explode(PHP_EOL, $result);
|
||||||
|
foreach ($resArray as $line) {
|
||||||
|
$fields = explode('=', $line);
|
||||||
|
if (strcmp($name, $fields[0]) == 0) {
|
||||||
|
$index = strpos($fields[1], ' ');
|
||||||
|
if ($index === false) {
|
||||||
|
$value = $fields[1];
|
||||||
|
} else {
|
||||||
|
$value = substr($fields[1], 0, $index);
|
||||||
|
}
|
||||||
|
$value = trim($value);
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$command = 'echo $' . $name;
|
||||||
|
$this->executeCommand($command);
|
||||||
|
$result = trim($this->commandResult);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function queryP4Password(IOInterface $io)
|
||||||
|
{
|
||||||
|
if (isset($this->p4Password)) {
|
||||||
|
return $this->p4Password;
|
||||||
|
}
|
||||||
|
$password = $this->getP4variable('P4PASSWD');
|
||||||
|
if (strlen($password) <= 0) {
|
||||||
|
$password = $io->askAndHideAnswer('Enter password for Perforce user ' . $this->getUser() . ': ');
|
||||||
|
}
|
||||||
|
$this->p4Password = $password;
|
||||||
|
|
||||||
|
return $password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateP4Command($command, $useClient = true)
|
||||||
|
{
|
||||||
|
$p4Command = 'p4 ';
|
||||||
|
$p4Command = $p4Command . '-u ' . $this->getUser() . ' ';
|
||||||
|
if ($useClient) {
|
||||||
|
$p4Command = $p4Command . '-c ' . $this->getClient() . ' ';
|
||||||
|
}
|
||||||
|
$p4Command = $p4Command . '-p ' . $this->getPort() . ' ';
|
||||||
|
$p4Command = $p4Command . $command;
|
||||||
|
|
||||||
|
return $p4Command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isLoggedIn()
|
||||||
|
{
|
||||||
|
$command = $this->generateP4Command('login -s', false);
|
||||||
|
$exitCode = $this->executeCommand($command);
|
||||||
|
if ($exitCode){
|
||||||
|
$errorOutput = $this->process->getErrorOutput();
|
||||||
|
$index = strpos($errorOutput, $this->getUser());
|
||||||
|
if ($index === false){
|
||||||
|
$index = strpos($errorOutput, 'p4');
|
||||||
|
if ($index===false){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw new \Exception('p4 command not found in path: ' . $errorOutput);
|
||||||
|
}
|
||||||
|
throw new \Exception('Invalid user name: ' . $this->getUser() );
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function connectClient()
|
||||||
|
{
|
||||||
|
$p4CreateClientCommand = $this->generateP4Command('client -i < ' . $this->getP4ClientSpec());
|
||||||
|
$this->executeCommand($p4CreateClientCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function syncCodeBase($label)
|
||||||
|
{
|
||||||
|
$prevDir = getcwd();
|
||||||
|
chdir($this->path);
|
||||||
|
|
||||||
|
$p4SyncCommand = $this->generateP4Command('sync -f ');
|
||||||
|
if (isset($label)) {
|
||||||
|
if (strcmp($label, 'dev-master') != 0) {
|
||||||
|
$p4SyncCommand = $p4SyncCommand . '@' . $label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->executeCommand($p4SyncCommand);
|
||||||
|
|
||||||
|
chdir($prevDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function writeClientSpecToFile($spec)
|
||||||
|
{
|
||||||
|
fwrite($spec, 'Client: ' . $this->getClient() . PHP_EOL . PHP_EOL);
|
||||||
|
fwrite($spec, 'Update: ' . date('Y/m/d H:i:s') . PHP_EOL . PHP_EOL);
|
||||||
|
fwrite($spec, 'Access: ' . date('Y/m/d H:i:s') . PHP_EOL);
|
||||||
|
fwrite($spec, 'Owner: ' . $this->getUser() . PHP_EOL . PHP_EOL);
|
||||||
|
fwrite($spec, 'Description:' . PHP_EOL);
|
||||||
|
fwrite($spec, ' Created by ' . $this->getUser() . ' from composer.' . PHP_EOL . PHP_EOL);
|
||||||
|
fwrite($spec, 'Root: ' . $this->getPath() . PHP_EOL . PHP_EOL);
|
||||||
|
fwrite($spec, 'Options: noallwrite noclobber nocompress unlocked modtime rmdir' . PHP_EOL . PHP_EOL);
|
||||||
|
fwrite($spec, 'SubmitOptions: revertunchanged' . PHP_EOL . PHP_EOL);
|
||||||
|
fwrite($spec, 'LineEnd: local' . PHP_EOL . PHP_EOL);
|
||||||
|
if ($this->isStream()) {
|
||||||
|
fwrite($spec, 'Stream:' . PHP_EOL);
|
||||||
|
fwrite($spec, ' ' . $this->getStreamWithoutLabel($this->p4Stream) . PHP_EOL);
|
||||||
|
} else {
|
||||||
|
fwrite(
|
||||||
|
$spec,
|
||||||
|
'View: ' . $this->getStream() . '/... //' . $this->getClient() . '/... ' . PHP_EOL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function writeP4ClientSpec()
|
||||||
|
{
|
||||||
|
$clientSpec = $this->getP4ClientSpec();
|
||||||
|
$spec = fopen($clientSpec, 'w');
|
||||||
|
try {
|
||||||
|
$this->writeClientSpecToFile($spec);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
fclose($spec);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
fclose($spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function read($pipe, $name)
|
||||||
|
{
|
||||||
|
if (feof($pipe)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$line = fgets($pipe);
|
||||||
|
while ($line != false) {
|
||||||
|
$line = fgets($pipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function windowsLogin($password)
|
||||||
|
{
|
||||||
|
$command = $this->generateP4Command(' login -a');
|
||||||
|
$process = new Process($command, null, null, $password);
|
||||||
|
|
||||||
|
return $process->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function p4Login(IOInterface $io)
|
||||||
|
{
|
||||||
|
$this->queryP4User($io);
|
||||||
|
if (!$this->isLoggedIn()) {
|
||||||
|
$password = $this->queryP4Password($io);
|
||||||
|
if ($this->windowsFlag) {
|
||||||
|
$this->windowsLogin($password);
|
||||||
|
} else {
|
||||||
|
$command = 'echo ' . $password . ' | ' . $this->generateP4Command(' login -a', false);
|
||||||
|
$exitCode = $this->executeCommand($command);
|
||||||
|
$result = trim($this->commandResult);
|
||||||
|
if ($exitCode){
|
||||||
|
throw new \Exception("Error logging in:" . $this->process->getErrorOutput());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function checkServerExists($url, ProcessExecutor $processExecutor)
|
||||||
|
{
|
||||||
|
$output = null;
|
||||||
|
return 0 === $processExecutor->execute('p4 -p ' . $url . ' info -s', $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComposerInformation($identifier)
|
||||||
|
{
|
||||||
|
$index = strpos($identifier, '@');
|
||||||
|
if ($index === false) {
|
||||||
|
$composerJson = $identifier. '/composer.json';
|
||||||
|
|
||||||
|
return $this->getComposerInformationFromPath($composerJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getComposerInformationFromLabel($identifier, $index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComposerInformationFromPath($composerJson)
|
||||||
|
{
|
||||||
|
$command = $this->generateP4Command(' print ' . $composerJson);
|
||||||
|
$this->executeCommand($command);
|
||||||
|
$result = $this->commandResult;
|
||||||
|
$index = strpos($result, '{');
|
||||||
|
if ($index === false) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if ($index >= 0) {
|
||||||
|
$rawData = substr($result, $index);
|
||||||
|
$composer_info = json_decode($rawData, true);
|
||||||
|
|
||||||
|
return $composer_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComposerInformationFromLabel($identifier, $index)
|
||||||
|
{
|
||||||
|
$composerJsonPath = substr($identifier, 0, $index) . '/composer.json' . substr($identifier, $index);
|
||||||
|
$command = $this->generateP4Command(' files ' . $composerJsonPath, false);
|
||||||
|
$this->executeCommand($command);
|
||||||
|
$result = $this->commandResult;
|
||||||
|
$index2 = strpos($result, 'no such file(s).');
|
||||||
|
if ($index2 === false) {
|
||||||
|
$index3 = strpos($result, 'change');
|
||||||
|
if (!($index3 === false)) {
|
||||||
|
$phrase = trim(substr($result, $index3));
|
||||||
|
$fields = explode(' ', $phrase);
|
||||||
|
$id = $fields[1];
|
||||||
|
$composerJson = substr($identifier, 0, $index) . '/composer.json@' . $id;
|
||||||
|
|
||||||
|
return $this->getComposerInformationFromPath($composerJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBranches()
|
||||||
|
{
|
||||||
|
$possibleBranches = array();
|
||||||
|
if (!$this->isStream()) {
|
||||||
|
$possibleBranches[$this->p4Branch] = $this->getStream();
|
||||||
|
} else {
|
||||||
|
$command = $this->generateP4Command('streams //' . $this->p4Depot . '/...');
|
||||||
|
$this->executeCommand($command);
|
||||||
|
$result = $this->commandResult;
|
||||||
|
$resArray = explode(PHP_EOL, $result);
|
||||||
|
foreach ($resArray as $line) {
|
||||||
|
$resBits = explode(' ', $line);
|
||||||
|
if (count($resBits) > 4) {
|
||||||
|
$branch = preg_replace('/[^A-Za-z0-9 ]/', '', $resBits[4]);
|
||||||
|
$possibleBranches[$branch] = $resBits[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$branches = array();
|
||||||
|
$branches['master'] = $possibleBranches[$this->p4Branch];
|
||||||
|
|
||||||
|
return $branches;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTags()
|
||||||
|
{
|
||||||
|
$command = $this->generateP4Command('labels');
|
||||||
|
$this->executeCommand($command);
|
||||||
|
$result = $this->commandResult;
|
||||||
|
$resArray = explode(PHP_EOL, $result);
|
||||||
|
$tags = array();
|
||||||
|
foreach ($resArray as $line) {
|
||||||
|
$index = strpos($line, 'Label');
|
||||||
|
if (!($index === false)) {
|
||||||
|
$fields = explode(' ', $line);
|
||||||
|
$tags[$fields[1]] = $this->getStream() . '@' . $fields[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkStream()
|
||||||
|
{
|
||||||
|
$command = $this->generateP4Command('depots', false);
|
||||||
|
$this->executeCommand($command);
|
||||||
|
$result = $this->commandResult;
|
||||||
|
$resArray = explode(PHP_EOL, $result);
|
||||||
|
foreach ($resArray as $line) {
|
||||||
|
$index = strpos($line, 'Depot');
|
||||||
|
if (!($index === false)) {
|
||||||
|
$fields = explode(' ', $line);
|
||||||
|
if (strcmp($this->p4Depot, $fields[1]) === 0) {
|
||||||
|
$this->p4DepotType = $fields[3];
|
||||||
|
|
||||||
|
return $this->isStream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getChangeList($reference)
|
||||||
|
{
|
||||||
|
$index = strpos($reference, '@');
|
||||||
|
if ($index === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$label = substr($reference, $index);
|
||||||
|
$command = $this->generateP4Command(' changes -m1 ' . $label);
|
||||||
|
$this->executeCommand($command);
|
||||||
|
$changes = $this->commandResult;
|
||||||
|
if (strpos($changes, 'Change') !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$fields = explode(' ', $changes);
|
||||||
|
$changeList = $fields[1];
|
||||||
|
|
||||||
|
return $changeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommitLogs($fromReference, $toReference)
|
||||||
|
{
|
||||||
|
$fromChangeList = $this->getChangeList($fromReference);
|
||||||
|
if ($fromChangeList == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$toChangeList = $this->getChangeList($toReference);
|
||||||
|
if ($toChangeList == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$index = strpos($fromReference, '@');
|
||||||
|
$main = substr($fromReference, 0, $index) . '/...';
|
||||||
|
$command = $this->generateP4Command('filelog ' . $main . '@' . $fromChangeList. ',' . $toChangeList);
|
||||||
|
$this->executeCommand($command);
|
||||||
|
$result = $this->commandResult;
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue