|
#!/usr/bin/env php |
|
<?php |
|
|
|
/** |
|
* dcomposer - Install Composer dependencies into a global location |
|
* |
|
* This is a work-around for the missing --global option in Composer. |
|
* see https://github.com/composer/composer/issues/55 |
|
* |
|
* It reads the dependencies from the composer.json file and installs them |
|
* into a global location (/usr/local/lib/composer) and creates a light-weight |
|
* local Composer installation providing the necessary autoload magic. |
|
* |
|
* @version 0.1.0 |
|
* @author Thomas Bruederli <bruederli(at)kolabsys.com> |
|
* |
|
* Copyright (C) 2013, Kolab Systems AG <contact(at)kolabsys.com> |
|
* |
|
* This program is free software: you can redistribute it and/or modify |
|
* it under the terms of the GNU Affero General Public License as |
|
* published by the Free Software Foundation, either version 3 of the |
|
* License, or (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU Affero General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU Affero General Public License |
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
// change these if necessary |
|
$COMPOSER_DIR = '/usr/local/lib/composer'; |
|
$COMPOSER_CMD = 'composer'; |
|
$verbose = true; |
|
|
|
// suppress error notices |
|
ini_set('error_reporting', E_ALL &~ E_NOTICE &~ E_STRICT); |
|
|
|
// read Composer input files |
|
$json = @json_decode(($json_src = @file_get_contents('./composer.json')), true); |
|
$global = @json_decode(@file_get_contents($COMPOSER_DIR . '/composer.json'), true); |
|
|
|
if (empty($json)) { |
|
die("Cannot read composer.json"); |
|
} |
|
else if (empty($global)) { |
|
die("Cannot read $COMPOSER_DIR/composer.json"); |
|
} |
|
|
|
$installed_comp = array(); |
|
$installed_pear = array(); |
|
|
|
// get installed Composer modules |
|
if (exec("$COMPOSER_CMD show --installed --working-dir=$COMPOSER_DIR", $installed_p)) { |
|
foreach ($installed_p as $i => $line) { |
|
list($package, $version) = preg_split('/\s+/', $line); |
|
$installed_comp[$package] = $version; |
|
} |
|
} |
|
|
|
// get installed PEAR modules |
|
if (exec("pear list", $installed_p)) { |
|
foreach ($installed_p as $i => $line) { |
|
if ($i < 2) continue; |
|
list($package, $version, $stable) = preg_split('/\s+/', $line); |
|
$installed_pear[strtolower($package)] = $version; |
|
} |
|
} |
|
|
|
$updates = 0; |
|
foreach ($json['require'] as $package => $version) { |
|
if ($package == 'php') |
|
continue; |
|
|
|
list($vendor, $pack) = explode("/", $package); |
|
if ($vendor == 'pear-pear') { |
|
require_pear($pack, $version); |
|
} |
|
else { |
|
$updates += require_composer($package, $version); |
|
} |
|
} |
|
|
|
// register custom repositories |
|
foreach ((array)$json['repositories'] as $k => $rep) { |
|
if ($rep['type'] == 'pear') |
|
continue; |
|
|
|
if (is_numeric($k)) { |
|
$k = substr(md5($rep['type'] . $rep['url']), 0, 8); |
|
} |
|
|
|
if (empty($global['repositories'][$k])) { |
|
_exec(sprintf("$COMPOSER_CMD --working-dir=$COMPOSER_DIR config repositories.%s %s %s", |
|
$k, |
|
escapeshellarg($rep['type']), |
|
escapeshellarg($rep['url']) |
|
)); |
|
$updates++; |
|
} |
|
} |
|
|
|
// update global composer repository |
|
if ($updates) { |
|
_exec("$COMPOSER_CMD --working-dir=$COMPOSER_DIR update"); |
|
} |
|
|
|
// create local instance with autoloader |
|
if ($updates || !is_file('./composer-local.json')) { |
|
unset($json['require-dev'], $json['repositories']); |
|
$json['require'] = array('php' => '>=5.3.2'); |
|
file_put_contents('./composer-local.json', str_replace('\\/', '/', json_encode($json)) . "\n"); |
|
_exec("COMPOSER=composer-local.json $COMPOSER_CMD -v update"); |
|
|
|
// append global composer autoloader |
|
if (is_writeable('./vendor/autoload.php')) { |
|
$autoload = file_get_contents('./vendor/autoload.php'); |
|
$autoload = preg_replace('/return\s+ComposerAutoloader/', 'ComposerAutoloader', $autoload); |
|
$autoload .= "\n// add global Composer autoloader\nrequire_once('$COMPOSER_DIR/vendor/autoload.php');\n"; |
|
file_put_contents('./vendor/autoload.php', $autoload); |
|
} |
|
else { |
|
echo "ERROR: Failed to patch vendor/autoload.php\n"; |
|
} |
|
} |
|
|
|
/** |
|
* |
|
*/ |
|
function require_composer($package, $version, $dev = false) |
|
{ |
|
global $COMPOSER_DIR, $COMPOSER_CMD, $installed_comp; |
|
|
|
if (!$installed_comp[$package] || _version_compare($version, $installed_comp[$package]) < 0) { |
|
_exec("$COMPOSER_CMD --working-dir=$COMPOSER_DIR --no-update require " . escapeshellarg($package.":".$version)); |
|
return 1; |
|
} |
|
else if (_version_compare($version, $installed_comp[$package]) > 0){ |
|
echo "A newer version of " . $package . " is installed. installed: " . $installed_comp[$package] . "; required: " . $version . "\n"; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* |
|
*/ |
|
function require_pear($package, $version) |
|
{ |
|
global $installed_pear; |
|
|
|
$package_lc = strtolower($package); |
|
if (!$installed_pear[$package_lc]) { |
|
_exec("sudo pear install $package"); |
|
} |
|
else if (_version_compare($version, $installed_pear[$package_lc]) < 0) { |
|
echo "A newer version of PEAR::" . $package . " is required. installed: " . $installed_pear[$package_lc] . "; required: " . $version . "\n"; |
|
} |
|
} |
|
|
|
function _version_compare($reqversion, $compversion) |
|
{ |
|
$op1 = $op2 = '>='; |
|
$regex = '/^([><=]*)([\d.*a-z-]+)/'; |
|
if (preg_match($regex, $reqversion, $m)) { |
|
$op1 = $m[1] ?: '>='; |
|
$reqversion = strtr($m[2], array('*'=>'99')); |
|
} |
|
if (preg_match($regex, $compversion, $m)) { |
|
$op2 = $m[1] ?: '>='; |
|
$compversion = rtrim($m[2], '.*'); |
|
} |
|
|
|
return version_compare($compversion, $reqversion); |
|
} |
|
|
|
function _exec($cmd) |
|
{ |
|
if ($GLOBALS['verbose']) echo "> $cmd\n"; |
|
return system($cmd, $lines); |
|
} |
|
|