These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /* (c) Anton Medvedev <[email protected]> |
||
3 | * |
||
4 | * For the full copyright and license information, please view the LICENSE |
||
5 | * file that was distributed with this source code. |
||
6 | */ |
||
7 | |||
8 | namespace Deployer; |
||
9 | |||
10 | use Deployer\Collection\Collection; |
||
11 | use Deployer\Component\ProcessRunner\Printer; |
||
12 | use Deployer\Component\ProcessRunner\ProcessRunner; |
||
13 | use Deployer\Component\Ssh\Client; |
||
14 | use Deployer\Configuration\Configuration; |
||
15 | use Deployer\Console\Application; |
||
16 | use Deployer\Console\CommandEvent; |
||
17 | use Deployer\Console\DiceCommand; |
||
18 | use Deployer\Console\InitCommand; |
||
19 | use Deployer\Console\RunCommand; |
||
20 | use Deployer\Console\SshCommand; |
||
21 | use Deployer\Console\MainCommand; |
||
22 | use Deployer\Console\TreeCommand; |
||
23 | use Deployer\Console\WorkerCommand; |
||
24 | use Deployer\Executor\ParallelExecutor; |
||
25 | use Deployer\Executor\Messenger; |
||
26 | use Deployer\Logger\Handler\FileHandler; |
||
27 | use Deployer\Logger\Handler\NullHandler; |
||
28 | use Deployer\Logger\Logger; |
||
29 | use Deployer\Task; |
||
30 | use Deployer\Utility\Reporter; |
||
31 | use Deployer\Utility\Rsync; |
||
32 | use Pimple\Container; |
||
33 | use Symfony\Component\Console; |
||
34 | use Symfony\Component\Console\Input\ArgvInput; |
||
35 | use Symfony\Component\Console\Input\InputInterface; |
||
36 | use Symfony\Component\Console\Output\ConsoleOutput; |
||
37 | use Symfony\Component\Console\Output\OutputInterface; |
||
38 | |||
39 | /** |
||
40 | * Deployer class represents DI container for configuring |
||
41 | * |
||
42 | * @property Application $console |
||
43 | * @property Task\TaskCollection|Task\Task[] $tasks |
||
44 | * @property Host\HostCollection|Collection|Host\Host[] $hosts |
||
45 | * @property Configuration $config |
||
46 | * @property Rsync $rsync |
||
47 | * @property Client $sshClient |
||
48 | * @property ProcessRunner $processRunner |
||
49 | * @property Task\ScriptManager $scriptManager |
||
50 | * @property Host\HostSelector $hostSelector |
||
51 | * @property ParallelExecutor $executor |
||
52 | * @property Messenger $messenger |
||
53 | * @property Messenger $logger |
||
54 | * @property Printer $pop |
||
55 | * @property Collection $fail |
||
56 | */ |
||
57 | class Deployer extends Container |
||
58 | { |
||
59 | /** |
||
60 | * Global instance of deployer. It's can be accessed only after constructor call. |
||
61 | * @var Deployer |
||
62 | */ |
||
63 | private static $instance; |
||
64 | |||
65 | /** |
||
66 | * @param Application $console |
||
67 | */ |
||
68 | 10 | public function __construct(Application $console, InputInterface $input, OutputInterface $output) |
|
69 | { |
||
70 | 10 | parent::__construct(); |
|
71 | |||
72 | /****************************** |
||
73 | * Console * |
||
74 | ******************************/ |
||
75 | |||
76 | $this['console'] = function () use ($console) { |
||
77 | return $console; |
||
78 | }; |
||
79 | |||
80 | $this['input'] = function () use ($input) { |
||
81 | return $input; |
||
82 | }; |
||
83 | |||
84 | $this['output'] = function () use ($output) { |
||
85 | 1 | return $output; |
|
86 | }; |
||
87 | |||
88 | /****************************** |
||
89 | * Config * |
||
90 | ******************************/ |
||
91 | |||
92 | $this['config'] = function () { |
||
93 | 10 | return new Configuration(); |
|
94 | }; |
||
95 | 10 | $this->config['ssh_multiplexing'] = true; |
|
96 | 10 | $this->config['default_stage'] = null; |
|
97 | |||
98 | /****************************** |
||
99 | * Core * |
||
100 | ******************************/ |
||
101 | |||
102 | $this['pop'] = function ($c) { |
||
103 | 1 | return new Printer($c['output']); |
|
104 | }; |
||
105 | $this['sshClient'] = function ($c) { |
||
106 | return new Client($c['output'], $c['pop'], $c['logger']); |
||
107 | }; |
||
108 | $this['rsync'] = function ($c) { |
||
109 | return new Rsync($c['pop'], $c['output']); |
||
110 | }; |
||
111 | $this['processRunner'] = function ($c) { |
||
112 | 1 | return new ProcessRunner($c['pop'], $c['logger']); |
|
113 | }; |
||
114 | $this['tasks'] = function () { |
||
115 | 10 | return new Task\TaskCollection(); |
|
116 | }; |
||
117 | $this['hosts'] = function () { |
||
118 | 4 | return new Host\HostCollection(); |
|
119 | }; |
||
120 | $this['scriptManager'] = function ($c) { |
||
121 | 2 | return new Task\ScriptManager($c['tasks']); |
|
122 | }; |
||
123 | $this['hostSelector'] = function ($c) { |
||
124 | $defaultStage = $c['config']['default_stage']; |
||
125 | if (is_object($defaultStage) && ($defaultStage instanceof \Closure)) { |
||
126 | $defaultStage = call_user_func($defaultStage); |
||
127 | } |
||
128 | return new Host\HostSelector($c['hosts'], $defaultStage); |
||
0 ignored issues
–
show
|
|||
129 | }; |
||
130 | $this['fail'] = function () { |
||
131 | return new Collection(); |
||
132 | }; |
||
133 | $this['messenger'] = function ($c) { |
||
134 | return new Messenger($c['input'], $c['output']); |
||
135 | }; |
||
136 | $this['executor'] = function ($c) { |
||
137 | return new ParallelExecutor( |
||
138 | $c['input'], |
||
139 | $c['output'], |
||
140 | $c['messenger'], |
||
141 | $c['console'], |
||
142 | $c['sshClient'], |
||
143 | $c['config'] |
||
144 | ); |
||
145 | }; |
||
146 | |||
147 | /****************************** |
||
148 | * Logger * |
||
149 | ******************************/ |
||
150 | |||
151 | $this['log_handler'] = function () { |
||
152 | 1 | return !empty($this->config['log_file']) |
|
153 | ? new FileHandler($this->config['log_file']) |
||
154 | 1 | : new NullHandler(); |
|
155 | }; |
||
156 | $this['logger'] = function () { |
||
157 | 1 | return new Logger($this['log_handler']); |
|
158 | }; |
||
159 | |||
160 | 10 | self::$instance = $this; |
|
161 | |||
162 | task('connect', function () { |
||
163 | $this['sshClient']->connect(currentHost()); |
||
164 | 10 | })->desc('Connect to remote server'); |
|
165 | 10 | } |
|
166 | |||
167 | /** |
||
168 | * @return Deployer |
||
169 | */ |
||
170 | 25 | public static function get() |
|
171 | { |
||
172 | 25 | return self::$instance; |
|
173 | } |
||
174 | |||
175 | /** |
||
176 | * Init console application |
||
177 | */ |
||
178 | public function init() |
||
179 | { |
||
180 | $this->addConsoleCommands(); |
||
181 | $this->getConsole()->add(new WorkerCommand($this)); |
||
182 | $this->getConsole()->add(new DiceCommand()); |
||
183 | $this->getConsole()->add(new InitCommand()); |
||
184 | $this->getConsole()->add(new SshCommand($this)); |
||
185 | $this->getConsole()->add(new RunCommand($this)); |
||
186 | $this->getConsole()->add(new TreeCommand($this)); |
||
187 | $this->getConsole()->afterRun([$this, 'collectAnonymousStats']); |
||
188 | } |
||
189 | |||
190 | /** |
||
191 | * Transform tasks to console commands. |
||
192 | */ |
||
193 | public function addConsoleCommands() |
||
194 | { |
||
195 | $this->getConsole()->addUserArgumentsAndOptions(); |
||
196 | |||
197 | foreach ($this->tasks->all() as $name => $task) { |
||
198 | if ($task->isHidden()) { |
||
199 | continue; |
||
200 | } |
||
201 | |||
202 | $this->getConsole()->add(new MainCommand($name, $task->getDescription(), $this)); |
||
203 | } |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * @param string $name |
||
208 | * @return mixed |
||
209 | * @throws \InvalidArgumentException |
||
210 | */ |
||
211 | 23 | public function __get($name) |
|
212 | { |
||
213 | 23 | if (isset($this[$name])) { |
|
214 | 23 | return $this[$name]; |
|
215 | } else { |
||
216 | 1 | throw new \InvalidArgumentException("Property \"$name\" does not exist."); |
|
217 | } |
||
218 | } |
||
219 | |||
220 | /** |
||
221 | * @return Application |
||
222 | */ |
||
223 | public function getConsole() |
||
224 | { |
||
225 | return $this['console']; |
||
226 | } |
||
227 | |||
228 | /** |
||
229 | * @return Console\Input\InputInterface |
||
230 | */ |
||
231 | public function getInput() |
||
232 | { |
||
233 | return $this['input']; |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * @return Console\Output\OutputInterface |
||
238 | */ |
||
239 | public function getOutput() |
||
240 | { |
||
241 | return $this['output']; |
||
242 | } |
||
243 | |||
244 | /** |
||
245 | * @param string $name |
||
246 | * @return Console\Helper\HelperInterface |
||
247 | */ |
||
248 | public function getHelper($name) |
||
249 | { |
||
250 | return $this->getConsole()->getHelperSet()->get($name); |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * Run Deployer |
||
255 | * |
||
256 | * @param string $version |
||
257 | * @param string $deployFile |
||
258 | */ |
||
259 | public static function run($version, $deployFile) |
||
260 | { |
||
261 | // Init Deployer |
||
262 | $console = new Application('Deployer', $version); |
||
263 | $input = new ArgvInput(); |
||
264 | $output = new ConsoleOutput(); |
||
265 | $deployer = new self($console, $input, $output); |
||
266 | |||
267 | try { |
||
268 | // Require deploy.php file |
||
269 | self::load($deployFile); |
||
270 | } catch (\Throwable $exception) { |
||
271 | $class = get_class($exception); |
||
272 | $file = basename($exception->getFile()); |
||
273 | $output->writeln([ |
||
274 | "<fg=white;bg=red> {$class} </> <comment>in {$file} on line {$exception->getLine()}:</>", |
||
275 | "", |
||
276 | implode("\n", array_map(function ($line) { |
||
277 | return " " . $line; |
||
278 | }, explode("\n", $exception->getMessage()))), |
||
279 | "", |
||
280 | ]); |
||
281 | $output->writeln($exception->getTraceAsString()); |
||
282 | return; |
||
283 | } |
||
284 | |||
285 | // Run Deployer |
||
286 | $deployer->init(); |
||
287 | $console->run($input, $output); |
||
288 | } |
||
289 | |||
290 | /** |
||
291 | * Collect anonymous stats about Deployer usage for improving developer experience. |
||
292 | * If you are not comfortable with this, you will always be able to disable this |
||
293 | * by setting `allow_anonymous_stats` to false in your deploy.php file. |
||
294 | * |
||
295 | * @param CommandEvent $commandEvent |
||
296 | * @codeCoverageIgnore |
||
297 | */ |
||
298 | public function collectAnonymousStats(CommandEvent $commandEvent) |
||
299 | { |
||
300 | if ($this->config->has('allow_anonymous_stats') && $this->config['allow_anonymous_stats'] === false) { |
||
301 | return; |
||
302 | } |
||
303 | |||
304 | $stats = [ |
||
305 | 'status' => 'success', |
||
306 | 'command_name' => $commandEvent->getCommand()->getName(), |
||
307 | 'project_hash' => empty($this->config['repository']) ? null : sha1($this->config['repository']), |
||
308 | 'hosts_count' => $this->hosts->count(), |
||
309 | 'deployer_version' => $this->getConsole()->getVersion(), |
||
310 | 'deployer_phar' => $this->getConsole()->isPharArchive(), |
||
311 | 'php_version' => phpversion(), |
||
312 | 'extension_pcntl' => extension_loaded('pcntl'), |
||
313 | 'extension_curl' => extension_loaded('curl'), |
||
314 | 'os' => defined('PHP_OS_FAMILY') ? PHP_OS_FAMILY : (stristr(PHP_OS, 'DAR') ? 'OSX' : (stristr(PHP_OS, 'WIN') ? 'WIN' : (stristr(PHP_OS, 'LINUX') ? 'LINUX' : PHP_OS))), |
||
315 | 'exception' => null, |
||
316 | ]; |
||
317 | |||
318 | if ($commandEvent->getException() !== null) { |
||
319 | $stats['status'] = 'error'; |
||
320 | $stats['exception'] = get_class($commandEvent->getException()); |
||
321 | } |
||
322 | |||
323 | if ($stats['command_name'] === 'init') { |
||
324 | $stats['allow_anonymous_stats'] = $GLOBALS['allow_anonymous_stats'] ?? false; |
||
325 | } |
||
326 | |||
327 | if (in_array($stats['command_name'], ['worker', 'list', 'help'], true)) { |
||
328 | return; |
||
329 | } |
||
330 | |||
331 | Reporter::report($stats); |
||
332 | } |
||
333 | |||
334 | /** |
||
335 | * Load recipe file. |
||
336 | * |
||
337 | * @param string $deployFile |
||
338 | */ |
||
339 | public static function load(string $deployFile) |
||
340 | { |
||
341 | if (is_readable($deployFile)) { |
||
342 | // Prevent variable leak into deploy.php file |
||
343 | call_user_func(function () use ($deployFile) { |
||
344 | // Reorder autoload stack |
||
345 | $originStack = spl_autoload_functions(); |
||
346 | |||
347 | require $deployFile; |
||
348 | |||
349 | $newStack = spl_autoload_functions(); |
||
350 | if ($originStack[0] !== $newStack[0]) { |
||
351 | foreach (array_reverse($originStack) as $loader) { |
||
352 | spl_autoload_unregister($loader); |
||
353 | spl_autoload_register($loader, true, true); |
||
354 | } |
||
355 | } |
||
356 | }); |
||
357 | } |
||
358 | } |
||
359 | } |
||
360 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.