Completed
Push — master ( 0e105c...39986e )
by Christian
02:52
created

Task::getCreatedAt()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/**
4
 * This file is part of tenside/core.
5
 *
6
 * (c) Christian Schiffler <[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
 * This project is provided in good faith and hope to be usable by anyone.
12
 *
13
 * @package    tenside/core
14
 * @author     Christian Schiffler <[email protected]>
15
 * @copyright  2015 Christian Schiffler <[email protected]>
16
 * @license    https://github.com/tenside/core/blob/master/LICENSE MIT
17
 * @link       https://github.com/tenside/core
18
 * @filesource
19
 */
20
21
namespace Tenside\Core\Task;
22
23
use Composer\IO\ConsoleIO;
24
use Composer\IO\IOInterface;
25
use Symfony\Component\Console\Helper\HelperSet;
26
use Symfony\Component\Console\Input\ArrayInput;
27
use Symfony\Component\Console\Input\InputInterface;
28
use Tenside\Core\Util\JsonArray;
29
30
/**
31
 * Abstract base class for tasks.
32
 */
33
abstract class Task
1 ignored issue
show
Coding Style introduced by
Task does not seem to conform to the naming convention (^Abstract|Factory$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
34
{
35
    /**
36
     * The type of the task.
37
     */
38
    const SETTING_TYPE = 'type';
39
40
    /**
41
     * The id of the task.
42
     */
43
    const SETTING_ID = 'id';
44
45
    /**
46
     * The ISO 8601 date when the task got created.
47
     */
48
    const SETTING_CREATED_AT = 'created-at';
49
50
    /**
51
     * This state determines that the task is still awaiting to be executed.
52
     */
53
    const STATE_PENDING = 'PENDING';
54
55
    /**
56
     * This state determines that the task is still running.
57
     */
58
    const STATE_RUNNING = 'RUNNING';
59
60
    /**
61
     * This state determines that the task has been finished.
62
     */
63
    const STATE_FINISHED = 'FINISHED';
64
65
    /**
66
     * This state determines that the task has been finished with errors.
67
     */
68
    const STATE_ERROR = 'ERROR';
69
70
    /**
71
     * The task file to write to.
72
     *
73
     * @var JsonArray
74
     */
75
    protected $file;
76
77
    /**
78
     * The log file to write to.
79
     *
80
     * @var string
81
     */
82
    protected $logFile;
83
84
    /**
85
     * The input/output handler.
86
     *
87
     * @var IOInterface
88
     */
89
    private $inputOutput;
90
91
    /**
92
     * Task constructor.
93
     *
94
     * @param JsonArray $file The json file to write to.
95
     */
96
    public function __construct(JsonArray $file)
97
    {
98
        $this->file = $file;
99
100
        if ($this->file->has('log')) {
101
            $this->logFile = $this->file->get('log');
1 ignored issue
show
Documentation Bug introduced by
It seems like $this->file->get('log') can also be of type array or integer. However, the property $logFile is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
102
        }
103
    }
104
105
    /**
106
     * Retrieve the task id.
107
     *
108
     * @return string
1 ignored issue
show
Documentation introduced by
Should the return type not be array|string|integer|null? Also, consider making the array more specific, something like array<String>, or String[].

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.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
109
     */
110
    public function getId()
111
    {
112
        return $this->file->get('id');
113
    }
114
115
    /**
116
     * Retrieve the current output.
117
     *
118
     * @param null|int $offset The offset in bytes to read from.
119
     *
120
     * @return string
121
     */
122
    public function getOutput($offset = null)
123
    {
124
        if (!$this->logFile) {
125
            return '';
126
        }
127
128
        return (string) file_get_contents($this->logFile, FILE_BINARY, null, $offset);
129
    }
130
131
    /**
132
     * Retrieve the task type name.
133
     *
134
     * @return string
135
     */
136
    abstract public function getType();
137
138
    /**
139
     * Perform the task.
140
     *
141
     * @param string $logFile The log file to write to.
142
     *
143
     * @return void
144
     *
145
     * @throws \LogicException   When the task has already been run.
146
     *
147
     * @throws \RuntimeException When the execution. failed.
148
     */
149
    public function perform($logFile)
150
    {
151
        if (self::STATE_PENDING !== $this->getStatus()) {
152
            throw new \LogicException('Attempted to run task ' . $this->getId() . ' twice.');
153
        }
154
155
        try {
156
            if (!is_dir(dirname($logFile))) {
157
                mkdir(dirname($logFile), 0777, true);
158
            }
159
160
            file_put_contents($logFile, '', FILE_BINARY);
161
162
            $this->logFile = $logFile;
163
            $this->file->set('log', $logFile);
164
165
            $this->setStatus(self::STATE_RUNNING);
166
            $this->addOutput('Task ' . $this->getId() . ' started.' . "\n");
167
168
            $this->doPerform();
169
        } catch (\Exception $exception) {
170
            $this->addOutput('--------------------------------------------------------' . "\n");
171
            $this->addOutput('Exception occured: ' . $exception->getMessage() . "\n");
172
            $this->addOutput($exception->getTraceAsString() . "\n");
173
            $loopException = $exception;
174
            while ($loopException = $loopException->getPrevious()) {
175
                $this->addOutput('Chained exception: ' . $loopException->getMessage() . "\n");
176
                $this->addOutput($loopException->getTraceAsString() . "\n");
177
            }
178
            $this->addOutput('--------------------------------------------------------' . "\n");
179
180
            $this->setStatus(self::STATE_ERROR);
181
182
            throw new \RuntimeException(
183
                'Task ' . $this->getId() . ' errored: ' . $exception->getMessage(),
184
                1,
185
                $exception
186
            );
187
        }
188
189
        $this->addOutput('Finished without error.' . "\n");
190
        $this->setStatus(self::STATE_FINISHED);
191
    }
192
193
    /**
194
     * Perform the task.
195
     *
196
     * @return void
197
     */
198
    abstract public function doPerform();
199
200
    /**
201
     * Add some output.
202
     *
203
     * @param string $string The output string to append to the output.
204
     *
205
     * @return void
206
     *
207
     * @throws \LogicException When called prior to perform().
208
     */
209
    public function addOutput($string)
210
    {
211
        if (!$this->logFile) {
212
            throw new \LogicException('The has not started to run yet.');
213
        }
214
215
        file_put_contents($this->logFile, $string, (FILE_APPEND | FILE_BINARY));
216
    }
217
218
    /**
219
     * Retrieve the IO interface.
220
     *
221
     * @return IOInterface
222
     */
223
    public function getIO()
224
    {
225
        if (!isset($this->inputOutput)) {
226
            $this->inputOutput = new ConsoleIO($this->getInput(), new TaskOutput($this), new HelperSet([]));
227
        }
228
229
        return $this->inputOutput;
230
    }
231
232
    /**
233
     * Retrieve the current status of a task.
234
     *
235
     * @return string
1 ignored issue
show
Documentation introduced by
Should the return type not be array|string|integer|null? Also, consider making the array more specific, something like array<String>, or String[].

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.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
236
     */
237
    public function getStatus()
238
    {
239
        return $this->file->get('status');
240
    }
241
242
    /**
243
     * Retrieve when this task got created as ISO 8601 date string.
244
     *
245
     * @return \DateTime
246
     */
247
    public function getCreatedAt()
248
    {
249
        return \DateTime::createFromFormat(DATE_ISO8601, $this->file->get('created-at'));
250
    }
251
252
    /**
253
     * Remove the attached files for this task from disk.
254
     *
255
     * @return void
256
     */
257
    public function removeAssets()
258
    {
259
        // Base implementation does only know about log file.
260
        if ($this->logFile) {
261
            if (file_exists($this->logFile)) {
262
                unlink($this->logFile);
263
            }
264
            $this->logFile = null;
265
            $this->file->remove('log');
266
        }
267
    }
268
269
    /**
270
     * Set the task state.
271
     *
272
     * @param string $status The status code.
273
     *
274
     * @return void
275
     */
276
    protected function setStatus($status)
277
    {
278
        $this->file->set('status', $status);
279
    }
280
281
    /**
282
     * Retrieve the Input handler.
283
     *
284
     * @return InputInterface
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use ArrayInput.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
285
     */
286
    private function getInput()
287
    {
288
        $input = new ArrayInput([]);
289
290
        $input->setInteractive(false);
291
292
        return $input;
293
    }
294
}
295