Completed
Push — master ( c5384d...c1ead9 )
by Nazar
04:21
created

Deferred_tasks::started()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 2
eloc 8
nc 2
nop 1
1
<?php
2
/**
3
 * @package   Deferred tasks
4
 * @category  modules
5
 * @author    Nazar Mokrynskyi <[email protected]>
6
 * @copyright Copyright (c) 2013-2016, Nazar Mokrynskyi
7
 * @license   MIT License, see license.txt
8
 */
9
namespace cs\modules\Deferred_tasks;
10
use
11
	cs\Config,
12
	cs\Event,
13
	cs\ExitException,
14
	cs\CRUD,
15
	cs\Singleton;
16
17
/**
18
 * @method static $this instance($check = false)
19
 */
20
class Deferred_tasks {
21
	use
22
		CRUD,
23
		Singleton;
24
	protected $data_model = [
25
		'id'           => 'int',
26
		'begin'        => 'int:0',
27
		'started'      => 'int:0',
28
		'started_hash' => 'text',
29
		'expected'     => 'int:1',
30
		'priority'     => 'int:0..2',
31
		'module'       => 'text',
32
		'data'         => 'json'
33
	];
34
	protected $table      = '[prefix]deferred_tasks_tasks';
35
	/**
36
	 * @var int
37
	 */
38
	protected $max_number_of_workers;
39
	/**
40
	 * @var string
41
	 */
42
	protected $security_key;
43
	/**
44
	 * @var string
45
	 */
46
	protected $core_url;
47
48
	protected function construct () {
49
		$Config                      = Config::instance();
50
		$module_data                 = $Config->module('Deferred_tasks');
51
		$this->max_number_of_workers = $module_data->max_number_of_workers;
52
		$this->security_key          = $module_data->security_key;
53
		$this->core_url              = $Config->core_url();
54
	}
55
	/**
56
	 * Returns database index
57
	 *
58
	 * @return int
59
	 */
60
	protected function cdb () {
61
		return Config::instance()->module('Deferred_tasks')->db('tasks');
62
	}
63
	/**
64
	 * Add new task
65
	 *
66
	 * @param string $module   Module, to what task belongs
67
	 * @param mixed  $data     Any data, needed for task execution. Can be array, string, number...
68
	 * @param int    $expected Max time in seconds, during which task is expected to be finished
69
	 * @param int    $begin    Unix timestamp, task will not be executed until this time
70
	 * @param int    $priority Priority 0..2, higher number - higher priority
71
	 *
72
	 * @return false|int Id of created task or `false` on failure
1 ignored issue
show
Documentation introduced by
Should the return type not be false|integer|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.

Loading history...
73
	 */
74
	function add ($module, $data, $expected, $begin = 0, $priority = 1) {
75
		return $this->create($begin, 0, '', $expected, $priority, $module, $data);
76
	}
77
	/**
78
	 * Get task
79
	 *
80
	 * @param int $id
81
	 *
82
	 * @return false|mixed
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array|false|integer|string.

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...
83
	 */
84
	protected function get ($id) {
85
		return $this->read($id);
86
	}
87
	/**
88
	 * Delete task
89
	 *
90
	 * @param int $id
91
	 *
92
	 * @return false|mixed
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use boolean.

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...
93
	 */
94
	function del ($id) {
95
		return $this->delete($id);
96
	}
97
	/**
98
	 * Run specified task
99
	 *
100
	 * @param int $task
101
	 *
102
	 * @throws ExitException
103
	 */
104
	function run_task ($task) {
105
		$data = $this->read($task);
106
		if (!$data) {
107
			throw new ExitException(404);
108
		}
109
		Event::instance()->fire(
110
			"Deferred_tasks/$data[module]",
111
			[
112
				'id'   => $data['id'],
113
				'data' => $data['data']
114
			]
115
		);
116
		$this->del($data['id']);
117
	}
118
	/**
119
	 * Run worker
120
	 */
121
	function run_tasks () {
122
		/**
123
		 * Disable time limit
124
		 */
125
		set_time_limit(0);
126
		@ini_set('max_input_time', 900);
127
		while ($this->tasks_running() < $this->max_number_of_workers) {
128
			$id = $this->next_task();
129
			if (!$id) {
130
				return;
131
			}
132
			if (!$this->started($id)) {
133
				continue;
134
			}
135
			file_get_contents("$this->core_url/Deferred_tasks/$this->security_key/$id");
136
		}
137
	}
138
	/**
139
	 * Update time of task start
140
	 *
141
	 * @param int $id
142
	 *
143
	 * @return bool `false` if another worker occupied this task in the meantime
144
	 */
145
	protected function started ($id) {
146
		$hash                 = md5(random_bytes(1000));
147
		$data                 = $this->read($id);
148
		$data['started']      = time();
149
		$data['started_hash'] = $hash;
150
		$this->update($data);
151
		$new_data = $this->read($id);
152
		return $new_data && $hash === $new_data['started_hash'];
153
	}
154
	/**
155
	 * Get number of runned workers
156
	 *
157
	 * @return int
1 ignored issue
show
Documentation introduced by
Should the return type not be array[]|integer|integer[]|string|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.

Loading history...
158
	 */
159
	protected function tasks_running () {
160
		return $this->db()->qfs(
161
			"SELECT COUNT(`id`)
162
			FROM `$this->table`
163
			WHERE
164
				`started` > 0 AND
165
				`started` + `expected` < %d",
166
			time()
167
		) ?: 0;
168
	}
169
	/**
170
	 * Get id of next task that should be executed
171
	 *
172
	 * @return false|int
173
	 */
174
	protected function next_task () {
175
		return $this->db()->qfs(
176
			"SELECT `id`
177
			FROM `$this->table`
178
			WHERE
179
				(
180
					`started` = 0 OR
181
					`started` + `expected` > %1\$d
182
				) AND
183
				`begin`	> %1\$d
184
			ORDER BY
185
				`priority` DESC,
186
				`id` ASC,
187
				`started` ASC
188
			LIMIT 1",
189
			time()
190
		);
191
	}
192
}
193