Completed
Push — master ( 3260ca...0c0ec5 )
by Christian
02:12
created

AbstractCronCommand::getJobs()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 9.52
c 0
b 0
f 0
cc 3
nc 3
nop 0
1
<?php
2
3
namespace N98\Magento\Command\System\Cron;
4
5
use Magento\Store\Model\ScopeInterface as ScopeInterfaceAlias;
6
use N98\Magento\Command\AbstractMagentoCommand;
7
use Symfony\Component\Console\Exception\RuntimeException;
8
use Symfony\Component\Console\Helper\QuestionHelper;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Output\OutputInterface;
11
use Symfony\Component\Console\Question\ChoiceQuestion;
12
13
/**
14
 * Class AbstractCronCommand
15
 * @package N98\Magento\Command\System\Cron
16
 */
17
abstract class AbstractCronCommand extends AbstractMagentoCommand
18
{
19
    /**
20
     * @var \Magento\Framework\App\State
21
     */
22
    protected $state;
23
24
    /**
25
     * @var \Magento\Cron\Model\ConfigInterface
26
     */
27
    protected $cronConfig;
28
29
    /**
30
     * @var \Magento\Framework\App\Config\ScopeConfigInterface
31
     */
32
    protected $scopeConfig;
33
34
    /**
35
     * @var \Magento\Cron\Model\ResourceModel\Schedule\Collection
36
     */
37
    protected $cronScheduleCollection;
38
39
    /**
40
     * @var \Magento\Framework\App\ProductMetadataInterface $productMetadata
41
     */
42
    private $productMetadata;
43
44
    /**
45
     * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface
46
     */
47
    protected $timezone;
48
49
    /**
50
     * @var \Magento\Framework\Stdlib\DateTime\DateTime
51
     */
52
    private $dateTime;
53
54
    /**
55
     * @var \Magento\Cron\Model\ScheduleFactory
56
     */
57
    private $cronScheduleFactory;
58
59
    /**
60
     * @param \Magento\Framework\App\State $state
61
     * @param \Magento\Cron\Model\ConfigInterface $cronConfig
62
     * @param \Magento\Framework\App\ProductMetadataInterface $productMetadata
63
     * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone
64
     * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime
65
     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
66
     * @param \Magento\Cron\Model\ResourceModel\Schedule\Collection $cronScheduleCollection
67
     * @param \Magento\Cron\Model\ScheduleFactory $cronSchedulFactory
68
     */
69
    public function inject(
70
        \Magento\Framework\App\State $state,
71
        \Magento\Cron\Model\ConfigInterface $cronConfig,
72
        \Magento\Framework\App\ProductMetadataInterface $productMetadata,
73
        \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone,
74
        \Magento\Framework\Stdlib\DateTime\DateTime $dateTime,
75
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
76
        \Magento\Cron\Model\ResourceModel\Schedule\Collection $cronScheduleCollection,
77
        \Magento\Cron\Model\ScheduleFactory $cronSchedulFactory
78
    ) {
79
        $this->state = $state;
80
        $this->cronConfig = $cronConfig;
81
        $this->scopeConfig = $scopeConfig;
82
        $this->cronScheduleCollection = $cronScheduleCollection;
83
        $this->productMetadata = $productMetadata;
84
        $this->timezone = $timezone;
85
        $this->dateTime = $dateTime;
86
        $this->cronScheduleFactory = $cronSchedulFactory;
87
    }
88
89
    /**
90
     * @return array
91
     * @throws \Magento\Framework\Exception\CronException
92
     */
93
    protected function getJobs()
94
    {
95
        $table = [];
96
97
        $jobs = $this->cronConfig->getJobs();
98
99
        foreach ($jobs as $jobGroupCode => $jobGroup) {
100
            foreach ($jobGroup as $jobKey => $job) {
101
                $row = [
102
                    'Job'   => $job['name'] ?? $jobKey,
103
                    'Group' => $jobGroupCode,
104
                ];
105
106
                $row += $this->getSchedule($job);
107
108
                $table[] = $row;
109
            }
110
        }
111
112
        usort($table, static function ($a, $b) {
113
            return strcmp($a['Job'], $b['Job']);
114
        });
115
116
        return $table;
117
    }
118
119
    /**
120
     * @param string $jobCode
121
     * @return array
122
     */
123
    protected function getJobConfig($jobCode)
124
    {
125
        foreach ($this->cronConfig->getJobs() as $jobGroup) {
126
            foreach ($jobGroup as $jobKey => $job) {
127
                if (isset($job['name']) && ($job['name'] == $jobCode || $jobKey == $jobCode)) {
128
                    return $job;
129
                }
130
            }
131
        }
132
133
        return [];
134
    }
135
136
    /**
137
     * @param array $jobConfig
138
     * @return array
139
     * @throws \Magento\Framework\Exception\CronException
140
     */
141
    protected function getSchedule(array $jobConfig)
142
    {
143
        if (isset($jobConfig['schedule'])) {
144
            $expr = $this->getCronExpression($jobConfig);
145
146
            if ($expr == 'always') {
147
                return ['m' => '*', 'h' => '*', 'D' => '*', 'M' => '*', 'WD' => '*'];
148
            }
149
150
            /** @var \Magento\Cron\Model\Schedule $schedule */
151
            $schedule = $this->cronScheduleFactory->create();
152
            $schedule->setCronExpr($expr);
153
            $array = $schedule->getCronExprArr();
154
155
            return [
156
                'm'  => $array[0],
157
                'h'  => $array[1],
158
                'D'  => $array[2],
159
                'M'  => $array[3],
160
                'WD' => $array[4],
161
            ];
162
        }
163
164
        return ['m' => '-', 'h' => '-', 'D' => '-', 'M' => '-', 'WD' => '-'];
165
    }
166
167
    /**
168
     * Get cron expression of cron job.
169
     *
170
     * @param array $jobConfig
171
     * @return null|string
172
     */
173
    private function getCronExpression($jobConfig)
174
    {
175
        $cronExpression = null;
176
177
        if (isset($jobConfig['config_path'])) {
178
            $cronExpression = $this->getConfigSchedule($jobConfig) ?: null;
179
        }
180
181
        if (!$cronExpression && isset($jobConfig['schedule'])) {
182
            $cronExpression = $jobConfig['schedule'];
183
        }
184
185
        return $cronExpression;
186
    }
187
188
    /**
189
     * Get config of schedule.
190
     *
191
     * @param array $jobConfig
192
     * @return mixed
193
     */
194
    private function getConfigSchedule($jobConfig)
195
    {
196
        return $this->scopeConfig->getValue(
197
            $jobConfig['config_path'],
198
            ScopeInterfaceAlias::SCOPE_STORE
199
        );
200
    }
201
202
    /**
203
     * @param InputInterface $input
204
     * @param OutputInterface $output
205
     * @param array $jobs
206
     * @return string
207
     * @throws \InvalidArgumentException
208
     * @throws \Exception
209
     */
210
    protected function askJobCode(InputInterface $input, OutputInterface $output, $jobs)
211
    {
212
        $choices = [];
213
        foreach ($jobs as $key => $job) {
214
            $choices[$key + 1] = $job['Job'];
215
        }
216
217
        $question = new ChoiceQuestion('<question>Please select a job:</question>', $choices);
218 View Code Duplication
        $question->setValidator(function ($typeInput) use ($jobs) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
219
            if (!isset($jobs[$typeInput - 1])) {
220
                throw new \InvalidArgumentException('Invalid job');
221
            }
222
            return $jobs[$typeInput - 1]['Job'];
223
        });
224
225
        /** @var $questionHelper QuestionHelper */
226
        $questionHelper = $this->getHelper('question');
227
        return $questionHelper->ask($input, $output, $question);
228
    }
229
230
    /**
231
     * @param InputInterface $input
232
     * @param OutputInterface $output
233
     * @return array
234
     * @throws \Exception
235
     */
236
    protected function getJobForExecuteMethod(InputInterface $input, OutputInterface $output)
237
    {
238
        $jobCode = $input->getArgument('job');
239
        $jobs = $this->getJobs();
240
241
        if (!$jobCode) {
242
            $this->writeSection($output, 'Cronjob');
243
            $jobCode = $this->askJobCode($input, $output, $jobs);
244
        }
245
246
        $jobConfig = $this->getJobConfig($jobCode);
0 ignored issues
show
Bug introduced by
It seems like $jobCode defined by $input->getArgument('job') on line 238 can also be of type array<integer,string>; however, N98\Magento\Command\Syst...Command::getJobConfig() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
247
248
        if (empty($jobCode) || !isset($jobConfig['instance'])) {
249
            throw new \InvalidArgumentException('No job config found!');
250
        }
251
252
        $model = $this->getObjectManager()->get($jobConfig['instance']);
253
254
        if (!$model || !is_callable([$model, $jobConfig['method']])) {
255
            throw new RuntimeException(
256
                sprintf(
257
                    'Invalid callback: %s::%s does not exist',
258
                    $jobConfig['instance'],
259
                    $jobConfig['method']
260
                )
261
            );
262
        }
263
264
        return [$jobCode, $jobConfig, $model];
265
    }
266
267
    /**
268
     * Get timestamp used for time related database fields in the cron tables
269
     *
270
     * Note: The timestamp used will change from Magento 2.1.7 to 2.2.0 and
271
     *       these changes are branched by Magento version in this method.
272
     *
273
     * @return int
274
     */
275
    protected function getCronTimestamp()
276
    {
277
        /* @var $version string e.g. "2.1.7" */
278
        $version = $this->productMetadata->getVersion();
279
280
        if (version_compare($version, '2.2.0') >= 0) {
281
            return $this->dateTime->gmtTimestamp();
282
        }
283
284
        return $this->timezone->scopeTimeStamp();
285
    }
286
}
287