DbUpdater::Log()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package		fwolflib
4
 * @subpackage	class
5
 * @copyright	Copyright 2006-2009, Fwolf
6
 * @author		Fwolf <[email protected]>
7
 */
8
9
10
require_once(dirname(__FILE__) . '/fwolflib.php');
11
require_once(FWOLFLIB . 'class/adodb.php');
12
require_once(FWOLFLIB . 'func/env.php');
13
14
15
//// From fwolflib r12, Don't modify it outside fwolflib ! ////
16
17
/**
18
 * Database mantance & update tools
19
 * Used in develop or sync multi databases' structure
20
 *
21
 * It use a table to 'remember' every update done or to be done to db
22
 * and, all update to db here is defined to 'sql' lang format,
23
 * I hope this can meets all my needs.
24
 *
25
 * All updates MUST be done by step order, so when a last-update-id
26
 * is set, it's consided all updates before last-update-id is done.
27
 *
28
 * Additional tools do similar task:
29
 * http://xml2ddl.berlios.de/
30
 *
31
 * @deprecated  Use Fwlib\Db\SyncDbSchema
32
 * @package		fwolflib
33
 * @subpackage	class
34
 * @copyright	Copyright 2006-2009, Fwolf
35
 * @author		Fwolf <[email protected]>
36
 * @since		2006-12-10
37
 * @version		$Id$
38
 */
39
class DbUpdater extends Fwolflib {
0 ignored issues
show
Deprecated Code introduced by
The class Fwolflib has been deprecated with message: Use classes in Fwlib namespace, see PSR-0/1/2

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
40
	/**
41
	 * Db server information array
42
	 * 	Array item: type, host, user, pass, name.
43
	 * @var	array
44
	 */
45
	private $aServer = array();
46
47
	/**
48
	 * Last update id
49
	 * @var int
50
	 */
51
	public $iLastId = 0;
52
53
	/**
54
	 * Last done update id
55
	 * @var	int
56
	 */
57
	public $iLastDoneId = 0;
58
59
	/**
60
	 * Charset of database
61
	 * If charset of db diff from os, do convert when execute sql.
62
	 * @var	string
63
	 */
64
	public $sCharsetDb = '';
65
66
	/**
67
	 * Charset of operation system
68
	 * @var string
69
	 * @see	$sCharsetDb
70
	 */
71
	public $sCharsetOs = '';
72
73
	/**
74
	 * Summary text
75
	 * @var string
76
	 */
77
	public $sSummary = '';
78
79
	/**
80
	 * Table to save modifications and logs
81
	 *
82
	 * If u want to change log table name in use(had log some sql already)
83
	 * remember to rename table in database also.
84
	 * @var	string
85
	 */
86
	public $sTblLog = 'log_dbupdater';
87
88
89
	/**
90
	 * Db connection object
91
	 * @var object
92
	 */
93
	public $oDb;
94
95
	/**
96
	 * Construct function
97
	 * @access public
98
	 * @param	array	$server	Db server information
0 ignored issues
show
Documentation introduced by
There is no parameter named $server. Did you maybe mean $dbserver?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
99
	 */
100
	function __construct($dbserver=array())
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
101
	{
102
		if (!empty($dbserver))
103
			$this->SetDatabase($dbserver);
104
105
		// Check and install log table
106
		$this->CheckLogTbl();
107
	} // end of func construct
108
109
110
	/**
111
	 * Check and install log table if not exists
112
	 * @access	public
113
	 */
114
	public function CheckLogTbl()
115
	{
116
		$logtbl_not_exists = false;
117
		if ('sybase' == $this->aServer['type'] ||
118
			'sybase_ase' == $this->aServer['type'])
119
		{
120
			$sql = "select count(1) as c from sysobjects where name = '$this->sTblLog' and type = 'U'";
121
			$rs = $this->oDb->Execute($sql);
122
			if (0 == $rs->fields['c'])
123
				$logtbl_not_exists = true;
124
		}
125
		elseif ('mysql' == $this->aServer['type'] ||
126
				'mysqli' == $this->aServer['type'])
127
		{
128
			$sql = "SHOW TABLES LIKE '$this->sTblLog'";
129
			$rs = $this->oDb->Execute($sql);
130
			if (0 == $rs->RowCount())
131
				$logtbl_not_exists = true;
132
		}
133
		else
134
		{
135
			$sql = "select 1 from $this->sTblLog";
136
			$rs = $this->oDb->Execute($sql);
0 ignored issues
show
Unused Code introduced by
$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 $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...
137
			if (0 == $this->oDb->ErrorNo())
138
				$logtbl_not_exists = true;
139
		}
140
141
		if (true == $logtbl_not_exists)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
142
		{
143
			// Table doesn't exist, create it
144
			// 'sql' is a reserved word, so sqtext is used.
145
			// SQL for Create table diffs from several db
146
			if ('sybase' == $this->aServer['type'] || 'sybase_ase' == $this->aServer['type'])
147
				$sql = "
148
CREATE TABLE $this->sTblLog (
149
	id		numeric(8) NOT NULL,
150
	comment	varchar(200) NOT NULL,
151
	done	int default 0,	-- 0:not do, -1:error, 1:done ok
152
	sqltext	text,
153
	ts		timestamp NOT NULL,
154
	constraint PK_$this->sTblLog PRIMARY KEY (id)
155
)
156
					";
157
			else
158
				// :DELETED: ) ENGINE=MyISAM AUTO_INCREMENT=1
159
				$sql = "
160
CREATE TABLE $this->sTblLog (
161
	id		int(8) NOT NULL auto_increment,
162
	comment	varchar(200) NOT NULL,
163
	done	tinyint(1) default 0,	-- 0:not do, -1:error, 1:done ok
164
	sqltext	text,
165
	ts		timestamp NOT NULL default CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
166
	PRIMARY KEY (id)
167
) AUTO_INCREMENT=1
168
					";
169
			$this->oDb->Execute($sql);
170
			if (0 < $this->oDb->ErrorNo())
171
			{
172
				$this->Log($this->oDb->ErrorNo() . ' - '  . $this->oDb->ErrorMsg() . "\n");
173
				$this->Summary();
174
				die("Log table $this->sTblLog doesn't exists and create fail.\n");
175
			}
176
177
			// Log table create information
178
			$this->Log("Log table $this->sTblLog doesn't exists, create it, done.\n");
179
		}
180
		else
181
		{
182
			// Log table exist information
183
			$this->Log("Log table $this->sTblLog already exists.\n");
184
		}
185
186
		// Get last-done-id for later usage
187
		$this->GetLastDoneId();
188
	} // end of func CheckLogTbl
189
190
191
	/**
192
	 * 获得数据库连接
193
	 * @param	array	$server
194
	 * @return object
195
	 */
196
	private function &DbConn($server)
197
	{
198
		$conn = new Adodb($server);
0 ignored issues
show
Deprecated Code introduced by
The class Adodb has been deprecated with message: Use Fwlib\Bridge\Adodb

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
199
		$conn->Connect();
200
		return $conn;
201
	} // end of func DbConn
202
203
204
	/**
205
	 * Del error record when last done.
206
	 *
207
	 * So it can rewrite/update these record in db.
208
	 * Only del failed sql is not enough,
209
	 * you need del all sql start from the failed ONE.
210
	 */
211
	public function DelErrorSql() {
212
		$sql = "SELECT id FROM {$this->sTblLog} WHERE done=-1 ORDER BY id ASC LIMIT 1";
213
		$rs = $this->oDb->Execute($sql);
214
		if (!empty($rs) && (0 < $rs->RecordCount())) {
215
			// Del sql after it
216
			$id = $rs->fields['id'];
217
			$sql = "DELETE FROM {$this->sTblLog} WHERE id >= $id";
218
			$rs = $this->oDb->Execute($sql);
0 ignored issues
show
Unused Code introduced by
$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 $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...
219
			$i = $this->oDb->Affected_Rows();
220
			// $i should > 0
221
			$this->Log("Clear $i sql start from failed sql $id.\n");
222
		}
223
	} // end of func DelErrorSql
224
225
226
	/**
227
	 * Do updates according to update-records in log table
228
	 */
229
	public function DoUpdate()
230
	{
231
		$sql = "SELECT id, sqltext FROM $this->sTblLog where done<>1 order by id asc";
232
		$rs = $this->oDb->Execute($sql);
233
		if ($rs->EOF)
234
		{
235
			$this->Log("No un-done update to do.\n");
236
		}
237
		else
238
		{
239
			// Do these updates one by one
240
			$ar = $rs->GetArray();
241
			$i_total = count($ar);
242
			for ($i=0; $i<$i_total; $i++)
243
			{
244
				$id = $ar[$i]['id'];
245
				$sqltext = stripslashes($ar[$i]['sqltext']);
246
				// Do on update
247
				// Cancel transaction because some ddl sql can't use in trans.
248
				//$this->oDb->StartTrans();
249
				$this->oDb->Execute($sqltext);
250
251
				// Bad sybase support, select db will got errormsg
252
				// Avoid sybase errormsg like: Changed database context to 'jygl'
253
				if ((0 == strlen($this->oDb->ErrorMsg()) && 0 == $this->oDb->ErrorNo()) || ('Changed database context t' == substr($this->oDb->ErrorMsg(), 0, 26)))
254
				{
255
					$this->Log("Update id $id done successful.\n");
256
					$this->SetUpdateDone($id, 1);
257
					//$this->oDb->CompleteTrans();
258
				}
259
				else
260
				{
261
					$this->Log("Update id $id done failed.\n");
262
					$this->Log($this->oDb->ErrorNo() . '-' . $this->oDb->ErrorMsg() . "\n");
263
					//$this->oDb->CompleteTrans();
264
					$this->SetUpdateDone($id, -1);
265
					$this->Summary(true);
266
					die("Doing update aborted because of failed.\n");
267
				}
268
			}
269
			// Log
270
			$this->Log("Total $i/$i_total updates done.\n");
271
		}
272
		return true;
273
	} // end of func DoUpdate
274
275
276
	/**
277
	 * Get last update id, whether done or not
278
	 * @access	public
279
	 * @return	int
280
	 */
281 View Code Duplication
	public function GetLastId()
0 ignored issues
show
Duplication introduced by
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.

Loading history...
282
	{
283
		$sql = "select id from $this->sTblLog order by id desc";
284
		$rs = $this->oDb->SelectLimit($sql, 1);
285
		if ($rs->EOF)
286
			$id = 0;
287
		else
288
			$id = $rs->fields['id'];
289
		$this->iLastId = $id;
290
		return $id;
291
	} // end of func GetLastId
292
293
294
	/**
295
	 * Get last done update id
296
	 * @access	public
297
	 * @return	int
298
	 */
299 View Code Duplication
	public function GetLastDoneId()
0 ignored issues
show
Duplication introduced by
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.

Loading history...
300
	{
301
		$sql = "select id from $this->sTblLog where done=1 order by id desc";
302
		$rs = $this->oDb->SelectLimit($sql, 1);
303
		if ($rs->EOF)
304
		{
305
			$id = 0;
306
		}
307
		else
308
		{
309
			$id = $rs->fields['id'];
310
		}
311
		$this->iLastDoneId = $id;
312
		return $id;
313
	} // end of func GetLastDoneId
314
315
316
	/**
317
	 * Return if an update is already done
318
	 * Use cached $iLastDoneId if it's non-zero, doesn't retrieve id from db.
319
	 * @access	public
320
	 * @param	int	$id
321
	 * @return	boolean
322
	 * @see		$iLastDoneId
323
	 * @see		GetLastDoneId()
324
	 */
325
	public function IfDone($id)
326
	{
327
		if (0 == $this->iLastDoneId)
328
			$this->GetLastDoneId();
329
		return ($id <= $this->iLastDoneId);
330
	} // end of func IfDone
331
332
333
	/*
334
	 * Save log
335
	 * @access	private
336
	 * @param	string	$log
337
	 */
338
	public function Log($log)
339
	{
340
		$this->sSummary .= $log;
341
	} // end of func Log
342
343
344
	/**
345
	 * Accept database information from outside class
346
	 *	Didnot validate data send in.
347
	 *	And connect to db after store infomation.
348
	 * @access	public
349
	 * @var	array	$server	array items: type, host, user, pass, name
350
	 */
351 View Code Duplication
	public function SetDatabase($server)
0 ignored issues
show
Duplication introduced by
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.

Loading history...
352
	{
353
		if (!empty($server) && is_array($server))
354
		{
355
			$this->aServer = $server;
356
			$this->oDb = &$this->DbConn($this->aServer);
357
		}
358
	} // end of func SetDatabase
359
360
361
	/**
362
	 * Set a update step, but doesn't execute it
363
	 * Use $iLastId if it's non-zero.
364
	 * @access	public
365
	 * @param	int	$id
366
	 * @param	string	$comment
367
	 * @param	string	$sqltext
368
	 * @see		$iLastId
369
	 */
370
	public function SetUpdate($id, $comment, $sqltext)
371
	{
372
		if (0 == $this->iLastId)
373
			$this->GetLastId();
374
		// Update can't be recoverd, so only worked when $id > $iLastId
375
		// And notice that update id MUST be assigned order ASC.
376
		if ($id > $this->iLastId)
377
		{
378
			$comment = addslashes($comment);
379
			$sqltext = addslashes($sqltext);
380
			$sql = "INSERT INTO $this->sTblLog (id, comment, sqltext) VALUES ($id, '$comment', '$sqltext')";
381
			// Check if iconv for sqltext is needed
382
			if ($this->sCharsetDb != $this->sCharsetOs)
383
				$sql = mb_convert_encoding($sql, $this->sCharsetDb, $this->sCharsetOs);
384
			//
385
			$this->oDb->Execute($sql);
386
			if (0 != $this->oDb->ErrorNo())
387
			{
388
				$this->Log($this->oDb->ErrorNo() . '-' . $this->oDb->ErrorMsg() . "\n");
389
				$this->Summary(true);
390
				die("Set update failed.\n");
391
			}
392
			else
393
			{
394
				$this->iLastId ++;
395
				$this->Log("Update id $id saved.\n");
396
			}
397
		}
398
	} // end of func SetUpdate
399
400
401
	/**
402
	 * Set a update record's status done or not or failed
403
	 * Didn't validate $id or $status.
404
	 * @param	int		$id		Update id
405
	 * @param	int		$status Update id's status(0/1/-1)
406
	 */
407
	private function SetUpdateDone($id, $status)
408
	{
409
		if (-1 == $status)
410
			$this->Log("Error when do update $id, {$this->oDb->ErrorNo()}:{$this->oDb->ErrorMsg()}\n");
411
		$sql = "UPDATE $this->sTblLog set done=$status where id=$id";
412
		$this->oDb->Execute($sql);
413
		//if (0 == $this->oDb->ErrorNo() && 0 == strlen($this->oDb->ErrorMsg()))
414
		if ((0 == strlen($this->oDb->ErrorMsg()) && 0 == $this->oDb->ErrorNo()) || ('Changed database context t' == substr($this->oDb->ErrorMsg(), 0, 26)))
415
			$this->Log("Update id $id's done is set to $status.\n");
416
		else
417
			die("Failed when set update id $id's done status.({$this->oDb->ErrorNo()}:{$this->oDb->ErrorMsg()})\n");
418
	} // end of func SetUpdateDone
419
420
421
	/**
422
	 * Return summary text of the whole backup process
423
	 * @param	boolean	print
424
	 * @return string
425
	 */
426
	public function Summary($print = false)
427
	{
428
		$s = '';
0 ignored issues
show
Unused Code introduced by
$s 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...
429
		if (true == IsCli())
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
Deprecated Code introduced by
The function IsCli() has been deprecated with message: Use Fwlib\Util\Env::isCli()

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
430
			$s = $this->sSummary . "\n";
431
		else
432
			$s = nl2br($this->sSummary);
433
434
		if (true == $print)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
435
			echo $s;
436
		return $s;
437
	} // end of func Summary
438
439
440
} // end of class DbUpdater
441
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
442