Passed
Push — master ( ab8b9d...7e5c9a )
by Valentin
08:11 queued 06:26
created

Pheanstalk   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 331
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 43
eloc 76
c 2
b 0
f 0
dl 0
loc 331
ccs 101
cts 101
cp 1
rs 8.96

30 Methods

Rating   Name   Duplication   Size   Complexity  
A workflowExists() 0 7 2
A setConnection() 0 5 1
A setCurrentClass() 0 4 1
A getCurrentClass() 0 3 1
A delete() 0 3 1
A getSchedule() 0 3 1
A deleteTube() 0 3 1
A __construct() 0 3 1
A getConnection() 0 3 1
A deleteSchedule() 0 3 1
A put() 0 5 1
A getWorkflowInstances() 0 19 6
A tubeExists() 0 3 1
A peek() 0 5 1
A _dispatch() 0 3 1
A getWorkflowInstancesDetails() 0 3 1
A getWorkflow() 0 3 1
A update() 0 4 1
A listTubes() 0 3 1
A stats() 0 3 1
B create() 0 32 8
A listSchedules() 0 3 1
A statsTube() 0 3 1
A createTask() 0 7 1
A createSchedule() 0 6 1
A createTube() 0 3 1
A cancel() 0 3 1
A updateSchedule() 0 4 1
A kill() 0 3 1
A updateTube() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Pheanstalk 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 Pheanstalk, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Pheanstalk;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Pheanstalk\Command\CreateCommand;
7
use Pheanstalk\Command\CreateScheduleCommand;
8
use Pheanstalk\Command\CreateTubeCommand;
9
use Pheanstalk\Command\GetWorkflowCommand;
10
use Pheanstalk\Command\GetWorkflowInstancesCommand;
11
use Pheanstalk\Command\GetWorkflowInstancesDetailCommand;
12
use Pheanstalk\Command\ListWorkflowsCommand;
13
use Pheanstalk\Command\ReleaseCommand;
0 ignored issues
show
Bug introduced by
The type Pheanstalk\Command\ReleaseCommand 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...
14
use Pheanstalk\Command\UpdateTubeCommand;
15
use Pheanstalk\Command\WorkflowExistsCommand;
16
use Pheanstalk\Exception\ServerDuplicateEntryException;
17
use Pheanstalk\Structure\Job;
18
use Pheanstalk\Structure\Schedule;
19
use Pheanstalk\Structure\Task;
20
use Pheanstalk\Structure\TaskInstance;
21
use Pheanstalk\Structure\TimeSchedule;
22
use Pheanstalk\Structure\Tube;
23
use Pheanstalk\Structure\Workflow;
24
use Pheanstalk\Structure\WorkflowInstance;
25
26
/**
27
 * Pheanstalk is a PHP client for the beanstalkd workqueue.
28
 *
29
 * The Pheanstalk class is a simple facade for the various underlying components.
30
 *
31
 * @see http://github.com/kr/beanstalkd
32
 * @see http://xph.us/software/beanstalkd/
33
 *
34
 * @author  Paul Annesley
35
 * @package Pheanstalk
36
 * @license http://www.opensource.org/licenses/mit-license.php
37
 */
38
class Pheanstalk implements PheanstalkInterface
39
{
40
41
    /** @var Connection $connection */
42
    private $connection;
43
44
    /** @var PheanstalkInterface $currentClass */
45
    private $currentClass;
46
47
    /**
48
     * @param string $host
49
     * @param string $user
50
     * @param string $password
51
     * @param int    $port
52
     * @param int    $connectTimeout
53
     * @param bool   $connectPersistent
54
     */
55 21
    public function __construct($host, $user = null, $password = null, $port = PheanstalkInterface::DEFAULT_PORT, $connectTimeout = null, $connectPersistent = false)
56
    {
57 21
        $this->setConnection(new Connection($host, $user, $password, $port, $connectTimeout, $connectPersistent));
58
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63 21
    public function setConnection(Connection $connection)
64
    {
65 21
        $this->connection = $connection;
66
67 21
        return $this;
68
    }
69
70
    /**
71
     * {@inheritdoc}
72
     */
73 2
    public function getConnection()
74
    {
75 2
        return $this->connection;
76
    }
77
78
    /**
79
     * @return PheanstalkInterface
80
     */
81 14
    public function getCurrentClass(): PheanstalkInterface
82
    {
83 14
        return $this->currentClass ?? $this;
84
    }
85
86
    /**
87
     * @param PheanstalkInterface $currentClass
88
     *
89
     * @return Pheanstalk
90
     */
91 1
    public function setCurrentClass(PheanstalkInterface $currentClass): PheanstalkInterface
92
    {
93 1
        $this->currentClass = $currentClass;
94 1
        return $this;
95
    }
96
97
    // ----------------------------------------
98
99
    /**
100
     * {@inheritdoc}
101
     */
102 1
    public function deleteSchedule(Schedule $schedule)
103
    {
104 1
        return $this->_dispatch(new Command\DeleteScheduleCommand($schedule));
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110 6
    public function delete(Workflow $workflow)
111
    {
112 6
        return $this->_dispatch(new Command\DeleteCommand($workflow));
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 1
    public function deleteTube(Tube $tube)
119
    {
120 1
        return $this->_dispatch(new Command\DeleteTubeCommand($tube));
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126 8
    public function workflowExists($name)
127
    {
128 8
        $workflow = $this->_dispatch(new Command\WorkflowExistsCommand($name));
129 7
        if ($workflow instanceof Workflow) {
130 7
            return $this->getCurrentClass()->getWorkflow($workflow);
131
        }
132 1
        return false;
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138 1
    public function getSchedule(int $scheduleId)
139
    {
140 1
        return $this->_dispatch(new Command\GetScheduleCommand($scheduleId));
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146 1
    public function listSchedules()
147
    {
148 1
        return $this->_dispatch(new Command\ListSchedulesCommand());
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154 7
    public function getWorkflow(Workflow $workflow)
155
    {
156 7
        return $this->_dispatch(new Command\GetWorkflowCommand($workflow));
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162 5
    public function getWorkflowInstances(?Workflow $workflow, string $status = null)
163
    {
164 5
        $paramsStatus = empty($status) ? GetWorkflowInstancesDetailCommand::FILTERS : [$status];
165 5
        $instances = new ArrayCollection([]);
166 5
        foreach ($paramsStatus as $stat) {
167 5
            $instances[strtolower($stat)] = $this->_dispatch(new Command\GetWorkflowInstancesCommand($workflow, $stat));
168
                /** @var ArrayCollection $workflowCollection */
169 5
                $workflowCollection = $instances[strtolower($stat)]->get('workflow_instances');
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

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

169
                /** @scrutinizer ignore-call */ 
170
                $workflowCollection = $instances[strtolower($stat)]->get('workflow_instances');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
170 5
            if (!empty($workflowCollection)) {
171 4
                foreach ($workflowCollection as $instance) {
172 4
                    $this->getCurrentClass()->getWorkflowInstancesDetails($instance);
173
                }
174
            }
175
        }
176 5
        if (!is_null($status)) {
177 3
            return $instances->get(strtolower($status))->get('workflow_instances');
178
        }
179
180 2
        return $instances;
181
    }
182
183
    /**
184
     * {@inheritdoc}
185
     */
186 4
    public function getWorkflowInstancesDetails(WorkflowInstance $workflowInstance)
187
    {
188 4
        return $this->_dispatch(new Command\GetWorkflowInstancesDetailCommand($workflowInstance));
189
    }
190
191
    /**
192
     * {@inheritdoc}
193
     */
194 9
    public function tubeExists($name)
195
    {
196 9
        return $this->_dispatch(new Command\TubeExistsCommand($name));
197
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202 1
    public function listTubes()
203
    {
204 1
        return $this->_dispatch(new Command\ListTubesCommand());
205
    }
206
207
    /**
208
     * {@inheritdoc}
209
     */
210 1
    public function peek()
211
    {
212 1
        $response = $this->_dispatch(new Command\PeekCommand());
213
214 1
        return $response;
215
    }
216
217
    /**
218
     * {@inheritdoc}
219
     */
220 6
    public function put(Workflow $workflow)
221
    {
222 6
        $response = $this->_dispatch(new Command\PutCommand($workflow));
223
224 6
        return $response['workflow-instance-id'];
225
    }
226
227
    /**
228
     * {@inheritdoc}
229
     */
230 1
    public function statsTube(Tube $tube)
231
    {
232 1
        return $this->_dispatch(new Command\StatsTubeCommand($tube));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_dispatch(...tatsTubeCommand($tube)) returns the type array which is incompatible with the return type mandated by Pheanstalk\PheanstalkInterface::statsTube() of object.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
233
    }
234
235
    /**
236
     * {@inheritdoc}
237
     */
238 1
    public function stats()
239
    {
240 1
        return $this->_dispatch(new Command\StatsCommand());
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_dispatch(...Command\StatsCommand()) returns the type array which is incompatible with the return type mandated by Pheanstalk\PheanstalkInterface::stats() of object.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
241
    }
242
243
    // ----------------------------------------
244
245
    /**
246
     * Dispatches the specified command to the connection object.
247
     *
248
     * If a SocketException occurs, the connection is reset, and the command is
249
     * re-attempted once.
250
     *
251
     * @throws Exception\ClientException
252
     * @param Command $command
253
     *
254
     * @return mixed
255
     */
256 18
    private function _dispatch($command)
257
    {
258 18
        return $this->connection->dispatchCommand($command);
259
    }
260
261
    /**
262
     * {@inheritdoc}
263
     */
264 6
    public function create(Workflow $workflow, $force = false): Workflow
265
    {
266
        try {
267 6
            $tubes = [];
268
            /** @var Job $job */
269 6
            foreach ($workflow->getJobs() as $job) {
270
                /** @var Task $task */
271 6
                foreach ($job->getTasks() as $task) {
272 6
                    $tubes = array_merge($tubes, [$task->getQueue()]);
273
                }
274
            }
275 6
            foreach ($tubes as $tube) {
276 6
                if (!$this->getCurrentClass()->tubeExists($tube)) {
277 1
                    $this->getCurrentClass()->createTube(new Tube($tube, 1));
278
                };
279
            }
280 6
            $workflow = $this->_dispatch(new Command\CreateCommand($workflow));
281 3
        } catch (ServerDuplicateEntryException $e) {
282 3
            if ($force) {
283 3
                $workflows = $this->_dispatch(new Command\ListWorkflowsCommand());
284
                $workflowToDelete = $workflows->filter(function(Workflow $listedWorkflow) use ($workflow) {
285 3
                    return $listedWorkflow->getName() === $workflow->getName()
286 3
                        && $listedWorkflow->getGroup() === $workflow->getGroup();
287 3
                })->first();
288 3
                $this->getCurrentClass()->delete($workflowToDelete);
289
290 3
                return $this->getCurrentClass()->create($workflow);
291
            }
292 1
            throw $e;
293
        }
294
295 6
        return $workflow;
296
    }
297
298
    /**
299
     * {@inheritdoc}
300
     */
301 1
    public function update(Workflow $workflow): Workflow
302
    {
303 1
        $workflow = $this->_dispatch(new Command\UpdateCommand($workflow));
304 1
        return $workflow;
305
    }
306
307
    /**
308
     * {@inheritdoc}
309
     */
310 1
    public function updateSchedule(Schedule $schedule): Schedule
311
    {
312 1
        $schedule = $this->_dispatch(new Command\UpdateScheduleCommand($schedule));
313 1
        return $schedule;
314
    }
315
316
    /**
317
     * {@inheritdoc}
318
     */
319 1
    public function createSchedule(Schedule $schedule)
320
    {
321 1
        $workflowSchedule = $this->_dispatch(
322 1
            new Command\CreateScheduleCommand($schedule)
323
        );
324 1
        return $workflowSchedule;
325
    }
326
327
    /**
328
     * {@inheritdoc}
329
     */
330 5
    public function createTask(string $name, string $group, string $path, $queue = 'default', $useAgent = false, $user = null, $host = null, $comment = null): Workflow
331
    {
332 5
        $task = new Task($path, $queue, $useAgent, $user, $host);
333 5
        $job = new Job(new ArrayCollection([$task]));
334 5
        $workflow = new Workflow($name, $group, new ArrayCollection([$job]), $comment);
335
336 5
        return $this->getCurrentClass()->create($workflow, true);
337
    }
338
339
    /**
340
     * {@inheritDoc}
341
     */
342 1
    public function createTube(Tube $tube): Tube
343
    {
344 1
        return $this->_dispatch(new Command\CreateTubeCommand($tube));
345
    }
346
347
    /**
348
     * {@inheritdoc}
349
     */
350 1
    public function updateTube(Tube $tube): Tube
351
    {
352 1
        return $this->_dispatch(new Command\UpdateTubeCommand($tube));
353
    }
354
355
    /**
356
     * {@inheritdoc}
357
     */
358 1
    public function cancel(WorkflowInstance $workflowInstance)
359
    {
360 1
        return $this->_dispatch(new Command\CancelCommand($workflowInstance));
361
    }
362
363
    /**
364
     * {@inheritdoc}
365
     */
366 2
    public function kill(WorkflowInstance $workflowInstance, TaskInstance $taskInstance)
367
    {
368 2
        return $this->_dispatch(new Command\KillCommand($workflowInstance, $taskInstance));
369
    }
370
}
371