Extension::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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