From 869aa5af22d70f1ae86b399a966278c3246a9fe4 Mon Sep 17 00:00:00 2001 From: Markcial Date: Mon, 16 Feb 2015 23:02:01 +0100 Subject: [PATCH 1/4] sandboxes and composer installations --- src/Psy/Command/ComposerCommand.php | 156 ++++++++++++++++ src/Psy/Command/SandboxCommand.php | 269 ++++++++++++++++++++++++++++ src/Psy/Shell.php | 2 + 3 files changed, 427 insertions(+) create mode 100644 src/Psy/Command/ComposerCommand.php create mode 100644 src/Psy/Command/SandboxCommand.php diff --git a/src/Psy/Command/ComposerCommand.php b/src/Psy/Command/ComposerCommand.php new file mode 100644 index 000000000..04f61d6b1 --- /dev/null +++ b/src/Psy/Command/ComposerCommand.php @@ -0,0 +1,156 @@ + array("pipe", "r"), + self::PIPE_READ => array("pipe", "w"), + self::PIPE_ERR => array("pipe", "w"), + ); + + /** + * @var string + */ + protected $installationType; + + /** + * @var InputInterface + */ + protected $input; + + /** + * @var OutputInterface + */ + protected $output; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('composer') + ->setDefinition(array( + new InputArgument('library', InputArgument::REQUIRED, 'library to install'), + )) + ->setDescription('Composer installation.') + ->setHelp( + <<input = $input; + $this->output = $output; + + $library = $this->input->getArgument('library'); + + if (!$this->checkComposerInstallation()) { + $this->installComposer(); + } + + $this->installLibrary($library); + } + + protected function getComposerPath() + { + if ($this->installationType === self::COMPOSER_IS_LOCAL) { + return 'php composer.phar'; + } + + return 'composer'; + } + + protected function bashCommand($command) + { + $process = proc_open('bash', $this->specification, $pipes); + stream_set_blocking($pipes[self::PIPE_ERR], 0); + + if (is_resource($process)) { + fwrite($pipes[self::PIPE_WRITE], $command); + fclose($pipes[self::PIPE_WRITE]); + + if ($err = stream_get_contents($pipes[self::PIPE_ERR])) { + $err = trim($err); + $this->output->writeln("$err"); + + return false; + } + + $return = stream_get_contents($pipes[self::PIPE_READ]); + fclose($pipes[self::PIPE_READ]); + + if (proc_close($process) === 0) { + return trim($return); + } + } + + return false; + } + + protected function checkComposerInstallation() + { + $whichComposer = $this->bashCommand('which composer'); + + if (! $whichComposer) { + // it will be local for sure + $this->installationType = self::COMPOSER_IS_LOCAL; + + // does it exists locally? + return file_exists('composer.phar'); + } + + $this->installationType = self::COMPOSER_IS_GLOBAL; + + return true; + } + + protected function installComposer() + { + $this->output->writeln("Composer not found, installing locally."); + $response = $this->bashCommand('php -r "readfile(\'https://getcomposer.org/installer\');" | php'); + $this->output->writeln("$response"); + } + + protected function installLibrary($library) + { + // require + $composer = $this->getComposerPath(); + + $this->output->writeln("Require $library"); + $this->bashCommand("$composer require '$library'"); + $this->output->writeln("composer update"); + $this->bashCommand("$composer update"); + $this->output->writeln("dumping autoload"); + $this->bashCommand("$composer dump-autoload"); + require_once 'vendor/autoload.php'; + } +} diff --git a/src/Psy/Command/SandboxCommand.php b/src/Psy/Command/SandboxCommand.php new file mode 100644 index 000000000..8dc26edfb --- /dev/null +++ b/src/Psy/Command/SandboxCommand.php @@ -0,0 +1,269 @@ +setName('sandbox') + ->setDefinition(array( + new InputArgument( + 'action', + InputArgument::REQUIRED, + 'Action to perform over the sandboxes add|list|switch|delete|exit' + ), + new InputArgument( + 'name', + InputArgument::OPTIONAL, + 'Optional name for the sandbox' + ), + )) + ->setDescription('Manages sandboxed environments.') + ->setHelp( + <<restoreFolder = exec('pwd'); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + + $action = $this->input->getArgument('action'); + $name = $this->input->getArgument('name'); + if (!in_array($action, $this->allowedActions)) { + throw new \InvalidArgumentException("Action $action is not allowed."); + } + + $this->performAction($action, $name); + } + + /** + * @param $action + * @param $name + */ + protected function performAction($action, $name) + { + switch ($action) { + case self::ACTION_ADD: + $identifier = $this->createSandbox($name); + $this->switchSandbox($identifier); + break; + case self::ACTION_LIST: + $this->listSandboxes(); + break; + case self::ACTION_WHICH: + $this->showCurrentSandbox(); + break; + case self::ACTION_SWITCH: + $this->switchSandbox($name); + break; + case self::ACTION_DELETE: + $this->removeSandbox($name); + break; + case self::ACTION_EXIT: + $this->restoreState(); + break; + } + } + + /** + * @param $name + * @return string + */ + protected function getSandboxPath($name) + { + return sys_get_temp_dir() . DIRECTORY_SEPARATOR . self::SANDBOX_FOLDER . DIRECTORY_SEPARATOR . $name; + } + + /** + * @param null $name + * @return null|string + */ + protected function createSandbox($name = null) + { + $name = !is_null($name) ? $name : uniqid(); + $path = $this->getSandboxPath($name); + if (file_exists($path)) { + throw new \InvalidArgumentException("Sandbox with name : $name already exists."); + } + + $this->output->writeln("Trying to create sandbox named : $name"); + $result = mkdir($path, 0777, true); + if (!$result) { + throw new \RuntimeException("Unable to create sandbox element."); + } + + $this->output->writeln("Storing sandbox named : $name"); + $this->sandboxes[$name] = $path; + + return $name; + } + + /** + * + */ + protected function listSandboxes() + { + $this->output->writeln("Retrieving sandbox list"); + foreach (array_keys($this->sandboxes) as $sandbox) { + $this->output->writeln("Sandbox : $sandbox"); + } + } + + /** + * + */ + protected function showCurrentSandbox() + { + if (is_null($this->current)) { + throw new \RuntimeException("You are not currently on a sandbox."); + } + + $this->output->writeln("Currently on sandbox : {$this->current} "); + } + + /** + * @param $name + */ + protected function removeSandbox($name = null) + { + if (is_null($name)) { + if (is_null($this->current)) { + throw new \RuntimeException("You are not currently on a sandbox."); + } + $name = $this->current; + } + $path = $this->getSandboxPath($name); + if (!file_exists($path)) { + throw new \RuntimeException("Sandbox named $name does not exist."); + } + + $this->output->writeln("Removing the sandbox $name"); + + @rmdir($path); + unset($this->sandboxes[$name]); + + if ($name === $this->current) { + unset($this->current); + @chdir($this->restoreFolder); + } + } + + /** + * @param $name + */ + protected function switchSandbox($name) + { + if (!in_array($name, array_keys($this->sandboxes))) { + throw new \InvalidArgumentException("Sandbox with name : $name does not exist."); + } + + $this->output->writeln("Switching to sandbox $name"); + + @chdir($this->sandboxes[$name]); + $this->current = $name; + } + + /** + * + */ + protected function clear() + { + foreach ($this->sandboxes as $id => $sandbox) { + $this->removeSandbox($id); + } + + unset($this->current); + } + + /** + * + */ + protected function restoreState() + { + $this->clear(); + $this->output->writeln('Restoring shell out from the sandbox state'); + @chdir($this->restoreFolder); + } + + /** + * + */ + public function __destruct() + { + $this->clear(); + @chdir($this->restoreFolder); + } +} diff --git a/src/Psy/Shell.php b/src/Psy/Shell.php index 9414c9c80..7f25804dc 100644 --- a/src/Psy/Shell.php +++ b/src/Psy/Shell.php @@ -203,6 +203,8 @@ protected function getDefaultCommands() $hist->setReadline($this->readline); return array( + new Command\SandboxCommand(), + new Command\ComposerCommand(), new Command\HelpCommand(), new Command\ListCommand(), new Command\DumpCommand(), From e3dc4669b24620ae8d30343ec788d11e5b45d6f9 Mon Sep 17 00:00:00 2001 From: Marc Garcia Date: Tue, 17 Feb 2015 13:11:49 +0100 Subject: [PATCH 2/4] silenced some verbose functions, code cleaning, added more functions to the composer shell --- src/Psy/Command/ComposerCommand.php | 102 ++++++++++++++----------- src/Psy/Command/SandboxCommand.php | 112 ++++++++++++++++++++-------- src/Psy/Shell.php | 5 +- 3 files changed, 143 insertions(+), 76 deletions(-) diff --git a/src/Psy/Command/ComposerCommand.php b/src/Psy/Command/ComposerCommand.php index 04f61d6b1..253a1cc72 100644 --- a/src/Psy/Command/ComposerCommand.php +++ b/src/Psy/Command/ComposerCommand.php @@ -2,8 +2,10 @@ namespace Psy\Command; +use Composer\Console\Application; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\StringInput; use Symfony\Component\Console\Output\OutputInterface; /** @@ -12,9 +14,6 @@ */ class ComposerCommand extends Command { - const COMPOSER_IS_GLOBAL = 'global'; - const COMPOSER_IS_LOCAL = 'local'; - const PIPE_WRITE = 0; const PIPE_READ = 1; const PIPE_ERR = 2; @@ -28,6 +27,11 @@ class ComposerCommand extends Command self::PIPE_ERR => array("pipe", "w"), ); + /** + * @var string + */ + protected $composerPath; + /** * @var string */ @@ -51,7 +55,8 @@ protected function configure() $this ->setName('composer') ->setDefinition(array( - new InputArgument('library', InputArgument::REQUIRED, 'library to install'), + new InputArgument('command-name', InputArgument::REQUIRED, ''), + new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''), )) ->setDescription('Composer installation.') ->setHelp( @@ -61,6 +66,7 @@ protected function configure() if composer is not found will install it locally HELP ); + $this->ignoreValidationErrors(); } /** @@ -71,27 +77,65 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->input = $input; $this->output = $output; - $library = $this->input->getArgument('library'); - if (!$this->checkComposerInstallation()) { $this->installComposer(); } - $this->installLibrary($library); + $this->callComposerBootstrap(); + + // extract real command name + $tokens = preg_split('{\s+}', $input->__toString()); + $args = array(); + foreach ($tokens as $token) { + if ($token && $token[0] !== '-') { + $args[] = $token; + if (count($args) >= 2) { + break; + } + } + } + // show help for this command if no command was found + if (count($args) < 2) { + return parent::run($input, $output); + } + + $app = new Application(); + $app->setAutoExit(false); + $input = new StringInput(implode(' ', array_slice($tokens, 1, count($tokens)))); + + return $app->doRun($input, $this->output); } - protected function getComposerPath() + public function setComposerPath($path) { - if ($this->installationType === self::COMPOSER_IS_LOCAL) { - return 'php composer.phar'; + $this->composerPath = $path; + } + + /** + * @return string + */ + protected function getSystemShell() + { + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + return 'cmd.exe'; } - return 'composer'; + return 'bash'; } - protected function bashCommand($command) + protected function callComposerBootstrap() { - $process = proc_open('bash', $this->specification, $pipes); + // TODO if the path is provided as paramater use it instead + require_once 'phar://composer.phar/src/bootstrap.php'; + } + + /** + * @param $command + * @return bool|string + */ + protected function shellCommand($command) + { + $process = proc_open($this->getSystemShell(), $this->specification, $pipes); stream_set_blocking($pipes[self::PIPE_ERR], 0); if (is_resource($process)) { @@ -118,39 +162,13 @@ protected function bashCommand($command) protected function checkComposerInstallation() { - $whichComposer = $this->bashCommand('which composer'); - - if (! $whichComposer) { - // it will be local for sure - $this->installationType = self::COMPOSER_IS_LOCAL; - - // does it exists locally? - return file_exists('composer.phar'); - } - - $this->installationType = self::COMPOSER_IS_GLOBAL; - - return true; + return @file_exists('composer.phar') or is_null($this->composerPath); } protected function installComposer() { - $this->output->writeln("Composer not found, installing locally."); - $response = $this->bashCommand('php -r "readfile(\'https://getcomposer.org/installer\');" | php'); + $this->output->writeln("Composer not found, downloading."); + $response = $this->shellCommand('php -r "readfile(\'https://getcomposer.org/installer\');" | php'); $this->output->writeln("$response"); } - - protected function installLibrary($library) - { - // require - $composer = $this->getComposerPath(); - - $this->output->writeln("Require $library"); - $this->bashCommand("$composer require '$library'"); - $this->output->writeln("composer update"); - $this->bashCommand("$composer update"); - $this->output->writeln("dumping autoload"); - $this->bashCommand("$composer dump-autoload"); - require_once 'vendor/autoload.php'; - } } diff --git a/src/Psy/Command/SandboxCommand.php b/src/Psy/Command/SandboxCommand.php index 8dc26edfb..22c0c2e21 100644 --- a/src/Psy/Command/SandboxCommand.php +++ b/src/Psy/Command/SandboxCommand.php @@ -4,6 +4,7 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** @@ -15,23 +16,21 @@ class SandboxCommand extends Command const SANDBOX_FOLDER = 'psysh_sandbox'; const ACTION_ADD = 'add'; + const ACTION_CREATE = 'create'; const ACTION_LIST = 'list'; const ACTION_WHICH = 'which'; + const ACTION_USE = 'use'; const ACTION_SWITCH = 'switch'; const ACTION_DELETE = 'delete'; + const ACTION_CLEAR = 'clear'; const ACTION_EXIT = 'exit'; + const ACTION_RESET = 'reset'; + const ACTION_HELP = 'help'; /** - * @var array + * @var String */ - protected $allowedActions = array( - self::ACTION_ADD, - self::ACTION_LIST, - self::ACTION_WHICH, - self::ACTION_SWITCH, - self::ACTION_DELETE, - self::ACTION_EXIT, - ); + protected $tempDir; /** * @var array @@ -68,28 +67,28 @@ protected function configure() ->setDefinition(array( new InputArgument( 'action', - InputArgument::REQUIRED, - 'Action to perform over the sandboxes add|list|switch|delete|exit' - ), - new InputArgument( - 'name', InputArgument::OPTIONAL, - 'Optional name for the sandbox' + 'Action to perform : add|create, list, which, use|switch, clear|delete, exit|reset' + ), + new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''), + new InputOption( + 'grep', + null, + InputOption::VALUE_OPTIONAL, + 'grep parameter' ), )) ->setDescription('Manages sandboxed environments.') ->setHelp( <<restoreFolder = exec('pwd'); } /** @@ -100,10 +99,23 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->input = $input; $this->output = $output; - $action = $this->input->getArgument('action'); - $name = $this->input->getArgument('name'); - if (!in_array($action, $this->allowedActions)) { - throw new \InvalidArgumentException("Action $action is not allowed."); + // extract real command name + $tokens = preg_split('{\s+}', $input->__toString()); + $args = array(); + foreach ($tokens as $token) { + if ($token && $token[0] !== '-') { + $args[] = $token; + if (count($args) >= 3) { + break; + } + } + } + + $name = null; + array_shift($args); // command name + $action = array_shift($args); + if (count($args) > 0) { + $name = array_shift($args); } $this->performAction($action, $name); @@ -117,6 +129,7 @@ protected function performAction($action, $name) { switch ($action) { case self::ACTION_ADD: + case self::ACTION_CREATE: $identifier = $this->createSandbox($name); $this->switchSandbox($identifier); break; @@ -127,24 +140,47 @@ protected function performAction($action, $name) $this->showCurrentSandbox(); break; case self::ACTION_SWITCH: + case self::ACTION_USE: $this->switchSandbox($name); break; case self::ACTION_DELETE: + case self::ACTION_CLEAR: $this->removeSandbox($name); break; case self::ACTION_EXIT: + case self::ACTION_RESET: $this->restoreState(); break; + case self::ACTION_HELP: + default: + $this->showUsage(); + break; } } + /** + * @param $dir + */ + public function setTempDir($dir) + { + $this->tempDir = $dir; + } + + /** + * + */ + protected function showUsage() + { + $this->output->writeln($this->asText()); + } + /** * @param $name * @return string */ protected function getSandboxPath($name) { - return sys_get_temp_dir() . DIRECTORY_SEPARATOR . self::SANDBOX_FOLDER . DIRECTORY_SEPARATOR . $name; + return $this->tempDir . DIRECTORY_SEPARATOR . self::SANDBOX_FOLDER . DIRECTORY_SEPARATOR . $name; } /** @@ -153,19 +189,22 @@ protected function getSandboxPath($name) */ protected function createSandbox($name = null) { + if (is_null($this->restoreFolder)) { + $this->restoreFolder = exec('pwd'); + } + $name = !is_null($name) ? $name : uniqid(); $path = $this->getSandboxPath($name); - if (file_exists($path)) { + if (@file_exists($path)) { throw new \InvalidArgumentException("Sandbox with name : $name already exists."); } - $this->output->writeln("Trying to create sandbox named : $name"); - $result = mkdir($path, 0777, true); + $result = @mkdir($path, 0777, true); if (!$result) { throw new \RuntimeException("Unable to create sandbox element."); } - $this->output->writeln("Storing sandbox named : $name"); + $this->output->writeln("Created sandbox named : $name"); $this->sandboxes[$name] = $path; return $name; @@ -176,9 +215,16 @@ protected function createSandbox($name = null) */ protected function listSandboxes() { - $this->output->writeln("Retrieving sandbox list"); + $grep = $this->input->getOption('grep'); + if (!is_null($grep)) { + $this->output->writeln("Retrieving sandbox list matching with : $grep"); + } else { + $this->output->writeln("Retrieving sandbox list"); + } foreach (array_keys($this->sandboxes) as $sandbox) { - $this->output->writeln("Sandbox : $sandbox"); + if (is_null($grep) || preg_match(sprintf('#%s#', preg_quote($grep)), $sandbox)) { + $this->output->writeln("Sandbox : $sandbox"); + } } } @@ -191,7 +237,7 @@ protected function showCurrentSandbox() throw new \RuntimeException("You are not currently on a sandbox."); } - $this->output->writeln("Currently on sandbox : {$this->current} "); + $this->output->writeln(sprintf("Currently on sandbox : %s ", $this->current)); } /** diff --git a/src/Psy/Shell.php b/src/Psy/Shell.php index 7f25804dc..efd81fdb1 100644 --- a/src/Psy/Shell.php +++ b/src/Psy/Shell.php @@ -202,8 +202,11 @@ protected function getDefaultCommands() $hist = new Command\HistoryCommand(); $hist->setReadline($this->readline); + $sandbox = new Command\SandboxCommand(); + $sandbox->setTempDir($this->config->getRuntimeDir()); + return array( - new Command\SandboxCommand(), + $sandbox, new Command\ComposerCommand(), new Command\HelpCommand(), new Command\ListCommand(), From dbf3d7cb209dfbabe9ebbac67f4ecbf4dd3854b6 Mon Sep 17 00:00:00 2001 From: Marc Garcia Date: Tue, 17 Feb 2015 14:53:57 +0100 Subject: [PATCH 3/4] refactoring of the arguments for the commands, replaced proc_open in deferral for process symfony library --- src/Psy/Command/ComposerCommand.php | 94 +++++++---------------------- src/Psy/Command/SandboxCommand.php | 26 ++------ 2 files changed, 28 insertions(+), 92 deletions(-) diff --git a/src/Psy/Command/ComposerCommand.php b/src/Psy/Command/ComposerCommand.php index 253a1cc72..529aef9c2 100644 --- a/src/Psy/Command/ComposerCommand.php +++ b/src/Psy/Command/ComposerCommand.php @@ -2,11 +2,11 @@ namespace Psy\Command; -use Composer\Console\Application; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\StringInput; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Process; /** * Class ComposerCommand @@ -14,29 +14,11 @@ */ class ComposerCommand extends Command { - const PIPE_WRITE = 0; - const PIPE_READ = 1; - const PIPE_ERR = 2; - - /** - * @var array - */ - protected $specification = array( - self::PIPE_WRITE => array("pipe", "r"), - self::PIPE_READ => array("pipe", "w"), - self::PIPE_ERR => array("pipe", "w"), - ); - /** * @var string */ protected $composerPath; - /** - * @var string - */ - protected $installationType; - /** * @var InputInterface */ @@ -55,7 +37,6 @@ protected function configure() $this ->setName('composer') ->setDefinition(array( - new InputArgument('command-name', InputArgument::REQUIRED, ''), new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''), )) ->setDescription('Composer installation.') @@ -83,25 +64,9 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->callComposerBootstrap(); - // extract real command name - $tokens = preg_split('{\s+}', $input->__toString()); - $args = array(); - foreach ($tokens as $token) { - if ($token && $token[0] !== '-') { - $args[] = $token; - if (count($args) >= 2) { - break; - } - } - } - // show help for this command if no command was found - if (count($args) < 2) { - return parent::run($input, $output); - } - - $app = new Application(); + $app = new \Composer\Console\Application(); $app->setAutoExit(false); - $input = new StringInput(implode(' ', array_slice($tokens, 1, count($tokens)))); + $input = new StringInput(trim(preg_replace(sprintf('#^%s#', $this->getName()), '', $input->__toString()))); return $app->doRun($input, $this->output); } @@ -112,21 +77,16 @@ public function setComposerPath($path) } /** - * @return string + * */ - protected function getSystemShell() - { - if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - return 'cmd.exe'; - } - - return 'bash'; - } - protected function callComposerBootstrap() { - // TODO if the path is provided as paramater use it instead - require_once 'phar://composer.phar/src/bootstrap.php'; + $dependency = sprintf( + 'phar://%s/src/bootstrap.php', + !is_null($this->composerPath) ? $this->composerPath : $this->getLocalComposerFile() + ); + + require_once $dependency; } /** @@ -135,34 +95,26 @@ protected function callComposerBootstrap() */ protected function shellCommand($command) { - $process = proc_open($this->getSystemShell(), $this->specification, $pipes); - stream_set_blocking($pipes[self::PIPE_ERR], 0); - - if (is_resource($process)) { - fwrite($pipes[self::PIPE_WRITE], $command); - fclose($pipes[self::PIPE_WRITE]); - - if ($err = stream_get_contents($pipes[self::PIPE_ERR])) { - $err = trim($err); - $this->output->writeln("$err"); - - return false; + /** PHP 5.4 compatibility, closures have no this scope in 5.4 versions */ + $output = $this->output; + $process = new Process($command); + $process->run(function ($type, $buffer) use ($output) { + if (Process::ERR === $type) { + $this->output->writeln("$buffer"); } + }); - $return = stream_get_contents($pipes[self::PIPE_READ]); - fclose($pipes[self::PIPE_READ]); - - if (proc_close($process) === 0) { - return trim($return); - } - } + return $process->getOutput(); + } - return false; + protected function getLocalComposerFile() + { + return getcwd() . DIRECTORY_SEPARATOR . 'composer.phar'; } protected function checkComposerInstallation() { - return @file_exists('composer.phar') or is_null($this->composerPath); + return @file_exists($this->getLocalComposerFile()) or !is_null($this->composerPath); } protected function installComposer() diff --git a/src/Psy/Command/SandboxCommand.php b/src/Psy/Command/SandboxCommand.php index 22c0c2e21..8d9ff0890 100644 --- a/src/Psy/Command/SandboxCommand.php +++ b/src/Psy/Command/SandboxCommand.php @@ -70,10 +70,10 @@ protected function configure() InputArgument::OPTIONAL, 'Action to perform : add|create, list, which, use|switch, clear|delete, exit|reset' ), - new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''), + new InputArgument('name', InputArgument::OPTIONAL), new InputOption( 'grep', - null, + 'g', InputOption::VALUE_OPTIONAL, 'grep parameter' ), @@ -99,24 +99,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->input = $input; $this->output = $output; - // extract real command name - $tokens = preg_split('{\s+}', $input->__toString()); - $args = array(); - foreach ($tokens as $token) { - if ($token && $token[0] !== '-') { - $args[] = $token; - if (count($args) >= 3) { - break; - } - } - } - - $name = null; - array_shift($args); // command name - $action = array_shift($args); - if (count($args) > 0) { - $name = array_shift($args); - } + $name = $input->getArgument('name'); + $action = $input->getArgument('action'); $this->performAction($action, $name); } @@ -190,7 +174,7 @@ protected function getSandboxPath($name) protected function createSandbox($name = null) { if (is_null($this->restoreFolder)) { - $this->restoreFolder = exec('pwd'); + $this->restoreFolder = getcwd(); } $name = !is_null($name) ? $name : uniqid(); From 74732a19b0cb4a80b89f8eb25f22a44f0b2f5a4b Mon Sep 17 00:00:00 2001 From: Marc Garcia Date: Tue, 24 Feb 2015 15:08:16 +0100 Subject: [PATCH 4/4] removed dependency on symfony process, replaced with proc_open function --- src/Psy/Command/ComposerCommand.php | 70 +++++++++++++++++++++++------ src/Psy/Command/SandboxCommand.php | 8 ++-- 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/Psy/Command/ComposerCommand.php b/src/Psy/Command/ComposerCommand.php index 529aef9c2..3deea2909 100644 --- a/src/Psy/Command/ComposerCommand.php +++ b/src/Psy/Command/ComposerCommand.php @@ -6,14 +6,22 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\StringInput; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Process\Process; -/** - * Class ComposerCommand - * @package Psy\Command - */ class ComposerCommand extends Command { + const PIPE_WRITE = 0; + const PIPE_READ = 1; + const PIPE_ERR = 2; + + /** + * @var array + */ + protected $specification = array( + self::PIPE_WRITE => array("pipe", "r"), + self::PIPE_READ => array("pipe", "w"), + self::PIPE_ERR => array("pipe", "w"), + ); + /** * @var string */ @@ -71,6 +79,9 @@ protected function execute(InputInterface $input, OutputInterface $output) return $app->doRun($input, $this->output); } + /** + * @param $path + */ public function setComposerPath($path) { $this->composerPath = $path; @@ -91,32 +102,65 @@ protected function callComposerBootstrap() /** * @param $command + * * @return bool|string */ protected function shellCommand($command) { - /** PHP 5.4 compatibility, closures have no this scope in 5.4 versions */ - $output = $this->output; - $process = new Process($command); - $process->run(function ($type, $buffer) use ($output) { - if (Process::ERR === $type) { - $this->output->writeln("$buffer"); + $process = proc_open($this->getSystemShell(), $this->specification, $pipes); + stream_set_blocking($pipes[self::PIPE_ERR], 0); + + if (is_resource($process)) { + fwrite($pipes[self::PIPE_WRITE], $command); + fclose($pipes[self::PIPE_WRITE]); + + if ($err = stream_get_contents($pipes[self::PIPE_ERR])) { + $err = trim($err); + $this->output->writeln("$err"); + + return false; } - }); - return $process->getOutput(); + $return = stream_get_contents($pipes[self::PIPE_READ]); + fclose($pipes[self::PIPE_READ]); + + if (proc_close($process) === 0) { + return trim($return); + } + } } + /** + * @return string + */ + protected function getSystemShell() + { + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + return 'cmd.exe'; + } + + return 'bash'; + } + + /** + * @return string + */ protected function getLocalComposerFile() { return getcwd() . DIRECTORY_SEPARATOR . 'composer.phar'; } + /** + * @return bool + */ protected function checkComposerInstallation() { return @file_exists($this->getLocalComposerFile()) or !is_null($this->composerPath); } + /** + * + */ protected function installComposer() { $this->output->writeln("Composer not found, downloading."); diff --git a/src/Psy/Command/SandboxCommand.php b/src/Psy/Command/SandboxCommand.php index 8d9ff0890..533d644b2 100644 --- a/src/Psy/Command/SandboxCommand.php +++ b/src/Psy/Command/SandboxCommand.php @@ -7,10 +7,6 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -/** - * Class SandboxCommand - * @package Psy\Command - */ class SandboxCommand extends Command { const SANDBOX_FOLDER = 'psysh_sandbox'; @@ -160,6 +156,7 @@ protected function showUsage() /** * @param $name + * * @return string */ protected function getSandboxPath($name) @@ -168,7 +165,8 @@ protected function getSandboxPath($name) } /** - * @param null $name + * @param null $name + * * @return null|string */ protected function createSandbox($name = null)