TasksHandler   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 265
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 96.15%

Importance

Changes 0
Metric Value
wmc 31
lcom 1
cbo 11
dl 0
loc 265
ccs 75
cts 78
cp 0.9615
rs 9.8
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __call() 0 10 2
A configureStrategy() 0 4 1
A task() 0 4 1
A command() 0 15 3
A before() 0 4 1
A after() 0 4 1
A clearRegisteredEvents() 0 5 1
B listenTo() 0 15 5
B addTaskListeners() 0 28 5
B getTasksListeners() 0 18 7
A delegateAndRebind() 0 8 1
A getEventHandle() 0 8 2
A add() 0 11 1
1
<?php
2
3
/*
4
 * This file is part of Rocketeer
5
 *
6
 * (c) Maxime Fabre <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 */
12
13
namespace Rocketeer\Services\Tasks;
14
15
use Closure;
16
use Illuminate\Support\Str;
17
use Rocketeer\Console\Commands\AbstractCommand;
18
use Rocketeer\Console\Commands\BaseTaskCommand;
19
use Rocketeer\Interfaces\IdentifierInterface;
20
use Rocketeer\Services\Container\Container;
21
use Rocketeer\Tasks\AbstractTask;
22
use Rocketeer\Tasks\Closure as ClosureTask;
23
use Rocketeer\Traits\ContainerAwareTrait;
24
25
/**
26
 * Handles the registering and firing of tasks and their events.
27
 */
28
class TasksHandler
29
{
30
    use ContainerAwareTrait;
31
32
    /**
33
     * Delegate methods to TasksQueue for now to
34
     * keep public API intact.
35
     *
36
     * @param string $method
37
     * @param array  $arguments
38
     *
39
     * @return mixed
40
     */
41 1
    public function __call($method, $arguments)
42
    {
43
        // Delegate calls to TasksQueue for facade purposes
44 1
        if (method_exists($this->queue, $method)) {
45
            return $this->queue->$method(...$arguments);
46
        }
47
48
        // Else we execute actions on the task
49 1
        $this->delegateAndRebind($method, $arguments, 'buildTask');
50 1
    }
51
52
    /**
53
     * Configure a strategy.
54
     *
55
     * @param array ...$arguments
56
     */
57 2
    public function configureStrategy(...$arguments)
58
    {
59 2
        $this->delegateAndRebind('configure', $arguments, 'buildStrategy');
60 2
    }
61
62
    ////////////////////////////////////////////////////////////////////
63
    ///////////////////////////// REGISTRATION /////////////////////////
64
    ////////////////////////////////////////////////////////////////////
65
66
    /**
67
     * Register a custom task or command with Rocketeer.
68
     *
69
     * @param string|Closure|AbstractTask $task
70
     * @param string|null                 $name
71
     * @param string|null                 $description
72
     *
73
     * @return BaseTaskCommand
74
     */
75 71
    public function add($task, $name = null, $description = null)
76
    {
77
        // Build task if necessary
78 71
        $task = $this->builder->buildTask($task, $name, $description);
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method buildTask does not exist on object<Rocketeer\Services\Builders\Builder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
79 70
        $this->container->add('rocketeer.'.$task->getIdentifier(), $task);
80
81
        // Add related command
82 70
        $bound = $this->command($task->getSlug(), new BaseTaskCommand($task));
0 ignored issues
show
Documentation introduced by Maxime Fabre
new \Rocketeer\Console\C...\BaseTaskCommand($task) is of type object<Rocketeer\Console...mmands\BaseTaskCommand>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
83
84 70
        return $bound;
85
    }
86
87
    /**
88
     * Register a task with Rocketeer.
89
     *
90
     * @param string                                            $name
91
     * @param string|Closure|\Rocketeer\Tasks\AbstractTask|null $task
92
     * @param string|null                                       $description
93
     *
94
     * @return BaseTaskCommand
0 ignored issues
show
Documentation introduced by Maxime Fabre
Should the return type not be AbstractTask?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
95
     */
96 69
    public function task($name, $task = null, $description = null)
97
    {
98 69
        return $this->add($task, $name, $description)->getTask();
0 ignored issues
show
Bug introduced by Maxime Fabre
It seems like $task defined by parameter $task on line 96 can also be of type null; however, Rocketeer\Services\Tasks\TasksHandler::add() does only seem to accept string|object<Closure>|o...eer\Tasks\AbstractTask>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
99
    }
100
101
    /**
102
     * @param string $name
103
     * @param string $command
104
     *
105
     * @return AbstractCommand
106
     */
107 71
    public function command($name, $command)
108
    {
109
        /** @var AbstractCommand $command */
110 71
        $command = is_string($command) ? new $command() : $command;
111 71
        $command->setContainer($this->container);
112
113
        // Set name
114 71
        $namespace = $this->config->get('application_name');
115 71
        $name = $name ?: $command->getName();
116 71
        $command->setName($namespace.':'.$name);
117
118 71
        $this->console->add($command);
119
120 71
        return $command;
121
    }
122
123
    ////////////////////////////////////////////////////////////////////
124
    /////////////////////////////// EVENTS /////////////////////////////
125
    ////////////////////////////////////////////////////////////////////
126
127
    /**
128
     * Execute a task before another one.
129
     *
130
     * @param string  $task
131
     * @param Closure $listeners
132
     * @param int     $priority
133
     */
134 5
    public function before($task, $listeners, $priority = 0)
135
    {
136 5
        $this->addTaskListeners($task, 'before', $listeners, $priority);
137 5
    }
138
139
    /**
140
     * Execute a task after another one.
141
     *
142
     * @param string  $task
143
     * @param Closure $listeners
144
     * @param int     $priority
145
     */
146 4
    public function after($task, $listeners, $priority = 0)
147
    {
148 4
        $this->addTaskListeners($task, 'after', $listeners, $priority);
149 4
    }
150
151
    /**
152
     * Clear the previously registered events.
153
     */
154 442
    public function clearRegisteredEvents()
155
    {
156 442
        $this->events->removeListenersWithTag('plugins');
157 442
        $this->events->removeListenersWithTag('hooks');
158 442
    }
159
160
    /**
161
     * Register listeners for a particular event.
162
     *
163
     * @param string         $event
164
     * @param array|callable $listeners
165
     * @param int            $priority
166
     *
167
     * @return string
168
     */
169 98
    public function listenTo($event, $listeners, $priority = 0)
170
    {
171
        /** @var \Rocketeer\Tasks\AbstractTask[] $listeners */
172 98
        $listeners = $this->builder->isCallable($listeners) ? [$listeners] : (array) $listeners;
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method isCallable does not exist on object<Rocketeer\Services\Builders\Builder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
173 98
        $listeners = $this->builder->buildTasks($listeners);
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method buildTasks does not exist on object<Rocketeer\Services\Builders\Builder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
174 98
        $event = Str::contains($event, ['commands.', 'strategies.', 'tasks.']) ? $event : 'tasks.'.$event;
175
176
        // Register events
177 98
        foreach ($listeners as $key => $listener) {
178 98
            $handle = $this->getEventHandle(null, $event);
179 98
            $this->events->addListener($handle, $listener, $priority ?: -$key);
180 98
        }
181
182 98
        return $event;
183
    }
184
185
    /**
186
     * Bind a listener to a task.
187
     *
188
     * @param string|array   $task
189
     * @param string         $event
190
     * @param array|callable $listeners
191
     * @param int            $priority
192
     *
193
     * @throws \Rocketeer\Services\Builders\TaskCompositionException
194
     *
195
     * @return string|null
196
     */
197 83
    public function addTaskListeners($task, $event, $listeners, $priority = 0)
198
    {
199
        // Recursive call
200 83
        if (is_array($task)) {
201 1
            foreach ($task as $t) {
202 1
                $this->addTaskListeners($t, $event, $listeners, $priority);
203 1
            }
204
205 1
            return;
206
        }
207
208
        // Cancel if no listeners
209 83
        if (!$listeners) {
210
            return;
211
        }
212
213
        // Prevent events on anonymous tasks
214 83
        $task = $this->builder->buildTask($task);
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method buildTask does not exist on object<Rocketeer\Services\Builders\Builder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
215 83
        if ($task->getSlug() === 'closure') {
216
            return;
217
        }
218
219
        // Get event name and register listeners
220 83
        $event = $this->getEventHandle($task, $event);
221 83
        $event = $this->listenTo($event, $listeners, $priority);
222
223 83
        return $event;
224
    }
225
226
    /**
227
     * Get all of a task's listeners.
228
     *
229
     * @param string|AbstractTask $task
230
     * @param string              $event
231
     * @param bool                $flatten
232
     *
233
     * @return array
0 ignored issues
show
Documentation introduced by Maxime Fabre
Should the return type not be \League\Event\ListenerIn...ent\ListenerInterface[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
234
     */
235 13
    public function getTasksListeners($task, $event, $flatten = false)
236
    {
237
        // Get events
238 13
        $task = $this->builder->buildTaskFromClass($task);
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method buildTaskFromClass does not exist on object<Rocketeer\Services\Builders\Builder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
239 13
        $handle = $this->getEventHandle($task, $event);
240 13
        $events = $this->events->getListeners($handle);
241
242
        // Flatten the queue if requested
243 13
        foreach ($events as $key => $listener) {
0 ignored issues
show
Bug introduced by Maxime Fabre
The expression $events of type object<League\Event\List...ent\ListenerInterface>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
244 13
            if ($flatten && $listener instanceof ClosureTask && $stringTask = $listener->getStringTask()) {
245 11
                $events[$key] = $stringTask;
246 13
            } elseif ($flatten && $listener instanceof AbstractTask) {
247 2
                $events[$key] = $listener->getSlug();
248 2
            }
249 13
        }
250
251 13
        return $events;
252
    }
253
254
    //////////////////////////////////////////////////////////////////////
255
    ////////////////////////////// HELPERS ///////////////////////////////
256
    //////////////////////////////////////////////////////////////////////
257
258
    /**
259
     * Call a method on an object, and rebind it into the container.
260
     *
261
     * @param string $method
262
     * @param array  $parameters
263
     * @param string $builder
264
     *
265
     * @throws \Rocketeer\Services\Builders\TaskCompositionException
266
     */
267 3
    protected function delegateAndRebind($method, array $parameters, $builder)
268
    {
269 3
        $object = (array) array_shift($parameters);
270 3
        $object = $this->builder->$builder(...$object);
271 3
        $object->$method(...$parameters);
272
273 3
        $this->container->add('rocketeer.'.$object->getIdentifier(), $object);
274 3
    }
275
276
    /**
277
     * Get the handle of an event.
278
     *
279
     * @param IdentifierInterface|null $entity
280
     * @param string|null              $event
281
     *
282
     * @return string
283
     */
284 124
    public function getEventHandle(IdentifierInterface $entity = null, $event = null)
285
    {
286
        // Concatenate identifier and event if it's not already done
287 124
        $event = $entity ? $entity->getIdentifier().'.'.$event : $event;
288 124
        $event = str_replace('rocketeer.', null, $event);
289
290 124
        return 'rocketeer.'.$event;
291
    }
292
}
293