Completed
Push — master ( fec136...e0f8d3 )
by Peter
40:05 queued 40:05
created

Mangan::getDbInstance()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 14.7187

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 23
ccs 1
cts 8
cp 0.125
rs 8.7972
cc 4
eloc 11
nc 4
nop 0
crap 14.7187
1
<?php
2
3
/**
4
 * This software package is licensed under AGPL or Commercial license.
5
 *
6
 * @package maslosoft/mangan
7
 * @licence AGPL or Commercial
8
 * @copyright Copyright (c) Piotr Masełkowski <[email protected]>
9
 * @copyright Copyright (c) Maslosoft
10
 * @copyright Copyright (c) Others as mentioned in code
11
 * @link http://maslosoft.com/mangan/
12
 */
13
14
namespace Maslosoft\Mangan;
15
16
use Maslosoft\Addendum\Interfaces\AnnotatedInterface;
17
use Maslosoft\EmbeDi\EmbeDi;
18
use Maslosoft\Mangan\Decorators\DbRefArrayDecorator;
19
use Maslosoft\Mangan\Decorators\DbRefDecorator;
20
use Maslosoft\Mangan\Decorators\EmbeddedArrayDecorator;
21
use Maslosoft\Mangan\Decorators\EmbeddedDecorator;
22
use Maslosoft\Mangan\Decorators\EmbedRefArrayDecorator;
23
use Maslosoft\Mangan\Decorators\EmbedRefDecorator;
24
use Maslosoft\Mangan\Decorators\Model\AliasDecorator;
25
use Maslosoft\Mangan\Decorators\Model\ClassNameDecorator;
26
use Maslosoft\Mangan\Decorators\Model\OwnerDecorator;
27
use Maslosoft\Mangan\Decorators\Property\I18NDecorator;
28
use Maslosoft\Mangan\Decorators\RelatedArrayDecorator;
29
use Maslosoft\Mangan\Decorators\RelatedDecorator;
30
use Maslosoft\Mangan\Exceptions\ManganException;
31
use Maslosoft\Mangan\Helpers\ConnectionStorage;
32
use Maslosoft\Mangan\Interfaces\Exception\ExceptionCodeInterface;
33
use Maslosoft\Mangan\Interfaces\ManganAwareInterface;
34
use Maslosoft\Mangan\Interfaces\ProfilerInterface;
35
use Maslosoft\Mangan\Interfaces\Transformators\TransformatorInterface;
36
use Maslosoft\Mangan\Meta\ManganMeta;
37
use Maslosoft\Mangan\Profillers\NullProfiler;
38
use Maslosoft\Mangan\Sanitizers\DateSanitizer;
39
use Maslosoft\Mangan\Sanitizers\DateWriteUnixSanitizer;
40
use Maslosoft\Mangan\Sanitizers\MongoObjectId;
41
use Maslosoft\Mangan\Sanitizers\MongoWriteStringId;
42
use Maslosoft\Mangan\Transformers\CriteriaArray;
43
use Maslosoft\Mangan\Transformers\DocumentArray;
44
use Maslosoft\Mangan\Transformers\Filters\DocumentArrayFilter;
45
use Maslosoft\Mangan\Transformers\Filters\JsonFilter;
46
use Maslosoft\Mangan\Transformers\Filters\PersistentFilter;
47
use Maslosoft\Mangan\Transformers\JsonArray;
48
use Maslosoft\Mangan\Transformers\RawArray;
49
use Maslosoft\Mangan\Transformers\SafeArray;
50
use Maslosoft\Mangan\Transformers\YamlArray;
51
use Maslosoft\Mangan\Validators\BuiltIn\CompareValidator;
52
use Maslosoft\Mangan\Validators\BuiltIn\EmailValidator;
53
use Maslosoft\Mangan\Validators\BuiltIn\NumberValidator;
54
use Maslosoft\Mangan\Validators\BuiltIn\RangeValidator;
55
use Maslosoft\Mangan\Validators\BuiltIn\RegexValidator;
56
use Maslosoft\Mangan\Validators\BuiltIn\RequiredValidator;
57
use Maslosoft\Mangan\Validators\BuiltIn\StringValidator;
58
use Maslosoft\Mangan\Validators\BuiltIn\UniqueValidator;
59
use Maslosoft\Mangan\Validators\BuiltIn\UrlValidator;
60
use Maslosoft\Mangan\Validators\Proxy\BooleanProxy;
61
use Maslosoft\Mangan\Validators\Proxy\BooleanValidator;
62
use Maslosoft\Mangan\Validators\Proxy\CompareProxy;
63
use Maslosoft\Mangan\Validators\Proxy\EmailProxy;
64
use Maslosoft\Mangan\Validators\Proxy\NumberProxy;
65
use Maslosoft\Mangan\Validators\Proxy\RangeProxy;
66
use Maslosoft\Mangan\Validators\Proxy\RegexProxy;
67
use Maslosoft\Mangan\Validators\Proxy\RequiredProxy;
68
use Maslosoft\Mangan\Validators\Proxy\StringProxy;
69
use Maslosoft\Mangan\Validators\Proxy\UniqueProxy;
70
use Maslosoft\Mangan\Validators\Proxy\UrlProxy;
71
use MongoClient;
72
use MongoDB;
73
use MongoException;
74
use Psr\Log\LoggerAwareInterface;
75
use Psr\Log\LoggerInterface;
76
use Psr\Log\NullLogger;
77
78
/**
79
 * MongoDB
80
 *
81
 * This is merge work of tyohan, Alexander Makarov and mine
82
 * @author Ianaré Sévi
83
 * @author Dariusz Górecki <[email protected]>
84
 * @author Invenzzia Group, open-source division of CleverIT company http://www.invenzzia.org
85
 * @copyright 2011 CleverIT http://www.cleverit.com.pl
86
 * @property LoggerInterface $logger Logger
87
 * @property-read string $version Current version
88
 * @since v1.0
89
 */
90
class Mangan implements LoggerAwareInterface
91
{
92
93
	const DefaultConnectionId = 'mongodb';
94
95
	use Traits\Defaults\MongoClientOptions;
96
97
	/**
98
	 * Correct syntax is:
99
	 * mongodb://[username:password@]host1[:port1][,host2[:port2:],...]
100
	 * @example mongodb://localhost:27017
101
	 * @var string host:port
102
	 * @since v1.0
103
	 */
104
	public $connectionString = 'mongodb://localhost:27017';
105
106
	/**
107
	 * Configuration of decorators for transformers
108
	 * Array key is decorator class name or interface, values are decorator class names.
109
	 * @var string[][]
110
	 */
111
	public $decorators = [
112
		TransformatorInterface::class => [
113
			EmbeddedArrayDecorator::class,
114
			EmbeddedDecorator::class,
115
			AliasDecorator::class,
116
			OwnerDecorator::class,
117
		],
118
		CriteriaArray::class => [
119
			I18NDecorator::class,
120
		],
121
		DocumentArray::class => [
122
			ClassNameDecorator::class,
123
			EmbedRefDecorator::class,
124
			EmbedRefArrayDecorator::class,
125
		],
126
		SafeArray::class => [
127
			ClassNameDecorator::class,
128
			EmbedRefDecorator::class,
129
			EmbedRefArrayDecorator::class,
130
		],
131
		JsonArray::class => [
132
			ClassNameDecorator::class,
133
			EmbedRefDecorator::class,
134
			EmbedRefArrayDecorator::class,
135
		],
136
		YamlArray::class => [
137
			ClassNameDecorator::class,
138
			EmbedRefDecorator::class,
139
			EmbedRefArrayDecorator::class,
140
		],
141
		RawArray::class => [
142
			DbRefArrayDecorator::class,
143
			DbRefDecorator::class,
144
			RelatedDecorator::class,
145
			RelatedArrayDecorator::class,
146
			I18NDecorator::class,
147
			ClassNameDecorator::class,
148
		]
149
	];
150
151
	/**
152
	 * Configuration for finalizers.
153
	 * @see https://github.com/Maslosoft/Mangan/issues/36
154
	 * @var string[][]
155
	 */
156
	public $finalizers = [
157
	];
158
159
	/**
160
	 * Configuration of property filters for transformers
161
	 * Array key is decorator class name or interface, values are filter class names.
162
	 * @var string[][]
163
	 */
164
	public $filters = [
165
		TransformatorInterface::class => [
166
		],
167
		DocumentArray::class => [
168
			DocumentArrayFilter::class,
169
		],
170
		JsonArray::class => [
171
			JsonFilter::class,
172
		],
173
		RawArray::class => [
174
			PersistentFilter::class
175
		],
176
		SafeArray::class => [
177
		],
178
	];
179
180
	/**
181
	 * Mapping for validators. Key is validator proxy class name, value is concrete validator implementation
182
	 * @var string[]
183
	 */
184
	public $validators = [
185
		BooleanProxy::class => BooleanValidator::class,
186
		CompareProxy::class => CompareValidator::class,
187
		UniqueProxy::class => UniqueValidator::class,
188
		EmailProxy::class => EmailValidator::class,
189
		NumberProxy::class => NumberValidator::class,
190
		RangeProxy::class => RangeValidator::class,
191
		RegexProxy::class => RegexValidator::class,
192
		RequiredProxy::class => RequiredValidator::class,
193
		StringProxy::class => StringValidator::class,
194
		UniqueProxy::class => UniqueValidator::class,
195
		UrlProxy::class => UrlValidator::class
196
	];
197
198
	/**
199
	 * Sanitizers ramapping for common scenarios.
200
	 * @var string[][]
201
	 */
202
	public $sanitizersMap = [
203
		JsonArray::class => [
204
			MongoObjectId::class => MongoWriteStringId::class,
205
			DateSanitizer::class => DateWriteUnixSanitizer::class
206
		],
207
		YamlArray::class => [
208
			MongoObjectId::class => MongoWriteStringId::class,
209
			DateSanitizer::class => DateWriteUnixSanitizer::class
210
		],
211
	];
212
213
	/**
214
	 * Connection ID
215
	 * @var string
216
	 */
217
	public $connectionId = 'mongodb';
218
219
	/**
220
	 * @var string $dbName name of the Mongo database to use
221
	 * @since v1.0
222
	 */
223
	public $dbName = null;
224
225
	/**
226
	 * If set to TRUE all internal DB operations will use SAFE flag with data modification requests.
227
	 *
228
	 * When SAFE flag is set to TRUE driver will wait for the response from DB, and throw an exception
229
	 * if something went wrong, is set to false, driver will only send operation to DB but will not wait
230
	 * for response from DB.
231
	 *
232
	 * MongoDB default value for this flag is: FALSE.
233
	 *
234
	 * @var boolean $safeFlag state of SAFE flag (global scope)
235
	 */
236
	public $safeFlag = false;
237
238
	/**
239
	 * If set to TRUE findAll* methods of models, will return {@see Cursor} instead of
240
	 * raw array of models.
241
	 *
242
	 * Generally you should want to have this set to TRUE as cursor use lazy-loading/instantiating of
243
	 * models, this is set to FALSE, by default to keep backwards compatibility.
244
	 *
245
	 * Note: {@see Cursor} does not implement ArrayAccess interface and cannot be used like an array,
246
	 * because offset access to cursor is highly ineffective and pointless.
247
	 *
248
	 * @var boolean $useCursor state of Use Cursor flag (global scope)
249
	 */
250
	public $useCursor = false;
251
252
	/**
253
	 * Queries profiling.
254
	 * Defaults to false. This should be mainly enabled and used during development
255
	 * to find out the bottleneck of mongo queries.
256
	 * @var boolean whether to enable profiling the mongo queries being executed.
257
	 */
258
	public $enableProfiling = false;
259
260
	/**
261
	 * Connection storage
262
	 * @var ConnectionStorage
263
	 */
264
	private $_cs = null;
265
266
	/**
267
	 * Embedi instance
268
	 * @var EmbeDi
269
	 */
270
	private $_di = null;
271
272
	/**
273
	 * Logger
274
	 * @var LoggerInterface
275
	 */
276
	private $_logger = null;
277
278
	/**
279
	 * Profiller
280
	 * @var ProfilerInterface
281
	 */
282
	private $_profiler = null;
283
284
	/**
285
	 * Version number holder
286
	 * @var string
287
	 */
288
	private static $_version = null;
289
290
	/**
291
	 * Instances of mangan
292
	 * @var Mangan[]
293
	 */
294
	private static $_mn = [];
295
296 25
	public function __construct($connectionId = self::DefaultConnectionId)
297
	{
298 25
		$this->_di = EmbeDi::fly($connectionId);
299 25
		if (!$connectionId)
300 25
		{
301
			$connectionId = self::DefaultConnectionId;
302
		}
303 25
		$this->connectionId = $connectionId;
304 25
		$this->_di->configure($this);
305 25
		$this->_cs = new ConnectionStorage($this, $connectionId);
306 25
		if (empty(self::$_mn[$connectionId]))
307 25
		{
308
			self::$_mn[$connectionId] = $this;
309
		}
310 25
	}
311
312
	public function __get($name)
313
	{
314
		return $this->{'get' . ucfirst($name)}();
315
	}
316
317
	public function __set($name, $value)
318
	{
319
		$this->{'set' . ucfirst($name)}($value);
320
	}
321
322
	/**
323
	 * Get mangan version
324
	 * @return string
325
	 */
326
	public function getVersion()
327
	{
328
		if (null === self::$_version)
329
		{
330
			self::$_version = require __DIR__ . '/version.php';
331
		}
332
		return self::$_version;
333
	}
334
335
	/**
336
	 * Set PSR compliant logger
337
	 * @param LoggerInterface $logger
338
	 * @return Mangan
339
	 */
340
	public function setLogger(LoggerInterface $logger)
341
	{
342
		$this->_logger = $logger;
343
		return $this;
344
	}
345
346
	/**
347
	 * Get PSR compliant logger
348
	 * @return LoggerInterface
349
	 */
350 1
	public function getLogger()
351
	{
352 1
		if (null === $this->_logger)
353 1
		{
354 1
			$this->_logger = new NullLogger;
355 1
		}
356 1
		return $this->_logger;
357
	}
358
359
	/**
360
	 * Get profiler instance. This is guaranted, if not configured will return NullProfiller.
361
	 * @see NullProfiler
362
	 * @return ProfilerInterface
363
	 */
364 13
	public function getProfiler()
365
	{
366 13
		if (null === $this->_profiler)
367 13
		{
368 1
			$this->_profiler = new NullProfiler;
369 1
		}
370 13
		if ($this->_profiler instanceof ManganAwareInterface)
371 13
		{
372
			$this->_profiler->setMangan($this);
373
		}
374 13
		return $this->_profiler;
375
	}
376
377
	/**
378
	 * Set profiler instance
379
	 * @param ProfilerInterface $profiller
380
	 * @return Mangan
381
	 */
382
	public function setProfiler(ProfilerInterface $profiller)
383
	{
384
		$this->_profiler = $profiller;
385
		return $this;
386
	}
387
388
	/**
389
	 * Get dependecy injector.
390
	 * @return EmbeDi
391
	 */
392 16
	public function getDi()
393
	{
394 16
		return $this->_di;
395
	}
396
397
	/**
398
	 * Get flyweight instance of Mangan component.
399
	 * Only one instance will be created for each `$connectionId`.
400
	 *
401
	 * @new
402
	 * @param string $connectionId
403
	 * @return Mangan
404
	 */
405 133
	public static function fly($connectionId = self::DefaultConnectionId)
406
	{
407 133
		if (empty($connectionId))
408 133
		{
409
			$connectionId = self::DefaultConnectionId;
410
		}
411 133
		if (empty(self::$_mn[$connectionId]))
412
		{
413
			self::$_mn[$connectionId] = new static($connectionId);
414
		}
415
		return self::$_mn[$connectionId];
416
	}
417
418
	/**
419 130
	 * Get instance of Mangan configured for particular model
420
	 * @param AnnotatedInterface $model
421 130
	 * @return Mangan
422 130
	 */
423
	public static function fromModel(AnnotatedInterface $model)
424
	{
425
		$connectionId = ManganMeta::create($model)->type()->connectionId;
426
		return self::fly($connectionId);
427
	}
428
429
	public function init()
430
	{
431
		$this->_di->store($this);
432
	}
433
434
	/**
435
	 * Connect to DB if connection is already connected this method return connection status.
436
	 *
437
	 * @return bool Returns true if connected
438
	 */
439
	public function connect()
440
	{
441
		if (!$this->getConnection()->connected)
442
		{
443
			return $this->getConnection()->connect();
444
		}
445
		return $this->getConnection()->connected;
446
	}
447
448
	/**
449
	 * Returns Mongo connection instance if not exists will create new
450
	 *
451
	 * @return MongoClient
452
	 * @throws ManganException
453
	 * @since v1.0
454
	 */
455
	public function getConnection()
456
	{
457
		if ($this->_cs->mongoClient === null)
458
		{
459
			if (!$this->connectionString)
460
			{
461
				throw new ManganException('Mangan.connectionString cannot be empty.');
462
			}
463
464
			$options = [];
465
			foreach ($this->_getOptionNames() as $name)
466
			{
467
				if (isset($this->$name))
468
				{
469
					$options[$name] = $this->$name;
470
				}
471
			}
472
473
			$this->_cs->mongoClient = new MongoClient($this->connectionString, $options);
474
475
			return $this->_cs->mongoClient;
476
		}
477
		else
478
		{
479
			return $this->_cs->mongoClient;
480
		}
481
	}
482
483
	/**
484
	 * Set the connection by suppling `MongoClient` instance.
485
	 *
486
	 * Use this to set connection from external source.
487
	 * In most scenarios this does not need to be called.
488
	 *
489
	 * @param MongoClient $connection
490
	 */
491
	public function setConnection(MongoClient $connection)
492
	{
493
		$this->_cs->mongoClient = $connection;
494
	}
495
496
	/**
497 83
	 * Get MongoDB instance
498
	 *
499 83
	 * @return MongoDB Mongo DB instance
500 83
	 */
501
	public function getDbInstance()
502
	{
503
		if ($this->_cs->mongoDB === null)
504
		{
505
			if (!$this->dbName)
506
			{
507
				throw new ManganException(sprintf("Database name is required for connectionId: `%s`", $this->connectionId), ExceptionCodeInterface::RequireDbName);
508
			}
509
			try
510
			{
511
				$db = $this->getConnection()->selectDB($this->dbName);
512
			}
513
			catch (MongoException $e)
514
			{
515
				throw new ManganException(sprintf('Could not select db name: `%s`, for connectionId: `%s` - %s', $this->dbName, $this->connectionId, $e->getMessage()), ExceptionCodeInterface::CouldNotSelect, $e);
516
			}
517 83
			return $this->_cs->mongoDB = $db;
518
		}
519
		else
520
		{
521
			return $this->_cs->mongoDB;
522
		}
523
	}
524
525
	/**
526
	 * Set MongoDB instance by suppling database name.
527
	 *
528
	 * Use this to select db from external source.
529
	 * In most scenarios this does not need to be called.
530
	 *
531
	 * @param string $name
532
	 */
533
	public function setDbInstance($name)
534
	{
535
		$this->_cs->mongoDB = $this->getConnection()->selectDb($name);
536
	}
537
538
	/**
539
	 * Closes the currently active Mongo connection.
540
	 * It does nothing if the connection is already closed.
541
	 */
542
	protected function close()
543
	{
544
		if ($this->_cs->mongoClient !== null)
545
		{
546
			$this->_cs->mongoClient->close();
547
			$this->_cs->mongoClient = null;
548
			$this->_cs->mongoDB = null;
549
		}
550
	}
551
552
	/**
553
	 * Drop current database
554
	 */
555
	public function dropDb()
556
	{
557
		$this->getDbInstance()->drop();
558
	}
559
560
}
561