Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
79.41% covered (warning)
79.41%
27 / 34
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
ExecuteCommandEndpoint
79.41% covered (warning)
79.41%
27 / 34
0.00% covered (danger)
0.00%
0 / 1
12.06
0.00% covered (danger)
0.00%
0 / 1
 handle
79.41% covered (warning)
79.41%
27 / 34
0.00% covered (danger)
0.00%
0 / 1
12.06
1<?php
2
3namespace Olz\Apps\Commands\Endpoints;
4
5use Doctrine\DBAL\Exception\DriverException;
6use Olz\Api\OlzTypedEndpoint;
7use PhpTypeScriptApi\HttpError;
8use Symfony\Component\Console\Input\ArgvInput;
9use Symfony\Component\Console\Output\BufferedOutput;
10
11/**
12 * @extends OlzTypedEndpoint<
13 *   array{command: non-empty-string, argv?: ?non-empty-string},
14 *   array{error: bool, output: non-empty-string}
15 * >
16 */
17class ExecuteCommandEndpoint extends OlzTypedEndpoint {
18    protected function handle(mixed $input): mixed {
19        $command_name = $input['command'];
20        try {
21            $has_access = $this->authUtils()->hasPermission('commands');
22            $has_command_access = $this->authUtils()->hasPermission("command_{$command_name}");
23            if (!$has_access && !$has_command_access) {
24                throw new HttpError(403, "Kein Zugriff!");
25            }
26        } catch (DriverException $exc) {
27            // Could be a migration issue
28            // => if the command is db-migrate or db-reset, continue nevertheless!
29            $should_continue = (
30                $command_name === 'cache:clear'
31                || $command_name === 'olz:db-migrate'
32                || $command_name === 'olz:db-reset'
33            );
34            if (!$should_continue) {
35                throw $exc;
36            }
37        }
38
39        set_time_limit(4000);
40        ignore_user_abort(true);
41
42        $argv_arg = $input['argv'] ?? null;
43        $argv = ($argv_arg ? preg_split('/\s+/', $argv_arg) : []) ?: [];
44        $command_input = new ArgvInput(['bin/console', $command_name, ...$argv]);
45        $command_input->setInteractive(false);
46        $command_output = new BufferedOutput();
47        try {
48            $this->symfonyUtils()->callCommand($command_name, $command_input, $command_output);
49            $output = $command_output->fetch();
50            $this->log()->info("Command {$command_name} successfully executed via endpoint.");
51            return [
52                'error' => false,
53                'output' => $output ? $output : '(no output)',
54            ];
55        } catch (\Throwable $th) {
56            $this->log()->notice("Failed to execute command {$command_name} via endpoint.");
57            $output = $command_output->fetch();
58            return [
59                'error' => true,
60                'output' => $output."\n".$th->getMessage(),
61            ];
62        }
63    }
64}