2012-03-31 22:39:43 +00:00
< ? 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 ;
use Symfony\Component\Console\Input\InputInterface ;
use Symfony\Component\Console\Input\InputArgument ;
use Symfony\Component\Console\Input\InputOption ;
use Symfony\Component\Console\Output\OutputInterface ;
2012-05-26 12:45:19 +00:00
use Composer\Factory ;
2012-05-26 13:17:52 +00:00
use Composer\Installer ;
2012-03-31 22:39:43 +00:00
use Composer\Json\JsonFile ;
2012-05-26 12:45:19 +00:00
use Composer\Json\JsonManipulator ;
2013-08-26 11:29:47 +00:00
use Composer\Package\Version\VersionParser ;
2013-09-05 12:30:03 +00:00
use Composer\Plugin\CommandEvent ;
use Composer\Plugin\PluginEvents ;
2014-12-04 14:10:56 +00:00
use Composer\Repository\CompositeRepository ;
use Composer\Repository\PlatformRepository ;
2012-03-31 22:39:43 +00:00
/**
* @ author Jérémy Romey < jeremy @ free - agent . fr >
2012-05-26 12:45:19 +00:00
* @ author Jordi Boggiano < j . boggiano @ seld . be >
2012-03-31 22:39:43 +00:00
*/
class RequireCommand extends InitCommand
{
protected function configure ()
{
$this
-> setName ( 'require' )
2012-05-26 13:17:52 +00:00
-> setDescription ( 'Adds required packages to your composer.json and installs them' )
2012-03-31 22:39:43 +00:00
-> setDefinition ( array (
2015-01-10 16:25:31 +00:00
new InputArgument ( 'packages' , InputArgument :: IS_ARRAY | InputArgument :: OPTIONAL , 'Required package name optionally including a version constraint, e.g. foo/bar or foo/bar:1.0.0 or foo/bar=1.0.0 or "foo/bar 1.0.0"' ),
2012-05-26 12:45:19 +00:00
new InputOption ( 'dev' , null , InputOption :: VALUE_NONE , 'Add requirement to require-dev.' ),
2012-05-26 13:17:52 +00:00
new InputOption ( 'prefer-source' , null , InputOption :: VALUE_NONE , 'Forces installation from package sources when possible, including VCS information.' ),
2012-10-02 11:42:07 +00:00
new InputOption ( 'prefer-dist' , null , InputOption :: VALUE_NONE , 'Forces installation from package dist even for dev versions.' ),
2012-12-11 14:30:09 +00:00
new InputOption ( 'no-progress' , null , InputOption :: VALUE_NONE , 'Do not output download progress.' ),
2012-07-19 15:17:08 +00:00
new InputOption ( 'no-update' , null , InputOption :: VALUE_NONE , 'Disables the automatic update of the dependencies.' ),
2014-04-10 03:08:10 +00:00
new InputOption ( 'update-no-dev' , null , InputOption :: VALUE_NONE , 'Run the dependency update with the --no-dev option.' ),
2014-02-10 20:24:46 +00:00
new InputOption ( 'update-with-dependencies' , null , InputOption :: VALUE_NONE , 'Allows inherited dependencies to be updated with explicit dependencies.' ),
2014-11-14 16:32:42 +00:00
new InputOption ( 'ignore-platform-reqs' , null , InputOption :: VALUE_NONE , 'Ignore platform requirements (php & ext- packages).' ),
2014-12-12 23:17:14 +00:00
new InputOption ( 'sort-packages' , null , InputOption :: VALUE_NONE , 'Sorts packages when adding/updating a new dependency' ),
2012-03-31 22:39:43 +00:00
))
-> setHelp ( <<< EOT
2015-01-10 16:25:31 +00:00
The require command adds required packages to your composer . json and installs them .
If you do not specify a version constraint , composer will choose a suitable one based on the available package versions .
2012-03-31 22:39:43 +00:00
2012-07-19 15:17:08 +00:00
If you do not want to install the new dependencies immediately you can call it with -- no - update
2012-03-31 22:39:43 +00:00
EOT
)
;
}
protected function execute ( InputInterface $input , OutputInterface $output )
{
2012-12-03 00:24:39 +00:00
$file = Factory :: getComposerFile ();
2012-03-31 22:39:43 +00:00
2014-10-04 16:23:04 +00:00
$newlyCreated = ! file_exists ( $file );
2013-01-13 15:02:50 +00:00
if ( ! file_exists ( $file ) && ! file_put_contents ( $file , " { \n } \n " )) {
2015-02-06 12:52:44 +00:00
$this -> getIO () -> writeError ( '<error>' . $file . ' could not be created.</error>' );
2012-05-28 14:38:52 +00:00
2012-03-31 22:39:43 +00:00
return 1 ;
}
if ( ! is_readable ( $file )) {
2015-02-06 12:52:44 +00:00
$this -> getIO () -> writeError ( '<error>' . $file . ' is not readable.</error>' );
2012-05-28 14:38:52 +00:00
2012-03-31 22:39:43 +00:00
return 1 ;
}
2013-02-02 09:49:32 +00:00
if ( ! is_writable ( $file )) {
2015-02-06 12:52:44 +00:00
$this -> getIO () -> writeError ( '<error>' . $file . ' is not writable.</error>' );
2013-02-02 09:49:32 +00:00
return 1 ;
}
2012-03-31 22:39:43 +00:00
2012-05-26 12:45:19 +00:00
$json = new JsonFile ( $file );
2014-12-04 14:10:56 +00:00
$composerDefinition = $json -> read ();
2013-02-01 09:24:05 +00:00
$composerBackup = file_get_contents ( $json -> getPath ());
2012-03-31 22:39:43 +00:00
2014-12-04 14:10:56 +00:00
$composer = $this -> getComposer ();
$repos = $composer -> getRepositoryManager () -> getRepositories ();
$this -> repos = new CompositeRepository ( array_merge (
array ( new PlatformRepository ),
$repos
));
2012-05-26 12:45:19 +00:00
$requirements = $this -> determineRequirements ( $input , $output , $input -> getArgument ( 'packages' ));
2012-03-31 22:39:43 +00:00
2012-05-26 12:45:19 +00:00
$requireKey = $input -> getOption ( 'dev' ) ? 'require-dev' : 'require' ;
2014-04-10 02:46:24 +00:00
$removeKey = $input -> getOption ( 'dev' ) ? 'require' : 'require-dev' ;
2014-12-04 14:10:56 +00:00
$baseRequirements = array_key_exists ( $requireKey , $composerDefinition ) ? $composerDefinition [ $requireKey ] : array ();
2012-05-27 21:21:10 +00:00
$requirements = $this -> formatRequirements ( $requirements );
2012-03-31 22:39:43 +00:00
2013-08-26 11:29:47 +00:00
// validate requirements format
$versionParser = new VersionParser ();
foreach ( $requirements as $constraint ) {
$versionParser -> parseConstraints ( $constraint );
}
2014-12-12 23:17:14 +00:00
$sortPackages = $input -> getOption ( 'sort-packages' );
if ( ! $this -> updateFileCleanly ( $json , $baseRequirements , $requirements , $requireKey , $removeKey , $sortPackages )) {
2012-05-26 12:45:19 +00:00
foreach ( $requirements as $package => $version ) {
2012-03-31 22:39:43 +00:00
$baseRequirements [ $package ] = $version ;
2014-04-10 02:46:24 +00:00
2014-12-04 14:10:56 +00:00
if ( isset ( $composerDefinition [ $removeKey ][ $package ])) {
unset ( $composerDefinition [ $removeKey ][ $package ]);
2014-04-10 02:46:24 +00:00
}
2012-03-31 22:39:43 +00:00
}
2012-05-26 12:45:19 +00:00
2014-12-04 14:10:56 +00:00
$composerDefinition [ $requireKey ] = $baseRequirements ;
$json -> write ( $composerDefinition );
2012-03-31 22:39:43 +00:00
}
2015-02-06 12:52:44 +00:00
$this -> getIO () -> writeError ( '<info>' . $file . ' has been ' . ( $newlyCreated ? 'created' : 'updated' ) . '</info>' );
2012-05-26 13:17:52 +00:00
2012-07-19 15:17:08 +00:00
if ( $input -> getOption ( 'no-update' )) {
return 0 ;
}
2014-04-10 19:10:45 +00:00
$updateDevMode = ! $input -> getOption ( 'update-no-dev' );
2012-07-19 15:17:08 +00:00
2012-05-26 13:17:52 +00:00
// Update packages
2014-12-05 11:58:21 +00:00
$this -> resetComposer ();
$composer = $this -> getComposer ();
2012-12-11 14:30:09 +00:00
$composer -> getDownloadManager () -> setOutputProgress ( ! $input -> getOption ( 'no-progress' ));
2012-05-26 13:17:52 +00:00
$io = $this -> getIO ();
2013-09-05 12:30:03 +00:00
$commandEvent = new CommandEvent ( PluginEvents :: COMMAND , 'require' , $input , $output );
$composer -> getEventDispatcher () -> dispatch ( $commandEvent -> getName (), $commandEvent );
2012-05-26 13:17:52 +00:00
$install = Installer :: create ( $io , $composer );
$install
-> setVerbose ( $input -> getOption ( 'verbose' ))
-> setPreferSource ( $input -> getOption ( 'prefer-source' ))
2012-10-02 11:42:07 +00:00
-> setPreferDist ( $input -> getOption ( 'prefer-dist' ))
2014-04-10 03:08:10 +00:00
-> setDevMode ( $updateDevMode )
2012-05-26 13:17:52 +00:00
-> setUpdate ( true )
2014-02-10 20:24:46 +00:00
-> setUpdateWhitelist ( array_keys ( $requirements ))
2014-10-17 17:46:01 +00:00
-> setWhitelistDependencies ( $input -> getOption ( 'update-with-dependencies' ))
2014-11-14 16:32:42 +00:00
-> setIgnorePlatformRequirements ( $input -> getOption ( 'ignore-platform-reqs' ))
2012-05-26 13:17:52 +00:00
;
2013-11-22 15:17:02 +00:00
$status = $install -> run ();
if ( $status !== 0 ) {
2014-10-04 16:23:04 +00:00
if ( $newlyCreated ) {
2015-02-06 12:52:44 +00:00
$this -> getIO () -> writeError ( " \n " . '<error>Installation failed, deleting ' . $file . '.</error>' );
2014-10-04 16:23:04 +00:00
unlink ( $json -> getPath ());
} else {
2015-02-06 12:52:44 +00:00
$this -> getIO () -> writeError ( " \n " . '<error>Installation failed, reverting ' . $file . ' to its original content.</error>' );
2014-10-04 16:23:04 +00:00
file_put_contents ( $json -> getPath (), $composerBackup );
}
2013-02-01 09:24:05 +00:00
}
2013-02-12 09:14:44 +00:00
2013-11-22 15:17:02 +00:00
return $status ;
2012-05-26 12:45:19 +00:00
}
2014-12-12 23:17:14 +00:00
private function updateFileCleanly ( $json , array $base , array $new , $requireKey , $removeKey , $sortPackages )
2012-05-26 12:45:19 +00:00
{
$contents = file_get_contents ( $json -> getPath ());
2012-03-31 22:39:43 +00:00
2012-05-26 12:45:19 +00:00
$manipulator = new JsonManipulator ( $contents );
2012-03-31 22:39:43 +00:00
2012-05-26 12:45:19 +00:00
foreach ( $new as $package => $constraint ) {
2014-12-12 23:17:14 +00:00
if ( ! $manipulator -> addLink ( $requireKey , $package , $constraint , $sortPackages )) {
2012-05-26 12:45:19 +00:00
return false ;
}
2014-04-10 02:46:24 +00:00
if ( ! $manipulator -> removeSubNode ( $removeKey , $package )) {
return false ;
}
2012-05-26 12:45:19 +00:00
}
file_put_contents ( $json -> getPath (), $manipulator -> getContents ());
return true ;
2012-03-31 22:39:43 +00:00
}
protected function interact ( InputInterface $input , OutputInterface $output )
{
return ;
}
}