MongoDB.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. */
  15. use Alcaeus\MongoDbAdapter\Helper;
  16. use Alcaeus\MongoDbAdapter\TypeConverter;
  17. use MongoDB\Model\CollectionInfo;
  18. /**
  19. * Instances of this class are used to interact with a database.
  20. * @link http://www.php.net/manual/en/class.mongodb.php
  21. */
  22. class MongoDB
  23. {
  24. use Helper\ReadPreference;
  25. use Helper\SlaveOkay;
  26. use Helper\WriteConcern;
  27. const PROFILING_OFF = 0;
  28. const PROFILING_SLOW = 1;
  29. const PROFILING_ON = 2;
  30. /**
  31. * @var MongoClient
  32. */
  33. protected $connection;
  34. /**
  35. * @var \MongoDB\Database
  36. */
  37. protected $db;
  38. /**
  39. * @var string
  40. */
  41. protected $name;
  42. /**
  43. * Creates a new database
  44. *
  45. * This method is not meant to be called directly. The preferred way to create an instance of MongoDB is through {@see Mongo::__get()} or {@see Mongo::selectDB()}.
  46. * @link http://www.php.net/manual/en/mongodb.construct.php
  47. * @param MongoClient $conn Database connection.
  48. * @param string $name Database name.
  49. * @throws Exception
  50. * @return MongoDB Returns the database.
  51. */
  52. public function __construct(MongoClient $conn, $name)
  53. {
  54. $this->connection = $conn;
  55. $this->name = $name;
  56. $this->setReadPreferenceFromArray($conn->getReadPreference());
  57. $this->setWriteConcernFromArray($conn->getWriteConcern());
  58. $this->createDatabaseObject();
  59. }
  60. /**
  61. * @return \MongoDB\Database
  62. * @internal This method is not part of the ext-mongo API
  63. */
  64. public function getDb()
  65. {
  66. return $this->db;
  67. }
  68. /**
  69. * The name of this database
  70. *
  71. * @link http://www.php.net/manual/en/mongodb.--tostring.php
  72. * @return string Returns this database's name.
  73. */
  74. public function __toString()
  75. {
  76. return $this->name;
  77. }
  78. /**
  79. * Gets a collection
  80. *
  81. * @link http://www.php.net/manual/en/mongodb.get.php
  82. * @param string $name The name of the collection.
  83. * @return MongoCollection
  84. */
  85. public function __get($name)
  86. {
  87. // Handle w and wtimeout properties that replicate data stored in $readPreference
  88. if ($name === 'w' || $name === 'wtimeout') {
  89. return $this->getWriteConcern()[$name];
  90. }
  91. return $this->selectCollection($name);
  92. }
  93. /**
  94. * @param string $name
  95. * @param mixed $value
  96. */
  97. public function __set($name, $value)
  98. {
  99. if ($name === 'w' || $name === 'wtimeout') {
  100. $this->setWriteConcernFromArray([$name => $value] + $this->getWriteConcern());
  101. $this->createDatabaseObject();
  102. }
  103. }
  104. /**
  105. * Returns information about collections in this database
  106. *
  107. * @link http://www.php.net/manual/en/mongodb.getcollectioninfo.php
  108. * @param array $options An array of options for listing the collections.
  109. * @return array
  110. */
  111. public function getCollectionInfo(array $options = [])
  112. {
  113. // The includeSystemCollections option is no longer supported
  114. if (isset($options['includeSystemCollections'])) {
  115. unset($options['includeSystemCollections']);
  116. }
  117. $collections = $this->db->listCollections($options);
  118. $getCollectionInfo = function (CollectionInfo $collectionInfo) {
  119. return [
  120. 'name' => $collectionInfo->getName(),
  121. 'options' => $collectionInfo->getOptions(),
  122. ];
  123. };
  124. return array_map($getCollectionInfo, iterator_to_array($collections));
  125. }
  126. /**
  127. * Get all collections from this database
  128. *
  129. * @link http://www.php.net/manual/en/mongodb.getcollectionnames.php
  130. * @param array $options An array of options for listing the collections.
  131. * @return array Returns the names of the all the collections in the database as an array
  132. */
  133. public function getCollectionNames(array $options = [])
  134. {
  135. // The includeSystemCollections option is no longer supported
  136. if (isset($options['includeSystemCollections'])) {
  137. unset($options['includeSystemCollections']);
  138. }
  139. $collections = $this->db->listCollections($options);
  140. $getCollectionName = function (CollectionInfo $collectionInfo) {
  141. return $collectionInfo->getName();
  142. };
  143. return array_map($getCollectionName, iterator_to_array($collections));
  144. }
  145. /**
  146. * @return MongoClient
  147. * @internal This method is not part of the ext-mongo API
  148. */
  149. public function getConnection()
  150. {
  151. return $this->connection;
  152. }
  153. /**
  154. * Fetches toolkit for dealing with files stored in this database
  155. *
  156. * @link http://www.php.net/manual/en/mongodb.getgridfs.php
  157. * @param string $prefix The prefix for the files and chunks collections.
  158. * @return MongoGridFS Returns a new gridfs object for this database.
  159. */
  160. public function getGridFS($prefix = "fs")
  161. {
  162. return new \MongoGridFS($this, $prefix, $prefix);
  163. }
  164. /**
  165. * Gets this database's profiling level
  166. *
  167. * @link http://www.php.net/manual/en/mongodb.getprofilinglevel.php
  168. * @return int Returns the profiling level.
  169. */
  170. public function getProfilingLevel()
  171. {
  172. $result = $this->command(['profile' => -1]);
  173. return ($result['ok'] && isset($result['was'])) ? $result['was'] : 0;
  174. }
  175. /**
  176. * Sets this database's profiling level
  177. *
  178. * @link http://www.php.net/manual/en/mongodb.setprofilinglevel.php
  179. * @param int $level Profiling level.
  180. * @return int Returns the previous profiling level.
  181. */
  182. public function setProfilingLevel($level)
  183. {
  184. $result = $this->command(['profile' => $level]);
  185. return ($result['ok'] && isset($result['was'])) ? $result['was'] : 0;
  186. }
  187. /**
  188. * Drops this database
  189. *
  190. * @link http://www.php.net/manual/en/mongodb.drop.php
  191. * @return array Returns the database response.
  192. */
  193. public function drop()
  194. {
  195. return TypeConverter::toLegacy($this->db->drop());
  196. }
  197. /**
  198. * Repairs and compacts this database
  199. *
  200. * @link http://www.php.net/manual/en/mongodb.repair.php
  201. * @param bool $preserve_cloned_files [optional] <p>If cloned files should be kept if the repair fails.</p>
  202. * @param bool $backup_original_files [optional] <p>If original files should be backed up.</p>
  203. * @return array <p>Returns db response.</p>
  204. */
  205. public function repair($preserve_cloned_files = FALSE, $backup_original_files = FALSE)
  206. {
  207. $this->notImplemented();
  208. }
  209. /**
  210. * Gets a collection
  211. *
  212. * @link http://www.php.net/manual/en/mongodb.selectcollection.php
  213. * @param string $name <b>The collection name.</b>
  214. * @throws Exception if the collection name is invalid.
  215. * @return MongoCollection Returns a new collection object.
  216. */
  217. public function selectCollection($name)
  218. {
  219. return new MongoCollection($this, $name);
  220. }
  221. /**
  222. * Creates a collection
  223. *
  224. * @link http://www.php.net/manual/en/mongodb.createcollection.php
  225. * @param string $name The name of the collection.
  226. * @param array $options
  227. * @return MongoCollection Returns a collection object representing the new collection.
  228. */
  229. public function createCollection($name, $options)
  230. {
  231. $this->db->createCollection($name, $options);
  232. return $this->selectCollection($name);
  233. }
  234. /**
  235. * Drops a collection
  236. *
  237. * @link http://www.php.net/manual/en/mongodb.dropcollection.php
  238. * @param MongoCollection|string $coll MongoCollection or name of collection to drop.
  239. * @return array Returns the database response.
  240. *
  241. * @deprecated Use MongoCollection::drop() instead.
  242. */
  243. public function dropCollection($coll)
  244. {
  245. if ($coll instanceof MongoCollection) {
  246. $coll = $coll->getName();
  247. }
  248. return TypeConverter::toLegacy($this->db->dropCollection((string) $coll));
  249. }
  250. /**
  251. * Get a list of collections in this database
  252. *
  253. * @link http://www.php.net/manual/en/mongodb.listcollections.php
  254. * @param array $options
  255. * @return MongoCollection[] Returns a list of MongoCollections.
  256. */
  257. public function listCollections(array $options = [])
  258. {
  259. return array_map([$this, 'selectCollection'], $this->getCollectionNames($options));
  260. }
  261. /**
  262. * Creates a database reference
  263. *
  264. * @link http://www.php.net/manual/en/mongodb.createdbref.php
  265. * @param string $collection The collection to which the database reference will point.
  266. * @param mixed $document_or_id
  267. * @return array Returns a database reference array.
  268. */
  269. public function createDBRef($collection, $document_or_id)
  270. {
  271. if ($document_or_id instanceof \MongoId) {
  272. $id = $document_or_id;
  273. } elseif (is_object($document_or_id)) {
  274. if (! isset($document_or_id->_id)) {
  275. return null;
  276. }
  277. $id = $document_or_id->_id;
  278. } elseif (is_array($document_or_id)) {
  279. if (! isset($document_or_id['_id'])) {
  280. return null;
  281. }
  282. $id = $document_or_id['_id'];
  283. } else {
  284. $id = $document_or_id;
  285. }
  286. return MongoDBRef::create($collection, $id, $this->name);
  287. }
  288. /**
  289. * Fetches the document pointed to by a database reference
  290. *
  291. * @link http://www.php.net/manual/en/mongodb.getdbref.php
  292. * @param array $ref A database reference.
  293. * @return array Returns the document pointed to by the reference.
  294. */
  295. public function getDBRef(array $ref)
  296. {
  297. return MongoDBRef::get($this, $ref);
  298. }
  299. /**
  300. * Runs JavaScript code on the database server.
  301. *
  302. * @link http://www.php.net/manual/en/mongodb.execute.php
  303. * @param MongoCode|string $code Code to execute.
  304. * @param array $args [optional] Arguments to be passed to code.
  305. * @return array Returns the result of the evaluation.
  306. */
  307. public function execute($code, array $args = [])
  308. {
  309. return $this->command(['eval' => $code, 'args' => $args]);
  310. }
  311. /**
  312. * Execute a database command
  313. *
  314. * @link http://www.php.net/manual/en/mongodb.command.php
  315. * @param array $data The query to send.
  316. * @param array $options
  317. * @return array Returns database response.
  318. */
  319. public function command(array $data, $options = [], &$hash = null)
  320. {
  321. try {
  322. $cursor = new \MongoCommandCursor($this->connection, $this->name, $data);
  323. $cursor->setReadPreference($this->getReadPreference());
  324. return iterator_to_array($cursor)[0];
  325. } catch (\MongoDB\Driver\Exception\RuntimeException $e) {
  326. return [
  327. 'ok' => 0,
  328. 'errmsg' => $e->getMessage(),
  329. 'code' => $e->getCode(),
  330. ];
  331. }
  332. }
  333. /**
  334. * Check if there was an error on the most recent db operation performed
  335. *
  336. * @link http://www.php.net/manual/en/mongodb.lasterror.php
  337. * @return array Returns the error, if there was one.
  338. */
  339. public function lastError()
  340. {
  341. return $this->command(array('getLastError' => 1));
  342. }
  343. /**
  344. * Checks for the last error thrown during a database operation
  345. *
  346. * @link http://www.php.net/manual/en/mongodb.preverror.php
  347. * @return array Returns the error and the number of operations ago it occurred.
  348. */
  349. public function prevError()
  350. {
  351. return $this->command(array('getPrevError' => 1));
  352. }
  353. /**
  354. * Clears any flagged errors on the database
  355. *
  356. * @link http://www.php.net/manual/en/mongodb.reseterror.php
  357. * @return array Returns the database response.
  358. */
  359. public function resetError()
  360. {
  361. return $this->command(array('resetError' => 1));
  362. }
  363. /**
  364. * Creates a database error
  365. *
  366. * @link http://www.php.net/manual/en/mongodb.forceerror.php
  367. * @return boolean Returns the database response.
  368. */
  369. public function forceError()
  370. {
  371. return $this->command(array('forceerror' => 1));
  372. }
  373. /**
  374. * Log in to this database
  375. *
  376. * @link http://www.php.net/manual/en/mongodb.authenticate.php
  377. * @param string $username The username.
  378. * @param string $password The password (in plaintext).
  379. * @return array Returns database response. If the login was successful, it will return 1.
  380. */
  381. public function authenticate($username, $password)
  382. {
  383. $this->notImplemented();
  384. }
  385. /**
  386. * {@inheritdoc}
  387. */
  388. public function setReadPreference($readPreference, $tags = null)
  389. {
  390. $result = $this->setReadPreferenceFromParameters($readPreference, $tags);
  391. $this->createDatabaseObject();
  392. return $result;
  393. }
  394. /**
  395. * {@inheritdoc}
  396. */
  397. public function setWriteConcern($wstring, $wtimeout = 0)
  398. {
  399. $result = $this->setWriteConcernFromParameters($wstring, $wtimeout);
  400. $this->createDatabaseObject();
  401. return $result;
  402. }
  403. protected function notImplemented()
  404. {
  405. throw new \Exception('Not implemented');
  406. }
  407. /**
  408. * @return \MongoDB\Database
  409. */
  410. private function createDatabaseObject()
  411. {
  412. $options = [
  413. 'readPreference' => $this->readPreference,
  414. 'writeConcern' => $this->writeConcern,
  415. ];
  416. if ($this->db === null) {
  417. $this->db = $this->connection->getClient()->selectDatabase($this->name, $options);
  418. } else {
  419. $this->db = $this->db->withOptions($options);
  420. }
  421. }
  422. }