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

src/Task/Task.php (4 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;
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);
0 ignored issues
show
The call to Task::createWorklog() has too many arguments starting with $this->pid.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
267
268
            $this->result = $this->run();
269
270
            $this->end_timestamp = microtime(true);
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)
0 ignored issues
show
The method tablePrefix cannot be called on $this->dbh (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
$w_result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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