Total Complexity | 114 |
Total Lines | 892 |
Duplicated Lines | 0 % |
Changes | 2 | ||
Bugs | 0 | Features | 0 |
Complex classes like TDbCronModule often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use TDbCronModule, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
54 | class TDbCronModule extends TCronModule implements \Prado\Util\IDbModule |
||
55 | { |
||
56 | /** Name Regular Expression, no spaces, single or double quotes, less than or greater than, no percent, and cannot start with star */ |
||
57 | public const NAME_VALIDATOR_REGEX = '/^[^\s`\'\"\\*<>%][^\s`\'\"<>%]*$/i'; |
||
58 | |||
59 | public const PERM_CRON_LOG_READ = 'cron_log_read'; |
||
60 | |||
61 | public const PERM_CRON_LOG_DELETE = 'cron_log_delete'; |
||
62 | |||
63 | public const PERM_CRON_ADD_TASK = 'cron_add_task'; |
||
64 | |||
65 | public const PERM_CRON_UPDATE_TASK = 'cron_update_task'; |
||
66 | |||
67 | public const PERM_CRON_REMOVE_TASK = 'cron_remove_task'; |
||
68 | |||
69 | /** @var string name of the db table for cron tasks, default 'crontabs' */ |
||
70 | private $_tableName = 'crontabs'; |
||
71 | |||
72 | /** @var bool auto create the db table for cron, default true */ |
||
73 | private $_autoCreate = true; |
||
74 | |||
75 | /** @var bool has the table been verified to be in the DB */ |
||
76 | private $_tableEnsured = false; |
||
77 | |||
78 | /** @var bool log the cron tasks, in the table, as they run, default true */ |
||
79 | private $_logCronTasks = true; |
||
80 | |||
81 | /** @var array[]|TCronTask[] the tasks created from the (parent) application configuration */ |
||
82 | private $_configTasks; |
||
83 | |||
84 | /** @var bool are the tasks Initialized */ |
||
85 | private $_tasksInitialized = false; |
||
86 | |||
87 | /** @var array[]|TCronTask[] the tasks manually added to the database */ |
||
88 | private $_tasks = []; |
||
89 | |||
90 | /** @var array[] the row data from the database */ |
||
91 | private $_taskRows; |
||
92 | |||
93 | /** @var string the ID of TDataSourceConfig module */ |
||
94 | private $_connID = ''; |
||
95 | |||
96 | /** @var TDbConnection the DB connection instance */ |
||
97 | private $_conn; |
||
98 | |||
99 | /** @var TCronTask[] */ |
||
100 | private $_runtimeTasks; |
||
101 | |||
102 | /** |
||
103 | * constructs the instances, sets the _shellClass. |
||
104 | */ |
||
105 | public function __construct() |
||
106 | { |
||
107 | $this->_shellClass = \Prado\Util\Cron\TShellDbCronAction::class; |
||
108 | parent::__construct(); |
||
109 | } |
||
110 | /** |
||
111 | * Initializes the module. Keeps track of the configured tasks different than db tasks. |
||
112 | * @param array|\Prado\Xml\TXmlElement $config |
||
113 | */ |
||
114 | public function init($config) |
||
115 | { |
||
116 | parent::init($config); |
||
117 | |||
118 | $this->_configTasks = parent::getRawTasks(); |
||
119 | } |
||
120 | |||
121 | /** |
||
122 | * the global event handling requests for cron task info |
||
123 | * @param TDbCronModule $cron |
||
124 | * @param null $param |
||
|
|||
125 | */ |
||
126 | public function fxGetCronTaskInfos($cron, $param) |
||
127 | { |
||
128 | return new TCronTaskInfo('cronclean', \Prado\Util\Cron\TDbCronCleanLogTask::class, $this->getId(), Prado::localize('DbCron Clean Log Task'), Prado::localize('Clears the database of cron log items before the specified time period.')); |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * @param \Prado\Security\Permissions\TPermissionsManager $manager |
||
133 | * @return \Prado\Security\Permissions\TPermissionEvent[] |
||
134 | */ |
||
135 | public function getPermissions($manager) |
||
136 | { |
||
137 | $userIsOwnerAllowedRule = new TUserOwnerRule(); |
||
138 | return array_merge([ |
||
139 | new TPermissionEvent(static::PERM_CRON_LOG_READ, 'Cron read Db log.', ['dyGetCronLog', 'dyGetCronLogCount']), |
||
140 | new TPermissionEvent(static::PERM_CRON_LOG_DELETE, 'Cron delete Db log.', ['dyClearCronLog', 'dyRemoveCronLogItem']), |
||
141 | new TPermissionEvent(static::PERM_CRON_ADD_TASK, 'Cron add Db Task.', ['dyAddTask']), |
||
142 | new TPermissionEvent(static::PERM_CRON_UPDATE_TASK, 'Cron update Db task.', ['dyUpdateTask'], $userIsOwnerAllowedRule), |
||
143 | new TPermissionEvent(static::PERM_CRON_REMOVE_TASK, 'Cron remove Db task.', ['dyRemoveTask'], $userIsOwnerAllowedRule), |
||
144 | ], parent::getPermissions($manager)); |
||
145 | } |
||
146 | |||
147 | /** |
||
148 | * This checks for "name". The name cannot by '*' or have spaces, `, ', ", <, or >t characters. |
||
149 | * @param array $properties the task as an array of properties |
||
150 | * @throws TConfigurationException when the name is invalid. |
||
151 | */ |
||
152 | public function validateTask($properties) |
||
153 | { |
||
154 | $name = $properties[parent::NAME_KEY] ?? ''; |
||
155 | if (!preg_match(TDbCronModule::NAME_VALIDATOR_REGEX, $name)) { |
||
156 | throw new TConfigurationException('dbcron_invalid_name', $name); |
||
157 | } |
||
158 | parent::validateTask($properties); |
||
159 | } |
||
160 | |||
161 | /** |
||
162 | * Sets the lastExecTime and processCount for a task from the DB. |
||
163 | * Also resets the lastExecTime when not in the DB |
||
164 | * @param string $name name of the task to get Persistent Data from |
||
165 | * @param object $task |
||
166 | * @return bool is the persistent data set or not |
||
167 | */ |
||
168 | protected function setPersistentData($name, $task) |
||
169 | { |
||
170 | if (isset($this->_taskRows[$name])) { |
||
171 | $task->setLastExecTime($this->_taskRows[$name]['lastexectime']); |
||
172 | $task->setProcessCount((int) $this->_taskRows[$name]['processcount']); |
||
173 | if (serialize($task) !== $this->_taskRows[$name]['options']) { |
||
174 | $this->updateTaskInternal($task); |
||
175 | return false; |
||
176 | } |
||
177 | return true; |
||
178 | } else { |
||
179 | $this->addTaskInternal($task); |
||
180 | } |
||
181 | return false; |
||
182 | } |
||
183 | |||
184 | /** |
||
185 | * reads in all the tasks from the db, instances them if they are active db tasks. |
||
186 | * otherwise the rows are kept for persistent data. |
||
187 | * @param bool $initConfigTasks initialize the configuration |
||
188 | * @return \Prado\Util\Cron\TCronTask[] |
||
189 | */ |
||
190 | protected function ensureTasks($initConfigTasks = true) |
||
191 | { |
||
192 | if ($this->_tasksInitialized !== true) { |
||
193 | $this->ensureTable(); |
||
194 | $this->_taskRows = $this->_tasks = []; |
||
195 | $cmd = $this->getDbConnection()->createCommand( |
||
196 | "SELECT * FROM {$this->_tableName} WHERE active IS NOT NULL ORDER BY tabuid" |
||
197 | ); |
||
198 | $results = $cmd->query(); |
||
199 | |||
200 | Prado::log('Reading DB Cron Configuration', TLogger::NOTICE, TDbCronModule::class); |
||
201 | foreach ($results->readAll() as $data) { |
||
202 | if ($data['active']) { |
||
203 | $task = $this->_tasks[$data['name']] = @unserialize($data['options']); |
||
204 | } |
||
205 | $this->_taskRows[$data['name']] = $data; |
||
206 | } |
||
207 | $this->_tasksInitialized = true; |
||
208 | } |
||
209 | if ($initConfigTasks) { |
||
210 | $this->_configTasks = parent::ensureTasks(); |
||
211 | } |
||
212 | return array_merge($this->_tasks, $this->_configTasks ?? []); |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * @throws TConfigurationException when the configuration task names interfere with the db tasks names. |
||
217 | * @return array[TCronTask] combines the active configuration and db cron tasks |
||
218 | */ |
||
219 | public function getTasks() |
||
220 | { |
||
221 | $this->ensureTasks(); |
||
222 | if ($colliding = array_intersect_key($this->_tasks, $this->_configTasks)) { |
||
223 | throw new TConfigurationException('dbcron_conflicting_task_names', implode(', ', array_keys($colliding))); |
||
224 | } |
||
225 | return array_merge($this->_tasks, $this->_configTasks); |
||
226 | } |
||
227 | |||
228 | /** |
||
229 | * checks for the table, and if not there and autoCreate, then creates the table else throw error. |
||
230 | * @throws TConfigurationException if the table does not exist and cannot autoCreate |
||
231 | */ |
||
232 | protected function ensureTable() |
||
233 | { |
||
234 | if ($this->_tableEnsured) { |
||
235 | return; |
||
236 | } |
||
237 | $db = $this->getDbConnection(); |
||
238 | $sql = 'SELECT * FROM ' . $this->_tableName . ' WHERE 0=1'; |
||
239 | try { |
||
240 | $db->createCommand($sql)->query()->close(); |
||
241 | } catch (Exception $e) { |
||
242 | // DB table not exists |
||
243 | if ($this->_autoCreate) { |
||
244 | $this->createDbTable(); |
||
245 | } else { |
||
246 | throw new TConfigurationException('dbcron_table_nonexistent', $this->_tableName); |
||
247 | } |
||
248 | } |
||
249 | $this->_tableEnsured = true; |
||
250 | } |
||
251 | |||
252 | |||
253 | /** |
||
254 | * creates the module table |
||
255 | */ |
||
256 | protected function createDbTable() |
||
257 | { |
||
258 | $db = $this->getDbConnection(); |
||
259 | $driver = $db->getDriverName(); |
||
260 | $autotype = 'INTEGER'; |
||
261 | $autoidAttributes = ''; |
||
262 | if ($driver === 'mysql') { |
||
263 | $autoidAttributes = ' AUTO_INCREMENT'; |
||
264 | } elseif ($driver === 'sqlite') { |
||
265 | $autoidAttributes = ' AUTOINCREMENT'; |
||
266 | } elseif ($driver === 'postgresql') { |
||
267 | $autotype = 'SERIAL'; |
||
268 | } |
||
269 | $postIndices = '; CREATE INDEX tname ON ' . $this->_tableName . '(`name`);' . |
||
270 | 'CREATE INDEX tclass ON ' . $this->_tableName . '(`task`);' . |
||
271 | 'CREATE INDEX tactive ON ' . $this->_tableName . '(`active`);'; |
||
272 | |||
273 | $sql = 'CREATE TABLE IF NOT EXISTS ' . $this->_tableName . ' ( |
||
274 | `tabuid` ' . $autotype . ' PRIMARY KEY' . $autoidAttributes . ', |
||
275 | `name` VARCHAR (127) NOT NULL, |
||
276 | `schedule` VARCHAR (127) NOT NULL, |
||
277 | `task` VARCHAR (256) NOT NULL, |
||
278 | `moduleid` VARCHAR (127) NULL, |
||
279 | `username` VARCHAR (127) NULL, |
||
280 | `options` MEDIUMTEXT NULL, |
||
281 | `processcount` INT NOT NULL DEFAULT 0, |
||
282 | `lastexectime` VARCHAR (20) NULL DEFAULT `0`, |
||
283 | `active` BOOLEAN NULL |
||
284 | )' . $postIndices; |
||
285 | |||
286 | //`lastexectime` DECIMAL(12,8) NULL DEFAULT 0, |
||
287 | |||
288 | $cmd = $this->getDbConnection()->createCommand($sql); |
||
289 | |||
290 | $cmd->execute(); |
||
291 | } |
||
292 | |||
293 | /** |
||
294 | * logCronTask adds a task log to the table. |
||
295 | * @param TCronTask $task |
||
296 | * @param string $username |
||
297 | */ |
||
298 | protected function logCronTask($task, $username) |
||
299 | { |
||
300 | parent::logCronTask($task, $username); |
||
301 | |||
302 | $app = $this->getApplication(); |
||
303 | |||
304 | $logid = null; |
||
305 | if ($this->getLogCronTasks()) { |
||
306 | $this->ensureTable(); |
||
307 | |||
308 | $cmd = $this->getDbConnection()->createCommand( |
||
309 | "INSERT INTO {$this->_tableName} " . |
||
310 | "(name, schedule, task, moduleid, username, options, processcount, lastexectime, active)" . |
||
311 | " VALUES (:name, :schedule, :task, :mid, :username, :options, :count, :time, NULL)" |
||
312 | ); |
||
313 | $cmd->bindValue(":name", $task->getName(), PDO::PARAM_STR); |
||
314 | $cmd->bindValue(":task", $task->getTask(), PDO::PARAM_STR); |
||
315 | $cmd->bindValue(":schedule", $task->getSchedule(), PDO::PARAM_STR); |
||
316 | $cmd->bindValue(":mid", $task->getModuleId(), PDO::PARAM_STR); |
||
317 | $cmd->bindValue(":username", $username, PDO::PARAM_STR); |
||
318 | $cmd->bindValue(":options", serialize($task), PDO::PARAM_STR); |
||
319 | $cmd->bindValue(":count", $task->getProcessCount(), PDO::PARAM_INT); |
||
320 | $cmd->bindValue(":time", (int) microtime(true), PDO::PARAM_STR); |
||
321 | $cmd->execute(); |
||
322 | $logid = $this->getDbConnection()->getLastInsertID(); |
||
323 | } |
||
324 | return $logid; |
||
325 | } |
||
326 | |||
327 | /** |
||
328 | * This updates the LastExecTime and ProcessCount in the database |
||
329 | * @param TCronTask $task |
||
330 | */ |
||
331 | protected function updateTaskInfo($task) |
||
332 | { |
||
333 | $task->setLastExecTime($time = (int) microtime(true)); |
||
334 | $task->setProcessCount($count = ($task->getProcessCount() + 1)); |
||
335 | |||
336 | $cmd = $this->getDbConnection()->createCommand( |
||
337 | "UPDATE {$this->_tableName} SET processcount=:count, lastexectime=:time, options=:task WHERE name=:name AND active IS NOT NULL" |
||
338 | ); |
||
339 | $cmd->bindValue(":count", $count, PDO::PARAM_STR); |
||
340 | $cmd->bindValue(":time", $time, PDO::PARAM_STR); |
||
341 | $cmd->bindValue(":task", serialize($task), PDO::PARAM_STR); |
||
342 | $cmd->bindValue(":name", $task->getName(), PDO::PARAM_STR); |
||
343 | $cmd->execute(); |
||
344 | } |
||
345 | |||
346 | /** |
||
347 | * this removes any stale database rows from changing configTasks |
||
348 | */ |
||
349 | protected function filterStaleTasks() |
||
350 | { |
||
351 | $this->ensureTasks(); |
||
352 | $configTasks = $this->_taskRows; |
||
353 | |||
354 | //remove non-configuration tasks |
||
355 | foreach ($this->_taskRows as $name => $data) { |
||
356 | if ($data['active']) { |
||
357 | unset($configTasks[$name]); |
||
358 | } |
||
359 | } |
||
360 | |||
361 | //remove configuration tasks |
||
362 | foreach ($this->_configTasks as $name => $data) { |
||
363 | unset($configTasks[$name]); |
||
364 | } |
||
365 | |||
366 | //remaining are stale |
||
367 | if (count($configTasks)) { |
||
368 | foreach ($configTasks as $name => $task) { |
||
369 | $this->removeTaskInternal($name); |
||
370 | } |
||
371 | } |
||
372 | } |
||
373 | |||
374 | /** |
||
375 | * This executes the Run Time Tasks, this method is automatically added |
||
376 | * to TApplication::onEndRequest when there are RuntimeTasks via {@see addRuntimeTask}. |
||
377 | * @param null|\Prado\TApplication $sender |
||
378 | * @param null|mixed $param |
||
379 | * @return int number of tasks run |
||
380 | */ |
||
381 | public function executeRuntimeTasks($sender = null, $param = null) |
||
382 | { |
||
383 | $runtimeTasks = $this->getRuntimeTasks(); |
||
384 | if (!$runtimeTasks) { |
||
385 | return; |
||
386 | } |
||
387 | $numtasks = count($runtimeTasks); |
||
388 | $cronlogger = $this->asa(TCronModule::SHELL_LOG_BEHAVIOR); |
||
389 | if ($cronlogger) { |
||
390 | $enabled = $cronlogger->getEnabled(); |
||
391 | $cronlogger->setEnabled(false); |
||
392 | } |
||
393 | foreach ($runtimeTasks as $key => $task) { |
||
394 | $this->runTask($task); |
||
395 | } |
||
396 | if ($cronlogger) { |
||
397 | $cronlogger->setEnabled($enabled); |
||
398 | } |
||
399 | return $numtasks; |
||
400 | } |
||
401 | |||
402 | /** |
||
403 | * Adds a task to being run time. If this is the first runtime task this |
||
404 | * method adds {@see executeRuntimeTasks} to TApplication::onEndRequest. |
||
405 | * @param TCronTask $task |
||
406 | */ |
||
407 | public function addRuntimeTask($task) |
||
414 | } |
||
415 | |||
416 | /** |
||
417 | * Gets the runtime tasks. |
||
418 | * @return \Prado\Util\Cron\TCronTask the tasks to run on {@see executeRuntimeTasks} |
||
419 | */ |
||
420 | public function getRuntimeTasks() |
||
421 | { |
||
422 | return $this->_runtimeTasks; |
||
423 | } |
||
424 | |||
425 | /** |
||
426 | * Removes a task from being run time. If there are no runtime tasks left |
||
427 | * then it removes {@see executeRuntimeTasks} from TApplication::onEndRequest. |
||
428 | * @param TCronTask $untask |
||
429 | */ |
||
430 | public function removeRuntimeTask($untask) |
||
431 | { |
||
432 | if ($this->_runtimeTasks === null) { |
||
433 | return; |
||
434 | } |
||
435 | $name = is_string($untask) ? $untask : $untask->getName(); |
||
436 | unset($this->_runtimeTasks[$name]); |
||
437 | if (!$this->_runtimeTasks) { |
||
438 | $this->_runtimeTasks = null; |
||
439 | Prado::getApplication()->detachEventHandler('onEndRequest', [$this, 'executeRuntimeTasks']); |
||
440 | } |
||
441 | } |
||
442 | |||
443 | /** |
||
444 | * Clears all tasks from being run time, and removes the handler from onEndRequest. |
||
445 | */ |
||
446 | public function clearRuntimeTasks() |
||
447 | { |
||
448 | if ($this->_runtimeTasks === null) { |
||
449 | return; |
||
450 | } |
||
451 | $this->_runtimeTasks = null; |
||
452 | Prado::getApplication()->detachEventHandler('onEndRequest', [$this, 'executeRuntimeTasks']); |
||
453 | } |
||
454 | |||
455 | /** |
||
456 | * |
||
457 | * @param string $taskName |
||
458 | * @param bool $checkExisting |
||
459 | * @param bool $asObject returns the database row if false. |
||
460 | */ |
||
461 | public function getTask($taskName, $checkExisting = true, $asObject = true) |
||
462 | { |
||
463 | $this->ensureTable(); |
||
464 | |||
465 | if ($checkExisting) { |
||
466 | $this->ensureTasks(); |
||
467 | if ($asObject) { |
||
468 | if (isset($this->_tasks[$taskName])) { |
||
469 | return $this->_tasks[$taskName]; |
||
470 | } |
||
471 | if (isset($this->_configTasks[$taskName])) { |
||
472 | return $this->_configTasks[$taskName]; |
||
473 | } |
||
474 | } else { |
||
475 | if (isset($this->_taskRows[$taskName])) { |
||
476 | return $this->_taskRows[$taskName]; |
||
477 | } |
||
478 | } |
||
479 | } |
||
480 | |||
481 | |||
482 | $cmd = $this->getDbConnection()->createCommand( |
||
483 | "SELECT * FROM {$this->_tableName} WHERE name=:name AND active IS NOT NULL LIMIT 1" |
||
484 | ); |
||
485 | $cmd->bindValue(":name", $taskName, PDO::PARAM_STR); |
||
486 | |||
487 | $result = $cmd->queryRow(); |
||
488 | |||
489 | if (!$result) { |
||
490 | return null; |
||
491 | } |
||
492 | |||
493 | if ($asObject) { |
||
494 | return @unserialize($result['options']); |
||
495 | } |
||
496 | |||
497 | return $result; |
||
498 | } |
||
499 | |||
500 | /** |
||
501 | * Adds a task to the database. Validates the name and cannot add a task with an existing name. |
||
502 | * This updates the table row data as well. |
||
503 | * @param TCronTask $task |
||
504 | * @param bool $runtime should the task be added to the Run Time Task after being added |
||
505 | * @return bool was the task added |
||
506 | */ |
||
507 | public function addTask($task, $runtime = false) |
||
508 | { |
||
509 | if ($this->dyAddTask(false, $task, $runtime) === true) { |
||
510 | return false; |
||
511 | } |
||
512 | return $this->addTaskInternal($task, $runtime); |
||
513 | } |
||
514 | |||
515 | /** |
||
516 | * Adds a task to the database. Validates the name and cannot add a task with an existing name. |
||
517 | * This updates the table row data as well. |
||
518 | * @param \Prado\Util\Cron\TCronTask $task |
||
519 | * @param bool $runtime should the task be added to the Run Time Task after being added |
||
520 | * @return bool was the task added |
||
521 | */ |
||
522 | protected function addTaskInternal($task, $runtime = false) |
||
523 | { |
||
524 | $this->ensureTable(); |
||
525 | $this->ensureTasks(false); |
||
526 | $name = $task->getName(); |
||
527 | if (!preg_match(TDbCronModule::NAME_VALIDATOR_REGEX, $name)) { |
||
528 | return false; |
||
529 | } |
||
530 | if (isset($this->_tasks[$name])) { |
||
531 | return false; |
||
532 | } |
||
533 | try { |
||
534 | $task->getScheduler(); |
||
535 | } catch (TInvalidDataValueException $e) { |
||
536 | return false; |
||
537 | } |
||
538 | $task->resetTaskLastExecTime(); |
||
539 | |||
540 | $cmd = $this->getDbConnection()->createCommand( |
||
541 | "INSERT INTO {$this->_tableName} " . |
||
542 | "(name, schedule, task, moduleid, username, options, lastexectime, processcount, active)" . |
||
543 | " VALUES (:name, :schedule, :task, :mid, :username, :options, :time, :count, :active)" |
||
544 | ); |
||
545 | $cmd->bindValue(":name", $name, PDO::PARAM_STR); |
||
546 | $cmd->bindValue(":schedule", $schedule = $task->getSchedule(), PDO::PARAM_STR); |
||
547 | $cmd->bindValue(":task", $taskExec = $task->getTask(), PDO::PARAM_STR); |
||
548 | $cmd->bindValue(":mid", $mid = $task->getModuleId(), PDO::PARAM_STR); |
||
549 | $cmd->bindValue(":username", $username = $task->getUserName(), PDO::PARAM_STR); |
||
550 | $cmd->bindValue(":options", $serial = serialize($task), PDO::PARAM_STR); |
||
551 | $cmd->bindValue(":time", $time = $task->getLastExecTime(), PDO::PARAM_STR); |
||
552 | $cmd->bindValue(":count", $count = $task->getProcessCount(), PDO::PARAM_INT); |
||
553 | $cmd->bindValue(":active", $active = (isset($this->_configTasks[$name]) ? '0' : '1'), PDO::PARAM_INT); |
||
554 | $cmd->execute(); |
||
555 | |||
556 | if ($this->_tasks !== null && !isset($this->_configTasks[$name])) { |
||
557 | $this->_tasks[$name] = $task; |
||
558 | $this->_taskRows[$name] = []; |
||
559 | $this->_taskRows[$name]['name'] = $name; |
||
560 | $this->_taskRows[$name]['schedule'] = $schedule; |
||
561 | $this->_taskRows[$name]['task'] = $taskExec; |
||
562 | $this->_taskRows[$name]['moduleid'] = $mid; |
||
563 | $this->_taskRows[$name]['username'] = $username; |
||
564 | $this->_taskRows[$name]['options'] = $serial; |
||
565 | $this->_taskRows[$name]['processcount'] = $count; |
||
566 | $this->_taskRows[$name]['lastexectime'] = $time; |
||
567 | $this->_taskRows[$name]['active'] = $active; |
||
568 | } |
||
569 | if ($runtime) { |
||
570 | $this->addRuntimeTask($task); |
||
571 | } |
||
572 | return true; |
||
573 | } |
||
574 | |||
575 | /** |
||
576 | * Updates a task from its unique name. If the Task is not in the DB it returns false |
||
577 | * @param TCronTask $task |
||
578 | * @return bool was the task updated |
||
579 | */ |
||
580 | public function updateTask($task) |
||
581 | { |
||
582 | if ($this->dyUpdateTask(false, $task, ['extra' => ['username' => $task->getUserName()]]) === true) { |
||
583 | return false; |
||
584 | } |
||
585 | return $this->updateTaskInternal($task); |
||
586 | } |
||
587 | |||
588 | /** |
||
589 | * Updates a task from its unique name. If the Task is not in the DB it returns false |
||
590 | * @param \Prado\Util\Cron\TCronTask $task |
||
591 | * @return bool was the task updated |
||
592 | */ |
||
593 | protected function updateTaskInternal($task) |
||
634 | } |
||
635 | |||
636 | /** |
||
637 | * Removes a task from the database table. |
||
638 | * This also removes the task from the current tasks, the taskRow, and runtime Tasks. |
||
639 | * |
||
640 | * This cannot remove tasks that are current configuration tasks. Only tasks |
||
641 | * that exist can be removed. |
||
642 | * @param string|TCronTask $untask the task to remove from the DB |
||
643 | * @return bool was the task removed |
||
644 | */ |
||
645 | public function removeTask($untask) |
||
646 | { |
||
647 | $task = null; |
||
648 | if (is_string($untask)) { |
||
649 | $task = $this->getTask($untask); |
||
650 | if (!$task) { |
||
651 | return false; |
||
652 | } |
||
653 | } |
||
654 | if ($this->dyRemoveTask(false, $untask, ['extra' => ['username' => ($task ?? $untask)->getUserName()]]) === true) { |
||
655 | return false; |
||
656 | } |
||
657 | return $this->removeTaskInternal($untask); |
||
658 | } |
||
659 | |||
660 | /** |
||
661 | * Removes a task from the database table. |
||
662 | * This also removes the task from the current tasks, the taskRow, and runtime Tasks. |
||
663 | * |
||
664 | * This cannot remove tasks that are current configuration tasks. Only tasks |
||
665 | * that exist can be removed. |
||
666 | * @param \Prado\Util\Cron\TCronTask|string $untask the task to remove from the DB |
||
667 | * @return bool was the task removed |
||
668 | */ |
||
669 | protected function removeTaskInternal($untask) |
||
670 | { |
||
671 | $this->ensureTable(); |
||
672 | $this->ensureTasks(false); |
||
673 | $name = is_subclass_of($untask, \Prado\Util\Cron\TCronTask::class) ? $untask->getName() : $untask; |
||
674 | if (isset($this->_configTasks[$name])) { |
||
675 | return false; |
||
676 | } |
||
677 | if (!$this->taskExists($name)) { |
||
678 | return false; |
||
679 | } |
||
680 | |||
681 | $cmd = $this->getDbConnection()->createCommand( |
||
682 | "DELETE FROM {$this->_tableName} WHERE name=:name AND active IS NOT NULL" |
||
683 | ); |
||
684 | $cmd->bindValue(":name", $name, PDO::PARAM_STR); |
||
685 | $cmd->execute(); |
||
686 | |||
687 | // Remove task to list of tasks |
||
688 | unset($this->_tasks[$name]); |
||
689 | unset($this->_taskRows[$name]); |
||
690 | $this->removeRuntimeTask($name); |
||
691 | return true; |
||
692 | } |
||
693 | |||
694 | /** |
||
695 | * taskExists checks for a task or task name in the database |
||
696 | * @param string $name task to check in the database |
||
697 | * @throws \Prado\Exceptions\TDbException if the Fields and table is not correct |
||
698 | * @return bool whether the task name exists in the database table |
||
699 | */ |
||
700 | public function taskExists($name) |
||
701 | { |
||
702 | $this->ensureTable(); |
||
703 | |||
704 | $db = $this->getDbConnection(); |
||
705 | $cmd = $db->createCommand( |
||
706 | "SELECT COUNT(*) AS count FROM {$this->_tableName} WHERE name=:name AND active IS NOT NULL" |
||
707 | ); |
||
708 | $cmd->bindParameter(":name", $name, PDO::PARAM_STR); |
||
709 | return $cmd->queryScalar() > 0; |
||
710 | } |
||
711 | |||
712 | /** |
||
713 | * deletes the cron log items before time minus $seconds. |
||
714 | * @param int $seconds the number of seconds before Now |
||
715 | */ |
||
716 | public function clearCronLog($seconds) |
||
717 | { |
||
718 | if ($this->dyClearCronLog(false, $seconds) === true) { |
||
719 | return false; |
||
720 | } |
||
721 | $this->ensureTable(); |
||
722 | |||
723 | $seconds = (int) $seconds; |
||
724 | $cmd = $this->getDbConnection()->createCommand( |
||
725 | "SELECT COUNT(*) FROM {$this->_tableName} WHERE active IS NULL AND lastexectime <= :time" |
||
726 | ); |
||
727 | $time = time() - $seconds; |
||
728 | $cmd->bindParameter(":time", $time, PDO::PARAM_STR); |
||
729 | $count = $cmd->queryScalar(); |
||
730 | $cmd = $this->getDbConnection()->createCommand( |
||
731 | "DELETE FROM {$this->_tableName} WHERE active IS NULL AND lastexectime <= :time" |
||
732 | ); |
||
733 | $cmd->bindParameter(":time", $time, PDO::PARAM_STR); |
||
734 | $cmd->execute(); |
||
735 | |||
736 | return $count; |
||
737 | } |
||
738 | |||
739 | /** |
||
740 | * Deletes one cron log item from the database |
||
741 | * @param int $taskUID |
||
742 | */ |
||
743 | public function removeCronLogItem($taskUID) |
||
744 | { |
||
745 | if ($this->dyRemoveCronLogItem(false, $taskUID) === true) { |
||
746 | return false; |
||
747 | } |
||
748 | $this->ensureTable(); |
||
749 | $taskUID = (int) $taskUID; |
||
750 | |||
751 | $cmd = $this->getDbConnection()->createCommand( |
||
752 | "DELETE FROM {$this->_tableName} WHERE active IS NULL AND tabuid = :uid" |
||
753 | ); |
||
754 | $cmd->bindParameter(":uid", $taskUID, PDO::PARAM_INT); |
||
755 | $cmd->execute(); |
||
756 | } |
||
757 | |||
758 | /** |
||
759 | * @param null|string $name name of the logs to look for, or null for all |
||
760 | * @return int the number of log items of all or of $name |
||
761 | */ |
||
762 | public function getCronLogCount($name = null) |
||
763 | { |
||
764 | if ($this->dyGetCronLogCount(false, $name) === true) { |
||
765 | return false; |
||
766 | } |
||
767 | $this->ensureTable(); |
||
768 | |||
769 | $db = $this->getDbConnection(); |
||
770 | $where = ''; |
||
771 | if (is_string($name)) { |
||
772 | $where = 'name=:name AND '; |
||
773 | } |
||
774 | $cmd = $db->createCommand( |
||
775 | "SELECT COUNT(*) AS count FROM {$this->_tableName} WHERE {$where}active IS NULL" |
||
776 | ); |
||
777 | if (is_string($name)) { |
||
778 | $cmd->bindParameter(":name", $name, PDO::PARAM_STR); |
||
779 | } |
||
780 | return (int) $cmd->queryScalar(); |
||
781 | } |
||
782 | |||
783 | /** |
||
784 | * Gets the cron log table of specific named or all tasks. |
||
785 | * @param null|string $name name of the tasks to get from the log, or null for all |
||
786 | * @param int $pageSize |
||
787 | * @param int $offset |
||
788 | * @param null|bool $sortingDesc sort by descending execution time. |
||
789 | */ |
||
790 | public function getCronLog($name, $pageSize, $offset, $sortingDesc = null) |
||
791 | { |
||
792 | if ($this->dyGetCronLog(false, $name, $pageSize, $offset, $sortingDesc) === true) { |
||
793 | return false; |
||
794 | } |
||
795 | $this->ensureTable(); |
||
796 | |||
797 | $db = $this->getDbConnection(); |
||
798 | $driver = $db->getDriverName(); |
||
799 | |||
800 | $limit = $orderby = $where = ''; |
||
801 | if (is_string($name)) { |
||
802 | $where = 'name=:name AND '; |
||
803 | } |
||
804 | $pageSize = (int) $pageSize; |
||
805 | $offset = (int) $offset; |
||
806 | if ($pageSize !== 0) { |
||
807 | if ($offset !== 0) { |
||
808 | if ($driver === 'postgresql') { |
||
809 | $limit = " LIMIT {$pageSize} OFFSET {$offset}"; |
||
810 | } else { |
||
811 | $limit = " LIMIT {$offset}, {$pageSize}"; |
||
812 | } |
||
813 | } else { |
||
814 | $limit = " LIMIT {$pageSize}"; |
||
815 | } |
||
816 | $sortingDesc ??= true; |
||
817 | } |
||
818 | if ($sortingDesc !== null) { |
||
819 | $sortingDesc = TPropertyValue::ensureBoolean($sortingDesc) ? 'DESC' : 'ASC'; |
||
820 | $orderby = " ORDER BY lastExecTime $sortingDesc, tabuid $sortingDesc, processCount $sortingDesc"; |
||
821 | } |
||
822 | $cmd = $db->createCommand( |
||
823 | "SELECT * FROM {$this->_tableName} WHERE {$where}active IS NULL{$orderby}{$limit}" |
||
824 | ); |
||
825 | if (is_string($name)) { |
||
826 | $cmd->bindParameter(":name", $name, PDO::PARAM_STR); |
||
827 | } |
||
828 | $results = $cmd->query(); |
||
829 | return $results->readAll(); |
||
830 | } |
||
831 | |||
832 | /** |
||
833 | * Creates the DB connection. If no ConnectionId is provided, then this |
||
834 | * creates a sqlite database in runtime named 'cron.jobs'. |
||
835 | * @throws TConfigurationException if module ID is invalid or empty |
||
836 | * @return \Prado\Data\TDbConnection the created DB connection |
||
837 | */ |
||
838 | protected function createDbConnection() |
||
839 | { |
||
840 | if ($this->_connID !== '') { |
||
841 | $config = $this->getApplication()->getModule($this->_connID); |
||
842 | if ($config instanceof TDataSourceConfig) { |
||
843 | return $config->getDbConnection(); |
||
844 | } else { |
||
845 | throw new TConfigurationException('dbcron_connectionid_invalid', $this->_connID); |
||
846 | } |
||
847 | } else { |
||
848 | $db = new TDbConnection(); |
||
849 | // default to SQLite3 database |
||
850 | $dbFile = $this->getApplication()->getRuntimePath() . DIRECTORY_SEPARATOR . 'cron.jobs'; |
||
851 | $db->setConnectionString('sqlite:' . $dbFile); |
||
852 | return $db; |
||
853 | } |
||
854 | } |
||
855 | |||
856 | /** |
||
857 | * @return \Prado\Data\TDbConnection the DB connection instance |
||
858 | */ |
||
859 | public function getDbConnection() |
||
860 | { |
||
861 | if ($this->_conn === null) { |
||
862 | $this->_conn = $this->createDbConnection(); |
||
863 | $this->_conn->setActive(true); |
||
864 | } |
||
865 | return $this->_conn; |
||
866 | } |
||
867 | |||
868 | /** |
||
869 | * @return null|string the ID of a {@see \Prado\Data\TDataSourceConfig} module. Defaults to empty string, meaning not set. |
||
870 | */ |
||
871 | public function getConnectionID() |
||
872 | { |
||
873 | return $this->_connID; |
||
874 | } |
||
875 | |||
876 | /** |
||
877 | * Sets the ID of a TDataSourceConfig module. |
||
878 | * The datasource module will be used to establish the DB connection for this cron module. |
||
879 | * @param string $value ID of the {@see \Prado\Data\TDataSourceConfig} module |
||
880 | * @throws TInvalidOperationException when trying to set this property but the module is already initialized. |
||
881 | */ |
||
882 | public function setConnectionID($value) |
||
888 | } |
||
889 | |||
890 | /** |
||
891 | * @return bool should tasks that run be logged, default true |
||
892 | */ |
||
893 | public function getLogCronTasks() |
||
894 | { |
||
895 | return $this->_logCronTasks; |
||
896 | } |
||
897 | |||
898 | /** |
||
899 | * @param bool $log should tasks that run be logged |
||
900 | */ |
||
901 | public function setLogCronTasks($log) |
||
904 | } |
||
905 | |||
906 | /** |
||
907 | * @return string table in the database for cron tasks and logs. Defaults to 'crontabs' |
||
908 | */ |
||
909 | public function getTableName() |
||
910 | { |
||
911 | return $this->_tableName; |
||
912 | } |
||
913 | |||
914 | /** |
||
915 | * @param string $table table in the database for cron tasks and logs |
||
916 | * @throws TInvalidOperationException when trying to set this property but the module is already initialized. |
||
917 | */ |
||
918 | public function setTableName($table) |
||
919 | { |
||
920 | if ($this->_initialized) { |
||
921 | throw new TInvalidOperationException('dbcron_property_unchangeable', 'TableName'); |
||
922 | } |
||
923 | $this->_tableName = TPropertyValue::ensureString($table); |
||
924 | } |
||
925 | |||
926 | /** |
||
927 | * @return bool whether the cron DB table should be automatically created if not exists. Defaults to true. |
||
928 | * @see setTableName |
||
929 | */ |
||
930 | public function getAutoCreateCronTable() |
||
933 | } |
||
934 | |||
935 | /** |
||
936 | * @param bool $value whether the cron DB table should be automatically created if not exists. |
||
937 | * @throws TInvalidOperationException when trying to set this property but the module is already initialized. |
||
938 | * @see setTableName |
||
939 | */ |
||
940 | public function setAutoCreateCronTable($value) |
||
946 | } |
||
947 | } |
||
948 |