PageRenderTime 26ms CodeModel.GetById 47ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/mongodb/classes/libraries/mongodb/database.php

https://github.com/enormego/EightPHP
PHP | 425 lines | 173 code | 50 blank | 202 comment | 18 complexity | 5b5b3142fb8420c04cc9fa8daf0d9b18 MD5 | raw file
  1. <?php
  2. /**
  3. * This class wraps the functionality of Mongo (connection) and MongoDB (database object) into one class.
  4. * When used with Kohana it can be instantiated simply by:
  5. *
  6. * <code>
  7. * $db = Mongo_Database::instance();
  8. * </code>
  9. *
  10. * The above will assume the 'default' configuration from the APPPATH/config/mongo.php file.
  11. * Alternatively it may be instantiated with the name and configuration specified as arguments:
  12. *
  13. * <code>
  14. * $db = Mongo_Database::instance('test', array(
  15. * 'database' => 'test'
  16. * ));
  17. * </code>
  18. *
  19. * The Mongo_Collection class will gain access to the server by calling the instance method with a configuration name,
  20. * so if not using Kohana or the configuration name is not present in the config file then the instance should be created
  21. * before using any classes that extend Mongo_Collection or Mongo_Document.
  22. *
  23. * Mongo_Database can proxy all methods of MongoDB to the database instance as well as select collections using the __get
  24. * magic method.
  25. *
  26. * If using Kohana, profiling can be enabled/disabled via the configuration or on demand by setting the profiling property.
  27. *
  28. * @method array authenticate( string $username , string $password )
  29. * @method array command( array $data )
  30. * @method MongoCollection createCollection( string $name, bool $capped = FALSE, int $size = 0, int $max = 0 )
  31. * @method array createDBRef( mixed $ns , mixed $a )
  32. * @method array drop()
  33. * @method array dropCollection( mixed $coll )
  34. * @method bool forceError()
  35. * @method array getDBRef( array $ref )
  36. * @method MongoGridFS getGridFS( string $arg1 = "fs", string $arg2 = NULL )
  37. * @method int getProfilingLevel()
  38. * @method array getReadPreference()
  39. * @method bool getSlaveOkay()
  40. * @method array lastError()
  41. * @method array listCollections()
  42. * @method array prevError()
  43. * @method array repair( bool $preserve_cloned_files = FALSE, bool $backup_original_files = FALSE )
  44. * @method array resetError()
  45. * @method MongoCollection selectCollection( string $name )
  46. * @method int setProfilingLevel( int $level )
  47. * @method bool setReadPreference(int $read_preference, array $tags = array())
  48. * @method bool setSlaveOkay(bool $ok = true)
  49. *
  50. * @author Colin Mollenhour
  51. * @package Mongo_Database
  52. *
  53. * This class was adapted from http://github.com/Wouterrr/MangoDB
  54. */
  55. class MongoDB_Database {
  56. /* See http://bsonspec.org */
  57. const TYPE_DOUBLE = 1;
  58. const TYPE_STRING = 2;
  59. const TYPE_OBJECT = 3;
  60. const TYPE_ARRAY = 4;
  61. const TYPE_BINARY = 5;
  62. const TYPE_OBJECTID = 7;
  63. const TYPE_BOOLEAN = 8;
  64. const TYPE_DATE = 9;
  65. const TYPE_NULL = 10;
  66. const TYPE_REGEX = 11;
  67. const TYPE_CODE = 13;
  68. const TYPE_SYMBOL = 14;
  69. const TYPE_CODE_SCOPED = 15;
  70. const TYPE_INT32 = 16;
  71. const TYPE_TIMESTAMP = 17;
  72. const TYPE_INT64 = 18;
  73. const TYPE_MIN_KEY = 255;
  74. const TYPE_MAX_KEY = 127;
  75. /** Mongo_Database instances
  76. * @static array */
  77. protected static $instances = array();
  78. /**
  79. * Get a Mongo_Database instance. Configuration options are:
  80. *
  81. * <pre>
  82. * server A server connection string. See Mongo::__construct()
  83. * options The additional options for the connection ("connect" and "persist")
  84. * database *required* The database name to use for this instance
  85. * profiling Enable/disable profiling
  86. * </pre>
  87. *
  88. * @param string $name The configuration name
  89. * @param array $config Pass a configuration array to bypass the Kohana config
  90. * @return Mongo_Database
  91. * @static
  92. */
  93. public static function instance($name = 'default', array $config = NULL) {
  94. if( ! isset(self::$instances[$name]) ) {
  95. if ($config === NULL) {
  96. // Load the configuration for this database
  97. $config = Eight::config('mongodb.'.$name);
  98. }
  99. new self($name,$config);
  100. }
  101. return self::$instances[$name];
  102. }
  103. /** Mongo_Database instance name
  104. * @var string */
  105. protected $_name;
  106. /** Connection state
  107. * @var boolean */
  108. protected $_connected = FALSE;
  109. /** The Mongo server connection
  110. * @var Mongo */
  111. protected $_connection;
  112. /** The database instance for the database name chosen by the config
  113. * @var MongoDB */
  114. protected $_db;
  115. /** The class name for the MongoCollection wrapper. Defaults to Mongo_Collection.
  116. * @var string */
  117. protected $_collection_class;
  118. /** A flag to indicate if profiling is enabled and to allow it to be enabled/disabled on the fly
  119. * @var boolean */
  120. public $profiling;
  121. /** A callback called when profiling starts
  122. * @var callback */
  123. protected $_start_callback = array('Profiler','start');
  124. /** A callback called when profiling stops
  125. * @var callback */
  126. protected $_stop_callback = array('Profiler','stop');
  127. /**
  128. * This cannot be called directly, use Mongo_Database::instance() instead to get an instance of this class.
  129. *
  130. * @param string $name The configuration name
  131. * @param array $config The configuration data
  132. */
  133. protected function __construct($name, array $config)
  134. {
  135. $this->_name = $name;
  136. // Setup connection options merged over the defaults and store the connection
  137. $options = array();
  138. if(isset($config['options'])) {
  139. $options = array_merge($options, $config['options']);
  140. }
  141. // Use the default server string if no server option is given
  142. $server = isset($config['server']) ? $config['server'] : "mongodb://".ini_get('mongo.default_host').":".ini_get('mongo.default_port');
  143. $this->_connection = new Mongo($server, $options);
  144. // Save the database name for later use
  145. $this->_db = $config['database'];
  146. // Set the collection class name
  147. $this->_collection_class = (isset($config['collection']) ? $config['collection'] : 'Mongo_Collection');
  148. // Save profiling option in a public variable
  149. $this->profiling = (isset($config['profiling']) && $config['profiling']);
  150. // Store the database instance
  151. self::$instances[$name] = $this;
  152. }
  153. final public function __destruct() {
  154. try {
  155. $this->close();
  156. $this->_connection = NULL;
  157. $this->_connected = FALSE;
  158. } catch(Exception $e) {
  159. // can't throw exceptions in __destruct
  160. }
  161. }
  162. /**
  163. * @return string The configuration name
  164. */
  165. final public function __toString() {
  166. return $this->_name;
  167. }
  168. /**
  169. * Force the connection to be established.
  170. * This will automatically be called by any MongoDB methods that are proxied via __call
  171. *
  172. * @return boolean
  173. * @throws MongoException
  174. */
  175. public function connect() {
  176. if( ! $this->_connected) {
  177. if($this->profiling) {
  178. $_bm = $this->profiler_start("Mongo_Database::{$this->_name}","connect()");
  179. }
  180. $this->_connected = $this->_connection->connect();
  181. if(isset($_bm)) {
  182. $this->profiler_stop($_bm);
  183. }
  184. $this->_db = $this->_connection->selectDB("$this->_db");
  185. }
  186. return $this->_connected;
  187. }
  188. /**
  189. * Close the connection to Mongo
  190. *
  191. * @return boolean if the connection was successfully closed
  192. */
  193. public function close() {
  194. if ($this->_connected) {
  195. $this->_connected = $this->_connection->close();
  196. $this->_db = "$this->_db";
  197. }
  198. return $this->_connected;
  199. }
  200. /**
  201. * Expose the MongoDb instance directly.
  202. *
  203. * @return MongoDb
  204. */
  205. public function db() {
  206. $this->_connected OR $this->connect();
  207. return $this->_db;
  208. }
  209. /**
  210. * Proxy all methods for the MongoDB class.
  211. * Profiles all methods that have database interaction if profiling is enabled.
  212. * The database connection is established lazily.
  213. *
  214. * @param string $name
  215. * @param array $arguments
  216. * @return mixed
  217. */
  218. public function __call($name, $arguments)
  219. {
  220. $this->_connected OR $this->connect();
  221. if( ! method_exists($this->_db, $name))
  222. {
  223. throw new Exception("Method does not exist: MongoDb::$name");
  224. }
  225. if ( $this->profiling && ! strpos("Error",$name) && $name != 'createDBRef' )
  226. {
  227. $json_arguments = array(); foreach($arguments as $arg) $json_arguments[] = json_encode((is_array($arg) ? (object)$arg : $arg));
  228. $method = ($name == 'command' ? 'runCommand' : $name);
  229. $_bm = $this->profiler_start("Mongo_Database::{$this->_name}","db.$method(".implode(',',$json_arguments).")");
  230. }
  231. $retval = call_user_func_array(array($this->_db, $name), $arguments);
  232. if ( isset($_bm))
  233. {
  234. $this->profiler_stop($_bm);
  235. }
  236. return $retval;
  237. }
  238. /**
  239. * Same usage as MongoDB::execute except it throws an exception on error
  240. *
  241. * @param string $code
  242. * @param array $args
  243. * @param array $scope A scope for the code if $code is a string
  244. * @return mixed
  245. * @throws MongoException
  246. */
  247. public function execute_safe( $code, array $args = array(), $scope = array() )
  248. {
  249. if( ! $code instanceof MongoCode) {
  250. $code = new MongoCode($code, $scope);
  251. }
  252. $result = $this->execute($code, $args);
  253. if( empty($result['ok']) )
  254. {
  255. throw new MongoException($result['errmsg'], $result['errno']);
  256. }
  257. return $result['retval'];
  258. }
  259. /**
  260. * Run a command, but throw an exception on error
  261. *
  262. * @param array $command
  263. * @return array
  264. * @throws MongoException
  265. */
  266. public function command_safe($command)
  267. {
  268. $result = $this->command($command);
  269. if( empty($result['ok']) )
  270. {
  271. $message = isset($result['errmsg']) ? $result['errmsg'] : 'Error: '.json_encode($result);
  272. $code = isset($result['errno']) ? $result['errno'] : 0;
  273. throw new MongoException($message, $code);
  274. }
  275. return $result;
  276. }
  277. /**
  278. * Get a Mongo_Collection instance (wraps MongoCollection)
  279. *
  280. * @param string $name
  281. * @return Mongo_Collection
  282. */
  283. public function selectCollection($name)
  284. {
  285. $this->_connected OR $this->connect();
  286. return new $this->_collection_class($name, $this->_name);
  287. }
  288. /**
  289. * Get a Mongo_Collection instance with grid FS enabled (wraps MongoCollection)
  290. *
  291. * @param string $prefix
  292. * @return Mongo_Collection
  293. */
  294. public function getGridFS($prefix = 'fs')
  295. {
  296. $this->_connected OR $this->connect();
  297. return new $this->_collection_class($prefix, $this->_name, TRUE);
  298. }
  299. /**
  300. * Fetch a collection by using object access syntax
  301. *
  302. * @param string $name The collection name to select
  303. * @return Mongo_Collection
  304. */
  305. public function __get($name)
  306. {
  307. return $this->selectCollection($name);
  308. }
  309. /**
  310. * Simple findAndModify helper
  311. *
  312. * @param string $collection
  313. * @param array $command
  314. * @return null|array
  315. * @throws MongoException
  316. */
  317. public function findAndModify($collection, $command) {
  318. $command = array_merge(array('findAndModify' => (string)$collection), $command);
  319. $result = $this->command_safe($command);
  320. return $result['value'];
  321. }
  322. /**
  323. * Get the next auto-increment value for the given key
  324. *
  325. * @param string $key
  326. * @param string $collection
  327. * @return int
  328. * @throws MongoException
  329. */
  330. public function get_auto_increment($key, $collection = 'autoincrements') {
  331. $data = $this->findAndModify($collection, array(
  332. 'query' => array('_id' => $key),
  333. 'update' => array('$inc' => array('value' => 1)),
  334. 'upsert' => TRUE,
  335. 'new' => TRUE,
  336. ));
  337. return $data['value'];
  338. }
  339. /**
  340. * Allows one to override the default Mongo_Collection class.
  341. *
  342. * @param string $class_name
  343. */
  344. public function set_collection_class($class_name)
  345. {
  346. $this->_collection_class = $class_name;
  347. }
  348. /**
  349. * Set the profiler callback. Defaults to Kohana profiler.
  350. *
  351. * @param callback $start
  352. * @param callback $stop
  353. */
  354. public function set_profiler($start, $stop) {
  355. $this->_start_callback = $start;
  356. $this->_stop_callback = $stop;
  357. }
  358. /**
  359. * Start method for profiler
  360. *
  361. * @param string $group
  362. * @param string $query
  363. */
  364. public function profiler_start($group, $query) {
  365. return call_user_func($this->_start_callback, $group, $query);
  366. }
  367. /**
  368. * Stop method for profiler
  369. *
  370. * @param string $token
  371. */
  372. public function profiler_stop($token) {
  373. call_user_func($this->_stop_callback, $token);
  374. }
  375. } // End MongoDB Database Class