Passed
Pull Request — master (#28)
by Valentin
04:19
created

Pheanstalk   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 322
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 42
eloc 74
c 2
b 0
f 0
dl 0
loc 322
ccs 98
cts 98
cp 1
rs 9.0399

29 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 createTask() 0 7 1
A tubeExists() 0 3 1
A createSchedule() 0 6 1
A createTube() 0 3 1
A peek() 0 5 1
A _dispatch() 0 3 1
A getWorkflowInstancesDetails() 0 3 1
A getWorkflow() 0 3 1
A cancel() 0 3 1
A update() 0 4 1
A listTubes() 0 3 1
A kill() 0 3 1
A stats() 0 3 1
B create() 0 32 8
A updateTube() 0 3 1
A listSchedules() 0 3 1
A statsTube() 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 20
    public function __construct($host, $user = null, $password = null, $port = PheanstalkInterface::DEFAULT_PORT, $connectTimeout = null, $connectPersistent = false)
56
    {
57 20
        $this->setConnection(new Connection($host, $user, $password, $port, $connectTimeout, $connectPersistent));
58
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63 20
    public function setConnection(Connection $connection)
64
    {
65 20
        $this->connection = $connection;
66
67 20
        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 13
    public function getCurrentClass(): PheanstalkInterface
82
    {
83 13
        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 5
    public function delete(Workflow $workflow)
111
    {
112 5
        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 4
    public function getWorkflowInstances(?Workflow $workflow, string $status = null)
163
    {
164 4
        $paramsStatus = empty($status) ? GetWorkflowInstancesDetailCommand::FILTERS : [$status];
165 4
        $instances = new ArrayCollection([]);
166 4
        foreach ($paramsStatus as $stat) {
167 4
            $instances[strtolower($stat)] = $this->_dispatch(new Command\GetWorkflowInstancesCommand($workflow, $stat));
168
                /** @var ArrayCollection $workflowCollection */
169 4
                $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 4
            if (!empty($workflowCollection)) {
171 3
                foreach ($workflowCollection as $instance) {
172 3
                    $this->getCurrentClass()->getWorkflowInstancesDetails($instance);
173
                }
174
            }
175
        }
176 4
        if (!is_null($status)) {
177 2
            return $instances->get(strtolower($status))->get('workflow_instances');
178
        }
179
180 2
        return $instances;
181
    }
182
183
    /**
184
     * {@inheritdoc}
185
     */
186 3
    public function getWorkflowInstancesDetails(WorkflowInstance $workflowInstance)
187
    {
188 3
        return $this->_dispatch(new Command\GetWorkflowInstancesDetailCommand($workflowInstance));
189
    }
190
191
    /**
192
     * {@inheritdoc}
193
     */
194 8
    public function tubeExists($name)
195
    {
196 8
        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 5
    public function put(Workflow $workflow)
221
    {
222 5
        $response = $this->_dispatch(new Command\PutCommand($workflow));
223
224 5
        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 17
    private function _dispatch($command)
257
    {
258 17
        return $this->connection->dispatchCommand($command);
259
    }
260
261
    /**
262
     * {@inheritdoc}
263
     */
264 5
    public function create(Workflow $workflow, $force = false): Workflow
265
    {
266
        try {
267 5
            $tubes = [];
268
            /** @var Job $job */
269 5
            foreach ($workflow->getJobs() as $job) {
270
                /** @var Task $task */
271 5
                foreach ($job->getTasks() as $task) {
272 5
                    $tubes = array_merge($tubes, [$task->getQueue()]);
273
                }
274
            }
275 5
            foreach ($tubes as $tube) {
276 5
                if (!$this->getCurrentClass()->tubeExists($tube)) {
277 1
                    $this->getCurrentClass()->createTube(new Tube($tube, 1));
278
                };
279
            }
280 5
            $workflow = $this->_dispatch(new Command\CreateCommand($workflow));
281 2
        } catch (ServerDuplicateEntryException $e) {
282 2
            if ($force) {
283 2
                $workflows = $this->_dispatch(new Command\ListWorkflowsCommand());
284
                $workflowToDelete = $workflows->filter(function(Workflow $listedWorkflow) use ($workflow) {
285 2
                    return $listedWorkflow->getName() === $workflow->getName()
286 2
                        && $listedWorkflow->getGroup() === $workflow->getGroup();
287 2
                })->first();
288 2
                $this->getCurrentClass()->delete($workflowToDelete);
289
290 2
                return $this->getCurrentClass()->create($workflow);
291
            }
292 1
            throw $e;
293
        }
294
295 5
        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 createSchedule(Schedule $schedule)
311
    {
312 1
        $workflowSchedule = $this->_dispatch(
313 1
            new Command\CreateScheduleCommand($schedule)
314
        );
315 1
        return $workflowSchedule;
316
    }
317
318
    /**
319
     * {@inheritdoc}
320
     */
321 4
    public function createTask(string $name, string $group, string $path, $queue = 'default', $useAgent = false, $user = null, $host = null, $comment = null): Workflow
322
    {
323 4
        $task = new Task($path, $queue, $useAgent, $user, $host);
324 4
        $job = new Job(new ArrayCollection([$task]));
325 4
        $workflow = new Workflow($name, $group, new ArrayCollection([$job]), $comment);
326
327 4
        return $this->getCurrentClass()->create($workflow, true);
328
    }
329
330
    /**
331
     * {@inheritDoc}
332
     */
333 1
    public function createTube(Tube $tube): Tube
334
    {
335 1
        return $this->_dispatch(new Command\CreateTubeCommand($tube));
336
    }
337
338
    /**
339
     * {@inheritdoc}
340
     */
341 1
    public function updateTube(Tube $tube): Tube
342
    {
343 1
        return $this->_dispatch(new Command\UpdateTubeCommand($tube));
344
    }
345
346
    /**
347
     * {@inheritdoc}
348
     */
349 1
    public function cancel(WorkflowInstance $workflowInstance)
350
    {
351 1
        return $this->_dispatch(new Command\CancelCommand($workflowInstance));
352
    }
353
354
    /**
355
     * {@inheritdoc}
356
     */
357 1
    public function kill(WorkflowInstance $workflowInstance, TaskInstance $taskInstance)
358
    {
359 1
        return $this->_dispatch(new Command\KillCommand($workflowInstance, $taskInstance));
360
    }
361
}
362