* @license PHP License * @package WB * @subpackage db */ WBClass::load( 'WBLog' ); /** * Simple SQL class * * Allows to connect and query MySQL DB * * @version 0.2.1 * @package WB * @subpackage db */ class WBDatasource_SQL extends WBStdClass { /** * id of current database connection * @var string */ protected $dbcId = null; /** * bufferd DB connections * @var array */ static protected $dbcs = array(); /** * logger * @var WBLog */ protected $log; /** * config * * Holds configuration variables: * - host * - user * - database * - password * - driver * - port * * @var array */ protected $config = array( 'driver' => 'MySQL', 'port' => 3306, 'host' => null, 'user' => null, 'password' => null, 'database' => null, ); /** * constructor * * Parameter: * - "driver": db driver like MySQL or Postgres * - "host": database * - "database": database * - "user": db user * - "password": db user's password * - "port": db port to connect (optinal) * * @param array $parameter */ public function __construct( $parameter = array() ) { $this->log = WBLog::start(__CLASS__); $conf = WBClass::create( 'WBConfig' ); if (isset($parameter['database'])) { $param = array(); foreach (array('host', 'database', 'user', 'password') as $k) { if (!isset($parameter[$k])) { WBClass::load('WBException_Argument'); throw new WBException_Argument('Invalid parameter set to connect DB. Either set no or all (host, database, user, password) parameter.', 7, __CLASS__); } $param[$k] = $parameter[$k]; } } else { $conf->load( 'config' ); $param = $conf->get('db'); } $this->config = array_merge($this->config, $param); } /** * disconnect from database * */ public function __destruct() { if( !$this->dbcId ) { return; } // decrese reference counter and disconnect --self::$dbcs[$this->dbcId]['count']; $log = array( 'action' => 'diconnect', 'connection'=> 'keep', 'id' => $this->dbcId, 'refcount' => self::$dbcs[$this->dbcId]['count'] ); if( self::$dbcs[$this->dbcId]['count'] > 0 ) { $this->log->notice( $log ); return; } $log['connection'] = 'close'; $this->log->notice( $log ); self::$dbcs[$this->dbcId]['dbc']->close(); } /** * connect to databse * * dynamicly connect to mysql database. Each connection is buffered to avoid * multiple connection to the same databse. This implies, that there can be * be only one connection per user, host and databse. * * @todo switch on caching * @return mysqli */ public function connect() { // only once if( $this->dbcId ) { return self::$dbcs[$this->dbcId]['dbc']; } // allow only one instance per host, database and user $id = $this->getId(); if( isset( self::$dbcs[$id] ) && self::$dbcs[$id]['count'] > 0 ) { ++self::$dbcs[$id]['count']; $this->dbcId = $id; $log = array( 'action' => 'connect', 'connection'=> 'old', 'id' => $this->dbcId, 'refcount' => self::$dbcs[$this->dbcId]['count'] ); $this->log->notice( $log ); return self::$dbcs[$this->dbcId]['dbc']; } $dbc = new mysqli( $this->config['host'], $this->config['user'], $this->config['password'], $this->config['database'], $this->config['port'] ); if( mysqli_connect_errno() ) { WBClass::load( 'WBException_Datasource' ); throw new WBException_Datasource( 'Could not connect to database', 1, __CLASS__ ); return false; } $dbc->set_charset( 'utf8' ); // buffer self::$dbcs[$id] = array( 'dbc' => $dbc, 'count' => 1 ); $this->dbcId = $id; $log = array( 'action' => 'connect', 'connection'=> 'new', 'id' => $this->dbcId, 'refcount' => 1 ); $this->log->notice( $log ); return self::$dbcs[$this->dbcId]['dbc']; } /** * receive connection id * * Create connection id using current config * * @return string */ public function getId() { $id = $this->config['host'] . '_' . $this->config['user'] . '_' . $this->config['database']; return $id; } /** * Quote anything for SQL * * @param mixed $value * @param bool $like whether the quote should be done for LIKE * @return string */ public function quote($value, $like = false) { if ($like) { $dbc = $this->connect(); $value = $dbc->real_escape_string($value); $replace = array( '\\\\' => '\\\\\\\\', '%' => '\\%', '_' => '\\_' ); return str_replace(array_keys($replace), array_values($replace), $value); } // integer values are just OK if (is_int($value)) { return $value; } if (is_double($value) || is_float($value)) { return sprintf('%F', $value); } if (is_string($value) && preg_match('/^\d+$/', $value)) { // convert leading zero values to string if (preg_match('/^0+/', $value)) { return '"' . $value . '"'; } return $value; } if (is_bool($value)) { return $value ? '1' : '0'; } if (is_null($value)) { return 'NULL'; } $dbc = $this->connect(); return "'" . $dbc->real_escape_string($value) . "'"; } /** * execute query string * * Connects to database and execute query * * @param string $query * @return mysqli_result */ public function query($q) { $dbc = $this->connect(); $log = array( 'action' => 'query', 'id' => $this->dbcId, 'query' => $q, ); $res = $dbc->query($q); if (!$res) { $log['error'] = $dbc->error; $this->log->err($log); WBClass::load( 'WBException_Datasource' ); throw new WBException_Datasource('Database query failed! ' . $q, 2, __CLASS__); } $this->log->warn($log); return $res; } } ?>