Passed
Push — master ( 119499...b087c1 )
by Jitendra
01:50
created

Extension::command()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 4
nop 3
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace PhalconExt\Cli;
4
5
use Ahc\Cli\Application;
6
use Ahc\Cli\Input\Command;
7
use Ahc\Cli\IO\Interactor;
8
use Phalcon\Cli\Task;
0 ignored issues
show
Bug introduced by
The type Phalcon\Cli\Task was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use Phalcon\DiInterface;
0 ignored issues
show
Bug introduced by
The type Phalcon\DiInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use PhalconExt\Cli\Task\ScheduleTask;
11
use PhalconExt\Di\ProvidesDi;
12
13
trait Extension
14
{
15
    use ProvidesDi;
16
    use MiddlewareTrait;
17
18
    /** @var array Tasks namespaces */
19
    protected $namespaces = [];
20
21
    /** @var array Tasks provided by package already */
22
    protected $factoryTasks =  [
23
        'schedule' => ScheduleTask::class,
24
    ];
25
26
    /** @var array Scheduled taskIds mapped to schedule time */
27
    protected $scheduled = [];
28
29
    /** @var array Raw argv sent to handle() [OR read from $_SERVER] */
30
    protected $rawArgv = [];
31
32
    /** @var array Normalized argv */
33
    protected $argv = [];
34
35
    /** @var string */
36
    protected $lastCommand;
37
38
    public function __construct(DiInterface $di, string $name, string $version = '0.0.1')
39
    {
40
        parent::__construct($di);
41
42
        $di->setShared('console', $this);
43
        $di->setShared('interactor', Interactor::class);
44
45
        $this->initialize($name, $version);
46
        $this->bindEvents($this);
47
    }
48
49
    protected function initialize(string $name, string $version)
50
    {
51
        $this->app = new Application($name, $version, function () {
0 ignored issues
show
Bug Best Practice introduced by
The property app does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
52
            return false;
53
        });
54
55
        $this->initTasks();
56
    }
57
58
    /**
59
     * Get the console Application.
60
     *
61
     * @return Application The instance of Ahc\Cli\Application.
62
     */
63
    public function app(): Application
64
    {
65
        return $this->app;
66
    }
67
68
    /**
69
     * Get the raw or processed argv values.
70
     *
71
     * By processed it means the task/action segment has been merged or shifted.
72
     *
73
     * @param bool $raw If true default raw values are returned, otherwise processed values.
74
     *
75
     * @return array
76
     */
77
    public function argv(bool $raw = true): array
78
    {
79
        if ($raw) {
80
            return $this->rawArgv;
81
        }
82
83
        return $this->argv;
84
    }
85
86
    /**
87
     * Handle console request.
88
     *
89
     * @param array|null $argv
90
     *
91
     * @return mixed But mostly the task instance that was executed.
92
     */
93
    public function handle(array $argv = null)
94
    {
95
        $this->rawArgv = $argv ?? $_SERVER['argv'];
96
97
        $params = $this->getTaskParameters($this->rawArgv);
98
99
        // Normalize in the form: ['app', 'task:action', 'param1', 'param2', ...]
100
        $this->argv = \array_merge(
101
            [$argv[0] ?? null, $params['task'] . ':' . $params['action']],
102
            $params['params']
103
        );
104
105
        return $this->doHandle($params);
106
    }
107
108
    /**
109
     * Handle cli request.
110
     *
111
     * @param array $parameters ['task' => ..., 'action' => ..., 'params' => []]
112
     *
113
     * @return mixed But mostly the task instance that was executed.
114
     */
115
    public function doHandle(array $parameters)
116
    {
117
        if (isset($this->namespaces[$parameters['task']])) {
118
            $parameters['task'] = $this->namespaces[$parameters['task']];
119
        }
120
121
        return parent::handle($parameters);
122
    }
123
124
    /**
125
     * Register a new command to be managed/scheduled by console.
126
     *
127
     * This allows you to define args/options which are not only auto validated but
128
     * injected to DI container by the name `command`.
129
     *
130
     * (You can still run tasks without adding it here)
131
     *
132
     * @param string $command      Preferred format is 'task:action'.
133
     *                             (for 'main' action, it can be 'task' only)
134
     * @param string $descr        Task description in short.
135
     * @param bool   $allowUnknown Whether to allow unkown options.
136
     *
137
     * @return Command The cli command for which you can define args/options fluenlty.
138
     */
139
    public function command(string $command, string $descr = '', bool $allowUnknown = false): Command
140
    {
141
        $this->lastCommand = $command;
142
143
        if (\strpos($command, ':main')) {
144
            $alias = \str_replace(':main', '', $command);
145
        }
146
147
        if (\strpos($command, ':') === false) {
148
            $alias = $command . ':main';
149
        }
150
151
        return $this->app->command($command, $descr, $alias ?? '', $allowUnknown);
152
    }
153
154
    /**
155
     * Schedule a command to run at the time when given cron expression evaluates truthy.
156
     *
157
     * @param string $cronExpr Eg: `@hourly` (Take a look at Ahc\Cli\Expression for predefined values)
158
     * @param string $command  This is optional (by default it schedules last command added via `command()`)
159
     *                         If given, the name should match the name you passed to `addTask($name)`
160
     *
161
     * @return self
162
     */
163
    public function schedule(string $cronExpr, string $command = ''): self
164
    {
165
        $command = $command ?: $this->lastCommand;
166
167
        $this->scheduled[$command] = $cronExpr;
168
169
        return $this;
170
    }
171
172
    /**
173
     * Get all the scheduled items.
174
     *
175
     * @return array
176
     */
177
    public function scheduled(): array
178
    {
179
        return $this->scheduled;
180
    }
181
182
    protected function getTaskParameters(array $argv)
183
    {
184
        $taskAction = [];
185
        \array_shift($argv);
186
187
        foreach ($argv as $i => $value) {
188
            if ($value[0] === '-' || isset($taskAction[1])) {
189
                break;
190
            }
191
192
            $taskAction = \array_merge($taskAction, \explode(':', $value, 2));
193
            unset($argv[$i]);
194
        }
195
196
        // Respect phalcon default.
197
        $taskAction += ['main', 'main'];
198
199
        return [
200
            'task'   => $taskAction[0],
201
            'action' => $taskAction[1],
202
            // For BC, still send params to handle()
203
            'params' => \array_values($argv),
204
        ];
205
    }
206
207
    /**
208
     * Inits tasks. It is done automatically if you have listed them in `console.tasks` config.
209
     *
210
     * @return self
211
     */
212
    public function initTasks(): self
213
    {
214
        foreach ($this->getTaskClasses() as $name => $class) {
215
            if (!$this->di()->has($class)) {
216
                // Force load!
217
                $this->di($class);
218
            }
219
220
            $this->namespaces[$name] = \preg_replace('#Task$#', '', $class);
221
        }
222
223
        return $this;
224
    }
225
226
    protected function getTaskClasses(): array
227
    {
228
        if ($tasks = $this->di('config')->path('console.tasks')) {
229
            $tasks = $tasks->toArray();
230
        }
231
232
        return $this->factoryTasks + ($tasks ?: []);
233
    }
234
}
235