2012-01-04 00:57:31 +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 Composer\Json\JsonFile ;
2012-02-16 19:39:59 +00:00
use Composer\Repository\CompositeRepository ;
use Composer\Repository\PlatformRepository ;
use Composer\Repository\ComposerRepository ;
2012-01-04 00:57:31 +00:00
use Symfony\Component\Console\Input\InputInterface ;
use Symfony\Component\Console\Input\InputOption ;
use Symfony\Component\Console\Output\OutputInterface ;
2012-01-08 22:01:49 +00:00
use Symfony\Component\Process\Process ;
use Symfony\Component\Process\ExecutableFinder ;
2012-01-04 00:57:31 +00:00
/**
* @ author Justin Rainbow < justin . rainbow @ gmail . com >
2012-02-16 19:39:59 +00:00
* @ author Jordi Boggiano < j . boggiano @ seld . be >
2012-01-04 00:57:31 +00:00
*/
class InitCommand extends Command
{
2012-01-08 22:01:49 +00:00
private $gitConfig ;
2012-02-16 19:39:59 +00:00
private $repos ;
2012-01-08 22:01:49 +00:00
public function parseAuthorString ( $author )
{
if ( preg_match ( '/^(?P<name>[- \.,a-z0-9]+) <(?P<email>.+?)>$/i' , $author , $match )) {
if ( $match [ 'email' ] === filter_var ( $match [ 'email' ], FILTER_VALIDATE_EMAIL )) {
return array (
'name' => trim ( $match [ 'name' ]),
'email' => $match [ 'email' ]
);
}
}
throw new \InvalidArgumentException (
2012-02-16 19:40:57 +00:00
'Invalid author string. Must be in the format: ' .
'John Smith <john@example.com>'
2012-01-08 22:01:49 +00:00
);
}
2012-01-04 00:57:31 +00:00
protected function configure ()
{
$this
-> setName ( 'init' )
-> setDescription ( 'Creates a basic composer.json file in current directory.' )
-> setDefinition ( array (
new InputOption ( 'name' , null , InputOption :: VALUE_NONE , 'Name of the package' ),
new InputOption ( 'description' , null , InputOption :: VALUE_NONE , 'Description of package' ),
2012-01-08 22:01:49 +00:00
new InputOption ( 'author' , null , InputOption :: VALUE_NONE , 'Author name of package' ),
2012-01-04 00:57:31 +00:00
// new InputOption('version', null, InputOption::VALUE_NONE, 'Version of package'),
new InputOption ( 'homepage' , null , InputOption :: VALUE_NONE , 'Homepage of package' ),
2012-01-08 19:48:05 +00:00
new InputOption ( 'require' , null , InputOption :: VALUE_IS_ARRAY | InputOption :: VALUE_REQUIRED , 'An array required packages' ),
2012-01-04 00:57:31 +00:00
))
-> setHelp ( <<< EOT
2012-01-04 01:03:09 +00:00
The < info > init </ info > command creates a basic composer . json file
in the current directory .
2012-01-04 00:57:31 +00:00
2012-01-04 01:03:09 +00:00
< info > php composer . phar init </ info >
2012-01-04 00:57:31 +00:00
EOT
)
;
}
protected function execute ( InputInterface $input , OutputInterface $output )
{
2012-01-08 22:40:30 +00:00
$dialog = $this -> getHelperSet () -> get ( 'dialog' );
2012-01-04 00:57:31 +00:00
2012-01-08 22:01:49 +00:00
$whitelist = array ( 'name' , 'description' , 'author' , 'require' );
2012-01-04 00:57:31 +00:00
2012-01-08 22:01:49 +00:00
$options = array_filter ( array_intersect_key ( $input -> getOptions (), array_flip ( $whitelist )));
if ( isset ( $options [ 'author' ])) {
$options [ 'authors' ] = $this -> formatAuthors ( $options [ 'author' ]);
unset ( $options [ 'author' ]);
}
$options [ 'require' ] = isset ( $options [ 'require' ]) ?
$this -> formatRequirements ( $options [ 'require' ]) :
new \stdClass ;
2012-01-04 00:57:31 +00:00
2012-01-08 19:48:05 +00:00
$file = new JsonFile ( 'composer.json' );
2012-01-04 00:57:31 +00:00
2012-01-08 19:48:05 +00:00
$json = $file -> encode ( $options );
2012-01-04 00:57:31 +00:00
if ( $input -> isInteractive ()) {
$output -> writeln ( array (
'' ,
$json ,
''
));
if ( ! $dialog -> askConfirmation ( $output , $dialog -> getQuestion ( 'Do you confirm generation' , 'yes' , '?' ), true )) {
$output -> writeln ( '<error>Command aborted</error>' );
return 1 ;
}
}
$file -> write ( $options );
2012-01-08 22:27:19 +00:00
if ( $input -> isInteractive ()) {
$ignoreFile = realpath ( '.gitignore' );
if ( false === $ignoreFile ) {
$ignoreFile = realpath ( '.' ) . '/.gitignore' ;
}
if ( ! $this -> hasVendorIgnore ( $ignoreFile )) {
$question = 'Would you like the <info>vendor</info> directory added to your <info>.gitignore</info> [<comment>yes</comment>]?' ;
if ( $dialog -> askConfirmation ( $output , $question , true )) {
$this -> addVendorIgnore ( $ignoreFile );
}
}
}
2012-01-04 00:57:31 +00:00
}
protected function interact ( InputInterface $input , OutputInterface $output )
{
2012-01-08 22:01:49 +00:00
$git = $this -> getGitConfig ();
2012-01-08 22:40:30 +00:00
$dialog = $this -> getHelperSet () -> get ( 'dialog' );
2012-01-08 22:55:12 +00:00
$formatter = $this -> getHelperSet () -> get ( 'formatter' );
$output -> writeln ( array (
'' ,
$formatter -> formatBlock ( 'Welcome to the Composer config generator' , 'bg=blue;fg=white' , true ),
''
));
2012-01-04 00:57:31 +00:00
// namespace
$output -> writeln ( array (
'' ,
'This command will guide you through creating your composer.json config.' ,
'' ,
));
$cwd = realpath ( " . " );
2012-01-08 22:01:49 +00:00
if ( false === $name = $input -> getOption ( 'name' )) {
$name = basename ( $cwd );
if ( isset ( $git [ 'github.user' ])) {
$name = $git [ 'github.user' ] . '/' . $name ;
2012-02-16 19:40:57 +00:00
} elseif ( ! empty ( $_SERVER [ 'USERNAME' ])) {
$name = $_SERVER [ 'USERNAME' ] . '/' . $name ;
} elseif ( get_current_user ()) {
$name = get_current_user () . '/' . $name ;
2012-01-08 22:33:06 +00:00
} else {
// package names must be in the format foo/bar
$name = $name . '/' . $name ;
2012-01-08 22:01:49 +00:00
}
}
2012-01-08 22:33:06 +00:00
$name = $dialog -> askAndValidate (
2012-01-04 00:57:31 +00:00
$output ,
2012-02-16 19:40:57 +00:00
$dialog -> getQuestion ( 'Package name (<vendor>/<name>)' , $name ),
2012-01-08 22:33:06 +00:00
function ( $value ) use ( $name ) {
if ( null === $value ) {
return $name ;
}
if ( ! preg_match ( '{^[a-z0-9_.-]+/[a-z0-9_.-]+$}i' , $value )) {
throw new \InvalidArgumentException (
'The package name ' . $value . ' is invalid, it should have a vendor name, a forward slash, and a package name, matching: [a-z0-9_.-]+/[a-z0-9_.-]+'
);
}
return $value ;
}
2012-01-04 00:57:31 +00:00
);
$input -> setOption ( 'name' , $name );
$description = $input -> getOption ( 'description' ) ? : false ;
$description = $dialog -> ask (
$output ,
$dialog -> getQuestion ( 'Description' , $description )
);
$input -> setOption ( 'description' , $description );
2012-01-08 19:48:05 +00:00
2012-01-08 22:01:49 +00:00
if ( false === $author = $input -> getOption ( 'author' )) {
if ( isset ( $git [ 'user.name' ]) && isset ( $git [ 'user.email' ])) {
$author = sprintf ( '%s <%s>' , $git [ 'user.name' ], $git [ 'user.email' ]);
}
}
$self = $this ;
$author = $dialog -> askAndValidate (
$output ,
$dialog -> getQuestion ( 'Author' , $author ),
function ( $value ) use ( $self , $author ) {
if ( null === $value ) {
return $author ;
}
$author = $self -> parseAuthorString ( $value );
return sprintf ( '%s <%s>' , $author [ 'name' ], $author [ 'email' ]);
}
);
$input -> setOption ( 'author' , $author );
2012-01-08 19:48:05 +00:00
$output -> writeln ( array (
'' ,
'Define your dependencies.' ,
''
));
2012-01-08 22:01:49 +00:00
$requirements = array ();
2012-01-08 19:48:05 +00:00
if ( $dialog -> askConfirmation ( $output , $dialog -> getQuestion ( 'Would you like to define your dependencies interactively' , 'yes' , '?' ), true )) {
$requirements = $this -> determineRequirements ( $input , $output );
}
2012-01-08 22:01:49 +00:00
$input -> setOption ( 'require' , $requirements );
2012-01-04 00:57:31 +00:00
}
2012-01-08 19:48:05 +00:00
protected function findPackages ( $name )
{
$packages = array ();
2012-02-16 19:39:59 +00:00
// init repos
if ( ! $this -> repos ) {
$this -> repos = new CompositeRepository ( array (
new PlatformRepository ,
new ComposerRepository ( array ( 'url' => 'http://packagist.org' ))
));
}
2012-01-08 19:48:05 +00:00
$token = strtolower ( $name );
2012-02-16 19:39:59 +00:00
foreach ( $this -> repos -> getPackages () as $package ) {
if ( false === ( $pos = strpos ( $package -> getName (), $token ))) {
continue ;
2012-01-08 19:48:05 +00:00
}
2012-02-16 19:39:59 +00:00
$packages [] = $package ;
2012-01-08 19:48:05 +00:00
}
return $packages ;
}
protected function determineRequirements ( InputInterface $input , OutputInterface $output )
{
2012-01-08 22:40:30 +00:00
$dialog = $this -> getHelperSet () -> get ( 'dialog' );
2012-01-08 19:48:05 +00:00
$prompt = $dialog -> getQuestion ( 'Search for a package' , false , ':' );
$requires = $input -> getOption ( 'require' ) ? : array ();
while ( null !== $package = $dialog -> ask ( $output , $prompt )) {
$matches = $this -> findPackages ( $package );
if ( count ( $matches )) {
$output -> writeln ( array (
'' ,
sprintf ( 'Found <info>%s</info> packages matching <info>%s</info>' , count ( $matches ), $package ),
''
));
foreach ( $matches as $position => $package ) {
$output -> writeln ( sprintf ( ' <info>%5s</info> %s <comment>%s</comment>' , " [ $position ] " , $package -> getPrettyName (), $package -> getPrettyVersion ()));
}
$output -> writeln ( '' );
$validator = function ( $selection ) use ( $matches ) {
if ( '' === $selection ) {
return false ;
}
2012-02-16 19:41:12 +00:00
if ( ! is_numeric ( $selection ) && preg_match ( '{^\s*(\S+) +(\S.*)\s*}' , $selection , $matches )) {
return $matches [ 1 ] . ' ' . $matches [ 2 ];
}
2012-01-08 19:48:05 +00:00
if ( ! isset ( $matches [( int ) $selection ])) {
throw new \Exception ( 'Not a valid selection' );
}
2012-02-16 19:41:12 +00:00
$package = $matches [( int ) $selection ];
return sprintf ( '%s %s' , $package -> getName (), $package -> getPrettyVersion ());
2012-01-08 19:48:05 +00:00
};
2012-02-16 19:41:12 +00:00
$package = $dialog -> askAndValidate ( $output , $dialog -> getQuestion ( 'Enter package # to add, or a <package> <version> couple if it is not listed' , false , ':' ), $validator , 3 );
2012-01-08 19:48:05 +00:00
if ( false !== $package ) {
2012-02-16 19:41:12 +00:00
$requires [] = $package ;
2012-01-08 19:48:05 +00:00
}
}
}
return $requires ;
}
2012-01-08 22:01:49 +00:00
protected function formatAuthors ( $author )
{
return array ( $this -> parseAuthorString ( $author ));
}
2012-01-08 19:48:05 +00:00
protected function formatRequirements ( array $requirements )
{
$requires = array ();
foreach ( $requirements as $requirement ) {
2012-02-16 19:41:12 +00:00
list ( $packageName , $packageVersion ) = explode ( " " , $requirement , 2 );
2012-01-08 19:48:05 +00:00
$requires [ $packageName ] = $packageVersion ;
}
return empty ( $requires ) ? new \stdClass : $requires ;
}
2012-01-08 22:01:49 +00:00
protected function getGitConfig ()
{
if ( null !== $this -> gitConfig ) {
return $this -> gitConfig ;
}
$finder = new ExecutableFinder ();
$gitBin = $finder -> find ( 'git' );
$cmd = new Process ( sprintf ( '%s config -l' , $gitBin ));
$cmd -> run ();
if ( $cmd -> isSuccessful ()) {
return $this -> gitConfig = parse_ini_string ( $cmd -> getOutput (), false , INI_SCANNER_RAW );
}
return $this -> gitConfig = array ();
}
2012-01-08 22:27:19 +00:00
/**
* Checks the local . gitignore file for the Composer vendor directory .
*
* Tested patterns include :
* " / $vendor "
* " $vendor "
* " $vendor / "
* " / $vendor / "
* " / $vendor /* "
* " $vendor /* "
*
* @ param string $ignoreFile
* @ param string $vendor
*
* @ return Boolean
*/
protected function hasVendorIgnore ( $ignoreFile , $vendor = 'vendor' )
{
if ( ! file_exists ( $ignoreFile )) {
return false ;
}
$pattern = sprintf (
'~^/?%s(/|/\*)?$~' ,
preg_quote ( $vendor , '~' )
);
$lines = file ( $ignoreFile , FILE_IGNORE_NEW_LINES );
foreach ( $lines as $line ) {
if ( preg_match ( $pattern , $line )) {
return true ;
}
}
return false ;
}
protected function addVendorIgnore ( $ignoreFile , $vendor = 'vendor' )
{
$contents = " " ;
if ( file_exists ( $ignoreFile )) {
$contents = file_get_contents ( $ignoreFile );
if ( " \n " !== substr ( $contents , 0 , - 1 )) {
$contents .= " \n " ;
}
}
2012-02-16 19:40:57 +00:00
file_put_contents ( $ignoreFile , $contents . $vendor . " \n " );
2012-01-08 22:27:19 +00:00
}
2012-01-04 00:57:31 +00:00
}