Test Failed
Push — master ( 1a7d20...ad61cb )
by Matthew
10:06
created

CreateJobCommand::nullInterpretation()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
nc 2
nop 1
dl 0
loc 7
ccs 0
cts 0
cp 0
crap 12
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Dtc\QueueBundle\Command;
4
5
use Dtc\QueueBundle\Exception\WorkerNotRegisteredException;
6
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
7
use Symfony\Component\Console\Input\InputArgument;
8
use Symfony\Component\Console\Input\InputInterface;
9
use Symfony\Component\Console\Input\InputOption;
10
use Symfony\Component\Console\Output\OutputInterface;
11
12
class CreateJobCommand extends ContainerAwareCommand
13 1
{
14
    protected static $defaultName = 'dtc:queue:create_job';
15 1
16 1
    protected function configure()
17 1
    {
18 1
        $this
19 1
            ->addOption(
20 1
                'json-args',
21 1
                'j',
22
                InputOption::VALUE_NONE,
23 1
                'Consume the args as a single JSON-encoded array'
24
            )
25 1
            ->addOption(
26 1
                'php-args',
27 1
                'p',
28
                InputOption::VALUE_NONE,
29 1
                'Consume the args as a single PHP-serialized array'
30 1
            )
31 1
            ->addOption(
32
                'interpret-args',
33 1
                null,
34
                InputOption::VALUE_NONE,
35 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).'
36 1
            )
37
            ->addArgument(
38
                'worker_name',
39 1
                InputArgument::REQUIRED,
40 1
                'Name of worker',
41 1
                null
42
            )
43 1
            ->addArgument(
44 1
                'method',
45 1
                InputArgument::REQUIRED,
46 1
                'Method of worker to invoke',
47
                null
48 1
            )
49 1
            ->addArgument(
50
                'args',
51
                InputArgument::IS_ARRAY,
52
                'Argument(s) for invoking worker method'
53
            )
54
            ->setDescription('Create a job - for expert users')
55
            ->setHelp($this->getHelpMessage())
56
        ;
57
    }
58
59
    private function getHelpMessage()
60
    {
61
        $helpMessage = sprintf(
62
            "%s --json-args %s %s '%s'".PHP_EOL,
63
            $this->getName(), // command
64
            'my-worker-name', // worker_name
65
            'myMethod', // method
66
            json_encode([ // args
67
                'first parameter', // argv[0] (string)
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
68
                null, // argv[1] (null)
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
69
                3, // argv[2] (int)
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
70
                [ // argv[3] (array)
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
71
                    'fourth',
72
                    'param',
73
                    'is',
74
                    'an',
75
                    'array',
76
                ],
77
            ])
78
        );
79
        $helpMessage .= PHP_EOL;
80
        $helpMessage .= '';
81
82
        return $helpMessage;
83
    }
84
85
    protected function execute(InputInterface $input, OutputInterface $output)
86
    {
87
        $container = $this->getContainer();
88
        $jobManager = $container->get('dtc_queue.manager.job');
89
        $workerManager = $container->get('dtc_queue.manager.worker');
90
91
        $workerName = $input->getArgument('worker_name');
92
        $methodName = $input->getArgument('method');
93
94
        // @TODO for 5.0 - do minimal interpretation on arguments as to their types
95
        $args = $this->getArgs($input);
96
97
        $worker = $workerManager->getWorker($workerName);
98
99
        if (!$worker) {
100
            throw new WorkerNotRegisteredException("Worker `{$workerName}` is not registered.");
101
        }
102
103
        $when = \Dtc\QueueBundle\Util\Util::getMicrotimeDateTime();
104
        $batch = true;
105
        $priority = 1;
106
107
        $jobClass = $worker->getJobManager()->getJobClass();
108
        $job = new $jobClass($worker, $batch, $priority, $when);
109
        $job->setMethod($methodName);
110
        $job->setArgs($args);
111
112
        $jobManager->save($job);
113
    }
114
115
    protected function getArgs(InputInterface $input)
116
    {
117
        $args = $input->getArgument('args');
118
        $jsonArgs = $input->getOption('json-args');
119
        $phpArgs = $input->getOption('php-args');
120
        $interpretArgs = $input->getOption('interpret-args');
121
        $this->validateJsonArgs($jsonArgs, $phpArgs, $interpretArgs);
122
123
        if ($jsonArgs) {
124
            return $this->decodeJsonArgs($args);
125
        }
126
        if ($phpArgs) {
127
            return $this->decodePHPArgs($args);
128
        }
129
        if ($interpretArgs) {
130
            return $this->interpretArgs($args);
131
        }
132
133
        return $args;
134
    }
135
136
    protected function validateJsonArgs($jsonArgs, $phpArgs, $interpretArgs)
137
    {
138
        if ($jsonArgs) {
139
            if ($phpArgs || $interpretArgs) {
140
                throw new \InvalidArgumentException('Should not have both JSON args plus another type of args');
141
            }
142
        }
143
144
        $this->validatePHPArgs($phpArgs, $interpretArgs);
145
    }
146
147
    protected function validatePHPArgs($phpArgs, $interpretArgs)
148
    {
149
        if ($phpArgs) {
150
            if ($interpretArgs) {
151
                throw new \InvalidArgumentException('Should not have both PHP args plus another type of args');
152
            }
153
        }
154
    }
155
156
    protected function interpretArgs($args)
157
    {
158
        if (null === $args || 0 == count($args)) {
159
            return $args;
160
        }
161
162
        $finalArgs = [];
163
164
        foreach ($args as $arg) {
165
            $finalArgs[] = $this->booleanInterpretation($arg);
166
        }
167
168
        return $finalArgs;
169
    }
170
171
    private function booleanInterpretation($arg)
172
    {
173
        if ('true' === $arg || 'TRUE' === $arg) {
174
            return true;
175
        }
176
        if ('false' === $arg || 'FALSE' === $arg) {
177
            return false;
178
        }
179
180
        return $this->integerInterpretation($arg);
181
    }
182
183
    private function integerInterpretation($arg)
184
    {
185
        if (ctype_digit($arg)) {
186
            $intArg = intval($arg);
187
            if (strval($intArg) === $arg) {
188
                return $intArg;
189
            }
190
            // Must be a super-long number
191
            return $arg;
192
        }
193
194
        return $this->floatInterpretation($arg);
195
    }
196
197
    private function floatInterpretation($arg)
198
    {
199
        if (is_numeric($arg)) {
200
            $floatArg = floatval($arg);
201
202
            return $floatArg;
203
        }
204
205
        return $this->nullInterpretation($arg);
206
    }
207
208
    private function nullInterpretation($arg)
209
    {
210
        if ('null' === $arg || 'NULL' === $arg) {
211
            return null;
212
        }
213
214
        return $arg;
215
    }
216
217
    protected function decodeJsonArgs($jsonArgs)
218
    {
219
        if (1 !== count($jsonArgs)) {
220
            throw new \InvalidArgumentException('args should be a single string containing a JSON-encoded array when using --json-args');
221
        }
222
        $args = json_decode($jsonArgs[0], true);
223
        if (null === $args) {
224
            throw new \InvalidArgumentException('unable to decode JSON-encoded args: '.$jsonArgs[0]);
225
        }
226
227
        return $this->testArgs('JSON', $args);
228
    }
229
230
    /**
231
     * @param string $type
232
     * @param array  $args
233
     *
234
     * @return mixed
235
     */
236
    protected function testArgs($type, $args)
237
    {
238
        if (!is_array($args)) {
0 ignored issues
show
introduced by
The condition is_array($args) is always true.
Loading history...
239
            throw new \InvalidArgumentException('unable to decode '.$type.'-encoded args into an array.');
240
        }
241
        if (array_values($args) !== $args) {
242
            throw new \InvalidArgumentException('Expecting numerically-indexed array in '.$type.' arguments');
243
        }
244
245
        return $args;
246
    }
247
248
    /**
249
     * @param string $phpArgs
250
     *
251
     * @return mixed
252
     */
253
    protected function decodePHPArgs($phpArgs)
254
    {
255
        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 $var 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

255
        if (1 !== count(/** @scrutinizer ignore-type */ $phpArgs)) {
Loading history...
256
            throw new \InvalidArgumentException('args should be a single string containing a PHP-encoded array when using --php-args');
257
        }
258
        $args = unserialize($phpArgs[0]);
259
260
        return $this->testArgs('PHP', $args);
261
    }
262
}
263