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

src/Task/Task.php (1 issue)

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);
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)
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