Completed
Push — master ( 8a4ca9...acd23c )
by Marco
02:46
created

src/Task/Task.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php namespace Comodojo\Extender\Task;
2
3
use \Comodojo\Exception\TaskException;
4
use \Comodojo\Exception\DatabaseException;
5
use \Comodojo\Database\EnhancedDatabase;
6
use \Monolog\Logger;
7
use \Exception;
8
9
/**
10
 * Task class
11
 *
12
 * A task is an atomic PHP script that may be invoked in single or multithread mode.
13
 * 
14
 * In the first case, result and (eventually) exceptions are returned back to Extender (via JobRunner class)
15
 * directly; in second one, task is forked via pcntl and result is catched via finite stream.
16
 * This is the reason why task should give back a string.
17
 *
18
 * Each task manage its own worklog on database and may define a Monolog instance to log to.
19
 *
20
 * @package     Comodojo extender
21
 * @author      Marco Giovinazzi <[email protected]>
22
 * @license     GPL-3.0+
23
 *
24
 * LICENSE:
25
 * 
26
 * This program is free software: you can redistribute it and/or modify
27
 * it under the terms of the GNU Affero General Public License as
28
 * published by the Free Software Foundation, either version 3 of the
29
 * License, or (at your option) any later version.
30
 *
31
 * This program is distributed in the hope that it will be useful,
32
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
34
 * GNU Affero General Public License for more details.
35
 *
36
 * You should have received a copy of the GNU Affero General Public License
37
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
38
 */
39
40
abstract class Task {
41
        
42
    // Things a task may modify
43
44
    /**
45
     * The job name
46
     * 
47
     * @var string
48
     */
49
    private $name = 'EXTENDERTASK';
50
    
51
    /**
52
     * Job class (the one that extend cron_job).
53
     * 
54
     * @var string
55
     */
56
    private $class = null;
57
58
    /**
59
     * Logger, injected by extender
60
     *
61
     * @var \Monolog\Logger
62
     */
63
    protected $logger = null;
64
65
    /**
66
     * Start timestamp
67
     * 
68
     * @var int
69
     */
70
    private $start_timestamp = null;
71
    
72
    /**
73
     * End timestamp
74
     * 
75
     * @var int
76
     */
77
    private $end_timestamp = null;
78
    
79
    /**
80
     * Current process PID
81
     */
82
    private $pid = null;
83
    
84
    /**
85
     * Id of the job that launched this tasks
86
     */
87
    private $jobid = null;
88
89
    /**
90
     * The job result (if any)
91
     */
92
    private $job_result = null;
93
    
94
    /**
95
     * The job end state
96
     */
97
    private $job_success = false;
98
    
99
    /**
100
     * Worklog ID
101
     */
102
    private $worklog_id = null;
103
    
104
    /**
105
     * Parameters that extender may provide
106
     */
107
    private $parameters = array();
108
109
    /**
110
     * Database handler
111
     */
112
    private $dbh = array();
113
    
114
    /**
115
     * Task constructor.
116
     * 
117
     * @param   array           $parameters     Array of parameters (if any)
118
     * @param   \Monolog\Logger $logger
119
     * @param   int             $pid            Task PID (if any)
120
     * @param   string          $name           Task Name
121
     * @param   int             $timestamp      Start timestamp (if null will be retrieved directly)
122
     * @param   bool            $multithread    Multithread switch
123
     * 
124
     * @return  Object  $this 
125
     */
126
    final public function __construct($parameters, Logger $logger,
127
        $pid = null, $name = null, $timestamp = null, 
128
        $multithread = null, $jobid = null
129
    ) {
130
        
131
        // Setup task
132
133
        if ( !empty($parameters) ) $this->parameters = $parameters;
134
        
135
        if ( !is_null($name) ) $this->name = $name;
136
137
        $this->pid = is_null($pid) ? getmypid() : $pid;
138
139
        $this->start_timestamp = is_null($timestamp) ? microtime(true) : $timestamp;
0 ignored issues
show
Documentation Bug introduced by
It seems like is_null($timestamp) ? microtime(true) : $timestamp can also be of type double. However, the property $start_timestamp is declared as type integer. 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...
140
141
        $this->class = get_class($this);
142
143
        $this->jobid = is_numeric($jobid) ? $jobid : null;
144
145
        $this->logger = $logger;
146
147
        // Setup database (worklog!)
148
149
        try {
150
151
            $this->dbh = new EnhancedDatabase(
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Comodojo\Database\E...EXTENDER_DATABASE_PASS) of type object<Comodojo\Database\EnhancedDatabase> is incompatible with the declared type array of property $dbh.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
152
                EXTENDER_DATABASE_MODEL,
153
                EXTENDER_DATABASE_HOST,
154
                EXTENDER_DATABASE_PORT,
155
                EXTENDER_DATABASE_NAME,
156
                EXTENDER_DATABASE_USER,
157
                EXTENDER_DATABASE_PASS
158
            );
159
160
            $this->dbh->autoClean();
161
162
        } catch (DatabaseException $de) {
163
            
164
            throw $de;
165
166
        }
167
168
        // Setup an exit strategy if multithread enabled (parent may kill child process if timeout exceeded)
169
170
        if ( filter_var($multithread, FILTER_VALIDATE_BOOLEAN) ) {
171
172
            pcntl_signal(SIGTERM, function() {
173
174
                $end = microtime(true);
175
176
                if ( !is_null($this->worklog_id) ) $this->closeWorklog($this->worklog_id, false, 'Job killed by parent (timeout exceeded)', $end);
177
178
                exit(1);
179
180
            });
181
182
        }
183
184
    }
185
186
    /**
187
     * Class destructor, just to unset database handler
188
     * 
189
     */
190
    final public function __destruct() {
191
192
        unset($this->dbh);
193
194
    }
195
196
    /**
197
     * Get all provided parameters in array
198
     * 
199
     * @return  Array
200
     */
201
    final public function getParameters() {
202
203
        return $this->parameters;
204
205
    }
206
207
    /**
208
     * Get a provided parameter's value
209
     * 
210
     * @return  mixed   parameter value if provided or null otherwise
211
     */
212
    final public function getParameter($parameter) {
213
214
        if ( array_key_exists($parameter, $this->parameters) ) return $this->parameters[$parameter];
215
216
        else return null;
217
218
    }
219
220
    /**
221
     * Return PID from system (null if no multi-thread active)
222
     * 
223
     * @return int
224
     */
225
    final public function getPid() {
226
227
        return $this->pid;
228
229
    }
230
231
    /**
232
     * Start task!
233
     * 
234
     * @return  array
235
     */
236
    final public function start() {
237
238
        try {
239
240
            $job_run_info = $this->execTask();
241
242
        }
243
        catch (Exception $e) {
244
245
            throw new TaskException($e->getMessage(), $e->getCode(), $e, $this->worklog_id);
246
            
247
        }
248
249
        return $job_run_info;
250
251
    } 
252
    
253
    /**
254
     * Execute task.
255
     *
256
     * This method provides to:
257
     * - setup worklog
258
     * - invoke method "run", that should be defined in task implementation
259
     */
260
    private function execTask() {
261
262
        try {
263
264
            // open worklog
265
266
            $this->worklog_id = $this->createWorklog($this->pid, $this->name, $this->class, $this->start_timestamp);
267
268
            $this->result = $this->run();
269
270
            $this->end_timestamp = microtime(true);
0 ignored issues
show
Documentation Bug introduced by
The property $end_timestamp was declared of type integer, but microtime(true) is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
271
272
            $this->closeWorklog(true);
273
274
        }
275
        catch (Exception $e) {
276
277
            $this->result = $e->getMessage();
278
279
            if ( !is_null($this->worklog_id) ) {
280
281
                if ( is_null($this->end_timestamp) ) $this->end_timestamp = microtime(true);
282
283
                $this->closeWorklog(false);
284
285
            }
286
287
            throw $e;
288
289
        }
290
291
        return array(
292
            "success"   => true,
293
            "timestamp" => $this->end_timestamp,
294
            "result"    => $this->result,
295
            "worklogid" => $this->worklog_id
296
        );
297
298
    }
299
300
    /**
301
     * The run method; SHOULD be implemented by each task
302
     */
303
    abstract public function run();
304
305
    /**
306
     * Create the worklog for current job
307
     */
308
    protected function createWorklog() {
309
        
310
        try {
311
312
            $w_result = $this->dbh
313
                ->tablePrefix(EXTENDER_DATABASE_PREFIX)
314
                ->table(EXTENDER_DATABASE_TABLE_WORKLOGS)
315
                ->keys(array("pid", "name", "jobid", "task", "status", "start"))
316
                ->values(array($this->pid, $this->name, $this->jobid, $this->class, 'STARTED', $this->start_timestamp))
317
                ->store();
318
319
        }
320
        catch (DatabaseException $de) {
321
            
322
            throw $de;
323
324
        }
325
        
326
        return $w_result->getInsertId();
327
            
328
    }
329
    
330
    /**
331
     * Close worklog for current job
332
     */
333
    protected function closeWorklog($success) {
334
        
335
        try {
336
337
            $w_result = $this->dbh->tablePrefix(EXTENDER_DATABASE_PREFIX)
338
                ->table(EXTENDER_DATABASE_TABLE_WORKLOGS)
339
                ->keys(array("status", "success", "result", "end"))
340
                ->values(array("FINISHED", $success, $this->result, $this->end_timestamp))
341
                ->where("id", "=", $this->worklog_id)
342
                ->update();
343
344
        }
345
        catch (DatabaseException $de) {
346
            
347
            throw $de;
348
349
        }
350
        
351
    }
352
    
353
}
354