From 5bff74f20bd55d7ab4f7418dd5d61880ffb0066b Mon Sep 17 00:00:00 2001 From: Anton Grebnev Date: Tue, 15 Nov 2011 16:55:12 +0400 Subject: [PATCH] added MongoStatement class, modified DbDriver hierarchy --- model/DbDriver.php | 19 ++++++++ model/DbStatement.php | 79 ++++-------------------------- model/MongoDbCommand.php | 21 +++++--- model/MongoDriver.php | 85 ++++++++++++++++++++------------ model/MongoStatement.php | 121 ++++++++++++++++++++++++++++++++++++++++++++++ model/MySQLiStatement.php | 74 +++++++++++++++++++++++++--- model/NoSqlDbDriver.php | 2 +- model/SqlDbDriver.php | 17 ------- 8 files changed, 288 insertions(+), 130 deletions(-) create mode 100644 model/MongoStatement.php diff --git a/model/DbDriver.php b/model/DbDriver.php index b5f2efd..6660027 100644 --- a/model/DbDriver.php +++ b/model/DbDriver.php @@ -49,6 +49,23 @@ abstract class DbDriver return $this->connection; } + /** + * @param mixed $request + * @param mixed $params + * @return DbStatement + */ + public function query($request, $params = array()) + { + $this->connect(); + if (!is_array($params)) { + $params = array($params); + } + $stmt = $this->prepare($request); + $stmt->execute($params); + return $stmt; + } + + /* Abstract methods */ abstract public function insert($table, $data); @@ -56,6 +73,8 @@ abstract class DbDriver abstract public function update($table, $data, $condition); abstract public function delete($table, $condition); + + abstract public function prepare($request); abstract public function getInsertId($table = null, $key = null); diff --git a/model/DbStatement.php b/model/DbStatement.php index 301c574..323b61d 100644 --- a/model/DbStatement.php +++ b/model/DbStatement.php @@ -20,38 +20,16 @@ abstract class DbStatement /** * @var string */ - protected $sql; - - protected $map = null; + protected $request; protected $params = array(); protected $result; - public function __construct($driver, $sql) + public function __construct($driver, $request) { - $this->driver = $driver; - $this->sql = $sql; - } - - public function bindParam($param, &$value) - { - if ($this->map === null) { - $this->mapPlaceholders(); - } - if (count($this->map) > 0) { - if (!is_string($param) && !is_int($param)) { - throw new Exception('Placeholder must be an array or string'); - } - if (is_object($value) && ! ($value instanceof DbExpr)) { - throw new Exception('Objects excepts DbExpr not allowed.'); - } - if (isset($this->map[$param])) { - $this->params[$param] = &$value; - return true; - } - } - return false; + $this->driver = $driver; + $this->request = $request; } /** @@ -67,48 +45,7 @@ abstract class DbStatement } return $this->driverExecute($this->assemble()); } - - protected function mapPlaceholders() - { - $matches = array(); - if(preg_match_all('/(\?|:[A-z0-9_]+)/u', $this->sql, $matches, PREG_OFFSET_CAPTURE)) { - $noname = 0; - foreach ($matches[0] as $id=>$match) { - $match[2] = $matches[1][$id][0]; - $name = ($match[2][0] === ':') ? ltrim($match[2], ':') : $noname++; - $this->map[$name]['placeholder'] = $match[0]; - $this->map[$name]['offset'][] = $match[1]; - } - } - } - - protected function assemble() - { - if (empty($this->map)) { - return $this->sql; - } - - $query = $this->sql; - $placeholders = array(); - foreach($this->map as $name => $place) { - $value = $this->driver->quote($this->params[$name]); - foreach ($place['offset'] as $offset) { - $placeholders[$offset] = array('placeholder' => $place['placeholder'], 'value' => $value); - } - } - - ksort($placeholders); - - $increment = 0; - foreach($placeholders as $current_offset => $placeholder) { - $offset = $current_offset + $increment; - $length = mb_strlen($placeholder['placeholder']); - $query = mb_substr($query, 0, $offset) . $placeholder['value'] . mb_substr($query, $offset + $length); - $increment = (($increment - $length) + mb_strlen($placeholder['value'])); - } - return $query; - } - + public function __destruct() { $this->close(); @@ -164,6 +101,10 @@ abstract class DbStatement } /* Abstract methods */ + + abstract public function bindParam($param, &$value); + + abstract protected function assemble(); abstract public function fetch($style = Db::FETCH_OBJ); @@ -181,5 +122,5 @@ abstract class DbStatement /** * @return bool */ - abstract protected function driverExecute($sql); + abstract protected function driverExecute($request); } diff --git a/model/MongoDbCommand.php b/model/MongoDbCommand.php index 1becb44..0af1575 100644 --- a/model/MongoDbCommand.php +++ b/model/MongoDbCommand.php @@ -65,9 +65,15 @@ class FindMongoCommand extends MongoDbCommand protected $fields; + protected $multiple = true; + protected function concreteExecute() { - return $this->collection->find($this->condition, $this->fields); + if($this->multiple) { + return $this->collection->find($this->condition, $this->fields); + } else { + return $this->collection->findOne($this->condition, $this->fields); + } } protected function checkParams() @@ -84,7 +90,7 @@ class InsertMongoCommand extends MongoDbCommand { protected $data; - protected $safe; + protected $safe = true; protected function concreteExecute() { @@ -107,13 +113,16 @@ class UpdateMongoCommand extends MongoDbCommand protected $data; - protected $upsert; + protected $multiple = true; + + protected $upsert = false; - protected $safe; + protected $safe = true; protected function concreteExecute() { - return $this->collection->update($this->condition, $this->data, array('upsert' => $this->upsert, 'safe' => $this->safe)); + return $this->collection->update($this->condition, $this->data, + array('multiple' => $this->multiple, 'upsert' => $this->upsert, 'safe' => $this->safe)); } protected function checkParams() @@ -132,7 +141,7 @@ class RemoveMongoCommand extends MongoDbCommand { protected $condition; - protected $safe; + protected $safe = true; protected function concreteExecute() { diff --git a/model/MongoDriver.php b/model/MongoDriver.php index 8f75db0..4d1219b 100644 --- a/model/MongoDriver.php +++ b/model/MongoDriver.php @@ -12,66 +12,90 @@ */ class MongoDriver extends NoSqlDbDriver { - - - protected $last_inset_id = 0; + protected $last_insert_id = 0; protected $db_name = 'admin'; + protected $db = null; + protected function getCollection($name) { + if (!$this->connection) { + $this->connect(); + } return $this->connection->selectCollection($this->db, $name); } - public function find($collection, $condition = array(), $fields = array(), $cache_key = null) + public function find($collection, $condition = array(), $fields = array()) { - return $this->getCollection($collection)->find($condition, $fields); + $command = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->getCollection($collection)); + $params = array( + 'condition' => $condition, + 'fields' => $fields + ); + + return $this->query($command, $params); } public function get($collection, $condition, $fields = array()) { - return $this->getCollection($collection)->findOne($condition, $fields); + $command = MongoCommandBuilder::factory(MongoCommandBuilder::FIND, $this->getCollection($collection)); + $params = array( + 'condition' => $condition, + 'fields' => $fields, + 'multiple' => false + ); + return $this->query($command, $params); } public function insert($collection, $data, $safe = true) { - $result = $this->getCollection($collection)->insert($data, array('safe' => $safe)); - if (isset($result['ok']) && $result['ok'] == 1) { - $this->last_inset_id = (string) $data['_id']; - return true; - } else { - return false; - } + $command = MongoCommandBuilder::factory(MongoCommandBuilder::INSERT, $this->getCollection($collection)); + $params = array( + 'data' => $data, + 'safe' => $safe + ); + + return $this->query($command, $params)->affectedRows(); } - public function update($collection, $data, $condition = array(), $upsert = false, $safe = true) + public function update($collection, $data, $condition = array(), $multiple = true, $upsert = false, $safe = true) { - $result = $this->getCollection($collection)->update($condition, $data, array('upsert' => $upsert, 'safe' => $safe)); + $command = MongoCommandBuilder::factory(MongoCommandBuilder::UPDATE, $this->getCollection($collection)); + $params = array( + 'data' => $data, + 'condition' => $condition, + 'multiple' => $multiple, + 'upsert' => $upsert, + 'safe' => $safe + ); - if (isset($result['ok']) && $result['ok'] == 1) { - if(isset($result['updatedExisting']) && isset($result['upserted'])) { - $this->last_inset_id = (string) $result['upserted']; - } - return $result['n']; - } else { - return false; - } + return $this->query($command, $params)->affectedRows(); } public function delete($collection, $condition = array(), $safe = true) { - $result = $this->getCollection($collection)->remove($condition, array('safe' => $safe)); - if (isset($result['ok']) && $result['ok'] == 1) { - return $result['n']; - } else { - return false; - } + $command = MongoCommandBuilder::factory(MongoCommandBuilder::REMOVE, $this->getCollection($collection)); + $params = array( + 'condition' => $condition, + 'safe' => $safe + ); + + return $this->query($command, $params)->affectedRows(); } + /** + * @param mixed $request + * @return DbStatement + */ + public function prepare($request) + { + return new MongoStatement($this, $request); + } public function getInsertId($table = null, $key = null) { - return $this->last_inset_id; + return $this->last_insert_id; } public function isConnected() @@ -110,4 +134,3 @@ class MongoDriver extends NoSqlDbDriver $this->db = $this->connection->selectDB($this->config['db']); } } - diff --git a/model/MongoStatement.php b/model/MongoStatement.php new file mode 100644 index 0000000..da353ee --- /dev/null +++ b/model/MongoStatement.php @@ -0,0 +1,121 @@ + + * @link http://netmonsters.ru + * @package Majestic + * @subpackage db + * @since 2011-11-15 + */ + +/** + * @property MongoDriver $driver + * @property MongoCursor $result + */ +class MongoStatement extends DbStatement +{ + + public function fetch($style = Db::FETCH_OBJ) + { + if (!$this->result) { + return false; + } + + $row = false; + switch ($style) { + case Db::FETCH_OBJ: + $row = $this->fetchObject(); + break; + case Db::FETCH_ASSOC: + if ($this->result instanceof MongoCursor) { + $row = $this->result->getNext(); + } else { + $row = $this->result; + } + break; + default: + throw new Exception('Invalid fetch mode "' . $style . '" specified'); + } + return $row; + } + + public function fetchObject($class = 'stdClass') + { + if ($this->result instanceof MongoCursor) { + $row = $this->result->getNext(); + } else { + $row = $this->result; + } + if (is_array($row) && isset($row['_id'])) { + $row = new ArrayObject($row, ArrayObject::ARRAY_AS_PROPS); + } else { + $row = false; + } + return $row; + } + + public function close() + { + // TODO: Implement close() method. + } + + /** + * @return int + */ + public function affectedRows() + { + if (is_array($this->result)) { + if (isset($this->result['ok']) && $this->result['ok'] == 1 && isset($this->result['n'])) { + return $this->result['n']; + } else { + return false; + } + } + return false; + } + + public function numRows() + { + if ($this->result instanceof MongoCursor) { + return $this->result->count(); + } else { + return false; + } + } + + /** + * @param MongoDbComand $request + * @return bool + */ + protected function driverExecute($request) + { + $mongo = $this->driver->getConnection(); + if ($mongo instanceof Mongo) { + if (DEBUG) { + $profiler = Profiler::getInstance()->profilerCommand('Mongo', $request); + $result = $request->execute(); + $profiler->end(); + } else { + $result = $request->execute(); + } + if ($result === false) { + throw new Exception('MongoDB request error.'); + } + if ($result instanceof MongoCursor || is_array($result)) { + $this->result = $result; + } + return true; + } else { + throw new Exception('No connection to MongoDB server.'); + } + } + + public function bindParam($param, &$value) + { + $this->request->bindParam($param, $value); + } + + protected function assemble() + { + return $this->request; + } +} \ No newline at end of file diff --git a/model/MySQLiStatement.php b/model/MySQLiStatement.php index 6e0ec6b..e0fb4cf 100644 --- a/model/MySQLiStatement.php +++ b/model/MySQLiStatement.php @@ -15,7 +15,69 @@ */ class MySQLiStatement extends DbStatement { - + + protected $map = null; + + public function bindParam($param, &$value) + { + if ($this->map === null) { + $this->mapPlaceholders(); + } + if (count($this->map) > 0) { + if (!is_string($param) && !is_int($param)) { + throw new Exception('Placeholder must be an array or string'); + } + if (is_object($value) && ! ($value instanceof DbExpr)) { + throw new Exception('Objects excepts DbExpr not allowed.'); + } + if (isset($this->map[$param])) { + $this->params[$param] = &$value; + return true; + } + } + return false; + } + protected function mapPlaceholders() + { + $matches = array(); + if(preg_match_all('/(\?|:[A-z0-9_]+)/u', $this->request, $matches, PREG_OFFSET_CAPTURE)) { + $noname = 0; + foreach ($matches[0] as $id=>$match) { + $match[2] = $matches[1][$id][0]; + $name = ($match[2][0] === ':') ? ltrim($match[2], ':') : $noname++; + $this->map[$name]['placeholder'] = $match[0]; + $this->map[$name]['offset'][] = $match[1]; + } + } + } + + protected function assemble() + { + if (empty($this->map)) { + return $this->request; + } + + $query = $this->request; + $placeholders = array(); + foreach($this->map as $name => $place) { + $value = $this->driver->quote($this->params[$name]); + foreach ($place['offset'] as $offset) { + $placeholders[$offset] = array('placeholder' => $place['placeholder'], 'value' => $value); + } + } + + ksort($placeholders); + + $increment = 0; + foreach($placeholders as $current_offset => $placeholder) { + $offset = $current_offset + $increment; + $length = mb_strlen($placeholder['placeholder']); + $query = mb_substr($query, 0, $offset) . $placeholder['value'] . mb_substr($query, $offset + $length); + $increment = (($increment - $length) + mb_strlen($placeholder['value'])); + } + return $query; + } + /** * Fetches single row * @@ -77,21 +139,21 @@ class MySQLiStatement extends DbStatement return false; } - protected function driverExecute($sql) + protected function driverExecute($request) { /** * @var MySQLi */ $mysqli = $this->driver->getConnection(); if (DEBUG) { - $profiler = Profiler::getInstance()->profilerCommand('MySQL', $sql); - $result = $mysqli->query($sql); + $profiler = Profiler::getInstance()->profilerCommand('MySQL', $request); + $result = $mysqli->query($request); $profiler->end(); } else { - $result = $mysqli->query($sql); + $result = $mysqli->query($request); } if ($result === false) { - $message = $mysqli->error . "\nQuery: \"" . $sql . '"'; + $message = $mysqli->error . "\nQuery: \"" . $request . '"'; throw new Exception($message, $mysqli->errno); } if ($result instanceof MySQLi_Result) { diff --git a/model/NoSqlDbDriver.php b/model/NoSqlDbDriver.php index b82e3d3..19a7020 100644 --- a/model/NoSqlDbDriver.php +++ b/model/NoSqlDbDriver.php @@ -10,5 +10,5 @@ abstract class NoSqlDbDriver extends DbDriver { - abstract function find($collection, $condition = array(), $fields = array(), $cache_key = null); + abstract function find($collection, $condition = array(), $fields = array()); } \ No newline at end of file diff --git a/model/SqlDbDriver.php b/model/SqlDbDriver.php index 8d8b9af..de78409 100644 --- a/model/SqlDbDriver.php +++ b/model/SqlDbDriver.php @@ -36,22 +36,6 @@ abstract class SqlDbDriver extends DbDriver } /** - * @param string $sql - * @param mixed $params - * @return DbStatement - */ - public function query($sql, $params = array()) - { - $this->connect(); - if (!is_array($params)) { - $params = array($params); - } - $stmt = $this->prepare($sql); - $stmt->execute($params); - return $stmt; - } - - /** * @param string $table * @param mixed $bind * @param mixed $on_duplicate @@ -171,7 +155,6 @@ abstract class SqlDbDriver extends DbDriver /** * @return DbStatement */ - abstract public function prepare($sql); abstract protected function driverQuote($value);