File indexing completed on 2024-12-22 05:33:09
0001 <?php 0002 0003 /** 0004 * Flooer Framework 0005 * 0006 * LICENSE: BSD License (2 Clause) 0007 * 0008 * @category Flooer 0009 * @package Flooer_Db 0010 * @author Akira Ohgaki <akiraohgaki@gmail.com> 0011 * @copyright Akira Ohgaki 0012 * @license https://opensource.org/licenses/BSD-2-Clause BSD License (2 Clause) 0013 * @link https://github.com/akiraohgaki/flooer 0014 */ 0015 0016 require_once 'Flooer/Db/Statement.php'; 0017 require_once 'Flooer/Db/Table.php'; 0018 0019 /** 0020 * Usage 0021 * 0022 * $db = new Flooer_Db(array( 0023 * 'dsn' => 'mysql:host=localhost;dbname=database', 0024 * 'username' => 'username', 0025 * 'password' => 'password' 0026 * )); 0027 * $value = $db->TableName->RecordID->ColumnName; 0028 */ 0029 0030 /** 0031 * Database connection class of SQL database abstraction layer 0032 * 0033 * @category Flooer 0034 * @package Flooer_Db 0035 * @author Akira Ohgaki <akiraohgaki@gmail.com> 0036 */ 0037 class Flooer_Db extends PDO 0038 { 0039 0040 /** 0041 * Configuration options 0042 * 0043 * @var array 0044 */ 0045 protected $_config = array( 0046 'dsn' => null, 0047 'username' => null, 0048 'password' => null, 0049 'driverOptions' => array(), 0050 'tableConfig' => array() 0051 ); 0052 0053 /** 0054 * Table exists 0055 * 0056 * @var array 0057 */ 0058 protected $_tableExists = array(); 0059 0060 /** 0061 * Table object cache 0062 * 0063 * @var array 0064 */ 0065 protected $_tableCache = array(); 0066 0067 /** 0068 * SQL statement log 0069 * 0070 * @var array 0071 */ 0072 protected $_statementLog = array(); 0073 0074 /** 0075 * Constructor 0076 * 0077 * @param array $config 0078 * @return void 0079 */ 0080 public function __construct(array $config = null) 0081 { 0082 if ($config) { 0083 $this->_config = $config + $this->_config; 0084 } 0085 set_exception_handler(array(__CLASS__, 'exceptionHandler')); 0086 parent::__construct( 0087 $this->_config['dsn'], 0088 $this->_config['username'], 0089 $this->_config['password'], 0090 $this->_config['driverOptions'] 0091 ); 0092 restore_exception_handler(); 0093 if (!isset($this->_config['driverOptions'][parent::ATTR_STATEMENT_CLASS])) { 0094 parent::setAttribute( 0095 parent::ATTR_STATEMENT_CLASS, 0096 array('Flooer_Db_Statement') 0097 ); 0098 } 0099 if (!isset($this->_config['driverOptions'][parent::ATTR_DEFAULT_FETCH_MODE])) { 0100 parent::setAttribute( 0101 parent::ATTR_DEFAULT_FETCH_MODE, 0102 parent::FETCH_ASSOC 0103 ); 0104 } 0105 if (!isset($this->_config['driverOptions'][parent::ATTR_PERSISTENT])) { 0106 parent::setAttribute(parent::ATTR_PERSISTENT, true); 0107 } 0108 } 0109 0110 /** 0111 * Magic method to create a table 0112 * 0113 * @param string $key 0114 * @param array|object $value 0115 * @return void 0116 */ 0117 public function __set($key, $value) 0118 { 0119 if (is_array($value) || is_object($value)) { 0120 $this->__unset($key); 0121 $tableName = $key; 0122 if (isset($this->_config['tableConfig']['prefix'])) { 0123 $tableName = $this->_config['tableConfig']['prefix'] . $tableName; 0124 } 0125 $fields = array(); 0126 foreach ($value as $fieldName => $fieldType) { 0127 $fields[] = "$fieldName $fieldType"; 0128 } 0129 $definition = implode(',', $fields); 0130 $sql = "CREATE TABLE $tableName ($definition);"; 0131 $this->_statementLog[] = $sql; 0132 $count = parent::exec($sql); 0133 if ($count !== false) { 0134 $this->_tableExists[$tableName] = true; 0135 } 0136 return; 0137 } 0138 trigger_error( 0139 "Setting non-array or non-object property ($key) is not allowed", 0140 E_USER_NOTICE 0141 ); 0142 } 0143 0144 /** 0145 * Magic method to get a table object 0146 * 0147 * @param string $key 0148 * @return Flooer_Db_Table|null 0149 */ 0150 public function __get($key) 0151 { 0152 if ($this->__isset($key)) { 0153 $tableName = $key; 0154 if (isset($this->_config['tableConfig']['prefix'])) { 0155 $tableName = $this->_config['tableConfig']['prefix'] . $tableName; 0156 } 0157 if (empty($this->_tableCache[$tableName])) { 0158 $this->_tableCache[$tableName] = new Flooer_Db_Table( 0159 $this, 0160 array('name' => $key) 0161 + $this->_config['tableConfig'] 0162 ); 0163 } 0164 return $this->_tableCache[$tableName]; 0165 } 0166 return null; 0167 } 0168 0169 /** 0170 * Magic method to check a table 0171 * 0172 * @param string $key 0173 * @return bool 0174 */ 0175 public function __isset($key) 0176 { 0177 $tableName = $key; 0178 if (isset($this->_config['tableConfig']['prefix'])) { 0179 $tableName = $this->_config['tableConfig']['prefix'] . $tableName; 0180 } 0181 if (isset($this->_tableExists[$tableName])) { 0182 return $this->_tableExists[$tableName]; 0183 } 0184 $driver = parent::getAttribute(parent::ATTR_DRIVER_NAME); 0185 if ($driver == 'sqlite') { 0186 $sql = "SELECT 1" 0187 . " FROM sqlite_master" 0188 . " WHERE type = " . parent::quote('table') 0189 . " AND name = " . parent::quote($tableName) 0190 . " LIMIT 1;"; 0191 $this->_statementLog[] = $sql; 0192 $statement = parent::prepare($sql); 0193 $bool = $statement->execute(); 0194 $row = $statement->fetch(parent::FETCH_NUM); 0195 $statement->closeCursor(); 0196 if ($bool && $row) { 0197 if ($row[0]) { 0198 $this->_tableExists[$tableName] = true; 0199 return true; 0200 } 0201 $this->_tableExists[$tableName] = false; 0202 } 0203 } 0204 else { 0205 $sql = "SELECT COUNT(*) FROM $tableName;"; 0206 if ($driver == 'mysql' || $driver == 'pgsql') { 0207 $sql = "SELECT 1 FROM $tableName LIMIT 1;"; 0208 } 0209 else if ($driver == 'sqlsrv') { 0210 $sql = "SELECT TOP 1 1 FROM $tableName;"; 0211 } 0212 $this->_statementLog[] = $sql; 0213 $statement = parent::prepare($sql); 0214 $bool = $statement->execute(); 0215 $statement->closeCursor(); 0216 if ($bool) { 0217 $this->_tableExists[$tableName] = true; 0218 return true; 0219 } 0220 $this->_tableExists[$tableName] = false; 0221 } 0222 return false; 0223 } 0224 0225 /** 0226 * Magic method to drop a table 0227 * 0228 * @param string $key 0229 * @return void 0230 */ 0231 public function __unset($key) 0232 { 0233 if ($this->__isset($key)) { 0234 $tableName = $key; 0235 if (isset($this->_config['tableConfig']['prefix'])) { 0236 $tableName = $this->_config['tableConfig']['prefix'] . $tableName; 0237 } 0238 $sql = "DROP TABLE $tableName;"; 0239 $this->_statementLog[] = $sql; 0240 $count = parent::exec($sql); 0241 if ($count !== false) { 0242 $this->_tableExists[$tableName] = false; 0243 unset($this->_tableCache[$tableName]); 0244 } 0245 } 0246 } 0247 0248 /** 0249 * Exception handler 0250 * 0251 * @param PDOException $exception 0252 * @return void 0253 */ 0254 public static function exceptionHandler($exception) 0255 { 0256 $code = $exception->getCode(); 0257 $message = $exception->getMessage(); 0258 $file = $exception->getFile(); 0259 $line = $exception->getLine(); 0260 echo __CLASS__ . ": [$code]: $message; $file($line)\n"; 0261 } 0262 0263 /** 0264 * Set a configuration options for a table class 0265 * 0266 * @param array $config 0267 * @return void 0268 */ 0269 public function setTableConfig(array $config) 0270 { 0271 $this->_config['tableConfig'] = $config; 0272 } 0273 0274 /** 0275 * Get a configuration options for a table class 0276 * 0277 * @return array 0278 */ 0279 public function getTableConfig() 0280 { 0281 return $this->_config['tableConfig']; 0282 } 0283 0284 /** 0285 * Add a SQL statement log 0286 * 0287 * @param $sql 0288 * 0289 * @return void 0290 */ 0291 public function addStatementLog($sql) 0292 { 0293 $this->_statementLog[] = $sql; 0294 } 0295 0296 /** 0297 * Get a SQL statement log 0298 * 0299 * @return array 0300 */ 0301 public function getStatementLog() 0302 { 0303 return $this->_statementLog; 0304 } 0305 0306 }