This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Fwlib\Db; |
||
3 | |||
4 | use Fwlib\Bridge\Adodb; |
||
5 | use Fwlib\Util\UtilContainerAwareTrait; |
||
6 | |||
7 | /** |
||
8 | * Db schema synchronize & update tools |
||
9 | * |
||
10 | * Define schema operate SQL in array, and track their execute status with a |
||
11 | * log table. |
||
12 | * |
||
13 | * All SQL MUST execute by defined order, so when a SQL with maximum id is |
||
14 | * done, all SQL before it was done. |
||
15 | * |
||
16 | * Execute of SQL will stop when got error, after SQL fixed, next execute will |
||
17 | * automatic clear error SQL, or update it. |
||
18 | * |
||
19 | * SQL identify by id, so don't change them except you know what you are |
||
20 | * doing. Id can start from 0 or 1, but can only assign by ascending order. |
||
21 | * |
||
22 | * If there are too many schema SQL, put altogether in one define file will |
||
23 | * cost more memory and i/o. In this situation, you can split SQL define file |
||
24 | * to small ones by step(eg: 100), then use getLastIdDone() to help check |
||
25 | * which file to require. |
||
26 | * |
||
27 | * Other tools similar: |
||
28 | * @link http://xml2ddl.berlios.de/ |
||
29 | * |
||
30 | * @copyright Copyright 2006-2015 Fwolf |
||
31 | * @license http://www.gnu.org/licenses/lgpl.html LGPL-3.0+ |
||
32 | */ |
||
33 | class SyncDbSchema |
||
34 | { |
||
35 | use AdodbAwareTrait { |
||
36 | setDb as setDbInstance; |
||
37 | } |
||
38 | use UtilContainerAwareTrait; |
||
39 | |||
40 | |||
41 | /** |
||
42 | * Last schema SQL id |
||
43 | * |
||
44 | * @var int |
||
45 | */ |
||
46 | public $lastId = -1; |
||
47 | |||
48 | /** |
||
49 | * Last schema SQL id which is executed |
||
50 | * |
||
51 | * @var int |
||
52 | */ |
||
53 | public $lastIdDone = -1; |
||
54 | |||
55 | /** |
||
56 | * Table to track schema SQL execute status |
||
57 | * |
||
58 | * It should not include space in table name. |
||
59 | * |
||
60 | * In running product environment, if this table name changed, remember to |
||
61 | * rename corresponding table in dbms. |
||
62 | * |
||
63 | * @var string |
||
64 | */ |
||
65 | protected $logTable = 'log_sync_db_schema'; |
||
66 | |||
67 | |||
68 | /** |
||
69 | * Check and create log table if not exists |
||
70 | */ |
||
71 | public function checkLogTable() |
||
72 | { |
||
73 | $table = &$this->logTable; |
||
74 | $db = $this->getDb(); |
||
75 | |||
76 | if (! $db->isTableExist($table)) { |
||
77 | // SQL for Create table diffs by db type |
||
78 | // @codeCoverageIgnoreStart |
||
79 | if ($db->isDbSybase()) { |
||
80 | $sql = " |
||
81 | CREATE TABLE $table ( |
||
82 | id INT NOT NULL, |
||
83 | done INT DEFAULT 0, /* 0:not do, -1:error, 1:done ok */ |
||
84 | sqltext TEXT, |
||
85 | ts TIMESTAMP NOT NULL, |
||
86 | CONSTRAINT PK_$table PRIMARY KEY (id) |
||
87 | ) |
||
88 | "; |
||
89 | } elseif ($db->isDbMysql()) { |
||
90 | $sql = " |
||
91 | CREATE TABLE $table ( |
||
92 | id INT(8) NOT NULL, |
||
93 | done TINYINT DEFAULT 0, /* 0:not do, -1:error, 1:done ok */ |
||
94 | sqltext TEXT, |
||
95 | ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
||
96 | PRIMARY KEY (id) |
||
97 | ) |
||
98 | "; |
||
99 | } else { |
||
100 | $sql = " |
||
101 | CREATE TABLE $table ( |
||
102 | id INT NOT NULL, |
||
103 | done INT DEFAULT 0, /* 0:not do, -1:error, 1:done ok */ |
||
104 | sqltext TEXT, |
||
105 | PRIMARY KEY (id) |
||
106 | ) |
||
107 | "; |
||
108 | } |
||
109 | // @codeCoverageIgnoreEnd |
||
110 | |||
111 | $db->execute($sql); |
||
112 | if (0 < $db->getErrorCode()) { |
||
113 | // @codeCoverageIgnoreStart |
||
114 | $this->log( |
||
115 | $this->getDbError() . PHP_EOL . |
||
116 | "Log table $table does not exists and create fail." |
||
117 | ); |
||
118 | exit; |
||
119 | // @codeCoverageIgnoreEnd |
||
120 | } |
||
121 | |||
122 | $this->log("Log table $table does not exists, create it, done."); |
||
123 | |||
124 | } else { |
||
125 | $this->log("Log table $table already exists."); |
||
126 | |||
127 | // Get last-done-id for later usage |
||
128 | $this->getLastIdDone(); |
||
129 | } |
||
130 | } |
||
131 | |||
132 | |||
133 | /** |
||
134 | * Delete SQL from the error one |
||
135 | * |
||
136 | * All SQL after the error one(will be un-executed) will be deleted, |
||
137 | * ignore their execute status. This is good for debug, if you got an |
||
138 | * error SQL, just fix it and call sync script again. |
||
139 | */ |
||
140 | public function deleteErrorSql() |
||
141 | { |
||
142 | $db = $this->getDb(); |
||
143 | |||
144 | $sql = "SELECT id FROM $this->logTable WHERE done=-1 ORDER BY id ASC"; |
||
145 | $rs = $db->SelectLimit($sql, 1); |
||
0 ignored issues
–
show
|
|||
146 | |||
147 | if (!$rs->EOF) { |
||
148 | // Del SQL and SQL after it |
||
149 | $id = $rs->fields['id']; |
||
150 | $sql = "DELETE FROM {$this->logTable} WHERE id >= $id"; |
||
151 | $rs = $db->Execute($sql); |
||
0 ignored issues
–
show
$rs 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 ![]() |
|||
152 | |||
153 | // Check result, should be greater than 0 |
||
154 | $i = $db->Affected_Rows(); |
||
0 ignored issues
–
show
The method
Affected_Rows does not exist on object<Fwlib\Bridge\Adodb> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when class ParentClass {
private $data = array();
public function __call($method, array $args) {
if (0 === strpos($method, 'get')) {
return $this->data[strtolower(substr($method, 3))];
}
throw new \LogicException(sprintf('Unsupported method: %s', $method));
}
}
/**
* If this class knows which fields exist, you can specify the methods here:
*
* @method string getName()
*/
class SomeClass extends ParentClass { }
![]() |
|||
155 | $this->log("Clear $i SQL start from failed SQL $id."); |
||
156 | } |
||
157 | } |
||
158 | |||
159 | |||
160 | /** |
||
161 | * Execute SQLs |
||
162 | */ |
||
163 | public function execute() |
||
164 | { |
||
165 | // Clear previous failed SQL |
||
166 | $this->deleteErrorSql(); |
||
167 | |||
168 | $db = $this->getDb(); |
||
169 | |||
170 | $sql = "SELECT id, sqltext FROM $this->logTable WHERE done<>1 ORDER BY id ASC"; |
||
171 | $rs = $db->Execute($sql); |
||
172 | |||
173 | $cntDone = 0; |
||
174 | while (!$rs->EOF) { |
||
175 | $id = $rs->fields['id']; |
||
176 | $sql = stripslashes($rs->fields['sqltext']); |
||
177 | |||
178 | // Some DDL SQL can't use transaction, so do raw execute. |
||
179 | $db->execute($sql); |
||
180 | |||
181 | // Bad sybase support, select db will got error msg, eg: |
||
182 | // Changed database context to 'db_name' |
||
183 | // @codeCoverageIgnoreStart |
||
184 | if ((0 == $db->getErrorCode() |
||
185 | && 0 == strlen($db->getErrorMessage())) |
||
186 | || ('Changed database context t' == |
||
187 | substr($db->getErrorMessage(), 0, 26)) |
||
188 | // @codeCoverageIgnoreEnd |
||
189 | ) { |
||
190 | $this->log("Execute SQL $id successful."); |
||
191 | $this->setSqlStatus($id, 1); |
||
192 | $this->lastIdDone = $id; |
||
193 | |||
194 | } else { |
||
195 | $this->log("Execute SQL $id failed."); |
||
196 | $this->log($this->getDbError()); |
||
197 | |||
198 | $this->setSqlStatus($id, -1); |
||
199 | $this->log("Execute abort."); |
||
200 | return; |
||
201 | } |
||
202 | |||
203 | $cntDone ++; |
||
204 | $rs->MoveNext(); |
||
205 | } |
||
206 | |||
207 | if (0 == $cntDone) { |
||
208 | $this->log('No un-done SQL to do.'); |
||
209 | } else { |
||
210 | $this->log("Total $cntDone SQL executed successful."); |
||
211 | } |
||
212 | } |
||
213 | |||
214 | |||
215 | /** |
||
216 | * Get friendly db error msg |
||
217 | * |
||
218 | * @return string |
||
219 | */ |
||
220 | protected function getDbError() |
||
221 | { |
||
222 | $db = $this->getDb(); |
||
223 | |||
224 | return 'Error ' . $db->getErrorCode() . |
||
225 | ': ' . $db->getErrorMessage(); |
||
226 | } |
||
227 | |||
228 | |||
229 | /** |
||
230 | * Get id of last SQL, ignore their execute status |
||
231 | * |
||
232 | * @return int |
||
233 | */ |
||
234 | View Code Duplication | public function getLastId() |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
235 | { |
||
236 | $db = $this->getDb(); |
||
237 | |||
238 | $sql = "SELECT id FROM $this->logTable ORDER BY id DESC"; |
||
239 | $rs = $db->SelectLimit($sql, 1); |
||
0 ignored issues
–
show
The method
SelectLimit does not exist on object<Fwlib\Bridge\Adodb> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when class ParentClass {
private $data = array();
public function __call($method, array $args) {
if (0 === strpos($method, 'get')) {
return $this->data[strtolower(substr($method, 3))];
}
throw new \LogicException(sprintf('Unsupported method: %s', $method));
}
}
/**
* If this class knows which fields exist, you can specify the methods here:
*
* @method string getName()
*/
class SomeClass extends ParentClass { }
![]() |
|||
240 | |||
241 | if ($rs->EOF) { |
||
242 | $id = -1; |
||
243 | } else { |
||
244 | $id = $rs->fields['id']; |
||
245 | } |
||
246 | |||
247 | $this->lastId = $id; |
||
248 | return $id; |
||
249 | } |
||
250 | |||
251 | |||
252 | /** |
||
253 | * Get id of last successful executed sql |
||
254 | * |
||
255 | * @return int |
||
256 | */ |
||
257 | View Code Duplication | public function getLastIdDone() |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
258 | { |
||
259 | $db = $this->getDb(); |
||
260 | |||
261 | $sql = "SELECT id FROM $this->logTable WHERE done=1 ORDER BY id DESC"; |
||
262 | $rs = $db->SelectLimit($sql, 1); |
||
0 ignored issues
–
show
The method
SelectLimit does not exist on object<Fwlib\Bridge\Adodb> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when class ParentClass {
private $data = array();
public function __call($method, array $args) {
if (0 === strpos($method, 'get')) {
return $this->data[strtolower(substr($method, 3))];
}
throw new \LogicException(sprintf('Unsupported method: %s', $method));
}
}
/**
* If this class knows which fields exist, you can specify the methods here:
*
* @method string getName()
*/
class SomeClass extends ParentClass { }
![]() |
|||
263 | |||
264 | if ($rs->EOF) { |
||
265 | $id = -1; |
||
266 | } else { |
||
267 | $id = $rs->fields['id']; |
||
268 | } |
||
269 | |||
270 | $this->lastIdDone = $id; |
||
271 | return $id; |
||
272 | } |
||
273 | |||
274 | |||
275 | /** |
||
276 | * Getter of $logTable |
||
277 | * |
||
278 | * @return string |
||
279 | */ |
||
280 | public function getLogTable() |
||
281 | { |
||
282 | return $this->logTable; |
||
283 | } |
||
284 | |||
285 | |||
286 | /** |
||
287 | * Print log message |
||
288 | * |
||
289 | * @param string $msg |
||
290 | * @param boolean $newline |
||
291 | */ |
||
292 | public function log($msg = '', $newline = true) |
||
293 | { |
||
294 | if ($newline) { |
||
295 | $msg = $this->getUtilContainer()->getEnv()->ecl($msg, true); |
||
296 | } |
||
297 | |||
298 | echo $msg; |
||
299 | } |
||
300 | |||
301 | |||
302 | /** |
||
303 | * @param Adodb $db |
||
304 | * @return static |
||
305 | */ |
||
306 | public function setDb(Adodb $db) |
||
307 | { |
||
308 | $this->setDbInstance($db); |
||
309 | |||
310 | $this->checkLogTable(); |
||
311 | |||
312 | return $this; |
||
313 | } |
||
314 | |||
315 | |||
316 | /** |
||
317 | * @param string $logTable |
||
318 | * @return static |
||
319 | */ |
||
320 | public function setLogTable($logTable) |
||
321 | { |
||
322 | $this->logTable = $logTable; |
||
323 | |||
324 | return $this; |
||
325 | } |
||
326 | |||
327 | |||
328 | /** |
||
329 | * Write sql to log table, without execute |
||
330 | * |
||
331 | * This method will call directly in schema SQL define file, one call for |
||
332 | * one SQL, so it's hard to use db prepare for speed optimize, and it's a |
||
333 | * little over design too. |
||
334 | * |
||
335 | * @param int $id |
||
336 | * @param string $sqltext |
||
337 | */ |
||
338 | public function setSql($id, $sqltext) |
||
339 | { |
||
340 | $db = $this->getDb(); |
||
341 | |||
342 | if (-1 == $this->lastId) { |
||
343 | $this->getLastId(); |
||
344 | } |
||
345 | |||
346 | // Will not overwrite exists id. |
||
347 | if ($id > $this->lastId) { |
||
348 | $sqltext = addslashes($sqltext); |
||
349 | $sqltext = $db->convertEncodingSql($sqltext); |
||
350 | |||
351 | $sql = "INSERT INTO $this->logTable (id, sqltext) |
||
352 | VALUES ($id, '$sqltext')"; |
||
353 | $db->Execute($sql); |
||
354 | |||
355 | if (0 != $db->getErrorCode()) { |
||
356 | // Should not occur |
||
357 | // @codeCoverageIgnoreStart |
||
358 | $this->log($this->getDbError()); |
||
359 | $this->log('Store SQL failed.'); |
||
360 | exit; |
||
361 | // @codeCoverageIgnoreEnd |
||
362 | |||
363 | } else { |
||
364 | $this->lastId = $id; |
||
365 | } |
||
366 | } |
||
367 | } |
||
368 | |||
369 | |||
370 | /** |
||
371 | * Set status of a SQL stored in log table |
||
372 | * |
||
373 | * @param int $id |
||
374 | * @param int $status {0: not_executed, 1: execute_ok, -1: execute_fail} |
||
375 | */ |
||
376 | protected function setSqlStatus($id, $status) |
||
377 | { |
||
378 | $db = $this->getDb(); |
||
379 | |||
380 | $sql = "UPDATE $this->logTable SET done=$status WHERE id=$id"; |
||
381 | $db->Execute($sql); |
||
382 | } |
||
383 | } |
||
384 |
If you implement
__call
and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.This is often the case, when
__call
is implemented by a parent class and only the child class knows which methods exist: