00001 <?php
00007 namespace Habari;
00008
00014 class DatabaseConnection
00015 {
00016 private $fetch_mode = \PDO::FETCH_CLASS;
00017 private $fetch_class_name = 'QueryRecord';
00018 private $driver;
00019 private $keep_profile = DEBUG;
00021 protected $pdo = null;
00023 private $pdo_statement = null;
00024 private $pdo_transaction = false;
00025
00029 private $tables = array(
00030 'blocks',
00031 'blocks_areas',
00032 'commentinfo',
00033 'comments',
00034 'commentstatus',
00035 'commenttype',
00036 'crontab',
00037 'groups',
00038 'group_token_permissions',
00039 'log',
00040 'log_types',
00041 'object_terms',
00042 'object_types',
00043 'options',
00044 'post_tokens',
00045 'postinfo',
00046 'posts',
00047 'poststatus',
00048 'posttype',
00049 'revisions',
00050 'rewrite_rules',
00051 'scopes',
00052 'sessions',
00053 'terms',
00054 'terminfo',
00055 'tokens',
00056 'userinfo',
00057 'users',
00058 'user_token_permissions',
00059 'users_groups',
00060 'vocabularies',
00061 );
00062
00066 private $sql_tables = array();
00067 private $sql_tables_repl = array();
00068 private $errors = array();
00069 private $profiles = array();
00070
00071 protected $prefix = '';
00072 private $current_table;
00073
00080 public static function ConnectionFactory( $connect_string )
00081 {
00082 list( $engine ) = explode( ':', $connect_string, 2 );
00083 $engines = array(
00084 'sqlite' => '\Habari\SQLiteConnection',
00085 'mysql' => '\Habari\MySQLConnection',
00086 'pgsql' => '\Habari\PGSQLConnection',
00087 );
00088
00089 require_once( HABARI_PATH . "/system/schema/{$engine}/connection.php" );
00090 $engine_class = $engines[$engine];
00091 return new $engine_class();
00092 }
00093
00099 protected function load_tables()
00100 {
00101 if ( isset( Config::get( 'db_connection' )->prefix ) ) {
00102 $prefix = Config::get( 'db_connection' )->prefix;
00103 }
00104 else {
00105 $prefix = $this->prefix;
00106 }
00107 $this->prefix = $prefix;
00108
00109
00110 foreach ( $this->tables as $t ) {
00111 $this->sql_tables[$t] = $prefix . $t;
00112 $this->sql_tables_repl[$t] = '{' . $t . '}';
00113 }
00114 }
00115
00124 public function connect ( $connect_string, $db_user, $db_pass )
00125 {
00126
00127 $this->pdo = @new \PDO( $connect_string, $db_user, $db_pass );
00128 $this->pdo->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_WARNING );
00129 $this->load_tables();
00130 return true;
00131 }
00132
00138 public function disconnect()
00139 {
00140 $this->pdo = null;
00141
00142 return true;
00143 }
00144
00150 public function is_connected()
00151 {
00152 return ( null != $this->pdo );
00153 }
00154
00160 public function in_transaction()
00161 {
00162 return $this->pdo_transaction;
00163 }
00164
00171 public function table( $name )
00172 {
00173 if ( isset( $this->sql_tables[$name] ) ) {
00174 return $this->sql_tables[$name];
00175 }
00176 else {
00177 return $name;
00178 }
00179 }
00180
00188 public function register_table( $name )
00189 {
00190 $this->tables[] = $name;
00191 $this->load_tables();
00192 }
00193
00199 public function set_fetch_mode( $mode )
00200 {
00201 $this->fetch_mode = $mode;
00202 }
00203
00209 public function set_fetch_class( $class_name )
00210 {
00211 $this->fetch_class_name = $class_name;
00212 }
00213
00222 public function exec( $query )
00223 {
00224
00225 $query = Plugins::filter( 'db_exec', $query, array() );
00226
00227 $query = $this->sql_t( $query, array() );
00228
00229 $query = self::filter_tables( $query );
00230
00231 $query = Plugins::filter( 'db_exec_postprocess', $query, array() );
00232
00233 return ( $this->pdo->exec( $query ) !== false );
00234 }
00235
00243 public function query( $query, $args = array() )
00244 {
00245 if ( $this->pdo_statement != null ) {
00246 $this->pdo_statement->closeCursor();
00247 }
00248
00249
00250 $query = Plugins::filter( 'query', $query, $args );
00251
00252 $query = $this->sql_t( $query, $args );
00253
00254 $query = self::filter_tables( $query );
00255
00256 $query = Plugins::filter( 'query_postprocess', $query, $args );
00257
00258 if ( $this->pdo_statement = $this->pdo->prepare( $query ) ) {
00259 if ( $this->fetch_mode == \PDO::FETCH_CLASS ) {
00260
00261 if ( ! class_exists( $this->fetch_class_name, true ) ) {
00262 $class_name = $this->fetch_class_name;
00263
00264 if(strpos($class_name, '\\') == false) {
00265 $class_name = '\\Habari\\' . $class_name;
00266 $this->fetch_class_name = $class_name;
00267 if ( ! class_exists( $this->fetch_class_name, true ) ) {
00268 throw new \Exception('The model class that was specified for data retreival could not be loaded.');
00269 }
00270 }
00271 }
00272
00273 if ( class_exists( $this->fetch_class_name ) ) {
00274 $this->pdo_statement->setFetchMode( \PDO::FETCH_CLASS, $this->fetch_class_name, array() );
00275 }
00276 else {
00277
00278 echo '<br><br>' . _t( 'Attempt to fetch in class mode with a non-included class' ) . '<br><br>';
00279 return false;
00280 }
00281 }
00282 else {
00283 $this->pdo_statement->setFetchMode( $this->fetch_mode );
00284 }
00285
00286
00287 if ( $this->keep_profile ) {
00288 $profile = new QueryProfile( $query );
00289 $profile->params = $args;
00290 $profile->start();
00291 }
00292 if ( ! $this->pdo_statement->execute( $args ) ) {
00293 $this->add_error( array( 'query'=>$query,'error'=>$this->pdo_statement->errorInfo() ) );
00294 return false;
00295 }
00296 if ( $this->keep_profile ) {
00297 $profile->stop();
00298 $this->profiles[] = $profile;
00299 }
00300 return true;
00301 }
00302 else {
00303 $this->add_error( array(
00304 'query' => $query,
00305 'error' => $this->pdo->errorInfo(),
00306 ) );
00307 return false;
00308 }
00309 }
00310
00324 public function execute_procedure( $procedure, $args = array() )
00325 {
00326
00327 $pdo = $this->pdo;
00328 $pdo_statement = $this->pdo_statement;
00329
00330 if ( $pdo_statement != null ) {
00331 $pdo_statement->closeCursor();
00332 }
00333
00334 $query = 'CALL ' . $procedure . '( ';
00335 if ( count( $args ) > 0 ) {
00336 $query .= str_repeat( '?,', count( $args ) );
00337 $query = substr( $query, 0, strlen( $query ) - 1 );
00338 }
00339 $query .= ' )';
00340 $query = $this->sql_t( $query, $args );
00341
00342 if ( $pdo_statement = $pdo->prepare( $query ) ) {
00343
00344 if ( $this->keep_profile ) {
00345 $profile = new QueryProfile( $query );
00346 $profile->start();
00347 }
00348 if ( ! $pdo_statement->execute( $args ) ) {
00349 $this->add_error( array( 'query'=>$query,'error'=>$pdo_statement->errorInfo() ) );
00350 return false;
00351 }
00352 if ( $this->keep_profile ) {
00353 $profile->stop();
00354 $this->profiles[] = $profile;
00355 }
00356 return true;
00357 }
00358 else {
00359 $this->add_error( array( 'query'=>$query,'error'=>$pdo_statement->errorInfo() ) );
00360 return false;
00361 }
00362 }
00363
00368 public function begin_transaction()
00369 {
00370 if ( ! $this->pdo_transaction ) {
00371 if( $this->pdo->beginTransaction() ) {
00372 $this->pdo_transaction = true;
00373 }
00374 }
00375 }
00376
00382 public function rollback()
00383 {
00384 if ( $this->pdo_transaction ) {
00385 if( $this->pdo->rollBack() ) {
00386 $this->pdo_transaction = false;
00387 }
00388 }
00389 }
00390
00394 public function commit()
00395 {
00396 if ( $this->pdo_transaction ) {
00397 if( $this->pdo->commit() ) {
00398 $this->pdo_transaction = false;
00399 }
00400 }
00401 }
00402
00408 public function get_profiles()
00409 {
00410 return $this->profiles;
00411 }
00412
00418 public function add_error( $error )
00419 {
00420 $backtrace1 = debug_backtrace();
00421 $backtrace = array();
00422 foreach ( $backtrace1 as $trace ) {
00423 $backtrace[] = array_intersect_key( $trace, array('file'=>1, 'line'=>1, 'function'=>1, 'class'=>1) );
00424 }
00425 $this->errors[] = array_merge( $error, array( 'backtrace'=> $backtrace ) );
00426 }
00427
00432 public function get_errors()
00433 {
00434 return $this->errors;
00435 }
00436
00441 public function has_errors()
00442 {
00443 return ( count( $this->errors ) > 0 );
00444 }
00445
00449 public function clear_errors()
00450 {
00451 $this->errors = array();
00452 }
00453
00458 public function get_last_error()
00459 {
00460 $error = end( $this->errors );
00461 return ( array( 'query'=>$error['query'], 'message'=>$error['error'][2] ) );
00462 }
00463
00472 public function get_results( $query, $args = array(), $class_name = '\Habari\QueryRecord' )
00473 {
00474 $this->set_fetch_mode( \PDO::FETCH_CLASS );
00475 $this->set_fetch_class( $class_name );
00476 if ( $this->query( $query, $args ) ) {
00477 return $this->pdo_statement->fetchAll();
00478 }
00479 else {
00480 return false;
00481 }
00482 }
00483
00492 public function get_row( $query, $args = array(), $class_name = '\Habari\QueryRecord' )
00493 {
00494 $this->set_fetch_mode( \PDO::FETCH_CLASS );
00495 $this->set_fetch_class( $class_name );
00496
00497 if ( $this->query( $query, $args ) ) {
00498 return $this->pdo_statement->fetch();
00499 }
00500 else {
00501 return false;
00502 }
00503 }
00504
00513 public function get_column( $query, $args = array() )
00514 {
00515 if ( $this->query( $query, $args ) ) {
00516 return $this->pdo_statement->fetchAll( \PDO::FETCH_COLUMN );
00517 }
00518 else {
00519 return false;
00520 }
00521 }
00522
00530 public function get_value( $query, $args = array() )
00531 {
00532 if ( $this->query( $query, $args ) ) {
00533 $result = $this->pdo_statement->fetch( \PDO::FETCH_NUM );
00534 return $result[0];
00535 }
00536 else {
00537 return false;
00538 }
00539 }
00540
00549 public function get_keyvalue( $query, $args = array() )
00550 {
00551 if ( $this->query( $query, $args ) ) {
00552 $output = $this->pdo_statement->fetchAll( \PDO::FETCH_KEY_PAIR );
00553 return $output;
00554 }
00555 else {
00556 return false;
00557 }
00558 }
00559
00567 public function insert( $table, $fieldvalues )
00568 {
00569 ksort( $fieldvalues );
00570
00571 $fields = array_keys( $fieldvalues );
00572 $values = array_values( $fieldvalues );
00573
00574 $query = "INSERT INTO {$table} ( " . implode( ', ', $fields ) . ' ) VALUES ( ' . implode( ', ', array_fill( 0, count( $values ), '?' ) ) . ' )';
00575
00576
00577 $this->current_table = $table;
00578
00579 return $this->query( $query, $values );
00580 }
00581
00589 public function exists( $table, $keyfieldvalues )
00590 {
00591 $qry = "SELECT 1 as c FROM {$table} WHERE 1=1 ";
00592
00593 $values = array();
00594 foreach ( $keyfieldvalues as $keyfield => $keyvalue ) {
00595 $qry .= " AND {$keyfield} = ? ";
00596 $values[] = $keyvalue;
00597 }
00598 $result = $this->get_row( $qry, $values );
00599 return ( $result !== false );
00600 }
00601
00612 public function update( $table, $fieldvalues, $keyfields )
00613 {
00614 ksort( $fieldvalues );
00615 ksort( $keyfields );
00616
00617 $keyfieldvalues = array();
00618 foreach ( $keyfields as $keyfield => $keyvalue ) {
00619 if ( is_numeric( $keyfield ) ) {
00620
00621 $keyfieldvalues[$keyvalue] = $fieldvalues[$keyvalue];
00622 }
00623 else {
00624
00625 $keyfieldvalues[$keyfield] = $keyvalue;
00626 }
00627 }
00628 if ( $this->exists( $table, $keyfieldvalues ) ) {
00629 $qry = "UPDATE {$table} SET";
00630 $values = array();
00631 $comma = '';
00632 foreach ( $fieldvalues as $fieldname => $fieldvalue ) {
00633 $qry .= $comma . " {$fieldname} = ?";
00634 $values[] = $fieldvalue;
00635 $comma = ' ,';
00636 }
00637 $qry .= ' WHERE 1=1 ';
00638
00639 foreach ( $keyfieldvalues as $keyfield => $keyvalue ) {
00640 $qry .= "AND {$keyfield} = ? ";
00641 $values[] = $keyvalue;
00642 }
00643 return $this->query( $qry, $values );
00644 }
00645 else {
00646
00647
00648 $fieldvalues = $fieldvalues + $keyfieldvalues;
00649 return $this->insert( $table, $fieldvalues );
00650 }
00651 }
00652
00660 public function delete( $table, $keyfields )
00661 {
00662 $qry = "DELETE FROM {$table} WHERE 1=1 ";
00663 foreach ( $keyfields as $keyfield => $keyvalue ) {
00664 $qry .= "AND {$keyfield} = ? ";
00665 $values[] = $keyvalue;
00666 }
00667
00668 return $this->query( $qry, $values );
00669 }
00670
00680 public function last_insert_id()
00681 {
00682 if ( $this->pdo->getAttribute( \PDO::ATTR_DRIVER_NAME ) == 'pgsql' ) {
00683 return $this->pdo->lastInsertId( $this->current_table. '_pkey_seq' );
00684 }
00685 else {
00686 return $this->pdo->lastInsertId( func_num_args() == 1 ? func_get_arg( 0 ) : '' );
00687 }
00688 }
00689
00694 public function dbdelta( $queries, $execute = true, $silent = true ){}
00695
00696 public function upgrade_pre( $old_version ){}
00697
00698 public function upgrade_post( $old_version ){}
00699
00700
00709 public function upgrade( $old_version, $upgrade_path = '' )
00710 {
00711
00712 $upgrade_files = Utils::glob( "{$upgrade_path}/*.sql" );
00713
00714
00715 $upgrades = array();
00716 foreach ( $upgrade_files as $file ) {
00717 if ( intval( basename( $file, '.sql' ) ) > $old_version ) {
00718 $upgrades[ sprintf( '%010s_0', basename( $file, '.sql' ) )] = $file;
00719 }
00720 }
00721
00722 $upgrade_functions = get_class_methods( $this );
00723 foreach ( $upgrade_functions as $fn ) {
00724 if ( preg_match( '%^upgrade_([0-9]+)$%i', $fn, $matches ) ) {
00725 if ( intval( $matches[1] ) > $old_version ) {
00726 $upgrades[ sprintf( '%010s_1', $matches[1] )] = array( $this, $fn );
00727 }
00728 }
00729 }
00730
00731
00732 ksort( $upgrades );
00733
00734
00735 $result = true;
00736 foreach ( $upgrades as $upgrade ) {
00737 if ( is_array( $upgrade ) ) {
00739 $result &= $upgrade();
00740 }
00741 else {
00742 $result &= $this->query_file( $upgrade );
00743 }
00744 if ( !$result ) {
00745 break;
00746 }
00747 }
00748
00749 return $result;
00750 }
00751
00758 public function query_file( $file )
00759 {
00760 $upgrade_sql = trim( file_get_contents( $file ) );
00761 $upgrade_sql = str_replace( '{$prefix}', $this->prefix, $upgrade_sql );
00762
00763
00764 $queries = explode( ';', $upgrade_sql );
00765
00766 foreach ( $queries as $query ) {
00767 if ( trim( $query ) != '' ) {
00768 if ( !$this->query( $query ) ) {
00769 return false;
00770 }
00771 }
00772 }
00773
00774 return true;
00775 }
00776
00777
00784 public function sql_t( $query )
00785 {
00786 return $query;
00787 }
00788
00795 public function filter_tables( $query )
00796 {
00797 return str_replace( $this->sql_tables_repl, $this->sql_tables, $query );
00798 }
00799
00800 public function get_driver_name()
00801 {
00802 if ( ! $this->driver ) {
00803 $this->driver = $this->pdo->getAttribute( \PDO::ATTR_DRIVER_NAME );
00804 }
00805 return $this->driver;
00806 }
00807
00808 public function get_driver_version()
00809 {
00810 return $this->pdo->getAttribute( \PDO::ATTR_SERVER_VERSION );
00811 }
00812
00818 public function row_count()
00819 {
00820 return $this->pdo_statement->rowCount();
00821 }
00822
00828 public function list_tables()
00829 {
00830 return $this->sql_tables;
00831 }
00832
00841 public function quote ( $string )
00842 {
00843 return $this->pdo->quote( $string );
00844 }
00845
00846 }
00847
00848 ?>