Connection.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\mongodb;
  8. use MongoDB\Driver\Manager;
  9. use yii\base\Component;
  10. use yii\base\InvalidConfigException;
  11. use Yii;
  12. /**
  13. * Connection represents a connection to a MongoDb server.
  14. *
  15. * Connection works together with [[Database]] and [[Collection]] to provide data access
  16. * to the Mongo database. They are wrappers of the [[MongoDB PHP extension]](http://us1.php.net/manual/en/book.mongo.php).
  17. *
  18. * To establish a DB connection, set [[dsn]] and then call [[open()]] to be true.
  19. *
  20. * The following example shows how to create a Connection instance and establish
  21. * the DB connection:
  22. *
  23. * ```php
  24. * $connection = new \yii\mongodb\Connection([
  25. * 'dsn' => $dsn,
  26. * ]);
  27. * $connection->open();
  28. * ```
  29. *
  30. * After the Mongo connection is established, one can access Mongo databases and collections:
  31. *
  32. * ```php
  33. * $database = $connection->getDatabase('my_mongo_db');
  34. * $collection = $database->getCollection('customer');
  35. * $collection->insert(['name' => 'John Smith', 'status' => 1]);
  36. * ```
  37. *
  38. * You can work with several different databases at the same server using this class.
  39. * However, while it is unlikely your application will actually need it, the Connection class
  40. * provides ability to use [[defaultDatabaseName]] as well as a shortcut method [[getCollection()]]
  41. * to retrieve a particular collection instance:
  42. *
  43. * ```php
  44. * // get collection 'customer' from default database:
  45. * $collection = $connection->getCollection('customer');
  46. * // get collection 'customer' from database 'mydatabase':
  47. * $collection = $connection->getCollection(['mydatabase', 'customer']);
  48. * ```
  49. *
  50. * Connection is often used as an application component and configured in the application
  51. * configuration like the following:
  52. *
  53. * ```php
  54. * [
  55. * 'components' => [
  56. * 'mongodb' => [
  57. * 'class' => '\yii\mongodb\Connection',
  58. * 'dsn' => 'mongodb://developer:password@localhost:27017/mydatabase',
  59. * ],
  60. * ],
  61. * ]
  62. * ```
  63. *
  64. * @property Database $database Database instance. This property is read-only.
  65. * @property string $defaultDatabaseName Default database name.
  66. * @property file\Collection $fileCollection Mongo GridFS collection instance. This property is read-only.
  67. * @property bool $isActive Whether the Mongo connection is established. This property is read-only.
  68. * @property LogBuilder $logBuilder The log builder for this connection. Note that the type of this property
  69. * differs in getter and setter. See [[getLogBuilder()]] and [[setLogBuilder()]] for details.
  70. * @property QueryBuilder $queryBuilder The query builder for the this MongoDB connection. Note that the type
  71. * of this property differs in getter and setter. See [[getQueryBuilder()]] and [[setQueryBuilder()]] for
  72. * details.
  73. *
  74. * @author Paul Klimov <klimov.paul@gmail.com>
  75. * @since 2.0
  76. */
  77. class Connection extends Component
  78. {
  79. /**
  80. * @event Event an event that is triggered after a DB connection is established
  81. */
  82. const EVENT_AFTER_OPEN = 'afterOpen';
  83. /**
  84. * @var string host:port
  85. *
  86. * Correct syntax is:
  87. * mongodb://[username:password@]host1[:port1][,host2[:port2:],...][/dbname]
  88. * For example:
  89. * mongodb://localhost:27017
  90. * mongodb://developer:password@localhost:27017
  91. * mongodb://developer:password@localhost:27017/mydatabase
  92. */
  93. public $dsn;
  94. /**
  95. * @var array connection options.
  96. * For example:
  97. *
  98. * ```php
  99. * [
  100. * 'socketTimeoutMS' => 1000, // how long a send or receive on a socket can take before timing out
  101. * 'ssl' => true // initiate the connection with TLS/SSL
  102. * ]
  103. * ```
  104. *
  105. * @see https://docs.mongodb.com/manual/reference/connection-string/#connections-connection-options
  106. */
  107. public $options = [];
  108. /**
  109. * @var array options for the MongoDB driver.
  110. * Any driver-specific options not included in MongoDB connection string specification.
  111. *
  112. * @see http://php.net/manual/en/mongodb-driver-manager.construct.php
  113. */
  114. public $driverOptions = [];
  115. /**
  116. * @var Manager MongoDB driver manager.
  117. * @since 2.1
  118. */
  119. public $manager;
  120. /**
  121. * @var array type map to use for BSON unserialization.
  122. * Note: default type map will be automatically merged into this field, possibly overriding user-defined values.
  123. * @see http://php.net/manual/en/mongodb-driver-cursor.settypemap.php
  124. * @since 2.1
  125. */
  126. public $typeMap = [];
  127. /**
  128. * @var bool whether to log command and query executions.
  129. * When enabled this option may reduce performance. MongoDB commands may contain large data,
  130. * consuming both CPU and memory.
  131. * It makes sense to disable this option in the production environment.
  132. * @since 2.1
  133. */
  134. public $enableLogging = true;
  135. /**
  136. * @var bool whether to enable profiling the commands and queries being executed.
  137. * This option will have no effect in case [[enableLogging]] is disabled.
  138. * @since 2.1
  139. */
  140. public $enableProfiling = true;
  141. /**
  142. * @var string name of the protocol, which should be used for the GridFS stream wrapper.
  143. * Only alphanumeric values are allowed: do not use any URL special characters, such as '/', '&', ':' etc.
  144. * @see \yii\mongodb\file\StreamWrapper
  145. * @since 2.1
  146. */
  147. public $fileStreamProtocol = 'gridfs';
  148. /**
  149. * @var string name of the class, which should serve as a stream wrapper for [[fileStreamProtocol]] protocol.
  150. * @since 2.1
  151. */
  152. public $fileStreamWrapperClass = 'yii\mongodb\file\StreamWrapper';
  153. /**
  154. * @var string name of the MongoDB database to use by default.
  155. * If this field left blank, connection instance will attempt to determine it from
  156. * [[dsn]] automatically, if needed.
  157. */
  158. private $_defaultDatabaseName;
  159. /**
  160. * @var Database[] list of Mongo databases
  161. */
  162. private $_databases = [];
  163. /**
  164. * @var QueryBuilder|array|string the query builder for this connection
  165. * @since 2.1
  166. */
  167. private $_queryBuilder = 'yii\mongodb\QueryBuilder';
  168. /**
  169. * @var LogBuilder|array|string log entries builder used for this connecton.
  170. * @since 2.1
  171. */
  172. private $_logBuilder = 'yii\mongodb\LogBuilder';
  173. /**
  174. * @var bool whether GridFS stream wrapper has been already registered.
  175. * @since 2.1
  176. */
  177. private $_fileStreamWrapperRegistered = false;
  178. /**
  179. * Sets default database name.
  180. * @param string $name default database name.
  181. */
  182. public function setDefaultDatabaseName($name)
  183. {
  184. $this->_defaultDatabaseName = $name;
  185. }
  186. /**
  187. * Returns default database name, if it is not set,
  188. * attempts to determine it from [[dsn]] value.
  189. * @return string default database name
  190. * @throws \yii\base\InvalidConfigException if unable to determine default database name.
  191. */
  192. public function getDefaultDatabaseName()
  193. {
  194. if ($this->_defaultDatabaseName === null) {
  195. if (preg_match('/^mongodb:\\/\\/.+\\/([^?&]+)/s', $this->dsn, $matches)) {
  196. $this->_defaultDatabaseName = $matches[1];
  197. } else {
  198. throw new InvalidConfigException("Unable to determine default database name from dsn.");
  199. }
  200. }
  201. return $this->_defaultDatabaseName;
  202. }
  203. /**
  204. * Returns the query builder for the this MongoDB connection.
  205. * @return QueryBuilder the query builder for the this MongoDB connection.
  206. * @since 2.1
  207. */
  208. public function getQueryBuilder()
  209. {
  210. if (!is_object($this->_queryBuilder)) {
  211. $this->_queryBuilder = Yii::createObject($this->_queryBuilder, [$this]);
  212. }
  213. return $this->_queryBuilder;
  214. }
  215. /**
  216. * Sets the query builder for the this MongoDB connection.
  217. * @param QueryBuilder|array|string|null $queryBuilder the query builder for this MongoDB connection.
  218. * @since 2.1
  219. */
  220. public function setQueryBuilder($queryBuilder)
  221. {
  222. $this->_queryBuilder = $queryBuilder;
  223. }
  224. /**
  225. * Returns log builder for this connection.
  226. * @return LogBuilder the log builder for this connection.
  227. * @since 2.1
  228. */
  229. public function getLogBuilder()
  230. {
  231. if (!is_object($this->_logBuilder)) {
  232. $this->_logBuilder = Yii::createObject($this->_logBuilder);
  233. }
  234. return $this->_logBuilder;
  235. }
  236. /**
  237. * Sets log builder used for this connection.
  238. * @param array|string|LogBuilder $logBuilder the log builder for this connection.
  239. * @since 2.1
  240. */
  241. public function setLogBuilder($logBuilder)
  242. {
  243. $this->_logBuilder = $logBuilder;
  244. }
  245. /**
  246. * Returns the MongoDB database with the given name.
  247. * @param string|null $name database name, if null default one will be used.
  248. * @param bool $refresh whether to reestablish the database connection even, if it is found in the cache.
  249. * @return Database database instance.
  250. */
  251. public function getDatabase($name = null, $refresh = false)
  252. {
  253. if ($name === null) {
  254. $name = $this->getDefaultDatabaseName();
  255. }
  256. if ($refresh || !array_key_exists($name, $this->_databases)) {
  257. $this->_databases[$name] = $this->selectDatabase($name);
  258. }
  259. return $this->_databases[$name];
  260. }
  261. /**
  262. * Selects the database with given name.
  263. * @param string $name database name.
  264. * @return Database database instance.
  265. */
  266. protected function selectDatabase($name)
  267. {
  268. return Yii::createObject([
  269. 'class' => 'yii\mongodb\Database',
  270. 'name' => $name,
  271. 'connection' => $this,
  272. ]);
  273. }
  274. /**
  275. * Returns the MongoDB collection with the given name.
  276. * @param string|array $name collection name. If string considered as the name of the collection
  277. * inside the default database. If array - first element considered as the name of the database,
  278. * second - as name of collection inside that database
  279. * @param bool $refresh whether to reload the collection instance even if it is found in the cache.
  280. * @return Collection Mongo collection instance.
  281. */
  282. public function getCollection($name, $refresh = false)
  283. {
  284. if (is_array($name)) {
  285. list ($dbName, $collectionName) = $name;
  286. return $this->getDatabase($dbName)->getCollection($collectionName, $refresh);
  287. }
  288. return $this->getDatabase()->getCollection($name, $refresh);
  289. }
  290. /**
  291. * Returns the MongoDB GridFS collection.
  292. * @param string|array $prefix collection prefix. If string considered as the prefix of the GridFS
  293. * collection inside the default database. If array - first element considered as the name of the database,
  294. * second - as prefix of the GridFS collection inside that database, if no second element present
  295. * default "fs" prefix will be used.
  296. * @param bool $refresh whether to reload the collection instance even if it is found in the cache.
  297. * @return file\Collection Mongo GridFS collection instance.
  298. */
  299. public function getFileCollection($prefix = 'fs', $refresh = false)
  300. {
  301. if (is_array($prefix)) {
  302. list ($dbName, $collectionPrefix) = $prefix;
  303. if (!isset($collectionPrefix)) {
  304. $collectionPrefix = 'fs';
  305. }
  306. return $this->getDatabase($dbName)->getFileCollection($collectionPrefix, $refresh);
  307. }
  308. return $this->getDatabase()->getFileCollection($prefix, $refresh);
  309. }
  310. /**
  311. * Returns a value indicating whether the Mongo connection is established.
  312. * @return bool whether the Mongo connection is established
  313. */
  314. public function getIsActive()
  315. {
  316. return is_object($this->manager) && $this->manager->getServers() !== [];
  317. }
  318. /**
  319. * Establishes a Mongo connection.
  320. * It does nothing if a MongoDB connection has already been established.
  321. * @throws Exception if connection fails
  322. */
  323. public function open()
  324. {
  325. if ($this->manager === null) {
  326. if (empty($this->dsn)) {
  327. throw new InvalidConfigException($this->className() . '::dsn cannot be empty.');
  328. }
  329. $token = 'Opening MongoDB connection: ' . $this->dsn;
  330. try {
  331. Yii::trace($token, __METHOD__);
  332. Yii::beginProfile($token, __METHOD__);
  333. $options = $this->options;
  334. $this->manager = new Manager($this->dsn, $options, $this->driverOptions);
  335. $this->manager->selectServer($this->manager->getReadPreference());
  336. $this->initConnection();
  337. Yii::endProfile($token, __METHOD__);
  338. } catch (\Exception $e) {
  339. Yii::endProfile($token, __METHOD__);
  340. throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
  341. }
  342. $this->typeMap = array_merge(
  343. $this->typeMap,
  344. [
  345. 'root' => 'array',
  346. 'document' => 'array'
  347. ]
  348. );
  349. }
  350. }
  351. /**
  352. * Closes the currently active DB connection.
  353. * It does nothing if the connection is already closed.
  354. */
  355. public function close()
  356. {
  357. if ($this->manager !== null) {
  358. Yii::trace('Closing MongoDB connection: ' . $this->dsn, __METHOD__);
  359. $this->manager = null;
  360. foreach ($this->_databases as $database) {
  361. $database->clearCollections();
  362. }
  363. $this->_databases = [];
  364. }
  365. }
  366. /**
  367. * Initializes the DB connection.
  368. * This method is invoked right after the DB connection is established.
  369. * The default implementation triggers an [[EVENT_AFTER_OPEN]] event.
  370. */
  371. protected function initConnection()
  372. {
  373. $this->trigger(self::EVENT_AFTER_OPEN);
  374. }
  375. /**
  376. * Creates MongoDB command.
  377. * @param array $document command document contents.
  378. * @param string|null $databaseName database name, if not set [[defaultDatabaseName]] will be used.
  379. * @return Command command instance.
  380. * @since 2.1
  381. */
  382. public function createCommand($document = [], $databaseName = null)
  383. {
  384. return new Command([
  385. 'db' => $this,
  386. 'databaseName' => $databaseName,
  387. 'document' => $document,
  388. ]);
  389. }
  390. /**
  391. * Registers GridFS stream wrapper for the [[fileStreamProtocol]] protocol.
  392. * @param bool $force whether to enforce registration even wrapper has been already registered.
  393. * @return string registered stream protocol name.
  394. */
  395. public function registerFileStreamWrapper($force = false)
  396. {
  397. if ($force || !$this->_fileStreamWrapperRegistered) {
  398. /* @var $class \yii\mongodb\file\StreamWrapper */
  399. $class = $this->fileStreamWrapperClass;
  400. $class::register($this->fileStreamProtocol, $force);
  401. $this->_fileStreamWrapperRegistered = true;
  402. }
  403. return $this->fileStreamProtocol;
  404. }
  405. }