1
0
Fork 0

initial refactoring

pull/19/head
everzet 2011-09-16 22:14:06 +03:00
parent bcb4e58940
commit 1e1ecb80b7
7 changed files with 401 additions and 205 deletions

View File

@ -13,7 +13,7 @@ use Composer\Console\Application;
setlocale(LC_ALL, 'C');
// initialize composer
$composer = new Composer();
$composer = new ConfigurableComposer('composer.json', 'composer.lock');
$composer->addDownloader('git', new GitDownloader);
$composer->addDownloader('pear', new PearDownloader);
$composer->addDownloader('zip', new ZipDownloader);

View File

@ -12,20 +12,14 @@
namespace Composer\Command;
use Composer\DependencyResolver\Pool;
use Composer\DependencyResolver\Request;
use Composer\DependencyResolver\DefaultPolicy;
use Composer\DependencyResolver\Solver;
use Composer\Repository\PlatformRepository;
use Composer\Package\MemoryPackage;
use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\DependencyResolver;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Ryan Weaver <ryan@knplabs.com>
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
class InstallCommand extends Command
{
@ -48,130 +42,48 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output)
{
// TODO this needs a parameter to enable installing from source (i.e. git clone, instead of downloading archives)
$sourceInstall = false;
if ($this->getLock()->isLocked()) {
$this->writeln('<info>Found lockfile. Reading</info>');
$config = $this->loadConfig();
$output->writeln('<info>Loading repositories</info>');
if (isset($config['repositories'])) {
foreach ($config['repositories'] as $name => $spec) {
$this->getComposer()->addRepository($name, $spec);
foreach ($this->getLock()->getLockedPackages() as $package) {
$installer = $this->getComposer()->getInstaller($package->getType());
if (!$installer->isInstalled($package)) {
$installer->install($package);
}
}
return 0;
}
// creating repository pool
$pool = new Pool;
$repoInstalled = new PlatformRepository;
$pool->addRepository($repoInstalled);
// TODO check the lock file to see what's currently installed
// $repoInstalled->addPackage(new MemoryPackage('$Package', '$Version'));
$output->writeln('Loading package list');
foreach ($this->getComposer()->getRepositories() as $repository) {
$pool->addRepository($repository);
}
// creating requirements request
$request = new Request($pool);
$output->writeln('Building up request');
// TODO there should be an update flag or dedicated update command
// TODO check lock file to remove packages that disappeared from the requirements
foreach ($config['require'] as $name => $version) {
if ('latest' === $version) {
$request->install($name);
} else {
preg_match('#^([>=<~]*)([\d.]+.*)$#', $version, $match);
if (!$match[1]) {
$match[1] = '=';
}
$constraint = new VersionConstraint($match[1], $match[2]);
$request->install($name, $constraint);
}
foreach ($this->getPackage()->getRequires() as $link) {
$request->install($link->getTarget(), $link->getConstraint());
}
$output->writeln('Solving dependencies');
// prepare solver
$platform = $this->getComposer()->getRepository('Platform');
$policy = new DependencyResolver\DefaultPolicy();
$solver = new DependencyResolver\Solver($policy, $pool, $platform);
$policy = new DefaultPolicy;
$solver = new Solver($policy, $pool, $repoInstalled);
$transaction = $solver->solve($request);
$lock = array();
foreach ($transaction as $task) {
switch ($task['job']) {
case 'install':
$package = $task['package'];
$output->writeln('> Installing '.$package->getPrettyName());
if ($sourceInstall) {
// TODO
} else {
if ($package->getDistType()) {
$downloaderType = $package->getDistType();
$type = 'dist';
} elseif ($package->getSourceType()) {
$output->writeln('Package '.$package->getPrettyName().' has no dist url, installing from source instead.');
$downloaderType = $package->getSourceType();
$type = 'source';
} else {
throw new \UnexpectedValueException('Package '.$package->getPrettyName().' has no source or dist URL.');
}
$downloader = $this->getComposer()->getDownloader($downloaderType);
$installer = $this->getComposer()->getInstaller($package->getType());
if (!$installer->install($package, $downloader, $type)) {
throw new \LogicException($package->getPrettyName().' could not be installed.');
}
}
$lock[$package->getName()] = array('version' => $package->getVersion());
break;
default:
throw new \UnexpectedValueException('Unhandled job type : '.$task['job']);
}
// solve dependencies and execute operations
$operations = $this->solveDependencies($request, $solver);
foreach ($operations as $operation) {
$operation->execute();
// TODO: collect installable packages into $installed
}
$output->writeln('> Done');
$this->storeLockFile($lock, $output);
}
protected function loadConfig()
{
if (!file_exists('composer.json')) {
throw new \UnexpectedValueException('composer.json config file not found in '.getcwd());
if (false) {
$config->lock($installed);
$output->writeln('> Locked');
}
$config = json_decode(file_get_contents('composer.json'), true);
if (!$config) {
switch (json_last_error()) {
case JSON_ERROR_NONE:
$msg = 'No error has occurred, is your composer.json file empty?';
break;
case JSON_ERROR_DEPTH:
$msg = 'The maximum stack depth has been exceeded';
break;
case JSON_ERROR_STATE_MISMATCH:
$msg = 'Invalid or malformed JSON';
break;
case JSON_ERROR_CTRL_CHAR:
$msg = 'Control character error, possibly incorrectly encoded';
break;
case JSON_ERROR_SYNTAX:
$msg = 'Syntax error';
break;
case JSON_ERROR_UTF8:
$msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
break;
}
throw new \UnexpectedValueException('Incorrect composer.json file: '.$msg);
}
return $config;
}
protected function storeLockFile(array $content, OutputInterface $output)
{
file_put_contents('composer.lock', json_encode($content, JSON_FORCE_OBJECT)."\n");
$output->writeln('> composer.lock dumped');
}
}
}

View File

@ -12,125 +12,61 @@
namespace Composer;
use Composer\Downloader\DownloaderInterface;
use Composer\Installer\InstallerInterface;
use Composer\Repository\ComposerRepository;
use Composer\Repository\PlatformRepository;
use Composer\Repository\GitRepository;
use Composer\Repository\PearRepository;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Konstantin Kudryashiv <ever.zet@gmail.com>
*/
class Composer
{
const VERSION = '1.0.0-DEV';
protected $repositories = array();
protected $downloaders = array();
protected $installers = array();
private $repositories = array();
private $installers = array();
public function __construct()
public function setInstaller($type, InstallerInterface $installer = null)
{
$this->addRepository('Packagist', array('composer' => 'http://packagist.org'));
}
if (null === $installer) {
unset($this->installers[$type]);
/**
* Add downloader for type
*
* @param string $type
* @param DownloaderInterface $downloader
*/
public function addDownloader($type, DownloaderInterface $downloader)
{
$type = strtolower($type);
$this->downloaders[$type] = $downloader;
}
/**
* Get type downloader
*
* @param string $type
*
* @return DownloaderInterface
*/
public function getDownloader($type)
{
$type = strtolower($type);
if (!isset($this->downloaders[$type])) {
throw new \UnexpectedValueException('Unknown source type: '.$type);
return;
}
return $this->downloaders[$type];
}
/**
* Add installer for type
*
* @param string $type
* @param InstallerInterface $installer
*/
public function addInstaller($type, InstallerInterface $installer)
{
$type = strtolower($type);
$this->installers[$type] = $installer;
}
/**
* Get type installer
*
* @param string $type
*
* @return InstallerInterface
*/
public function getInstaller($type)
{
$type = strtolower($type);
if (!isset($this->installers[$type])) {
throw new \UnexpectedValueException('Unknown dependency type: '.$type);
}
return $this->installers[$type];
}
public function addRepository($name, $spec)
public function setRepository($name, RepositoryInterface $repository = null)
{
if (null === $spec) {
if (null === $repository) {
unset($this->repositories[$name]);
return;
}
if (is_array($spec) && count($spec) === 1) {
return $this->repositories[$name] = $this->createRepository($name, key($spec), current($spec));
$this->repositories[$name] = $repository;
}
public function getRepository($name)
{
if (!isset($this->repositories[$name])) {
throw new \UnexpectedValueException('Unknown repository: '.$name);
}
throw new \UnexpectedValueException('Invalid repositories specification '.json_encode($spec).', should be: {"type": "url"}');
return $this->repositories[$name];
}
public function getRepositories()
{
return $this->repositories;
}
public function createRepository($name, $type, $spec)
{
if (is_string($spec)) {
$spec = array('url' => $spec);
}
$spec['url'] = rtrim($spec['url'], '/');
switch ($type) {
case 'git-bare':
case 'git-multi':
throw new \Exception($type.' repositories not supported yet');
break;
case 'git':
return new GitRepository($spec['url']);
case 'composer':
return new ComposerRepository($spec['url']);
case 'pear':
return new PearRepository($spec['url'], $name);
default:
throw new \UnexpectedValueException('Unknown repository type: '.$type.', could not create repository '.$name);
}
}
}

View File

@ -0,0 +1,200 @@
<?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;
use Composer\Repository;
use Composer\Package;
use Composer\Installer\LibraryInstaller;
/**
* @author Konstantin Kudryashiv <ever.zet@gmail.com>
*/
class ConfigurableComposer extends Composer
{
private $configFile;
private $lockFile;
private $isLocked = false;
private $lockedPackages = array();
public function __construct($configFile = 'composer.json', $lockFile = 'composer.lock')
{
$this->configFile = $configFile;
$this->lockFile = $lockFile;
$this->setRepository('Platform', new Repository\PlatformRepository());
if (!file_exists($configFile)) {
throw new \UnexpectedValueException('Can not find composer config file');
}
$config = $this->loadJsonConfig($configFile);
if (isset($config['path'])) {
$this->setInstaller('library', new LibraryInstaller($config['path']));
} else {
$this->setInstaller('library', new LibraryInstaller());
}
if (isset($config['repositories'])) {
$repositories = $this->loadRepositoriesFromConfig($config['repositories'])
foreach ($repositories as $name => $repository) {
$this->setRepository($name, $repository);
}
}
if (isset($config['require'])) {
$requirements = $this->loadRequirementsFromConfig($config['require']);
foreach ($requirements as $name => $constraint) {
$this->setRequirement($name, $constraint);
}
}
if (file_exists($lockFile)) {
$lock = $this->loadJsonConfig($lockFile);
$platform = $this->getRepository('Platform');
$packages = $this->loadPackagesFromLock($lock);
foreach ($packages as $package) {
if ($this->hasRequirement($package->getName())) {
$platform->addPackage($package);
$this->lockedPackages[] = $package;
}
}
$this->isLocked = true;
}
}
public function isLocked()
{
return $this->isLocked;
}
public function getLockedPackages()
{
return $this->lockedPackages;
}
public function lock(array $packages)
{
// TODO: write installed packages info into $this->lockFile
}
private function loadPackagesFromLock(array $lockList)
{
$packages = array();
foreach ($lockList as $info) {
$packages[] = new Package\MemoryPackage($info['package'], $info['version']);
}
return $packages;
}
private function loadRepositoriesFromConfig(array $repositoriesList)
{
$repositories = array();
foreach ($repositoriesList as $name => $spec) {
if (is_array($spec) && count($spec) === 1) {
$repositories[$name] = $this->createRepository($name, key($spec), current($spec));
} elseif (null === $spec) {
$repositories[$name] = null;
} else {
throw new \UnexpectedValueException(
'Invalid repositories specification '.
json_encode($spec).', should be: {"type": "url"}'
);
}
}
return $repositories;
}
private function loadRequirementsFromConfig(array $requirementsList)
{
$requirements = array();
foreach ($requirementsList as $name => $version) {
$name = $this->lowercase($name);
if ('latest' === $version) {
$requirements[$name] = null;
} else {
preg_match('#^([>=<~]*)([\d.]+.*)$#', $version, $match);
if (!$match[1]) {
$match[1] = '=';
}
$constraint = new Package\LinkConstraint\VersionConstraint($match[1], $match[2]);
$requirements[$name] = $constraint;
}
}
return $requirements;
}
private function loadJsonConfig($configFile)
{
$config = json_decode(file_get_contents($configFile), true);
if (!$config) {
switch (json_last_error()) {
case JSON_ERROR_NONE:
$msg = 'No error has occurred, is your composer.json file empty?';
break;
case JSON_ERROR_DEPTH:
$msg = 'The maximum stack depth has been exceeded';
break;
case JSON_ERROR_STATE_MISMATCH:
$msg = 'Invalid or malformed JSON';
break;
case JSON_ERROR_CTRL_CHAR:
$msg = 'Control character error, possibly incorrectly encoded';
break;
case JSON_ERROR_SYNTAX:
$msg = 'Syntax error';
break;
case JSON_ERROR_UTF8:
$msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
break;
}
throw new \UnexpectedValueException('Incorrect composer.json file: '.$msg);
}
return $config;
}
private function lowercase($str)
{
if (function_exists('mb_strtolower')) {
return mb_strtolower($str, 'UTF-8');
}
return strtolower($str, 'UTF-8');
}
private function createRepository($name, $type, $spec)
{
if (is_string($spec)) {
$spec = array('url' => $spec);
}
$spec['url'] = rtrim($spec['url'], '/');
switch ($type) {
case 'git-bare':
case 'git-multi':
throw new \Exception($type.' repositories not supported yet');
case 'git':
return new Repository\GitRepository($spec['url']);
case 'composer':
return new Repository\ComposerRepository($spec['url']);
case 'pear':
return new Repository\PearRepository($spec['url'], $name);
default:
throw new \UnexpectedValueException(
'Unknown repository type: '.$type.', could not create repository '.$name
);
}
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace Composer\Console\Package;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\Package\PackageInterface;
use Composer\Package\Manager;
class VerboseManager extends Manager
{
private $output;
public function __construct(Composer $composer, OutputInterface $output)
{
parent::__construct($output);
$this->composer = $composer;
}
public function install(PackageInterface $package)
{
$this->output->writeln('> Installing '.$package->getName());
parent::install($package);
}
public function update(PackageInterface $package)
{
$this->output->writeln('> Updating '.$package->getName());
parent::update($package);
}
public function remove(PackageInterface $package)
{
$this->output->writeln('> Removing '.$package->getName());
parent::remove($package);
}
}

View File

@ -38,4 +38,19 @@ class LibraryInstaller implements InstallerInterface
}
return true;
}
}
public function isInstalled(PackageInterface $package, $downloader, $type)
{
// TODO: implement installation check
}
public function update(PackageInterface $package, $downloader, $type)
{
// TODO: implement package update
}
public function remove(PackageInterface $package, $downloader, $type)
{
// TODO: implement package removal
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace Composer\Package;
use Composer\Package\PackageInterface;
class Manager
{
private $composer;
public function __construct(Composer $composer)
{
$this->composer = $composer;
}
public function isInstalled(PackageInterface $package)
{
$installer = $this->composer->getInstaller($package->getType());
$downloader = $this->getDownloaderForPackage($package);
$packageType = $this->getTypeForPackage($package);
return $installer->isInstalled($package, $downloader, $packageType);
}
public function install(PackageInterface $package)
{
$output->writeln('> Installing '.$package->getName());
$installer = $this->composer->getInstaller($package->getType());
$downloader = $this->getDownloaderForPackage($package);
$packageType = $this->getTypeForPackage($package);
if (!$installer->install($package, $downloader, $packageType)) {
throw new \LogicException($package->getName().' could not be installed.');
}
}
public function update(PackageInterface $package)
{
$output->writeln('> Updating '.$package->getName());
$installer = $this->composer->getInstaller($package->getType());
$downloader = $this->getDownloaderForPackage($package);
$packageType = $this->getTypeForPackage($package);
if (!$installer->update($package, $downloader, $packageType)) {
throw new \LogicException($package->getName().' could not be updated.');
}
}
public function remove(PackageInterface $package)
{
$output->writeln('> Removing '.$package->getName());
$installer = $this->composer->getInstaller($package->getType());
$downloader = $this->getDownloaderForPackage($package);
$packageType = $this->getTypeForPackage($package);
if (!$installer->remove($package, $downloader, $packageType)) {
throw new \LogicException($package->getName().' could not be removed.');
}
}
private function getDownloaderForPackage(PackageInterface $package)
{
if ($package->getDistType()) {
$downloader = $this->composer->getDownloader($package->getDistType);
} elseif ($package->getSourceType()) {
$downloader = $this->copmoser->getDownloader($package->getSourceType());
} else {
throw new \UnexpectedValueException(
'Package '.$package->getName().' has no source or dist URL.'
);
}
return $downloader;
}
private function getTypeForPackage(PackageInterface $package)
{
if ($package->getDistType()) {
$type = 'dist';
} elseif ($package->getSourceType()) {
$type = 'source';
} else {
throw new \UnexpectedValueException(
'Package '.$package->getName().' has no source or dist URL.'
);
}
return $type;
}
}