Browse Source

Add base installer/downloader and ComposerRepository

Jordi Boggiano 14 years ago
parent
commit
e03983697a

+ 16 - 0
bin/composer

@@ -0,0 +1,16 @@
+#!/bin/env php
+<?php
+
+require __DIR__.'/../tests/bootstrap.php';
+
+use Composer\Composer;
+use Composer\Downloader\GitDownloader;
+use Composer\Command\InstallCommand;
+use Composer\Installer\LibraryInstaller;
+
+$composer = new Composer();
+$composer->addDownloader('git', new GitDownloader);
+$composer->addInstaller('library', new LibraryInstaller);
+
+$cmd = new InstallCommand();
+$cmd->install($composer);

+ 71 - 0
src/Composer/Command/InstallCommand.php

@@ -0,0 +1,71 @@
+<?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\Command;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class InstallCommand
+{
+    protected $composer;
+
+    public function install($composer)
+    {
+        $this->composer = $composer;
+
+        $config = $this->loadConfig();
+
+        // TODO this should just do dependency solving based on all repositories
+        $packages = array();
+        foreach ($composer->getRepositories() as $repository) {
+            $packages = array_merge($packages, $repository->getPackages());
+        }
+
+        $lock = array();
+
+        // TODO this should use the transaction returned by the solver
+        foreach ($config['require'] as $name => $version) {
+            foreach ($packages as $pkg) {
+                if ($pkg->getName() === $name) {
+                    $package = $pkg;
+                    break;
+                }
+            }
+            if (!isset($package)) {
+                throw new \UnexpectedValueException('Could not find package '.$name.' in any of your repositories');
+            }
+            $downloader = $composer->getDownloader($package->getSourceType());
+            $installer = $composer->getInstaller($package->getType());
+            $lock[$name] = $installer->install($package, $downloader);
+        }
+
+        $this->storeLockFile($lock);
+    }
+
+    protected function loadConfig()
+    {
+        if (!file_exists('composer.json')) {
+            throw new \UnexpectedValueException('composer.json config file not found in '.getcwd());
+        }
+        $config = json_decode(file_get_contents('composer.json'), true);
+        if (!$config) {
+            throw new \UnexpectedValueException('Incorrect composer.json file');
+        }
+        return $config;
+    }
+
+    protected function storeLockFile(array $content)
+    {
+        file_put_contents('composer.lock', json_encode($content));
+    }
+}

+ 89 - 0
src/Composer/Composer.php

@@ -0,0 +1,89 @@
+<?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\ComposerRepository;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class Composer
+{
+    protected $repositories = array();
+    protected $downloaders = array();
+    protected $installers = array();
+
+    public function __construct()
+    {
+        $this->addRepository('Packagist', array('composer' => 'http://packagist.org'));
+    }
+
+    public function addDownloader($type, $downloader)
+    {
+        $this->downloaders[$type] = $downloader;
+    }
+
+    public function getDownloader($type)
+    {
+        if (!isset($this->downloaders[$type])) {
+            throw new \UnexpectedValueException('Unknown source type: '.$type);
+        }
+        return $this->downloaders[$type];
+    }
+
+    public function addInstaller($type, $installer)
+    {
+        $this->installers[$type] = $installer;
+    }
+
+    public function getInstaller($type)
+    {
+        if (!isset($this->installers[$type])) {
+            throw new \UnexpectedValueException('Unknown dependency type: '.$type);
+        }
+        return $this->installers[$type];
+    }
+
+    public function addRepository($name, $spec)
+    {
+        if (null === $spec) {
+            unset($this->repositories[$name]);
+        }
+        if (is_array($spec) && count($spec)) {
+            return $this->repositories[$name] = $this->createRepository(key($spec), current($spec));
+        }
+        throw new \UnexpectedValueException('Invalid repositories specification '.var_export($spec, true));
+    }
+
+    public function getRepositories()
+    {
+        return $this->repositories;
+    }
+
+    public function createRepository($type, $url)
+    {
+        $url = rtrim($url, '/');
+
+        switch ($type) {
+        case 'git-bare':
+        case 'git-package':
+        case 'git-multi':
+            throw new \Exception($type.' repositories not supported yet');
+            break;
+
+        case 'composer':
+            return new ComposerRepository($url);
+            break;
+        }
+    }
+}

+ 48 - 0
src/Composer/Downloader/GitDownloader.php

@@ -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\Downloader;
+
+use Composer\Package\PackageInterface;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class GitDownloader
+{
+    protected $clone;
+
+    public function __construct($clone = true)
+    {
+        $this->clone = $clone;
+    }
+
+    public function download(PackageInterface $package, $path)
+    {
+        $oldDir = getcwd();
+        if (!is_dir($path)) {
+            if (file_exists($path)) {
+                throw new \UnexpectedValueException($path.' exists and is not a directory.');
+            }
+            if (!mkdir($path, 0777, true)) {
+                throw new \UnexpectedValueException($path.' does not exist and could not be created.');
+            }
+        }
+        chdir($path);
+        if ($this->clone) {
+            exec('git clone '.escapeshellarg($package->getSourceUrl()).' -b master '.escapeshellarg($package->getName()));
+        } else {
+            exec('git archive --format=tar --prefix='.escapeshellarg($package->getName()).' --remote='.escapeshellarg($package->getSourceUrl()).' master | tar -xf -');
+        }
+        chdir($oldDir);
+    }
+}

+ 34 - 0
src/Composer/Installer/LibraryInstaller.php

@@ -0,0 +1,34 @@
+<?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\Package\PackageInterface;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class LibraryInstaller
+{
+    protected $dir;
+
+    public function __construct($dir = 'vendor')
+    {
+        $this->dir = $dir;
+    }
+
+    public function install(PackageInterface $package, $downloader)
+    {
+        $downloader->download($package, $this->dir);
+        return array('version' => $package->getVersion());
+    }
+}

+ 89 - 0
src/Composer/Repository/ComposerRepository.php

@@ -0,0 +1,89 @@
+<?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;
+
+use Composer\Package\MemoryPackage;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class ComposerRepository extends ArrayRepository
+{
+    protected $packages;
+
+    public function __construct($url)
+    {
+        $this->url = $url;
+    }
+
+    protected function initialize()
+    {
+        parent::initialize();
+        $packages = @json_decode(file_get_contents($this->url.'/packages.json'), true);
+        if (!$packages) {
+            throw new \UnexpectedValueException('Could not parse package list from the '.$this->url.' registry');
+        }
+
+        foreach ($packages as $data) {
+            $this->createPackages($data);
+        }
+    }
+
+    protected function createPackages($data)
+    {
+        foreach ($data['versions'] as $rev) {
+            $version = $this->parseVersion($rev['version']);
+
+            $package = new MemoryPackage($rev['name'], $version['version'], $version['type']);
+            $package->setSourceType($rev['source']['type']);
+            $package->setSourceUrl($rev['source']['url']);
+
+            if (isset($rev['license'])) {
+                $package->setLicense($rev['license']);
+            }
+            //if (isset($rev['require'])) {
+            //    $package->setRequires($rev['require']);
+            //}
+            //if (isset($rev['conflict'])) {
+            //    $package->setConflicts($rev['conflict']);
+            //}
+            //if (isset($rev['provide'])) {
+            //    $package->setProvides($rev['provide']);
+            //}
+            //if (isset($rev['replace'])) {
+            //    $package->setReplaces($rev['replace']);
+            //}
+            //if (isset($rev['recommend'])) {
+            //    $package->setRecommends($rev['recommend']);
+            //}
+            //if (isset($rev['suggest'])) {
+            //    $package->setSuggests($rev['suggest']);
+            //}
+            $this->addPackage($package);
+        }
+    }
+
+    protected function parseVersion($version)
+    {
+        if (!preg_match('#^v?(\d+)(\.\d+)?(\.\d+)?-?(beta|RC\d+|alpha|dev)?$#i', $version, $matches)) {
+            throw new \UnexpectedValueException('Invalid version string '.$version);
+        }
+
+        return array(
+            'version' => $matches[1]
+                .(!empty($matches[2]) ? $matches[2] : '.0')
+                .(!empty($matches[3]) ? $matches[3] : '.0'),
+            'type' => strtolower(!empty($matches[4]) ? $matches[4] : 'stable'),
+        );
+    }
+}