Completed
Push — master ( 06ef81...2fa00d )
by Greg
04:00
created

Result::ensureResult()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 19
rs 8.8571
c 1
b 0
f 0
cc 6
eloc 12
nc 6
nop 2
1
<?php
2
namespace Robo;
3
4
use Robo\Contract\TaskInterface;
5
use Robo\Exception\TaskExitException;
6
use Robo\State\Data;
7
8
class Result extends ResultData
9
{
10
    /**
11
     * @var bool
12
     */
13
    public static $stopOnFail = false;
14
15
    /**
16
     * @var \Robo\Contract\TaskInterface
17
     */
18
    protected $task;
19
20
    /**
21
     * @param \Robo\Contract\TaskInterface $task
22
     * @param string $exitCode
23
     * @param string $message
24
     * @param array $data
25
     */
26
    public function __construct(TaskInterface $task, $exitCode, $message = '', $data = [])
27
    {
28
        parent::__construct($exitCode, $message, $data);
29
        $this->task = $task;
30
        $this->printResult();
31
32
        if (self::$stopOnFail) {
33
            $this->stopOnFail();
34
        }
35
    }
36
37
    /**
38
     * Tasks should always return a Result. However, they are also
39
     * allowed to return NULL or an array to indicate success.
40
     */
41
    public static function ensureResult($task, $result)
42
    {
43
        if ($result instanceof Result) {
44
            return $result;
45
        }
46
        if (!isset($result)) {
47
            return static::success($task);
48
        }
49
        if ($result instanceof Data) {
50
            return static::success($task, $result->getMessage(), $result->getData());
51
        }
52
        if ($result instanceof ResultData) {
53
            return new Result($task, $result->getExitCode(), $result->getMessage(), $result->getData());
54
        }
55
        if (is_array($result)) {
56
            return static::success($task, '', $result);
57
        }
58
        throw new \Exception(sprintf('Task %s returned a %s instead of a \Robo\Result.', get_class($task), get_class($result)));
59
    }
60
61
    protected function printResult()
62
    {
63
        // For historic reasons, the Result constructor is responsible
64
        // for printing task results.
65
        // TODO: Make IO the responsibility of some other class. Maintaining
66
        // existing behavior for backwards compatibility. This is undesirable
67
        // in the long run, though, as it can result in unwanted repeated input
68
        // in task collections et. al.
69
        $resultPrinter = Robo::resultPrinter();
70
        if ($resultPrinter) {
71
            if ($resultPrinter->printResult($this)) {
72
                $this->data['already-printed'] = true;
0 ignored issues
show
Bug introduced by
The property data 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...
73
            }
74
        }
75
    }
76
77
    /**
78
     * @param \Robo\Contract\TaskInterface $task
79
     * @param string $extension
80
     * @param string $service
81
     *
82
     * @return \Robo\Result
83
     */
84
    public static function errorMissingExtension(TaskInterface $task, $extension, $service)
85
    {
86
        $messageTpl = 'PHP extension required for %s. Please enable %s';
87
        $message = sprintf($messageTpl, $service, $extension);
88
89
        return self::error($task, $message);
90
    }
91
92
    /**
93
     * @param \Robo\Contract\TaskInterface $task
94
     * @param string $class
95
     * @param string $package
96
     *
97
     * @return \Robo\Result
98
     */
99
    public static function errorMissingPackage(TaskInterface $task, $class, $package)
100
    {
101
        $messageTpl = 'Class %s not found. Please install %s Composer package';
102
        $message = sprintf($messageTpl, $class, $package);
103
104
        return self::error($task, $message);
105
    }
106
107
    /**
108
     * @param \Robo\Contract\TaskInterface $task
109
     * @param string $message
110
     * @param array $data
111
     *
112
     * @return \Robo\Result
113
     */
114
    public static function error(TaskInterface $task, $message, $data = [])
115
    {
116
        return new self($task, self::EXITCODE_ERROR, $message, $data);
117
    }
118
119
    /**
120
     * @param \Robo\Contract\TaskInterface $task
121
     * @param \Exception $e
122
     * @param array $data
123
     *
124
     * @return \Robo\Result
125
     */
126
    public static function fromException(TaskInterface $task, \Exception $e, $data = [])
127
    {
128
        $exitCode = $e->getCode();
129
        if (!$exitCode) {
130
            $exitCode = self::EXITCODE_ERROR;
131
        }
132
        return new self($task, $exitCode, $e->getMessage(), $data);
133
    }
134
135
    /**
136
     * @param \Robo\Contract\TaskInterface $task
137
     * @param string $message
138
     * @param array $data
139
     *
140
     * @return \Robo\Result
141
     */
142
    public static function success(TaskInterface $task, $message = '', $data = [])
143
    {
144
        return new self($task, self::EXITCODE_OK, $message, $data);
145
    }
146
147
    /**
148
     * Return a context useful for logging messages.
149
     *
150
     * @return array
151
     */
152
    public function getContext()
153
    {
154
        $task = $this->getTask();
155
156
        return TaskInfo::getTaskContext($task) + [
157
            'code' => $this->getExitCode(),
158
            'data' => $this->getArrayCopy(),
159
            'time' => $this->getExecutionTime(),
160
            'message' => $this->getMessage(),
161
        ];
162
    }
163
164
    /**
165
     * Add the results from the most recent task to the accumulated
166
     * results from all tasks that have run so far, merging data
167
     * as necessary.
168
     *
169
     * @param int|string $key
170
     * @param \Robo\Result $taskResult
171
     */
172
    public function accumulate($key, Result $taskResult)
173
    {
174
        // If the task is unnamed, then all of its data elements
175
        // just get merged in at the top-level of the final Result object.
176
        if (static::isUnnamed($key)) {
177
            $this->merge($taskResult);
178
        } elseif (isset($this[$key])) {
179
            // There can only be one task with a given name; however, if
180
            // there are tasks added 'before' or 'after' the named task,
181
            // then the results from these will be stored under the same
182
            // name unless they are given a name of their own when added.
183
            $current = $this[$key];
184
            $this[$key] = $taskResult->merge($current);
185
        } else {
186
            $this[$key] = $taskResult;
187
        }
188
    }
189
190
    /**
191
     * We assume that named values (e.g. for associative array keys)
192
     * are non-numeric; numeric keys are presumed to simply be the
193
     * index of an array, and therefore insignificant.
194
     *
195
     * @param int|string $key
196
     *
197
     * @return bool
198
     */
199
    public static function isUnnamed($key)
200
    {
201
        return is_numeric($key);
202
    }
203
204
    /**
205
     * @return \Robo\Contract\TaskInterface
206
     */
207
    public function getTask()
208
    {
209
        return $this->task;
210
    }
211
212
    /**
213
     * @return \Robo\Contract\TaskInterface
214
     */
215
    public function cloneTask()
216
    {
217
        $reflect  = new \ReflectionClass(get_class($this->task));
218
        return $reflect->newInstanceArgs(func_get_args());
219
    }
220
221
    /**
222
     * @return bool
223
     *
224
     * @deprecated since 1.0.
225
     *
226
     * @see wasSuccessful()
227
     */
228
    public function __invoke()
229
    {
230
        trigger_error(__METHOD__ . ' is deprecated: use wasSuccessful() instead.', E_USER_DEPRECATED);
231
        return $this->wasSuccessful();
232
    }
233
234
    /**
235
     * @return $this
236
     */
237
    public function stopOnFail()
238
    {
239
        if (!$this->wasSuccessful()) {
240
            $resultPrinter = Robo::resultPrinter();
241
            if ($resultPrinter) {
242
                $resultPrinter->printStopOnFail($this);
243
            }
244
            $this->exitEarly($this->getExitCode());
245
        }
246
        return $this;
247
    }
248
249
    /**
250
     * @param int $status
251
     *
252
     * @throws \Robo\Exception\TaskExitException
253
     */
254
    private function exitEarly($status)
255
    {
256
        throw new TaskExitException($this->getTask(), $this->getMessage(), $status);
257
    }
258
}
259