File modules/database/Database.class.php

Last commit: Mon Nov 30 09:57:36 2020 +0100	Jan Dankert	Fix: aborting transaction before changing the database connection; Refactoring: Cleanup databases
1 <?php 2 // OpenRat Content Management System 3 // Copyright (C) 2002-2006 Jan Dankert, jandankert@jandankert.de 4 // 5 // This program is free software; you can redistribute it and/or 6 // modify it under the terms of the GNU General Public License 7 // as published by the Free Software Foundation; either version 2 8 // of the License, or (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with this program; if not, write to the Free Software 17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19 namespace database; 20 use cms\base\Configuration as C; 21 use database\driver\PDODriver; 22 use logger\Logger; 23 use util\exception\DatabaseException; 24 25 /** 26 * Darstellung einer Datenbank-Verbindung. 27 * 28 * Fuer die echten DB-Aufrufe werden die entsprechenden 29 * Methoden des passenden Clients aufgerufen. 30 * 31 * Diese Klasse stammt urspruenglich aus dem PHP-Pear-DB-Projekt, wurde hier aber intensiv veraendert. 32 * 33 * @author Jan Dankert 34 * @package openrat.database 35 */ 36 class Database 37 { 38 /** 39 * Datenbank-Id. 40 * 41 * @var String 42 */ 43 public $id; 44 45 /** 46 * Konfiguration der Datenbank-Verbindung 47 * 48 * @var array 49 */ 50 public $conf; 51 52 /** 53 * Client. 54 * 55 * @var PDODriver 56 */ 57 private $client; 58 59 /** 60 * Schalter, ob eine Transaktion begonnen wurde. 61 * @var boolean 62 */ 63 private $transactionInProgress = false; 64 65 66 /** 67 * Default configuration. 68 * @var array 69 */ 70 private static $DEFAULT_CONFIG = [ 71 // we need at least 1 prefix or suffix, because the raw table names are partially keywords in ANSI SQL. 72 'prefix' => 'cms_', 73 'suffix' => '', 74 'enabled' => true, 75 'name' => '', 76 'description' => '', 77 'type' => 'pdo', // we are only supporting PDO 78 'driver' => 'mysql', 79 'dsn' => '', // if no DSN is given, it will be created from user,host,port. 80 'user' => '', 81 'password' => '', 82 'host' => 'localhost', 83 'port' => 0, 84 'database' => '', 85 'base64' => false, // should BLOBs be converted to Base64? 86 'persistent' => true, // persistent connections are faster 87 'charset' => 'UTF-8', // should be UTF-8 88 'connection_sql' => '', // Startup-SQL 89 'cmd' => '', // maybe you want to start a SSH tunnel here 90 'prepare' => true, // using prepared statements is a good idea 91 'transaction' => true, // using transaction is a good idea 92 'update' => 93 [ 94 ], 95 'auto_update' => true, // auto update should always be enabled 96 ]; 97 98 99 /** 100 * Kontruktor. 101 * Erwartet die Datenbank-Konfiguration als Parameter. 102 * 103 * @param array Konfiguration der Verbindung 104 */ 105 public function __construct( $dbconf ) 106 { 107 $this->conf = array_merge( 108 Database::$DEFAULT_CONFIG, // internal defaults 109 C::subset('database-default')->subset('defaults')->getConfig(), // defaults from config 110 $dbconf // per-connection DB configuration 111 ); 112 113 $this->connect(); 114 } 115 116 117 /** 118 * Verbindung zur Datenbank aufbauen. 119 * 120 * @return bool Status 121 */ 122 public function connect() 123 { 124 // Ausfuehren des Systemkommandos vor Verbindungsaufbau 125 if ( $this->conf['cmd'] ) 126 $this->executeSystemCommand( $this->conf['cmd'] ); 127 128 // Client instanziieren 129 $this->client = new PDODriver(); 130 131 // Verbindung aufbauen 132 $this->client->connect( $this->conf ); 133 134 // SQL nach Verbindungsaufbau ausfuehren. 135 if ( ! empty($this->conf['connection_sql']) ) 136 { 137 $cmd = $this->conf['connection_sql']; 138 139 $stmt = $this->sql($cmd); 140 141 $ok = $stmt->execute(); 142 143 if ( ! $ok ) 144 { 145 throw new DatabaseException( "Could not execute connection-query '".$cmd."'"); 146 } 147 } 148 149 // Setting isolation level to "read committed". 150 // if another session is committing data, we want to read that immediatly 151 if ( $this->conf['persistent']) 152 { 153 // $sql = $this->sql('ROLLBACK'); 154 // $sql->execute(); 155 // $sql = $this->sql('SET TRANSACTION ISOLATION LEVEL READ COMMITTED'); 156 // $sql->execute(); 157 } 158 159 160 Logger::debug('Database connection established'); 161 } 162 163 /** 164 * Startet eine Transaktion. 165 */ 166 public function start() 167 { 168 Logger::debug("Starting database transaction!"); 169 $this->transactionInProgress = true; 170 $this->client->start(); 171 } 172 173 174 /** 175 * Beendet und bestaetigt eine Transaktion. 176 * Falls der Schalter 'transaction' nicht gesetzt ist, passiert nichts. 177 */ 178 public function commit() 179 { 180 if ( $this->transactionInProgress ) 181 { 182 Logger::debug("Committing database transaction!"); 183 $this->client->commit(); 184 $this->transactionInProgress = false; 185 } else { 186 Logger::warn("No Transaction in progress, ignoring commit request."); 187 } 188 } 189 190 191 192 /** 193 * Setzt eine Transaktion zurueck. 194 * Falls der Schalter 'transaction' nicht gesetzt ist, passiert nichts. 195 */ 196 public function rollback() 197 { 198 if ( $this->transactionInProgress ) 199 { 200 Logger::debug("Rolling back database transaction!"); 201 $this->client->rollback(); 202 $this->transactionInProgress = false; 203 } else { 204 Logger::warn("No Transaction in progress, ignoring rollback request."); 205 } 206 } 207 208 209 public function disconnect() 210 { 211 $this->client->disconnect(); 212 $this->client = null; // clear references to the client 213 } 214 /** 215 * @param $sql string das SQL 216 * @return Statement 217 */ 218 public function sql($sql ) 219 { 220 return new Statement( $sql,$this->client,$this->conf); 221 } 222 223 224 private function executeSystemCommand( $cmd ) 225 { 226 $ausgabe = array(); 227 $rc = false; 228 229 Logger::debug("Database command executing: " . $cmd ); 230 exec($cmd, $ausgabe, $rc); 231 232 foreach ($ausgabe as $zeile) 233 Logger::debug("Database command output: " . $zeile); 234 235 if ($rc != 0) { 236 throw new DatabaseException('Command failed: ' . implode("", $ausgabe)); 237 } 238 } 239 240 241 /** 242 * database label. 243 * 244 * @return string 245 */ 246 public function getLabel() { 247 return array_values(array_filter( array( 248 @$this->conf['description'], 249 @$this->conf['name' ], 250 $this->id, 251 @$this->conf['host' ], 252 @$this->conf['driver'], 253 @$this->conf['type' ], 254 )))[0]; 255 } 256 }
Download modules/database/Database.class.php
History Mon, 30 Nov 2020 09:57:36 +0100 Jan Dankert Fix: aborting transaction before changing the database connection; Refactoring: Cleanup databases Thu, 19 Nov 2020 10:45:05 +0100 Jan Dankert Fix: Default database. Thu, 19 Nov 2020 00:45:44 +0100 Jan Dankert Security fix: We must update the login token on every login; Administrators are able to see the login tokens of users. Fri, 23 Oct 2020 23:09:52 +0200 Jan Dankert Refactoring: Using the new config classes. Sat, 26 Sep 2020 10:32:02 +0200 Jan Dankert Refactoring: No global $conf array any more. Sat, 29 Aug 2020 03:23:06 +0200 Jan Dankert Refactoring: Improved Exception-Handling; New: Generating pages using a page context which considers page aliases. Sun, 23 Feb 2020 00:03:40 +0100 Jan Dankert Refactoring: Namespaces for modules 'logger' and 'language' Mon, 20 May 2019 00:34:19 +0200 Jan Dankert Refactoring: Datenbankverbindung im Dispatcher erstellen. Bisher wurde in der Loginaction die DB-Verbindung aufgebaut, was dort falsch aufgehoben war. Tue, 23 Oct 2018 01:08:51 +0200 Jan Dankert Eine neue Methode zum Schließen der Verbindung. Wird momentan noch für das Login benötigt. Tue, 23 Oct 2018 00:54:47 +0200 Jan Dankert Warnmeldung erzeugen, wenn Commit/Rollback ohne Transaktion erfolgen soll. Sat, 21 Jul 2018 00:00:21 +0200 Jan Dankert Datenbank-Modul weiter aufgeräumt und alten Kram entfernt. Das erzeugte Prepared-Statement wird nun im Statement gespeichert, da wo es hingehört. Fri, 9 Feb 2018 03:10:35 +0100 Jan Dankert Beginn/Ende von Transaktionen loggen. Wed, 10 Jan 2018 22:06:52 +0100 Jan Dankert Abhängigkeit zu PDODriver, um IDE zu unterstützen. Hier müssste eigentlich noch ein Interface oder Oberklasse her. Sat, 9 Dec 2017 23:52:12 +0100 Jan Dankert Code aufgeräumt, Warnungen entfernt. Fri, 8 Dec 2017 22:29:13 +0100 Jan Dankert Neuer Objekttyp 'url' mit Modelklasse, Actionklasse und anderen Änderungen. Darüber hinaus benötigt die Methode query() aus dem Statement keinen Parameter mehr. Thu, 7 Dec 2017 23:02:20 +0100 Jan Dankert Kleinere Verbesserung im Datenbank-Treiber, z.B. Exception-Handling. Tue, 5 Dec 2017 23:56:04 +0100 Jan Dankert Datenbank-Klassen auf Namespace umgestellt. Sun, 3 Dec 2017 03:11:38 +0100 Jan Dankert Refactoring: Datenbank-Funktionen in ein eigenes "Modul" ausgelagert.