Completed
Push — master ( f7f76b...b77c5c )
by Jeroen
11s
created

TaskRunner::insertTask()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
3
/*
4
 * This file is part of the Conveyor package.
5
 *
6
 * (c) Jeroen Fiege <[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
namespace Webcreate\Conveyor\Task;
13
14
use Symfony\Component\EventDispatcher\Event;
15
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
16
use Symfony\Component\EventDispatcher\GenericEvent;
17
use Webcreate\Conveyor\DependencyInjection\TransporterAwareInterface;
18
use Webcreate\Conveyor\Event\TaskRunnerEvents;
19
use Webcreate\Conveyor\IO\IOInterface;
20
use Webcreate\Conveyor\Repository\Version;
21
22
/**
23
 * @todo remove Transporter dependency because it is not related to running tasks!
24
 */
25
class TaskRunner implements TransporterAwareInterface
26
{
27
    /**
28
     * @var Task[]
29
     */
30
    protected $tasks = array();
31
    protected $needsNewline = false;
32
    protected $transporter;
33
    protected $dispatcher;
34
35
    /**
36
     * Constructor.
37
     *
38
     * @todo I think it's better NOT to have the taskrunner depend on the IOinterface
39
     *
40
     * @param IOInterface              $io
41
     * @param EventDispatcherInterface $dispatcher
42
     */
43 3
    public function __construct(IOInterface $io, EventDispatcherInterface $dispatcher = null)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $io. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
44
    {
45 3
        $this->io = $io;
0 ignored issues
show
Bug introduced by
The property io does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
46 3
        $this->dispatcher = $dispatcher;
47 3
    }
48
49
    public function addTask(Task $task)
50
    {
51
        $this->tasks[] = $task;
52
    }
53
54
    public function insertTask(Task $task)
55
    {
56
        array_unshift($this->tasks, $task);
57
    }
58
59
    public function hasTasks()
60
    {
61
        return (count($this->tasks) > 0);
62
    }
63
64
    /**
65
     * @param  Task[] $tasks
66
     * @return $this
67
     */
68 3
    public function setTasks(array $tasks)
69
    {
70 3
        $this->tasks = $tasks;
71
72 3
        return $this;
73
    }
74
75
    /**
76
     * @return Task[]
77
     */
78 2
    public function getTasks()
79
    {
80 2
        return $this->tasks;
81
    }
82
83
    public function setTransporter($transporter)
84
    {
85
        $this->transporter = $transporter;
86
    }
87
88
    /**
89
     * @param string $target
90
     * @param \Webcreate\Conveyor\Repository\Version $version
91
     */
92 2
    public function execute($target, Version $version)
93
    {
94 2
        $total = count($this->tasks);
95
96 2
        foreach ($this->tasks as $i => $task) {
97 2
            while (true) {
98 2
                $this->dispatch(TaskRunnerEvents::TASKRUNNER_PRE_EXECUTE_TASK,
99 2
                    new GenericEvent($task, array('index' => $i, 'total' => $total))
100 2
                );
101
102 2
                $result = null;
103
104
                try {
105 2
                    if ($task instanceof TransporterAwareInterface) {
106
                        $task->setTransporter($this->transporter);
107
                    }
108
109 2
                    $result = $task->execute($target, $version);
110
111 2
                    break;
112
                } catch (\Exception $e) {
113
                    $this->io->renderException($e);
114
115
                    // @todo instead of relying on the IOInterface here for asking an action,
116
                    //       better to trigger an event and have the listener ask an action.
117
                    if (false === $this->tryAgain()) {
118
                        break;
119
                    }
120
                }
121
122
                // @todo might be better to trigger a different event here, smt like TASKRUNNER_RETRY_EXECUTE_TASK
123
                $this->dispatch(TaskRunnerEvents::TASKRUNNER_POST_EXECUTE_TASK,
124
                    new GenericEvent($task, array('index' => $i, 'total' => $total, 'result' => $result))
125
                );
126
            }
127
128 2
            $this->dispatch(TaskRunnerEvents::TASKRUNNER_POST_EXECUTE_TASK,
129 2
                new GenericEvent($task, array('index' => $i, 'total' => $total, 'result' => $result))
0 ignored issues
show
Bug introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
130 2
            );
131 2
        }
132 2
    }
133
134
    /**
135
     * @param string $target
136
     * @param \Webcreate\Conveyor\Repository\Version $version
137
     */
138
    public function simulate($target, $version)
139
    {
140
        $io = $this->io;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $io. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
141
142
        foreach ($this->tasks as $i => $task) {
143
            if ($i > 0) {
144
                if (true === $this->needsNewline) {
145
                    $this->io->write('');
146
                    $this->needsNewline = false;
147
                }
148
                $this->io->write('');
149
            }
150
151
            $this->io->write(sprintf('- Simulating task <info>%s</info>', get_class($task)));
152
            $this->io->increaseIndention(2);
153
154
            $self = $this;
155
156
            $task->setOutput(function ($output) use ($io, $self) {
157
                $io->overwrite(sprintf('%s', $output), false);
158
159
                $self->needsNewline = true;
160
            });
161
162
            $task->simulate($target, $version);
163
164
            $this->io->decreaseIndention(2);
165
        }
166
167
        if (true === $this->needsNewline) {
168
            $this->io->write('');
169
            $this->needsNewline = false;
170
        }
171
    }
172
173
    protected function tryAgain()
174
    {
175
        while (true) {
176
            try {
177
                if (!$this->io->isInteractive()) {
178
                    $answer = 'a';
179
                } else {
180
                    $answer = $this->io->select(
181
                        '<info>Select an action</info>',
182
                        array(
183
                            'a' => 'abort',
184
                            'r' => 'retry this task',
185
                            's' => 'skip this task and continue with the next',
186
                        ),
187
                        'r',
188
                        5
189
                    );
190
                }
191
            } catch (\RuntimeException $e) {
192
                // can happen when there is no stty available
193
                $answer = "a";
194
            }
195
196 View Code Duplication
            switch ($answer) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
197
                case "a":
198
                    $this->io->setIndention(0);
199
                    $this->io->write('Aborted.');
200
                    die();
0 ignored issues
show
Coding Style Compatibility introduced by
The method tryAgain() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
201
                    break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
202
                case "r":
203
                    return true;
204
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
205
                case "s":
206
                    return false;
207
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
208
            }
209
        }
210
211
        return true;
212
    }
213
214
    /**
215
     * Dispatch event when a dispatcher is available
216
     *
217
     * @param string $eventName
218
     * @param Event  $event
219
     */
220 2
    protected function dispatch($eventName, Event $event = null)
221
    {
222 2
        if (null !== $this->dispatcher) {
223 2
            $this->dispatcher->dispatch($eventName, $event);
224 2
        }
225 2
    }
226
227 3
    public function getDispatcher()
228
    {
229 3
        return $this->dispatcher;
230
    }
231
}
232