From 34bbfb70d2d662ef2022c98dca2d3eef709d2680 Mon Sep 17 00:00:00 2001 From: Justin Rainbow Date: Tue, 3 Jan 2012 17:57:31 -0700 Subject: [PATCH 01/13] Initial version of InitCommand This command allows a user to create a basic composer.json definition easily. The idea is to create a command similar to the `npm init` command. This version only has `name` and `description` support. Additional fields will be added such as `require`, `authors`, `version`, etc. --- src/Composer/Command/Helper/DialogHelper.php | 23 ++++ src/Composer/Command/InitCommand.php | 126 +++++++++++++++++++ src/Composer/Console/Application.php | 1 + 3 files changed, 150 insertions(+) create mode 100644 src/Composer/Command/Helper/DialogHelper.php create mode 100644 src/Composer/Command/InitCommand.php diff --git a/src/Composer/Command/Helper/DialogHelper.php b/src/Composer/Command/Helper/DialogHelper.php new file mode 100644 index 000000000..a0c034116 --- /dev/null +++ b/src/Composer/Command/Helper/DialogHelper.php @@ -0,0 +1,23 @@ +writeln(array( + '', + $this->getHelperSet()->get('formatter')->formatBlock($text, $style, true), + '', + )); + } + + public function getQuestion($question, $default, $sep = ':') + { + return $default ? sprintf('%s [%s]%s ', $question, $default, $sep) : sprintf('%s%s ', $question, $sep); + } +} \ No newline at end of file diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php new file mode 100644 index 000000000..758778b6b --- /dev/null +++ b/src/Composer/Command/InitCommand.php @@ -0,0 +1,126 @@ + + * Jordi Boggiano + * + * 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; +use Composer\Command\Helper\DialogHelper; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + + +if (!defined('JSON_PRETTY_PRINT')) { + define('JSON_PRETTY_PRINT', 128); +} + +/** + * @author Justin Rainbow + */ +class InitCommand extends Command +{ + 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'), + // new InputOption('version', null, InputOption::VALUE_NONE, 'Version of package'), + new InputOption('homepage', null, InputOption::VALUE_NONE, 'Homepage of package'), + )) + ->setHelp(<<install command reads the composer.json file from the +current directory, processes it, and downloads and installs all the +libraries and dependencies outlined in that file. + +php composer.phar install + +EOT + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + + $options = array_filter(array_intersect_key($input->getOptions(), array_flip(array('name','description')))); + + $file = new JsonFile("composer.json"); + + $indentSize = 2; + $lines = array(); + + foreach ($options as $key => $value) { + $lines[] = sprintf('%s%s: %s', str_repeat(' ', $indentSize), json_encode($key), json_encode($value)); + } + + $json = "{\n" . implode(",\n", $lines) . "\n}\n"; + + if ($input->isInteractive()) { + $output->writeln(array( + '', + $json, + '' + )); + if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) { + $output->writeln('Command aborted'); + + return 1; + } + } + + $file->write($options); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + $dialog->writeSection($output, 'Welcome to the Composer config generator'); + + // namespace + $output->writeln(array( + '', + 'This command will guide you through creating your composer.json config.', + '', + )); + + $cwd = realpath("."); + + $name = $input->getOption('name') ?: basename($cwd); + $name = $dialog->ask( + $output, + $dialog->getQuestion('Package name', $name), + $name + ); + $input->setOption('name', $name); + + $description = $input->getOption('description') ?: false; + $description = $dialog->ask( + $output, + $dialog->getQuestion('Description', $description) + ); + $input->setOption('description', $description); + } + + protected function getDialogHelper() + { + $dialog = $this->getHelperSet()->get('dialog'); + if (!$dialog || get_class($dialog) !== 'Composer\Command\Helper\DialogHelper') { + $this->getHelperSet()->set($dialog = new DialogHelper()); + } + + return $dialog; + } +} \ No newline at end of file diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 6d1d6ba0b..3b3979770 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -104,6 +104,7 @@ class Application extends BaseApplication { $this->add(new Command\AboutCommand()); $this->add(new Command\DependsCommand()); + $this->add(new Command\InitCommand()); $this->add(new Command\InstallCommand()); $this->add(new Command\UpdateCommand()); $this->add(new Command\SearchCommand()); From 10ca974f33f662a4fecd55f4ed3d84889797e84a Mon Sep 17 00:00:00 2001 From: Justin Rainbow Date: Tue, 3 Jan 2012 18:03:09 -0700 Subject: [PATCH 02/13] Quick change to the help text for InitCommand --- src/Composer/Command/InitCommand.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 758778b6b..aedfd1241 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -40,11 +40,10 @@ class InitCommand extends Command new InputOption('homepage', null, InputOption::VALUE_NONE, 'Homepage of package'), )) ->setHelp(<<install command reads the composer.json file from the -current directory, processes it, and downloads and installs all the -libraries and dependencies outlined in that file. +The init command creates a basic composer.json file +in the current directory. -php composer.phar install +php composer.phar init EOT ) From 5933f34d6f244aec277d3bb35c72eca903f32445 Mon Sep 17 00:00:00 2001 From: Justin Rainbow Date: Sun, 8 Jan 2012 12:47:48 -0700 Subject: [PATCH 03/13] Adding a JSON string formatter to the JsonFile class --- src/Composer/Json/JsonFormatter.php | 88 +++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/Composer/Json/JsonFormatter.php diff --git a/src/Composer/Json/JsonFormatter.php b/src/Composer/Json/JsonFormatter.php new file mode 100644 index 000000000..01e83ea06 --- /dev/null +++ b/src/Composer/Json/JsonFormatter.php @@ -0,0 +1,88 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Json; + +/** + * Format JSON output + * + * @author Justin Rainbow + */ +class JsonFormatter +{ + private $indent = ' '; + + private $level = 1; + + /** + * Indents a flat JSON string to make it more human-readable + * + * Original code for this function can be found at: + * http://recursive-design.com/blog/2008/03/11/format-json-with-php/ + * + * @param string $json The original JSON string to process + * @return string Indented version of the original JSON string + */ + public function format($json) + { + if (!is_string($json)) { + $json = json_encode($json); + } + + $result = ''; + $pos = 0; + $strLen = strlen($json); + $indentStr = $this->indent; + $newLine = "\n"; + $prevChar = ''; + $outOfQuotes = true; + + for ($i = 0; $i <= $strLen; $i++) { + // Grab the next character in the string + $char = substr($json, $i, 1); + + // Are we inside a quoted string? + if ($char == '"' && $prevChar != '\\') { + $outOfQuotes = !$outOfQuotes; + } else if (($char == '}' || $char == ']') && $outOfQuotes) { + // If this character is the end of an element, + // output a new line and indent the next line + $result .= $newLine; + $pos --; + for ($j=0; $j<$pos; $j++) { + $result .= $indentStr; + } + } + + // Add the character to the result string + $result .= $char; + + // If the last character was the beginning of an element, + // output a new line and indent the next line + if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) { + $result .= $newLine; + + if ($char == '{' || $char == '[') { + $pos ++; + } + + for ($j = 0; $j < $pos; $j++) { + $result .= $indentStr; + } + } + + $prevChar = $char; + } + + return $result; + } +} \ No newline at end of file From 2fbd9490b1d9d111550edcabc7561a40b5a30336 Mon Sep 17 00:00:00 2001 From: Justin Rainbow Date: Sun, 8 Jan 2012 12:48:05 -0700 Subject: [PATCH 04/13] Adding a requirement definition stage to init command --- src/Composer/Command/InitCommand.php | 112 +++++++++++++++++++++++---- 1 file changed, 98 insertions(+), 14 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index aedfd1241..829f5d5d5 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -18,11 +18,6 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; - -if (!defined('JSON_PRETTY_PRINT')) { - define('JSON_PRETTY_PRINT', 128); -} - /** * @author Justin Rainbow */ @@ -38,6 +33,7 @@ class InitCommand extends Command new InputOption('description', null, InputOption::VALUE_NONE, 'Description of package'), // new InputOption('version', null, InputOption::VALUE_NONE, 'Version of package'), new InputOption('homepage', null, InputOption::VALUE_NONE, 'Homepage of package'), + new InputOption('require', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'An array required packages'), )) ->setHelp(<<init command creates a basic composer.json file @@ -54,18 +50,13 @@ EOT { $dialog = $this->getDialogHelper(); - $options = array_filter(array_intersect_key($input->getOptions(), array_flip(array('name','description')))); + $options = array_filter(array_intersect_key($input->getOptions(), array_flip(array('name','description','require')))); - $file = new JsonFile("composer.json"); + $options['require'] = $this->formatRequirements($options['require']); - $indentSize = 2; - $lines = array(); + $file = new JsonFile('composer.json'); - foreach ($options as $key => $value) { - $lines[] = sprintf('%s%s: %s', str_repeat(' ', $indentSize), json_encode($key), json_encode($value)); - } - - $json = "{\n" . implode(",\n", $lines) . "\n}\n"; + $json = $file->encode($options); if ($input->isInteractive()) { $output->writeln(array( @@ -111,6 +102,18 @@ EOT $dialog->getQuestion('Description', $description) ); $input->setOption('description', $description); + + $output->writeln(array( + '', + 'Define your dependencies.', + '' + )); + + if ($dialog->askConfirmation($output, $dialog->getQuestion('Would you like to define your dependencies interactively', 'yes', '?'), true)) { + $requirements = $this->determineRequirements($input, $output); + + $input->setOption('require', $requirements); + } } protected function getDialogHelper() @@ -122,4 +125,85 @@ EOT return $dialog; } + + protected function findPackages($name) + { + $composer = $this->getComposer(); + + $packages = array(); + + // create local repo, this contains all packages that are installed in the local project + $localRepo = $composer->getRepositoryManager()->getLocalRepository(); + + $token = strtolower($name); + foreach ($composer->getRepositoryManager()->getRepositories() as $repository) { + foreach ($repository->getPackages() as $package) { + if (false === ($pos = strpos($package->getName(), $token))) { + continue; + } + + $packages[] = $package; + } + } + + return $packages; + } + + protected function determineRequirements(InputInterface $input, OutputInterface $output) + { + $dialog = $this->getDialogHelper(); + $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 %s packages matching %s', count($matches), $package), + '' + )); + + foreach ($matches as $position => $package) { + $output->writeln(sprintf(' %5s %s %s', "[$position]", $package->getPrettyName(), $package->getPrettyVersion())); + } + + $output->writeln(''); + + $validator = function ($selection) use ($matches) { + if ('' === $selection) { + return false; + } + + if (!isset($matches[(int) $selection])) { + throw new \Exception('Not a valid selection'); + } + + return $matches[(int) $selection]; + }; + + $package = $dialog->askAndValidate($output, $dialog->getQuestion('Enter package # to add', false, ':'), $validator, 3); + + if (false !== $package) { + $requires[] = sprintf('%s %s', $package->getName(), $package->getPrettyVersion()); + } + } + } + + return $requires; + } + + protected function formatRequirements(array $requirements) + { + $requires = array(); + foreach ($requirements as $requirement) { + list($packageName, $packageVersion) = explode(" ", $requirement); + + $requires[$packageName] = $packageVersion; + } + + return empty($requires) ? new \stdClass : $requires; + } } \ No newline at end of file From 42d55bf5106f70d4636cc2a6122b487b9a9ffdb9 Mon Sep 17 00:00:00 2001 From: Justin Rainbow Date: Sun, 8 Jan 2012 12:50:42 -0700 Subject: [PATCH 05/13] Removing the JsonFormatter as it is part of the JsonFile now --- src/Composer/Json/JsonFormatter.php | 88 ----------------------------- 1 file changed, 88 deletions(-) delete mode 100644 src/Composer/Json/JsonFormatter.php diff --git a/src/Composer/Json/JsonFormatter.php b/src/Composer/Json/JsonFormatter.php deleted file mode 100644 index 01e83ea06..000000000 --- a/src/Composer/Json/JsonFormatter.php +++ /dev/null @@ -1,88 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Json; - -/** - * Format JSON output - * - * @author Justin Rainbow - */ -class JsonFormatter -{ - private $indent = ' '; - - private $level = 1; - - /** - * Indents a flat JSON string to make it more human-readable - * - * Original code for this function can be found at: - * http://recursive-design.com/blog/2008/03/11/format-json-with-php/ - * - * @param string $json The original JSON string to process - * @return string Indented version of the original JSON string - */ - public function format($json) - { - if (!is_string($json)) { - $json = json_encode($json); - } - - $result = ''; - $pos = 0; - $strLen = strlen($json); - $indentStr = $this->indent; - $newLine = "\n"; - $prevChar = ''; - $outOfQuotes = true; - - for ($i = 0; $i <= $strLen; $i++) { - // Grab the next character in the string - $char = substr($json, $i, 1); - - // Are we inside a quoted string? - if ($char == '"' && $prevChar != '\\') { - $outOfQuotes = !$outOfQuotes; - } else if (($char == '}' || $char == ']') && $outOfQuotes) { - // If this character is the end of an element, - // output a new line and indent the next line - $result .= $newLine; - $pos --; - for ($j=0; $j<$pos; $j++) { - $result .= $indentStr; - } - } - - // Add the character to the result string - $result .= $char; - - // If the last character was the beginning of an element, - // output a new line and indent the next line - if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) { - $result .= $newLine; - - if ($char == '{' || $char == '[') { - $pos ++; - } - - for ($j = 0; $j < $pos; $j++) { - $result .= $indentStr; - } - } - - $prevChar = $char; - } - - return $result; - } -} \ No newline at end of file From 6ff7694de1380c3e222b5a9fa63641e8134ed45d Mon Sep 17 00:00:00 2001 From: Justin Rainbow Date: Sun, 8 Jan 2012 13:37:01 -0700 Subject: [PATCH 06/13] Fixing error when no requirements are defined --- src/Composer/Command/InitCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 829f5d5d5..0582a6fa8 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -52,7 +52,7 @@ EOT $options = array_filter(array_intersect_key($input->getOptions(), array_flip(array('name','description','require')))); - $options['require'] = $this->formatRequirements($options['require']); + $options['require'] = $this->formatRequirements(isset($options['require']) ? $options['require'] : array()); $file = new JsonFile('composer.json'); From cd9c86c70c0712d7ca15efc8968732a12fe44629 Mon Sep 17 00:00:00 2001 From: Justin Rainbow Date: Sun, 8 Jan 2012 15:01:49 -0700 Subject: [PATCH 07/13] Adding author defaults to init command --- src/Composer/Command/InitCommand.php | 95 ++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 5 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 0582a6fa8..2acaa2d47 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -17,12 +17,33 @@ use Composer\Command\Helper\DialogHelper; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Process; +use Symfony\Component\Process\ExecutableFinder; /** * @author Justin Rainbow */ class InitCommand extends Command { + private $gitConfig; + + public function parseAuthorString($author) + { + if (preg_match('/^(?P[- \.,a-z0-9]+) <(?P.+?)>$/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( + 'Invalid author string. Must be in the format:'. + ' John Smith ' + ); + } + protected function configure() { $this @@ -31,6 +52,7 @@ class InitCommand extends Command ->setDefinition(array( new InputOption('name', null, InputOption::VALUE_NONE, 'Name of the package'), new InputOption('description', null, InputOption::VALUE_NONE, 'Description of package'), + new InputOption('author', null, InputOption::VALUE_NONE, 'Author name of package'), // new InputOption('version', null, InputOption::VALUE_NONE, 'Version of package'), new InputOption('homepage', null, InputOption::VALUE_NONE, 'Homepage of package'), new InputOption('require', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'An array required packages'), @@ -50,9 +72,18 @@ EOT { $dialog = $this->getDialogHelper(); - $options = array_filter(array_intersect_key($input->getOptions(), array_flip(array('name','description','require')))); + $whitelist = array('name', 'description', 'author', 'require'); - $options['require'] = $this->formatRequirements(isset($options['require']) ? $options['require'] : array()); + $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; $file = new JsonFile('composer.json'); @@ -76,6 +107,8 @@ EOT protected function interact(InputInterface $input, OutputInterface $output) { + $git = $this->getGitConfig(); + $dialog = $this->getDialogHelper(); $dialog->writeSection($output, 'Welcome to the Composer config generator'); @@ -88,7 +121,13 @@ EOT $cwd = realpath("."); - $name = $input->getOption('name') ?: basename($cwd); + if (false === $name = $input->getOption('name')) { + $name = basename($cwd); + if (isset($git['github.user'])) { + $name = $git['github.user'] . '/' . $name; + } + } + $name = $dialog->ask( $output, $dialog->getQuestion('Package name', $name), @@ -103,17 +142,39 @@ EOT ); $input->setOption('description', $description); + 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); + $output->writeln(array( '', 'Define your dependencies.', '' )); + $requirements = array(); if ($dialog->askConfirmation($output, $dialog->getQuestion('Would you like to define your dependencies interactively', 'yes', '?'), true)) { $requirements = $this->determineRequirements($input, $output); - - $input->setOption('require', $requirements); } + $input->setOption('require', $requirements); } protected function getDialogHelper() @@ -195,6 +256,11 @@ EOT return $requires; } + protected function formatAuthors($author) + { + return array($this->parseAuthorString($author)); + } + protected function formatRequirements(array $requirements) { $requires = array(); @@ -206,4 +272,23 @@ EOT return empty($requires) ? new \stdClass : $requires; } + + 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(); + } } \ No newline at end of file From 950bbcbed90c7872e837b1702ee10d46a7fd2f49 Mon Sep 17 00:00:00 2001 From: Justin Rainbow Date: Sun, 8 Jan 2012 15:27:19 -0700 Subject: [PATCH 08/13] Init now asks user if they want to ignore the vendor dir --- src/Composer/Command/InitCommand.php | 67 ++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 2acaa2d47..e5cbaa34e 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -103,6 +103,22 @@ EOT } $file->write($options); + + if ($input->isInteractive()) { + $ignoreFile = realpath('.gitignore'); + + if (false === $ignoreFile) { + $ignoreFile = realpath('.') . '/.gitignore'; + } + + if (!$this->hasVendorIgnore($ignoreFile)) { + $question = 'Would you like the vendor directory added to your .gitignore [yes]?'; + + if ($dialog->askConfirmation($output, $question, true)) { + $this->addVendorIgnore($ignoreFile); + } + } + } } protected function interact(InputInterface $input, OutputInterface $output) @@ -291,4 +307,55 @@ EOT return $this->gitConfig = array(); } + + /** + * 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"; + } + } + + file_put_contents($ignoreFile, $contents . $vendor); + } } \ No newline at end of file From 9ec641659e831df23937ae3b3657c8a24874efad Mon Sep 17 00:00:00 2001 From: Justin Rainbow Date: Sun, 8 Jan 2012 15:33:06 -0700 Subject: [PATCH 09/13] Validating package name input for init command --- src/Composer/Command/InitCommand.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index e5cbaa34e..749e7ab00 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -141,13 +141,28 @@ EOT $name = basename($cwd); if (isset($git['github.user'])) { $name = $git['github.user'] . '/' . $name; + } else { + // package names must be in the format foo/bar + $name = $name . '/' . $name; } } - $name = $dialog->ask( + $name = $dialog->askAndValidate( $output, $dialog->getQuestion('Package name', $name), - $name + 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; + } ); $input->setOption('name', $name); From 7a0a1788e38b6b69ed4104656b2d61e53496d36f Mon Sep 17 00:00:00 2001 From: Justin Rainbow Date: Sun, 8 Jan 2012 15:40:30 -0700 Subject: [PATCH 10/13] Moved DialogHelper into the base Application --- src/Composer/Command/InitCommand.php | 17 +++-------------- src/Composer/Console/Application.php | 13 +++++++++++++ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 749e7ab00..d20fdedaa 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -13,7 +13,6 @@ namespace Composer\Command; use Composer\Json\JsonFile; -use Composer\Command\Helper\DialogHelper; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -70,7 +69,7 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { - $dialog = $this->getDialogHelper(); + $dialog = $this->getHelperSet()->get('dialog'); $whitelist = array('name', 'description', 'author', 'require'); @@ -125,7 +124,7 @@ EOT { $git = $this->getGitConfig(); - $dialog = $this->getDialogHelper(); + $dialog = $this->getHelperSet()->get('dialog'); $dialog->writeSection($output, 'Welcome to the Composer config generator'); // namespace @@ -208,16 +207,6 @@ EOT $input->setOption('require', $requirements); } - protected function getDialogHelper() - { - $dialog = $this->getHelperSet()->get('dialog'); - if (!$dialog || get_class($dialog) !== 'Composer\Command\Helper\DialogHelper') { - $this->getHelperSet()->set($dialog = new DialogHelper()); - } - - return $dialog; - } - protected function findPackages($name) { $composer = $this->getComposer(); @@ -243,7 +232,7 @@ EOT protected function determineRequirements(InputInterface $input, OutputInterface $output) { - $dialog = $this->getDialogHelper(); + $dialog = $this->getHelperSet()->get('dialog'); $prompt = $dialog->getQuestion('Search for a package', false, ':'); $requires = $input->getOption('require') ?: array(); diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 3b3979770..fe6b6cb13 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -20,6 +20,7 @@ use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Finder\Finder; use Composer\Command; +use Composer\Command\Helper\DialogHelper; use Composer\Composer; use Composer\Factory; use Composer\IO\IOInterface; @@ -115,4 +116,16 @@ class Application extends BaseApplication $this->add(new Command\SelfUpdateCommand()); } } + + /** + * {@inheritDoc} + */ + protected function getDefaultHelperSet() + { + $helperSet = parent::getDefaultHelperSet(); + + $helperSet->set(new DialogHelper()); + + return $helperSet; + } } From 3a5d09a8b0b22adb1efa78237114cd503ec4eedc Mon Sep 17 00:00:00 2001 From: Justin Rainbow Date: Sun, 8 Jan 2012 15:55:12 -0700 Subject: [PATCH 11/13] Removing the 'writeSection' method from 'DialogHelper' --- src/Composer/Command/Helper/DialogHelper.php | 34 ++++++++++++++------ src/Composer/Command/InitCommand.php | 7 +++- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/Composer/Command/Helper/DialogHelper.php b/src/Composer/Command/Helper/DialogHelper.php index a0c034116..674b03800 100644 --- a/src/Composer/Command/Helper/DialogHelper.php +++ b/src/Composer/Command/Helper/DialogHelper.php @@ -1,5 +1,15 @@ + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Composer\Command\Helper; use Symfony\Component\Console\Helper\DialogHelper as BaseDialogHelper; @@ -7,17 +17,21 @@ use Symfony\Component\Console\Output\OutputInterface; class DialogHelper extends BaseDialogHelper { - public function writeSection(OutputInterface $output, $text, $style = 'bg=blue;fg=white') - { - $output->writeln(array( - '', - $this->getHelperSet()->get('formatter')->formatBlock($text, $style, true), - '', - )); - } - + /** + * Build text for asking a question. For example: + * + * "Do you want to continue [yes]:" + * + * @param string $question The question you want to ask + * @param mixed $default Default value to add to message, if false no default will be shown + * @param string $sep Separation char for between message and user input + * + * @return string + */ public function getQuestion($question, $default, $sep = ':') { - return $default ? sprintf('%s [%s]%s ', $question, $default, $sep) : sprintf('%s%s ', $question, $sep); + return $default ? + sprintf('%s [%s]%s ', $question, $default, $sep) : + sprintf('%s%s ', $question, $sep); } } \ No newline at end of file diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index d20fdedaa..93023d2ad 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -125,7 +125,12 @@ EOT $git = $this->getGitConfig(); $dialog = $this->getHelperSet()->get('dialog'); - $dialog->writeSection($output, 'Welcome to the Composer config generator'); + $formatter = $this->getHelperSet()->get('formatter'); + $output->writeln(array( + '', + $formatter->formatBlock('Welcome to the Composer config generator', 'bg=blue;fg=white', true), + '' + )); // namespace $output->writeln(array( From 604f2836e30cb7245132d6e3835e62db2d2a9c9c Mon Sep 17 00:00:00 2001 From: Justin Rainbow Date: Sun, 8 Jan 2012 12:47:48 -0700 Subject: [PATCH 12/13] Adding a JSON string formatter to the JsonFile class --- src/Composer/Json/JsonFormatter.php | 88 +++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/Composer/Json/JsonFormatter.php diff --git a/src/Composer/Json/JsonFormatter.php b/src/Composer/Json/JsonFormatter.php new file mode 100644 index 000000000..01e83ea06 --- /dev/null +++ b/src/Composer/Json/JsonFormatter.php @@ -0,0 +1,88 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Json; + +/** + * Format JSON output + * + * @author Justin Rainbow + */ +class JsonFormatter +{ + private $indent = ' '; + + private $level = 1; + + /** + * Indents a flat JSON string to make it more human-readable + * + * Original code for this function can be found at: + * http://recursive-design.com/blog/2008/03/11/format-json-with-php/ + * + * @param string $json The original JSON string to process + * @return string Indented version of the original JSON string + */ + public function format($json) + { + if (!is_string($json)) { + $json = json_encode($json); + } + + $result = ''; + $pos = 0; + $strLen = strlen($json); + $indentStr = $this->indent; + $newLine = "\n"; + $prevChar = ''; + $outOfQuotes = true; + + for ($i = 0; $i <= $strLen; $i++) { + // Grab the next character in the string + $char = substr($json, $i, 1); + + // Are we inside a quoted string? + if ($char == '"' && $prevChar != '\\') { + $outOfQuotes = !$outOfQuotes; + } else if (($char == '}' || $char == ']') && $outOfQuotes) { + // If this character is the end of an element, + // output a new line and indent the next line + $result .= $newLine; + $pos --; + for ($j=0; $j<$pos; $j++) { + $result .= $indentStr; + } + } + + // Add the character to the result string + $result .= $char; + + // If the last character was the beginning of an element, + // output a new line and indent the next line + if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) { + $result .= $newLine; + + if ($char == '{' || $char == '[') { + $pos ++; + } + + for ($j = 0; $j < $pos; $j++) { + $result .= $indentStr; + } + } + + $prevChar = $char; + } + + return $result; + } +} \ No newline at end of file From 24d85a48f07e760795cc705b8ff2455dbe068cc3 Mon Sep 17 00:00:00 2001 From: Justin Rainbow Date: Sun, 8 Jan 2012 12:50:42 -0700 Subject: [PATCH 13/13] Removing the JsonFormatter as it is part of the JsonFile now --- src/Composer/Json/JsonFormatter.php | 88 ----------------------------- 1 file changed, 88 deletions(-) delete mode 100644 src/Composer/Json/JsonFormatter.php diff --git a/src/Composer/Json/JsonFormatter.php b/src/Composer/Json/JsonFormatter.php deleted file mode 100644 index 01e83ea06..000000000 --- a/src/Composer/Json/JsonFormatter.php +++ /dev/null @@ -1,88 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Json; - -/** - * Format JSON output - * - * @author Justin Rainbow - */ -class JsonFormatter -{ - private $indent = ' '; - - private $level = 1; - - /** - * Indents a flat JSON string to make it more human-readable - * - * Original code for this function can be found at: - * http://recursive-design.com/blog/2008/03/11/format-json-with-php/ - * - * @param string $json The original JSON string to process - * @return string Indented version of the original JSON string - */ - public function format($json) - { - if (!is_string($json)) { - $json = json_encode($json); - } - - $result = ''; - $pos = 0; - $strLen = strlen($json); - $indentStr = $this->indent; - $newLine = "\n"; - $prevChar = ''; - $outOfQuotes = true; - - for ($i = 0; $i <= $strLen; $i++) { - // Grab the next character in the string - $char = substr($json, $i, 1); - - // Are we inside a quoted string? - if ($char == '"' && $prevChar != '\\') { - $outOfQuotes = !$outOfQuotes; - } else if (($char == '}' || $char == ']') && $outOfQuotes) { - // If this character is the end of an element, - // output a new line and indent the next line - $result .= $newLine; - $pos --; - for ($j=0; $j<$pos; $j++) { - $result .= $indentStr; - } - } - - // Add the character to the result string - $result .= $char; - - // If the last character was the beginning of an element, - // output a new line and indent the next line - if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) { - $result .= $newLine; - - if ($char == '{' || $char == '[') { - $pos ++; - } - - for ($j = 0; $j < $pos; $j++) { - $result .= $indentStr; - } - } - - $prevChar = $char; - } - - return $result; - } -} \ No newline at end of file