diff --git a/src/Psy/Command/ComposerCommand.php b/src/Psy/Command/ComposerCommand.php new file mode 100644 index 000000000..3deea2909 --- /dev/null +++ b/src/Psy/Command/ComposerCommand.php @@ -0,0 +1,170 @@ + array("pipe", "r"), + self::PIPE_READ => array("pipe", "w"), + self::PIPE_ERR => array("pipe", "w"), + ); + + /** + * @var string + */ + protected $composerPath; + + /** + * @var InputInterface + */ + protected $input; + + /** + * @var OutputInterface + */ + protected $output; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('composer') + ->setDefinition(array( + new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''), + )) + ->setDescription('Composer installation.') + ->setHelp( + <<ignoreValidationErrors(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + + if (!$this->checkComposerInstallation()) { + $this->installComposer(); + } + + $this->callComposerBootstrap(); + + $app = new \Composer\Console\Application(); + $app->setAutoExit(false); + $input = new StringInput(trim(preg_replace(sprintf('#^%s#', $this->getName()), '', $input->__toString()))); + + return $app->doRun($input, $this->output); + } + + /** + * @param $path + */ + public function setComposerPath($path) + { + $this->composerPath = $path; + } + + /** + * + */ + protected function callComposerBootstrap() + { + $dependency = sprintf( + 'phar://%s/src/bootstrap.php', + !is_null($this->composerPath) ? $this->composerPath : $this->getLocalComposerFile() + ); + + require_once $dependency; + } + + /** + * @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)) { + 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 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."); + $response = $this->shellCommand('php -r "readfile(\'https://getcomposer.org/installer\');" | php'); + $this->output->writeln("$response"); + } +} diff --git a/src/Psy/Command/SandboxCommand.php b/src/Psy/Command/SandboxCommand.php new file mode 100644 index 000000000..533d644b2 --- /dev/null +++ b/src/Psy/Command/SandboxCommand.php @@ -0,0 +1,297 @@ +setName('sandbox') + ->setDefinition(array( + new InputArgument( + 'action', + InputArgument::OPTIONAL, + 'Action to perform : add|create, list, which, use|switch, clear|delete, exit|reset' + ), + new InputArgument('name', InputArgument::OPTIONAL), + new InputOption( + 'grep', + 'g', + InputOption::VALUE_OPTIONAL, + 'grep parameter' + ), + )) + ->setDescription('Manages sandboxed environments.') + ->setHelp( + <<input = $input; + $this->output = $output; + + $name = $input->getArgument('name'); + $action = $input->getArgument('action'); + + $this->performAction($action, $name); + } + + /** + * @param $action + * @param $name + */ + protected function performAction($action, $name) + { + switch ($action) { + case self::ACTION_ADD: + case self::ACTION_CREATE: + $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: + 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 $this->tempDir . DIRECTORY_SEPARATOR . self::SANDBOX_FOLDER . DIRECTORY_SEPARATOR . $name; + } + + /** + * @param null $name + * + * @return null|string + */ + protected function createSandbox($name = null) + { + if (is_null($this->restoreFolder)) { + $this->restoreFolder = getcwd(); + } + + $name = !is_null($name) ? $name : uniqid(); + $path = $this->getSandboxPath($name); + if (@file_exists($path)) { + throw new \InvalidArgumentException("Sandbox with name : $name already exists."); + } + + $result = @mkdir($path, 0777, true); + if (!$result) { + throw new \RuntimeException("Unable to create sandbox element."); + } + + $this->output->writeln("Created sandbox named : $name"); + $this->sandboxes[$name] = $path; + + return $name; + } + + /** + * + */ + protected function listSandboxes() + { + $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) { + if (is_null($grep) || preg_match(sprintf('#%s#', preg_quote($grep)), $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(sprintf("Currently on sandbox : %s ", $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..efd81fdb1 100644 --- a/src/Psy/Shell.php +++ b/src/Psy/Shell.php @@ -202,7 +202,12 @@ protected function getDefaultCommands() $hist = new Command\HistoryCommand(); $hist->setReadline($this->readline); + $sandbox = new Command\SandboxCommand(); + $sandbox->setTempDir($this->config->getRuntimeDir()); + return array( + $sandbox, + new Command\ComposerCommand(), new Command\HelpCommand(), new Command\ListCommand(), new Command\DumpCommand(),