Completed
Push — master ( 04d19a...743d3f )
by Simon
02:19
created

includes/PdoDatabase.php (2 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
3
/**
4
 * @param string $db
5
 * @return PdoDatabase
6
 * @throws Exception
7
 */
8
function gGetDb($db = "acc")
9
{
10
	return PdoDatabase::getDatabaseConnection($db);
11
}
12
13
class PdoDatabase extends PDO
14
{
15
	/**
16
	 * @var PdoDatabase[]
17
	 */
18
	private static $connections = array();
19
20
	/**
21
	 * @var bool True if a transaction is active
22
	 */
23
	protected $hasActiveTransaction = false;
24
    
25
	/**
26
	 * Summary of $queryLogStatement
27
	 * @var PDOStatement
28
	 */
29
	private $queryLogStatement;
30
31
	/**
32
	 * @param string $connectionName
33
	 * @return PdoDatabase
34
	 * @throws Exception
35
	 */
36
	public static function getDatabaseConnection($connectionName)
37
	{
38
		if (!isset(self::$connections[$connectionName])) {
39
			global $cDatabaseConfig;
40
41
			if (!array_key_exists($connectionName, $cDatabaseConfig)) {
42
				throw new Exception("Database configuration not found for alias $connectionName");
43
			}
44
45
			try {
46
				$databaseObject = new PdoDatabase(
47
					$cDatabaseConfig[$connectionName]["dsrcname"],
48
					$cDatabaseConfig[$connectionName]["username"],
49
					$cDatabaseConfig[$connectionName]["password"],
50
					$cDatabaseConfig[$connectionName]["options"]
51
				);
52
			}
53
			catch (PDOException $ex) {
54
				// wrap around any potential stack traces which may include passwords
55
				throw new Exception("Error connecting to database '$connectionName': " . $ex->getMessage());
56
			}
57
58
			$databaseObject->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
59
60
			// emulating prepared statements gives a performance boost on MySQL.
61
			//
62
			// however, our version of PDO doesn't seem to understand parameter types when emulating
63
			// the prepared statements, so we're forced to turn this off for now.
64
			// -- stw 2014-02-11
65
			$databaseObject->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
66
67
			self::$connections[$connectionName] = $databaseObject;
68
		}
69
70
		return self::$connections[$connectionName];
71
	}
72
73
	/**
74
	 * Determines if this connection has a transaction in progress or not
75
	 * @return boolean true if there is a transaction in progress.
76
	 */
77
	public function hasActiveTransaction()
78
	{
79
		return $this->hasActiveTransaction;
80
	}
81
82
	/**
83
	 * Summary of beginTransaction
84
	 * @return bool
85
	 */
86
	public function beginTransaction()
87
	{
88
		// Override the pre-existing method, which doesn't stop you from
89
		// starting transactions within transactions - which doesn't work and
90
		// will throw an exception. This eliminates the need to catch exceptions
91
		// all over the rest of the code
92
		if ($this->hasActiveTransaction) {
93
			return false;
94
		}
95
		else {
96
			// set the transaction isolation level for every transaction.
97
			$this->exec("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;");
98
99
			// start a new transaction, and return whether or not the start was
100
			// successful
101
			$this->hasActiveTransaction = parent::beginTransaction();
102
			return $this->hasActiveTransaction;
103
		}
104
	}
105
106
	/**
107
	 * Commits the active transaction
108
	 */
109
	public function commit()
110
	{
111
		parent::commit();
112
		$this->hasActiveTransaction = false;
113
	}
114
115
	/**
116
	 * Rolls back a transaction
117
	 */
118
	public function rollBack()
119
	{
120
		parent::rollback();
121
		$this->hasActiveTransaction = false;
122
	}
123
124
	/**
125
	 * Summary of transactionally
126
	 * @param Closure $method 
127
	 */
128
	public function transactionally($method)
129
	{
130
		if (!$this->beginTransaction()) {
131
			BootstrapSkin::displayAlertBox("Error starting database transaction.", "alert-error", "Database transaction error", true, false);
132
			BootstrapSkin::displayInternalFooter();
133
			die();
134
		}
135
136
		try {
137
			$method();
138
139
			$this->commit();
140
		}
141
		catch (TransactionException $ex) {
142
			$this->rollBack();
143
144
			BootstrapSkin::displayAlertBox($ex->getMessage(), $ex->getAlertType(), $ex->getTitle(), true, false);
145
146
			// TODO: yuk.
147
			if (defined("PUBLICMODE")) {
148
				BootstrapSkin::displayPublicFooter();
149
			}
150
			else {
151
				BootstrapSkin::displayInternalFooter();
152
			}
153
154
			die();
155
		}
156
	}
157
158
	/**
159
	 * Prepares a statement for execution.
160
	 * @param string $statement 
161
	 * @param array $driver_options 
162
	 * @return PDOStatement
163
	 */
164
	public function prepare($statement, $driver_options = array())
0 ignored issues
show
prepare uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style Naming introduced by
The parameter $driver_options is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
165
	{
166
		global $enableQueryLog;
167
		if ($enableQueryLog) {
168
			try {
169
				if ($this->queryLogStatement === null) {
170
					$this->queryLogStatement = 
171
						parent::prepare(<<<SQL
172
							INSERT INTO applicationlog (source, message, stack, request, request_ts) 
173
							VALUES (:source, :message, :stack, :request, :rqts);
174
SQL
175
						);
176
				}
177
178
				$this->queryLogStatement->execute(
179
					array(
180
						":source" => "QueryLog",
181
						":message" => $statement,
182
						":stack" => DebugHelper::getBacktrace(),
183
						":request" => $_SERVER["REQUEST_URI"],
184
						":rqts" => $_SERVER["REQUEST_TIME_FLOAT"],
185
					)
186
				);
187
			}
188
			catch (Exception $ex) {
189
				trigger_error("Error logging query. Disabling for this request. " . $ex->getMessage(), E_USER_NOTICE);
190
				$enableQueryLog = false;
191
			}
192
		}
193
        
194
		return parent::prepare($statement, $driver_options);   
195
	}
196
}
197