CreateJobCommand   A
last analyzed

Complexity

Total Complexity 41

Size/Duplication

Total Lines 255
Duplicated Lines 0 %

Test Coverage

Coverage 95.52%

Importance

Changes 2
Bugs 0 Features 1
Metric Value
eloc 125
c 2
b 0
f 1
dl 0
loc 255
ccs 128
cts 134
cp 0.9552
rs 9.1199
wmc 41

15 Methods

Rating   Name   Duplication   Size   Complexity  
A nullInterpretation() 0 7 3
A setWorkerManager() 0 3 1
A interpretArgs() 0 13 4
A booleanInterpretation() 0 10 5
A floatInterpretation() 0 9 2
A getHelpMessage() 0 24 1
A getArgs() 0 19 4
A testArgs() 0 10 3
A decodeJsonArgs() 0 11 3
A integerInterpretation() 0 12 3
A validatePHPArgs() 0 5 3
A validateJsonArgs() 0 9 4
A execute() 0 26 2
A decodePHPArgs() 0 8 2
A configure() 0 41 1

How to fix   Complexity   

Complex Class

Complex classes like CreateJobCommand often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CreateJobCommand, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Dtc\QueueBundle\Command;
4
5
use Dtc\QueueBundle\Exception\WorkerNotRegisteredException;
6
use Dtc\QueueBundle\Manager\WorkerManager;
7
use Dtc\QueueBundle\Model\Job;
8
use Symfony\Component\Console\Command\Command;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Command\Command 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 Symfony\Component\Console\Input\InputArgument;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Input\InputArgument 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 Symfony\Component\Console\Input\InputInterface;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Input\InputInterface 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...
11
use Symfony\Component\Console\Input\InputOption;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Input\InputOption 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...
12
use Symfony\Component\Console\Output\OutputInterface;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Output\OutputInterface 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...
13
14
class CreateJobCommand extends Command
15
{
16
17
    /** @var WorkerManager */
18
    private $workerManager;
19
20
    protected function configure()
21 1
    {
22
        $this
23
            ->setName("dtc:queue:create_job")
24 1
            ->addOption(
25 1
                'json-args',
26 1
                'j',
27 1
                InputOption::VALUE_NONE,
28 1
                'Consume the args as a single JSON-encoded array'
29
            )
30 1
            ->addOption(
31 1
                'php-args',
32 1
                'p',
33 1
                InputOption::VALUE_NONE,
34 1
                'Consume the args as a single PHP-serialized array'
35
            )
36 1
            ->addOption( // For 5.0 this should become the default
37 1
                'interpret-args',
38 1
                null,
39 1
                InputOption::VALUE_NONE,
40 1
                '(beta) Make a best guess as to the type of the argument passed in (DEFAULT for future releases - also note the "interpretation" may change in upcoming releases).'
41
            )
42 1
            ->addArgument(
43 1
                'worker_name',
44 1
                InputArgument::REQUIRED,
45 1
                'Name of worker',
46 1
                null
47
            )
48 1
            ->addArgument(
49 1
                'method',
50 1
                InputArgument::REQUIRED,
51 1
                'Method of worker to invoke',
52 1
                null
53
            )
54 1
            ->addArgument(
55 1
                'args',
56 1
                InputArgument::IS_ARRAY,
57 1
                'Argument(s) for invoking worker method'
58
            )
59 1
            ->setDescription('Create a job - for expert users')
60 1
            ->setHelp($this->getHelpMessage())
61 1
        ;
62
    }
63 1
64
    private function getHelpMessage()
65 1
    {
66
        $helpMessage = sprintf(
67 1
            "%s --json-args %s %s '%s'".PHP_EOL,
68 1
            $this->getName(), // command
69 1
            'my-worker-name', // worker_name
70 1
            'myMethod', // method
71 1
            json_encode([ // args
72 1
                'first parameter', // argv[0] (string)
73 1
                null, // argv[1] (null)
74
                3, // argv[2] (int)
75
                [ // argv[3] (array)
76
                    'fourth',
77
                    'param',
78
                    'is',
79
                    'an',
80
                    'array',
81
                ],
82
            ])
83
        );
84
        $helpMessage .= PHP_EOL;
85 1
        $helpMessage .= '';
86 1
87
        return $helpMessage;
88 1
    }
89
90
    public function setWorkerManager($workerManager)
91 1
    {
92
        $this->workerManager = $workerManager;
93 1
    }
94 1
95
    protected function execute(InputInterface $input, OutputInterface $output): int
96 1
    {
97
        $workerName = $input->getArgument('worker_name');
98 1
        $methodName = $input->getArgument('method');
99 1
100
        $args = $this->getArgs($input);
101 1
102
        $worker = $this->workerManager->getWorker($workerName);
103 1
104
        if (!$worker) {
105 1
            throw new WorkerNotRegisteredException("Worker `{$workerName}` is not registered.");
106
        }
107
108
        $when = \Dtc\QueueBundle\Util\Util::getMicrotimeDateTime();
109 1
        $batch = true;
110 1
        $priority = 1;
111 1
112
        $jobClass = $worker->getJobManager()->getJobClass();
113 1
        /** @var Job $job */
114
        $job = new $jobClass($worker, $batch, $priority, $when);
115 1
        $job->setMethod($methodName);
116 1
        $job->setArgs($args);
117 1
118
        $worker->getJobManager()->save($job);
119 1
120
        return $this::SUCCESS;
121 1
    }
122
123
    protected function getArgs(InputInterface $input)
124 1
    {
125
        $args = $input->getArgument('args');
126 1
        $jsonArgs = $input->getOption('json-args');
127 1
        $phpArgs = $input->getOption('php-args');
128 1
        $interpretArgs = $input->getOption('interpret-args');
129 1
        $this->validateJsonArgs($jsonArgs, $phpArgs, $interpretArgs);
130 1
131
        if ($jsonArgs) {
132 1
            return $this->decodeJsonArgs($args);
133 1
        }
134
        if ($phpArgs) {
135 1
            return $this->decodePHPArgs($args);
136 1
        }
137
        if ($interpretArgs) {
138 1
            return $this->interpretArgs($args);
139 1
        }
140
141
        return $args;
142
    }
143
144
    protected function validateJsonArgs($jsonArgs, $phpArgs, $interpretArgs)
145 1
    {
146
        if ($jsonArgs) {
147 1
            if ($phpArgs || $interpretArgs) {
148 1
                throw new \InvalidArgumentException('Should not have both JSON args plus another type of args');
149 1
            }
150
        }
151
152
        $this->validatePHPArgs($phpArgs, $interpretArgs);
153 1
    }
154 1
155
    protected function validatePHPArgs($phpArgs, $interpretArgs)
156 1
    {
157
        if ($phpArgs) {
158 1
            if ($interpretArgs) {
159 1
                throw new \InvalidArgumentException('Should not have both PHP args plus another type of args');
160 1
            }
161
        }
162
    }
163 1
164
    protected function interpretArgs($args)
165 1
    {
166
        if (null === $args || 0 == count($args)) {
167 1
            return $args;
168 1
        }
169
170
        $finalArgs = [];
171 1
172
        foreach ($args as $arg) {
173 1
            $finalArgs[] = $this->booleanInterpretation($arg);
174 1
        }
175
176
        return $finalArgs;
177 1
    }
178
179
    private function booleanInterpretation($arg)
180 1
    {
181
        if ('true' === $arg || 'TRUE' === $arg) {
182 1
            return true;
183 1
        }
184
        if ('false' === $arg || 'FALSE' === $arg) {
185 1
            return false;
186 1
        }
187
188
        return $this->integerInterpretation($arg);
189 1
    }
190
191
    private function integerInterpretation($arg)
192 1
    {
193
        if (ctype_digit($arg)) {
194 1
            $intArg = intval($arg);
195 1
            if (strval($intArg) === $arg) {
196 1
                return $intArg;
197 1
            }
198
            // Must be a super-long number
199
            return $arg;
200
        }
201
202
        return $this->floatInterpretation($arg);
203 1
    }
204
205
    private function floatInterpretation($arg)
206 1
    {
207
        if (is_numeric($arg)) {
208 1
            $floatArg = floatval($arg);
209 1
210
            return $floatArg;
211 1
        }
212
213
        return $this->nullInterpretation($arg);
214 1
    }
215
216
    private function nullInterpretation($arg)
217 1
    {
218
        if ('null' === $arg || 'NULL' === $arg) {
219 1
            return null;
220 1
        }
221
222
        return $arg;
223 1
    }
224
225
    protected function decodeJsonArgs($jsonArgs)
226 1
    {
227
        if (1 !== count($jsonArgs)) {
228 1
            throw new \InvalidArgumentException('args should be a single string containing a JSON-encoded array when using --json-args');
229 1
        }
230
        $args = json_decode($jsonArgs[0], true);
231 1
        if (null === $args) {
232 1
            throw new \InvalidArgumentException('unable to decode JSON-encoded args: '.$jsonArgs[0]);
233
        }
234
235
        return $this->testArgs('JSON', $args);
236 1
    }
237
238
    /**
239
     * @param string $type
240
     * @param array  $args
241
     *
242
     * @return mixed
243
     */
244
    protected function testArgs($type, $args)
245 1
    {
246
        if (!is_array($args)) {
0 ignored issues
show
introduced by
The condition is_array($args) is always true.
Loading history...
247 1
            throw new \InvalidArgumentException('unable to decode '.$type.'-encoded args into an array.');
248
        }
249
        if (array_values($args) !== $args) {
250 1
            throw new \InvalidArgumentException('Expecting numerically-indexed array in '.$type.' arguments');
251
        }
252
253
        return $args;
254 1
    }
255
256
    /**
257
     * @param string $phpArgs
258
     *
259
     * @return mixed
260
     */
261
    protected function decodePHPArgs($phpArgs)
262 1
    {
263
        if (1 !== count($phpArgs)) {
0 ignored issues
show
Bug introduced by
$phpArgs of type string is incompatible with the type Countable|array expected by parameter $value of count(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

263
        if (1 !== count(/** @scrutinizer ignore-type */ $phpArgs)) {
Loading history...
264 1
            throw new \InvalidArgumentException('args should be a single string containing a PHP-encoded array when using --php-args');
265 1
        }
266
        $args = unserialize($phpArgs[0]);
267 1
268
        return $this->testArgs('PHP', $args);
269 1
    }
270
}
271