Issues (2473)

Branch: master

Security Analysis    no vulnerabilities found

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

engine/classes/Elgg/Database.php (4 issues)

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
2
namespace Elgg;
3
use Elgg\Database\Config;
0 ignored issues
show
This use statement conflicts with another class in this namespace, Elgg\Config.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
4
5
/**
6
 * An object representing a single Elgg database.
7
 *
8
 * WARNING: THIS API IS IN FLUX. PLUGIN AUTHORS SHOULD NOT USE. See lib/database.php instead.
9
 *
10
 * @access private
11
 *
12
 * @package    Elgg.Core
13
 * @subpackage Database
14
 */
15
class Database {
16
17
	/** @var string $tablePrefix Prefix for database tables */
18
	private $tablePrefix;
19
20
	/** @var resource[] $dbLinks Database connection resources */
21
	private $dbLinks = array();
22
23
	/** @var int $queryCount The number of queries made */
24
	private $queryCount = 0;
25
26
	/**
27
	 * Query cache for select queries.
28
	 *
29
	 * Queries and their results are stored in this cache as:
30
	 * <code>
31
	 * $DB_QUERY_CACHE[query hash] => array(result1, result2, ... resultN)
32
	 * </code>
33
	 * @see \Elgg\Database::getResults() for details on the hash.
34
	 *
35
	 * @var \Elgg\Cache\LRUCache $queryCache The cache
36
	 */
37
	private $queryCache = null;
38
39
	/**
40
	 * @var int $queryCacheSize The number of queries to cache
41
	 */
42
	private $queryCacheSize = 50;
43
44
	/**
45
	 * Queries are saved to an array and executed using
46
	 * a function registered by register_shutdown_function().
47
	 *
48
	 * Queries are saved as an array in the format:
49
	 * <code>
50
	 * $this->delayedQueries[] = array(
51
	 * 	'q' => string $query,
52
	 * 	'l' => string $query_type,
53
	 * 	'h' => string $handler // a callback function
54
	 * );
55
	 * </code>
56
	 *
57
	 * @var array $delayedQueries Queries to be run during shutdown
58
	 */
59
	private $delayedQueries = array();
60
61
	/** @var bool $installed Is the database installed? */
62
	private $installed = false;
63
64
	/** @var \Elgg\Database\Config $config Database configuration */
65
	private $config;
66
67
	/** @var \Elgg\Logger $logger The logger */
68
	private $logger;
69
70
	/**
71
	 * Constructor
72
	 *
73
	 * @param \Elgg\Database\Config $config Database configuration
74
	 * @param \Elgg\Logger          $logger The logger
75
	 */
76 7
	public function __construct(\Elgg\Database\Config $config, \Elgg\Logger $logger) {
77
78 7
		$this->logger = $logger;
79 7
		$this->config = $config;
80
81 7
		$this->tablePrefix = $config->getTablePrefix();
82
83 7
		$this->enableQueryCache();
84 7
	}
85
86
	/**
87
	 * Gets (if required, also creates) a database link resource.
88
	 *
89
	 * The database link resources are created by
90
	 * {@link \Elgg\Database::setupConnections()}, which is called if no links exist.
91
	 *
92
	 * @param string $type The type of link we want: "read", "write" or "readwrite".
93
	 *
94
	 * @return resource Database link
95
	 * @throws \DatabaseException
96
	 * @todo make protected once we get rid of get_db_link()
97
	 */
98
	public function getLink($type) {
99
		if (isset($this->dbLinks[$type])) {
100
			return $this->dbLinks[$type];
101
		} else if (isset($this->dbLinks['readwrite'])) {
102
			return $this->dbLinks['readwrite'];
103
		} else {
104
			$this->setupConnections();
105
			return $this->getLink($type);
106
		}
107
	}
108
109
	/**
110
	 * Establish database connections
111
	 *
112
	 * If the configuration has been set up for multiple read/write databases, set those
113
	 * links up separately; otherwise just create the one database link.
114
	 *
115
	 * @return void
116
	 * @throws \DatabaseException
117
	 */
118
	public function setupConnections() {
119
		if ($this->config->isDatabaseSplit()) {
120
			$this->establishLink('read');
121
			$this->establishLink('write');
122
		} else {
123
			$this->establishLink('readwrite');
124
		}
125
	}
126
127
128
	/**
129
	 * Establish a connection to the database server
130
	 *
131
	 * Connect to the database server and use the Elgg database for a particular database link
132
	 *
133
	 * @param string $dblinkname The type of database connection. Used to identify the
134
	 * resource: "read", "write", or "readwrite".
135
	 *
136
	 * @return void
137
	 * @throws \DatabaseException
138
	 */
139
	public function establishLink($dblinkname = "readwrite") {
140
141
		$conf = $this->config->getConnectionConfig($dblinkname);
142
143
		// Connect to database
144
		$this->dbLinks[$dblinkname] = mysql_connect($conf['host'], $conf['user'], $conf['password'], true);
145
		if (!$this->dbLinks[$dblinkname]) {
146
			$msg = "Elgg couldn't connect to the database using the given credentials. Check the settings file.";
147
			throw new \DatabaseException($msg);
148
		}
149
150
		if (!mysql_select_db($conf['database'], $this->dbLinks[$dblinkname])) {
151
			$msg = "Elgg couldn't select the database '{$conf['database']}'. Please check that the database is created and you have access to it.";
152
			throw new \DatabaseException($msg);
153
		}
154
155
		// Set DB for UTF8
156
		mysql_query("SET NAMES utf8", $this->dbLinks[$dblinkname]);
157
	}
158
159
	/**
160
	 * Retrieve rows from the database.
161
	 *
162
	 * Queries are executed with {@link \Elgg\Database::executeQuery()} and results
163
	 * are retrieved with {@link mysql_fetch_object()}.  If a callback
164
	 * function $callback is defined, each row will be passed as a single
165
	 * argument to $callback.  If no callback function is defined, the
166
	 * entire result set is returned as an array.
167
	 *
168
	 * @param mixed  $query    The query being passed.
169
	 * @param string $callback Optionally, the name of a function to call back to on each row
170
	 *
171
	 * @return array An array of database result objects or callback function results. If the query
172
	 *               returned nothing, an empty array.
173
	 * @throws \DatabaseException
174
	 */
175
	public function getData($query, $callback = '') {
176
		return $this->getResults($query, $callback, false);
177
	}
178
179
	/**
180
	 * Retrieve a single row from the database.
181
	 *
182
	 * Similar to {@link \Elgg\Database::getData()} but returns only the first row
183
	 * matched.  If a callback function $callback is specified, the row will be passed
184
	 * as the only argument to $callback.
185
	 *
186
	 * @param mixed  $query    The query to execute.
187
	 * @param string $callback A callback function
188
	 *
189
	 * @return mixed A single database result object or the result of the callback function.
190
	 * @throws \DatabaseException
191
	 */
192
	public function getDataRow($query, $callback = '') {
193
		return $this->getResults($query, $callback, true);
194
	}
195
196
	/**
197
	 * Insert a row into the database.
198
	 *
199
	 * @note Altering the DB invalidates all queries in the query cache.
200
	 *
201
	 * @param mixed $query The query to execute.
202
	 *
203
	 * @return int|false The database id of the inserted row if a AUTO_INCREMENT field is
204
	 *                   defined, 0 if not, and false on failure.
205
	 * @throws \DatabaseException
206
	 */
207 View Code Duplication
	public function insertData($query) {
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.

Loading history...
208
209
		$this->logger->log("DB query $query", \Elgg\Logger::INFO);
210
211
		$dblink = $this->getLink('write');
212
213
		$this->invalidateQueryCache();
214
215
		if ($this->executeQuery("$query", $dblink)) {
216
			return mysql_insert_id($dblink);
217
		}
218
219
		return false;
220
	}
221
222
	/**
223
	 * Update the database.
224
	 *
225
	 * @note Altering the DB invalidates all queries in the query cache.
226
	 *
227
	 * @param string $query      The query to run.
228
	 * @param bool   $getNumRows Return the number of rows affected (default: false)
229
	 *
230
	 * @return bool|int
231
	 * @throws \DatabaseException
232
	 */
233
	public function updateData($query, $getNumRows = false) {
234
235
		$this->logger->log("DB query $query", \Elgg\Logger::INFO);
236
237
		$dblink = $this->getLink('write');
238
239
		$this->invalidateQueryCache();
240
241
		if ($this->executeQuery("$query", $dblink)) {
242
			if ($getNumRows) {
243
				return mysql_affected_rows($dblink);
244
			} else {
245
				return true;
246
			}
247
		}
248
249
		return false;
250
	}
251
252
	/**
253
	 * Delete data from the database
254
	 *
255
	 * @note Altering the DB invalidates all queries in query cache.
256
	 *
257
	 * @param string $query The SQL query to run
258
	 *
259
	 * @return int|false The number of affected rows or false on failure
260
	 * @throws \DatabaseException
261
	 */
262 View Code Duplication
	public function deleteData($query) {
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.

Loading history...
263
264
		$this->logger->log("DB query $query", \Elgg\Logger::INFO);
265
266
		$dblink = $this->getLink('write');
267
268
		$this->invalidateQueryCache();
269
270
		if ($this->executeQuery("$query", $dblink)) {
271
			return mysql_affected_rows($dblink);
272
		}
273
274
		return false;
275
	}
276
277
	/**
278
	 * Get a string that uniquely identifies a callback during the current request.
279
	 *
280
	 * This is used to cache queries whose results were transformed by the callback. If the callback involves
281
	 * object method calls of the same class, different instances will return different values.
282
	 *
283
	 * @param callable $callback The callable value to fingerprint
284
	 *
285
	 * @return string A string that is unique for each callable passed in
286
	 * @since 1.9.4
287
	 * @access private
288
	 * @todo Make this protected once we can setAccessible(true) via reflection
289
	 */
290 1
	public function fingerprintCallback($callback) {
291 1
		if (is_string($callback)) {
292 1
			return $callback;
293
		}
294 1
		if (is_object($callback)) {
295 1
			return spl_object_hash($callback) . "::__invoke";
296
		}
297 1
		if (is_array($callback)) {
298 1
			if (is_string($callback[0])) {
299 1
				return "{$callback[0]}::{$callback[1]}";
300
			}
301 1
			return spl_object_hash($callback[0]) . "::{$callback[1]}";
302
		}
303
		// this should not happen
304
		return "";
305
	}
306
307
	/**
308
	 * Handles queries that return results, running the results through a
309
	 * an optional callback function. This is for R queries (from CRUD).
310
	 *
311
	 * @param string $query    The select query to execute
312
	 * @param string $callback An optional callback function to run on each row
313
	 * @param bool   $single   Return only a single result?
314
	 *
315
	 * @return array An array of database result objects or callback function results. If the query
316
	 *               returned nothing, an empty array.
317
	 * @throws \DatabaseException
318
	 */
319
	protected function getResults($query, $callback = null, $single = false) {
320
321
		// Since we want to cache results of running the callback, we need to
322
		// need to namespace the query with the callback and single result request.
323
		// https://github.com/elgg/elgg/issues/4049
324
		$query_id = (int)$single . $query . '|';
325
		if ($callback) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $callback of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
326
			$is_callable = is_callable($callback);
327
			if ($is_callable) {
328
				$query_id .= $this->fingerprintCallback($callback);
329
			} else {
330
				// TODO do something about invalid callbacks
331
				$callback = null;
332
			}
333
		} else {
334
			$is_callable = false;
335
		}
336
		// MD5 yields smaller mem usage for cache and cleaner logs
337
		$hash = md5($query_id);
338
339
		// Is cached?
340 View Code Duplication
		if ($this->queryCache) {
341
			if (isset($this->queryCache[$hash])) {
342
				$this->logger->log("DB query $query results returned from cache (hash: $hash)", \Elgg\Logger::INFO);
343
				return $this->queryCache[$hash];
344
			}
345
		}
346
347
		$dblink = $this->getLink('read');
348
		$return = array();
349
350
		if ($result = $this->executeQuery("$query", $dblink)) {
351
			while ($row = mysql_fetch_object($result)) {
352
				if ($is_callable) {
353
					$row = call_user_func($callback, $row);
354
				}
355
356
				if ($single) {
357
					$return = $row;
358
					break;
359
				} else {
360
					$return[] = $row;
361
				}
362
			}
363
		}
364
365
		if (empty($return)) {
366
			$this->logger->log("DB query $query returned no results.", \Elgg\Logger::INFO);
367
		}
368
369
		// Cache result
370 View Code Duplication
		if ($this->queryCache) {
371
			$this->queryCache[$hash] = $return;
372
			$this->logger->log("DB query $query results cached (hash: $hash)", \Elgg\Logger::INFO);
373
		}
374
375
		return $return;
376
	}
377
378
	/**
379
	 * Execute a query.
380
	 *
381
	 * $query is executed via {@link mysql_query()}.  If there is an SQL error,
382
	 * a {@link DatabaseException} is thrown.
383
	 *
384
	 * @param string   $query  The query
385
	 * @param resource $dblink The DB link
386
	 *
387
	 * @return resource|bool The result of mysql_query()
388
	 * @throws \DatabaseException
389
	 * @todo should this be public?
390
	 */
391
	public function executeQuery($query, $dblink) {
392
393
		if ($query == null) {
394
			throw new \DatabaseException("Query cannot be null");
395
		}
396
397
		if (!is_resource($dblink)) {
398
			throw new \DatabaseException("Connection to database was lost.");
399
		}
400
401
		$this->queryCount++;
402
403
		$result = mysql_query($query, $dblink);
404
405
		if (mysql_errno($dblink)) {
406
			throw new \DatabaseException(mysql_error($dblink) . "\n\n QUERY: $query");
407
		}
408
409
		return $result;
410
	}
411
412
	/**
413
	 * Runs a full database script from disk.
414
	 *
415
	 * The file specified should be a standard SQL file as created by
416
	 * mysqldump or similar.  Statements must be terminated with ;
417
	 * and a newline character (\n or \r\n).
418
	 *
419
	 * The special string 'prefix_' is replaced with the database prefix
420
	 * as defined in {@link $this->tablePrefix}.
421
	 *
422
	 * @warning Only single line comments are supported. A comment
423
	 * must start with '-- ' or '# ', where the comment sign is at the
424
	 * very beginning of each line.
425
	 *
426
	 * @warning Errors do not halt execution of the script.  If a line
427
	 * generates an error, the error message is saved and the
428
	 * next line is executed.  After the file is run, any errors
429
	 * are displayed as a {@link DatabaseException}
430
	 *
431
	 * @param string $scriptlocation The full path to the script
432
	 *
433
	 * @return void
434
	 * @throws \DatabaseException
435
	 */
436 5
	public function runSqlScript($scriptlocation) {
437 5
		$script = file_get_contents($scriptlocation);
438 5
		if ($script) {
439
440 5
			$errors = array();
441
442
			// Remove MySQL '-- ' and '# ' style comments
443 5
			$script = preg_replace('/^(?:--|#) .*$/m', '', $script);
444
445
			// Statements must end with ; and a newline
446 5
			$sql_statements = preg_split('/;[\n\r]+/', "$script\n");
447
448 5
			foreach ($sql_statements as $statement) {
449 5
				$statement = trim($statement);
450 5
				$statement = str_replace("prefix_", $this->tablePrefix, $statement);
451 5
				if (!empty($statement)) {
452
					try {
453 5
						$this->updateData($statement);
454 5
					} catch (\DatabaseException $e) {
455
						$errors[] = $e->getMessage();
456
					}
457 5
				}
458 5
			}
459 5
			if (!empty($errors)) {
460
				$errortxt = "";
461
				foreach ($errors as $error) {
462
					$errortxt .= " {$error};";
463
				}
464
465
				$msg = "There were a number of issues: " . $errortxt;
466
				throw new \DatabaseException($msg);
467
			}
468 5
		} else {
469
			$msg = "Elgg couldn't find the requested database script at " . $scriptlocation . ".";
470
			throw new \DatabaseException($msg);
471
		}
472 5
	}
473
474
	/**
475
	 * Queue a query for execution upon shutdown.
476
	 *
477
	 * You can specify a handler function if you care about the result. This function will accept
478
	 * the raw result from {@link mysql_query()}.
479
	 *
480
	 * @param string $query   The query to execute
481
	 * @param string $type    The query type ('read' or 'write')
482
	 * @param string $handler A callback function to pass the results array to
483
	 *
484
	 * @return boolean Whether registering was successful.
485
	 * @todo deprecate passing resource for $type as that should not be part of public API
486
	 */
487
	public function registerDelayedQuery($query, $type, $handler = "") {
488
489
		if (!is_resource($type) && $type != 'read' && $type != 'write') {
490
			return false;
491
		}
492
493
		// Construct delayed query
494
		$delayed_query = array();
495
		$delayed_query['q'] = $query;
496
		$delayed_query['l'] = $type;
497
		$delayed_query['h'] = $handler;
498
499
		$this->delayedQueries[] = $delayed_query;
500
501
		return true;
502
	}
503
504
505
	/**
506
	 * Trigger all queries that were registered as "delayed" queries. This is
507
	 * called by the system automatically on shutdown.
508
	 *
509
	 * @return void
510
	 * @access private
511
	 * @todo make protected once this class is part of public API
512
	 */
513
	public function executeDelayedQueries() {
514
515
		foreach ($this->delayedQueries as $query_details) {
516
			try {
517
				$link = $query_details['l'];
518
519
				if ($link == 'read' || $link == 'write') {
520
					$link = $this->getLink($link);
521
				} elseif (!is_resource($link)) {
522
					$msg = "Link for delayed query not valid resource or db_link type. Query: {$query_details['q']}";
523
					$this->logger->log($msg, \Elgg\Logger::WARNING);
524
				}
525
526
				$result = $this->executeQuery($query_details['q'], $link);
527
528
				if ((isset($query_details['h'])) && (is_callable($query_details['h']))) {
529
					$query_details['h']($result);
530
				}
531
			} catch (\DatabaseException $e) {
532
				// Suppress all exceptions since page already sent to requestor
533
				$this->logger->log($e, \Elgg\Logger::ERROR);
534
			}
535
		}
536
	}
537
538
	/**
539
	 * Enable the query cache
540
	 * 
541
	 * This does not take precedence over the \Elgg\Database\Config setting.
542
	 * 
543
	 * @return void
544
	 */
545 7
	public function enableQueryCache() {
546 7
		if ($this->config->isQueryCacheEnabled() && $this->queryCache === null) {
547
			// @todo if we keep this cache, expose the size as a config parameter
548 7
			$this->queryCache = new \Elgg\Cache\LRUCache($this->queryCacheSize);
549 7
		}
550 7
	}
551
552
	/**
553
	 * Disable the query cache
554
	 * 
555
	 * This is useful for special scripts that pull large amounts of data back
556
	 * in single queries.
557
	 * 
558
	 * @return void
559
	 */
560
	public function disableQueryCache() {
561
		$this->queryCache = null;
562
	}
563
564
	/**
565
	 * Invalidate the query cache
566
	 *
567
	 * @return void
568
	 */
569
	protected function invalidateQueryCache() {
570
		if ($this->queryCache) {
571
			$this->queryCache->clear();
572
			$this->logger->log("Query cache invalidated", \Elgg\Logger::INFO);
573
		}
574
	}
575
576
	/**
577
	 * Test that the Elgg database is installed
578
	 *
579
	 * @return void
580
	 * @throws \InstallationException
581
	 */
582
	public function assertInstalled() {
583
584
		if ($this->installed) {
585
			return;
586
		}
587
588
		try {
589
			$dblink = $this->getLink('read');
590
			mysql_query("SELECT value FROM {$this->tablePrefix}datalists WHERE name = 'installed'", $dblink);
591
			if (mysql_errno($dblink) > 0) {
592
				throw new \DatabaseException();
593
			}
594
		} catch (\DatabaseException $e) {
595
			throw new \InstallationException("Unable to handle this request. This site is not configured or the database is down.");
596
		}
597
598
		$this->installed = true;
599
	}
600
601
	/**
602
	 * Get the number of queries made to the database
603
	 *
604
	 * @return int
605
	 */
606
	public function getQueryCount() {
607
		return $this->queryCount;
608
	}
609
610
	/**
611
	 * Get the prefix for Elgg's tables
612
	 *
613
	 * @return string
614
	 */
615 1
	public function getTablePrefix() {
616 1
		return $this->tablePrefix;
617
	}
618
619
	/**
620
	 * Sanitizes an integer value for use in a query
621
	 *
622
	 * @param int  $value  Value to sanitize
623
	 * @param bool $signed Whether negative values are allowed (default: true)
624
	 * @return int
625
	 */
626 3
	public function sanitizeInt($value, $signed = true) {
627 3
		$value = (int) $value;
628
629 3
		if ($signed === false) {
630
			if ($value < 0) {
631
				$value = 0;
632
			}
633
		}
634
635 3
		return $value;
636
	}
637
638
	/**
639
	 * Sanitizes a string for use in a query
640
	 *
641
	 * @param string $value Value to escape
642
	 * @return string
643
	 */
644
	public function sanitizeString($value) {
645
646
		// use resource if established, but don't open a connection to do it.
647
		if (isset($this->dbLinks['read'])) {
648
			return mysql_real_escape_string($value, $this->dbLinks['read']);
649
		}
650
651
		return mysql_real_escape_string($value);
652
	}
653
654
	/**
655
	 * Get the server version number
656
	 *
657
	 * @param string $type Connection type (Config constants, e.g. Config::READ_WRITE)
658
	 *
659
	 * @return string
660
	 */
661
	public function getServerVersion($type) {
662
		return mysql_get_server_info($this->getLink($type));
663
	}
664
}
665
666