Mercurial > hg > isophonics-drupal-site
comparison vendor/psy/psysh/src/Psy/Command/EditCommand.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 /* | |
4 * This file is part of Psy Shell. | |
5 * | |
6 * (c) 2012-2017 Justin Hileman | |
7 * | |
8 * For the full copyright and license information, please view the LICENSE | |
9 * file that was distributed with this source code. | |
10 */ | |
11 | |
12 namespace Psy\Command; | |
13 | |
14 use Psy\Context; | |
15 use Psy\ContextAware; | |
16 use Symfony\Component\Console\Input\InputArgument; | |
17 use Symfony\Component\Console\Input\InputInterface; | |
18 use Symfony\Component\Console\Input\InputOption; | |
19 use Symfony\Component\Console\Output\OutputInterface; | |
20 | |
21 class EditCommand extends Command implements ContextAware | |
22 { | |
23 /** | |
24 * @var string | |
25 */ | |
26 private $runtimeDir = ''; | |
27 | |
28 /** | |
29 * @var Context | |
30 */ | |
31 private $context; | |
32 | |
33 /** | |
34 * Constructor. | |
35 * | |
36 * @param string $runtimeDir The directory to use for temporary files | |
37 * @param string|null $name The name of the command; passing null means it must be set in configure() | |
38 * | |
39 * @throws \Symfony\Component\Console\Exception\LogicException When the command name is empty | |
40 */ | |
41 public function __construct($runtimeDir, $name = null) | |
42 { | |
43 parent::__construct($name); | |
44 | |
45 $this->runtimeDir = $runtimeDir; | |
46 } | |
47 | |
48 protected function configure() | |
49 { | |
50 $this | |
51 ->setName('edit') | |
52 ->setDefinition(array( | |
53 new InputArgument('file', InputArgument::OPTIONAL, 'The file to open for editing. If this is not given, edits a temporary file.', null), | |
54 new InputOption( | |
55 'exec', | |
56 'e', | |
57 InputOption::VALUE_NONE, | |
58 'Execute the file content after editing. This is the default when a file name argument is not given.', | |
59 null | |
60 ), | |
61 new InputOption( | |
62 'no-exec', | |
63 'E', | |
64 InputOption::VALUE_NONE, | |
65 'Do not execute the file content after editing. This is the default when a file name argument is given.', | |
66 null | |
67 ), | |
68 )) | |
69 ->setDescription('Open an external editor. Afterwards, get produced code in input buffer.') | |
70 ->setHelp('Set the EDITOR environment variable to something you\'d like to use.'); | |
71 } | |
72 | |
73 /** | |
74 * @param InputInterface $input | |
75 * @param OutputInterface $output | |
76 * | |
77 * @throws \InvalidArgumentException when both exec and no-exec flags are given or if a given variable is not found in the current context | |
78 * @throws \UnexpectedValueException if file_get_contents on the edited file returns false instead of a string | |
79 */ | |
80 protected function execute(InputInterface $input, OutputInterface $output) | |
81 { | |
82 if ($input->getOption('exec') && | |
83 $input->getOption('no-exec')) { | |
84 throw new \InvalidArgumentException('The --exec and --no-exec flags are mutually exclusive.'); | |
85 } | |
86 | |
87 $filePath = $this->extractFilePath($input->getArgument('file')); | |
88 | |
89 $execute = $this->shouldExecuteFile( | |
90 $input->getOption('exec'), | |
91 $input->getOption('no-exec'), | |
92 $filePath | |
93 ); | |
94 | |
95 $shouldRemoveFile = false; | |
96 | |
97 if ($filePath === null) { | |
98 $filePath = tempnam($this->runtimeDir, 'psysh-edit-command'); | |
99 $shouldRemoveFile = true; | |
100 } | |
101 | |
102 $editedContent = $this->editFile($filePath, $shouldRemoveFile); | |
103 | |
104 if ($execute) { | |
105 $this->getApplication()->addInput($editedContent); | |
106 } | |
107 } | |
108 | |
109 /** | |
110 * @param bool $execOption | |
111 * @param bool $noExecOption | |
112 * @param string|null $filePath | |
113 * | |
114 * @return bool | |
115 */ | |
116 private function shouldExecuteFile($execOption, $noExecOption, $filePath) | |
117 { | |
118 if ($execOption) { | |
119 return true; | |
120 } | |
121 | |
122 if ($noExecOption) { | |
123 return false; | |
124 } | |
125 | |
126 // By default, code that is edited is executed if there was no given input file path | |
127 return $filePath === null; | |
128 } | |
129 | |
130 /** | |
131 * @param string|null $fileArgument | |
132 * | |
133 * @return string|null The file path to edit, null if the input was null, or the value of the referenced variable | |
134 * | |
135 * @throws \InvalidArgumentException If the variable is not found in the current context | |
136 */ | |
137 private function extractFilePath($fileArgument) | |
138 { | |
139 // If the file argument was a variable, get it from the context | |
140 if ($fileArgument !== null && | |
141 strlen($fileArgument) > 0 && | |
142 $fileArgument[0] === '$') { | |
143 $fileArgument = $this->context->get(preg_replace('/^\$/', '', $fileArgument)); | |
144 } | |
145 | |
146 return $fileArgument; | |
147 } | |
148 | |
149 /** | |
150 * @param string $filePath | |
151 * @param string $shouldRemoveFile | |
152 * | |
153 * @return string | |
154 * | |
155 * @throws \UnexpectedValueException if file_get_contents on $filePath returns false instead of a string | |
156 */ | |
157 private function editFile($filePath, $shouldRemoveFile) | |
158 { | |
159 $escapedFilePath = escapeshellarg($filePath); | |
160 | |
161 $pipes = array(); | |
162 $proc = proc_open((getenv('EDITOR') ?: 'nano') . " {$escapedFilePath}", array(STDIN, STDOUT, STDERR), $pipes); | |
163 proc_close($proc); | |
164 | |
165 $editedContent = @file_get_contents($filePath); | |
166 | |
167 if ($shouldRemoveFile) { | |
168 @unlink($filePath); | |
169 } | |
170 | |
171 if ($editedContent === false) { | |
172 throw new \UnexpectedValueException("Reading {$filePath} returned false"); | |
173 } | |
174 | |
175 return $editedContent; | |
176 } | |
177 | |
178 /** | |
179 * Set the Context reference. | |
180 * | |
181 * @param Context $context | |
182 */ | |
183 public function setContext(Context $context) | |
184 { | |
185 $this->context = $context; | |
186 } | |
187 } |