openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

PDODriver.class.php (9013B)


      1 <?php
      2 
      3 //
      4 // +----------------------------------------------------------------------+
      5 // | PHP version 4.0                                                      |
      6 // +----------------------------------------------------------------------+
      7 // | Copyright (c) 1997-2001 The PHP Group                                |
      8 // +----------------------------------------------------------------------+
      9 // | This source file is subject to version 2.02 of the PHP license,      |
     10 // | that is bundled with this package in the file LICENSE, and is        |
     11 // | available at through the world-wide-web at                           |
     12 // | http://www.php.net/license/2_02.txt.                                 |
     13 // | If you did not receive a copy of the PHP license and are unable to   |
     14 // | obtain it through the world-wide-web, please send a note to          |
     15 // | license@php.net so we can mail you a copy immediately.               |
     16 // +----------------------------------------------------------------------+
     17 // | Authors: Stig Bakken <ssb@fast.no>                                   |
     18 // |          Jan Dankert <phpdb@jandankert.de>                           |
     19 // +----------------------------------------------------------------------+
     20 //
     21 namespace database\driver;
     22 
     23 use database\Sql;
     24 use logger\Logger;
     25 use \PDO;
     26 use \PDOException;
     27 use PDOStatement;
     28 use util\exception\DatabaseException;
     29 
     30 /**
     31  * Implementation of all database operations in PDO.
     32  *
     33  * PDO is available since PHP 5.1 and OpenRat CMS forces a newer PHP version than this. So there should be no problem to rely on PDO.
     34  *
     35  * @author Jan Dankert
     36  */
     37 class PDODriver
     38 {
     39 	/**
     40 	 * Die PDO-Verbindung.
     41 	 *
     42 	 * @var PDO
     43 	 */
     44 	private $connection;
     45 	
     46 
     47     /**
     48      * @var PDOStatement
     49      */
     50     public $stmt;
     51 
     52 
     53 	/**
     54 	 * Connect to a database
     55 	 *
     56 	 * @param $conf array connection configuration
     57 	 * @throws DatabaseException
     58 	 */
     59     function connect( $conf )
     60 	{
     61 		if ( !defined('PDO::ATTR_DRIVER_NAME') ) {
     62 			// This should never happen, because PHP is always bundled with PDO.
     63 			// but maybe... some installation could miss the module.
     64 			throw new DatabaseException('PDO unavailable');
     65 		}
     66 
     67 		$dsn    = $conf['dsn'     ]; // Optional a pre-configured DSN.
     68 		$user   = $conf['user'    ];
     69 		$pw     = $conf['password'];
     70 
     71 		if   ( ! $dsn ) {
     72 			// No DSN is configured, so we are building a DSN.
     73 			$driver = $conf['driver'];
     74 
     75 			if (!in_array($driver,PDO::getAvailableDrivers(),TRUE))
     76 				throw new DatabaseException('PDO driver '.$driver.' is not available');
     77 
     78 			$dsnParts = [];
     79 			if   ( $conf['host'] )
     80 				$dsnParts[ $driver.':host' ] = $conf['host']; // Hostname for RDBMS with IP-stack
     81 			elseif   ( $conf['file'] )
     82 					$dsnParts[ $driver.':'.$conf['file'] ] = $conf['host']; // Filename for SQLITE
     83 
     84 			if   ( $conf['database'] )
     85 				$dsnParts['dbname' ] = $conf['database'];
     86 			if   ( $conf['port'] )
     87 				$dsnParts['port' ] = $conf['port'];
     88 			if   ( $conf['charset'] )
     89 				$dsnParts['charset'] = $conf['charset' ];
     90 
     91 			// Building the DSN for PDO.
     92 			$dsn = implode('; ',array_map( function($key,$value) {
     93 				return $key.'='.$value;
     94 			},array_keys($dsnParts),$dsnParts));
     95 		}
     96 
     97 		// we must have a prefix or suffix
     98 		// this is because some table names are reserved words in some RDBMS
     99 		if   ( ! $conf['prefix'] && ! $conf['suffix'] )
    100 			throw new DatabaseException('database tables must have a prefix or a suffix, both are empty.');
    101 		
    102 		$options = array();
    103 		foreach( $conf as $c )
    104 			if	( is_string($c) && substr($c,0,7) == 'option_' )
    105 				$options[substr($c,8)] = $conf[$c];
    106 
    107 		if	( $conf['persistent'])
    108 		    // From the docs:
    109 		    // "Many web applications will benefit from making persistent connections to database servers.
    110 		    //  Persistent connections are not closed at the end of the script, but are cached and re-used
    111 		    //  when another script requests a connection using the same credentials."
    112 		    // "The persistent connection cache allows you to avoid the overhead of establishing a new
    113 		    // connection every time a script needs to talk to a database, resulting in a faster web application."
    114 			$options[ PDO::ATTR_PERSISTENT ] = true;
    115 
    116 		// From the docs:
    117 		// "try to use native prepared statements (if FALSE).
    118 		//  It will always fall back to emulating the prepared statement if the driver cannot successfully prepare the current query"
    119 		$options[ PDO::ATTR_EMULATE_PREPARES  ] = false;
    120 		
    121 		// Convert numeric values to strings when fetching => NO
    122 		$options[ PDO::ATTR_STRINGIFY_FETCHES ] = false;
    123 
    124 		// From the docs:
    125 		// "If this value is FALSE, PDO attempts to disable autocommit so that the connection begins a transaction."
    126         // We do NOT need transactions for reading actions (GET requests).
    127         // We are opening a transaction with PDO::beginTransaction at the beginning of a  POST-request.
    128         // do NOT set this to false, otherwise there will be left open transactions.
    129 		//$options[ PDO::ATTR_AUTOCOMMIT ] = true;
    130 			
    131 		// We like Exceptions
    132 		$options[ PDO::ERRMODE_EXCEPTION       ] = true;
    133 		$options[ PDO::ATTR_DEFAULT_FETCH_MODE ] = PDO::FETCH_ASSOC;
    134 
    135         try
    136         {
    137             $this->connection = new PDO($dsn, $user, $pw, $options);
    138         }
    139         catch(\PDOException $e)
    140         {
    141             throw new DatabaseException("Could not connect to database with DSN '$dsn'",$e);
    142         }
    143 
    144         // This should never happen, because PDO should throw an exception if the connection fails.
    145 		if	( !is_object($this->connection) )
    146 			throw new DatabaseException("Could not connect to database with DSN '$dsn', Reason: ".PDO::errorInfo() );
    147     }
    148 
    149 
    150     /**
    151      * Disconnects the database connection.
    152      *
    153      * @return bool
    154      */
    155     public function disconnect()
    156 	{
    157 	    // There is no disconnection-function.
    158         // So the GC will call the finalize-method of the connection object.
    159 		$this->connection = null;
    160 
    161 		return true;
    162 	}
    163 
    164 
    165     /**
    166      * @param $stmt PDOStatement
    167      * @param $query Sql
    168      * @return PDOStatement
    169      */
    170     public function execute($stmt, $query)
    171 	{
    172 		$erg = $stmt->execute();
    173 
    174 		if	( $erg === false )
    175 			throw new DatabaseException( 'Could not execute prepared statement "'.$query->query.'": '.implode('/',$stmt->errorInfo()) );
    176 
    177 		return $stmt;
    178 	}
    179 
    180 
    181 	/**
    182 	 * @param $stmt PDOStatement
    183 	 * @return array Row
    184 	 */
    185 	public function fetchAssocRow($stmt)
    186 	{
    187 		return $stmt->fetch( PDO::FETCH_ASSOC );
    188 	}
    189 
    190 
    191 	/**
    192 	 * Fetches all rows from the resultset
    193 	 * @param $stmt PDOStatement
    194 	 * @return array Row
    195 	 */
    196 	public function fetchAllRows($stmt)
    197 	{
    198 		return $stmt->fetchAll( PDO::FETCH_ASSOC );
    199 	}
    200 
    201 
    202 	/**
    203 	 * Fetches the next row with a numbered-based array.
    204 	 * @param $stmt PDOStatement
    205 	 * @return array Row
    206 	 */
    207 	public function fetchIndexedRow($stmt)
    208 	{
    209 		return $stmt->fetch( PDO::FETCH_NUM );
    210 	}
    211 
    212 
    213 
    214 	/**
    215 	 * Fetches the first column of the next row.
    216 	 *
    217 	 * @param $stmt PDOStatement
    218 	 * @return mixed Row
    219 	 */
    220 	public function fetchFirstColumn($stmt) {
    221 		return $stmt->fetchColumn();
    222 	}
    223 
    224 
    225 	/**
    226 	 * Fetches all first columns from the result set
    227 	 *
    228 	 * @param $stmt PDOStatement
    229 	 * @return array
    230 	 */
    231 	public function fetchAllFirstColumn($stmt) {
    232 		return $stmt->fetchAll( PDO::FETCH_COLUMN );
    233 	}
    234 
    235 
    236 	/**
    237 	 * Prepares a SQL query and gets the Statement.
    238 	 *
    239 	 * @param $query string SQL-query
    240 	 * @param $param array parameters
    241 	 * @return PDOStatement
    242 	 * @throws DatabaseException
    243 	 */
    244 	public function prepare( $query,$param)
    245 	{
    246 		$offset = 0;
    247 		foreach( $param as $name=>$pos)
    248 		{
    249 			$name = ':'.$name;
    250 			$pos  += $offset;
    251 			$query = substr($query,0,$pos).$name.substr($query,$pos);
    252 			
    253 			$offset = $offset + strlen($name);
    254 		}
    255 
    256 		$stmt = $this->connection->prepare($query);
    257 		
    258 		if	( $stmt === false )
    259 			throw new DatabaseException("Could not prepare statement:\n$query\nCause: ".implode(' / ',$this->connection->errorInfo()) );
    260 
    261 		return $stmt;
    262 	}
    263 
    264 
    265     /**
    266      * Binding a parameter value.
    267      *
    268      * @param $stmt PDOStatement
    269      * @param $param
    270      * @param $value
    271      */
    272 	public function bind( $stmt,$param,$value )
    273 	{
    274 		$name = ':'.$param;
    275 		
    276 		if	( is_string($value) )
    277 			$type = PDO::PARAM_STR;
    278 		elseif( is_int($value)) 
    279 			$type = PDO::PARAM_INT;
    280 		elseif( is_null($value))
    281 			$type = PDO::PARAM_NULL;
    282 		else
    283 			throw new DatabaseException( 'Unknown type for parameter '.$name.': '.gettype($value) );
    284 		
    285 		$stmt->bindValue($name,$value,$type);
    286 	}
    287 	
    288 	
    289 	
    290 	/**
    291      * Startet eine Transaktion.
    292      */
    293 	public function start()
    294 	{
    295 		$this->connection->beginTransaction();
    296 	}
    297 
    298 	
    299 	
    300 	/**
    301      * Beendet eine Transaktion.
    302      */
    303 	public function commit()
    304 	{
    305 		$this->connection->commit();
    306 	}
    307 
    308 
    309 	/**
    310      * Bricht eine Transaktion ab.
    311      */
    312 	public function rollback()
    313 	{
    314 		try
    315 		{
    316 			$this->connection->rollBack();
    317 		}
    318 		catch ( PDOException $e )
    319 		{
    320 			// Kommt vor, wenn keine Transaktion existiert.
    321 		}
    322 	}
    323 	
    324 
    325 	/**
    326 	 * Why this? See http://e-mats.org/2008/07/fatal-error-exception-thrown-without-a-stack-frame-in-unknown-on-line-0/
    327 	 * 
    328 	 * @return array
    329 	 */
    330 	function __sleep() {
    331     	return array();
    332   }  
    333 	
    334 }