Passed
Push — master ( d943e6...a9e422 )
by Fabio
05:14
created

TSqlMapXmlMappingConfiguration::configure()   F

Complexity

Conditions 12
Paths 2048

Size

Total Lines 54
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 14.7316

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 12
eloc 29
c 1
b 0
f 0
nc 2048
nop 1
dl 0
loc 54
ccs 22
cts 30
cp 0.7332
crap 14.7316
rs 2.8

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * TSqlMapXmlConfigBuilder, TSqlMapXmlConfiguration, TSqlMapXmlMappingConfiguration classes file.
4
 *
5
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
6
 * @link https://github.com/pradosoft/prado
7
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
8
 * @package Prado\Data\SqlMap\Configuration
9
 */
10
11
namespace Prado\Data\SqlMap\Configuration;
12
13
use Prado\Caching\TFileCacheDependency;
14
use Prado\Data\SqlMap\DataMapper\TPropertyAccess;
15
use Prado\Data\SqlMap\DataMapper\TSqlMapConfigurationException;
16
use Prado\Data\SqlMap\Statements\TCachingStatement;
17
use Prado\Data\SqlMap\Statements\TDeleteMappedStatement;
18
use Prado\Data\SqlMap\Statements\TInsertMappedStatement;
19
use Prado\Data\SqlMap\Statements\TMappedStatement;
20
use Prado\Data\SqlMap\Statements\TSimpleDynamicSql;
21
use Prado\Data\SqlMap\Statements\TStaticSql;
22
use Prado\Data\SqlMap\Statements\TUpdateMappedStatement;
23
use Prado\Prado;
24
25
/**
26
 * Loads the statements, result maps, parameters maps from xml configuration.
27
 *
28
 * description
29
 *
30
 * @author Wei Zhuo <weizho[at]gmail[dot]com>
31
 * @package Prado\Data\SqlMap\Configuration
32
 * @since 3.1
33
 */
34
class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder
35
{
36
	private $_xmlConfig;
37
	private $_configFile;
38
	private $_manager;
39
40
	private $_document;
41
42
	private $_FlushOnExecuteStatements = [];
43
44
	/**
45
	 * Regular expressions for escaping simple/inline parameter symbols
46
	 */
47
	public const SIMPLE_MARK = '$';
48
	public const INLINE_SYMBOL = '#';
49
	public const ESCAPED_SIMPLE_MARK_REGEXP = '/\$\$/';
50
	public const ESCAPED_INLINE_SYMBOL_REGEXP = '/\#\#/';
51
	public const SIMPLE_PLACEHOLDER = '`!!`';
52
	public const INLINE_PLACEHOLDER = '`!!!`';
53
54
	/**
55
	 * @param TSqlMapXmlConfiguration $xmlConfig parent xml configuration.
56
	 */
57 3
	public function __construct(TSqlMapXmlConfiguration $xmlConfig)
58
	{
59 3
		$this->_xmlConfig = $xmlConfig;
60 3
		$this->_manager = $xmlConfig->getManager();
61 3
	}
62
63 1
	protected function getConfigFile()
64
	{
65 1
		return $this->_configFile;
66
	}
67
68
	/**
69
	 * Configure an XML mapping.
70
	 * @param string $filename xml mapping filename.
71
	 */
72 3
	public function configure($filename)
73
	{
74 3
		$this->_configFile = $filename;
75 3
		$document = $this->loadXmlDocument($filename, $this->_xmlConfig);
76 3
		$this->_document = $document;
77
78 3
		static $bCacheDependencies;
79 3
		if ($bCacheDependencies === null) {
80
			$bCacheDependencies = true;
81
		} //Prado::getApplication()->getMode() !== TApplicationMode::Performance;
82
83 3
		if ($bCacheDependencies) {
84 3
			$this->_manager->getCacheDependencies()
85 3
					->getDependencies()
86 3
					->add(new TFileCacheDependency($filename));
87
		}
88
89 3
		foreach ($document->xpath('//resultMap') as $node) {
90
			$this->loadResultMap($node);
91
		}
92
93 3
		foreach ($document->xpath('//parameterMap') as $node) {
94
			$this->loadParameterMap($node);
95
		}
96
97 3
		foreach ($document->xpath('//statement') as $node) {
98 2
			$this->loadStatementTag($node);
99
		}
100
101 2
		foreach ($document->xpath('//select') as $node) {
102 2
			$this->loadSelectTag($node);
103
		}
104
105 2
		foreach ($document->xpath('//insert') as $node) {
106
			$this->loadInsertTag($node);
107
		}
108
109 2
		foreach ($document->xpath('//update') as $node) {
110
			$this->loadUpdateTag($node);
111
		}
112
113 2
		foreach ($document->xpath('//delete') as $node) {
114
			$this->loadDeleteTag($node);
115
		}
116
117 2
		foreach ($document->xpath('//procedure') as $node) {
118
			$this->loadProcedureTag($node);
119
		}
120
121 2
		foreach ($document->xpath('//cacheModel') as $node) {
122
			$this->loadCacheModel($node);
123
		}
124
125 2
		$this->registerCacheTriggers();
126 2
	}
127
128
	/**
129
	 * Load the result maps.
130
	 * @param \SimpleXmlElement $node result map node.
131
	 */
132
	protected function loadResultMap($node)
133
	{
134
		$resultMap = $this->createResultMap($node);
135
136
		//find extended result map.
137
		$extendMap = $resultMap->getExtends();
138
		if ($extendMap !== null && strlen($extendMap) > 0) {
139
			if (!$this->_manager->getResultMaps()->contains($extendMap)) {
140
				$extendNode = $this->getElementByIdValue($this->_document, 'resultMap', $extendMap);
141
				if ($extendNode !== null) {
142
					$this->loadResultMap($extendNode);
143
				}
144
			}
145
146
			if (!$this->_manager->getResultMaps()->contains($extendMap)) {
147
				throw new TSqlMapConfigurationException(
148
					'sqlmap_unable_to_find_parent_result_map',
149
					$node,
150
					$this->_configFile,
151
					$extendMap
152
				);
153
			}
154
155
			$superMap = $this->_manager->getResultMap($extendMap);
156
			$resultMap->getColumns()->mergeWith($superMap->getColumns());
157
		}
158
159
		//add the result map
160
		if (!$this->_manager->getResultMaps()->contains($resultMap->getID())) {
161
			$this->_manager->addResultMap($resultMap);
162
		}
163
	}
164
165
	/**
166
	 * Create a new result map and its associated result properties,
167
	 * disciminiator and sub maps.
168
	 * @param \SimpleXmlElement $node result map node
169
	 * @return TResultMap SqlMap result mapping.
170
	 */
171
	protected function createResultMap($node)
172
	{
173
		$resultMap = new TResultMap();
174
		$this->setObjectPropFromNode($resultMap, $node);
175
176
		//result nodes
177
		foreach ($node->result as $result) {
178
			$property = new TResultProperty($resultMap);
179
			$this->setObjectPropFromNode($property, $result);
180
			$resultMap->addResultProperty($property);
181
		}
182
183
		//create the discriminator
184
		$discriminator = null;
185
		if (isset($node->discriminator)) {
186
			$discriminator = new TDiscriminator();
187
			$this->setObjectPropFromNode($discriminator, $node->discriminator);
188
			$discriminator->initMapping($resultMap);
189
		}
190
191
		foreach ($node->xpath('subMap') as $subMapNode) {
192
			if ($discriminator === null) {
193
				throw new TSqlMapConfigurationException(
194
					'sqlmap_undefined_discriminator',
195
					$node,
196
					$this->_configFile,
197
					$subMapNode
198
				);
199
			}
200
			$subMap = new TSubMap;
201
			$this->setObjectPropFromNode($subMap, $subMapNode);
202
			$discriminator->addSubMap($subMap);
203
		}
204
205
		if ($discriminator !== null) {
206
			$resultMap->setDiscriminator($discriminator);
207
		}
208
209
		return $resultMap;
210
	}
211
212
	/**
213
	 * Load parameter map from xml.
214
	 *
215
	 * @param \SimpleXmlElement $node parameter map node.
216
	 */
217
	protected function loadParameterMap($node)
218
	{
219
		$parameterMap = $this->createParameterMap($node);
220
		$extendMap = $parameterMap->getExtends();
221
		if ($extendMap !== null && strlen($extendMap) > 0) {
222
			if (!$this->_manager->getParameterMaps()->contains($extendMap)) {
223
				$extendNode = $this->getElementByIdValue($this->_document, 'parameterMap', $extendMap);
224
				if ($extendNode !== null) {
225
					$this->loadParameterMap($extendNode);
226
				}
227
			}
228
229
			if (!$this->_manager->getParameterMaps()->contains($extendMap)) {
230
				throw new TSqlMapConfigurationException(
231
					'sqlmap_unable_to_find_parent_parameter_map',
232
					$node,
233
					$this->_configFile,
234
					$extendMap
235
				);
236
			}
237
			$superMap = $this->_manager->getParameterMap($extendMap);
238
			$index = 0;
239
			foreach ($superMap->getPropertyNames() as $propertyName) {
240
				$parameterMap->insertProperty($index++, $superMap->getProperty($propertyName));
241
			}
242
		}
243
		$this->_manager->addParameterMap($parameterMap);
244
	}
245
246
	/**
247
	 * Create a new parameter map from xml node.
248
	 * @param \SimpleXmlElement $node parameter map node.
249
	 * @return TParameterMap new parameter mapping.
250
	 */
251
	protected function createParameterMap($node)
252
	{
253
		$parameterMap = new TParameterMap();
254
		$this->setObjectPropFromNode($parameterMap, $node);
255
		foreach ($node->parameter as $parameter) {
256
			$property = new TParameterProperty();
257
			$this->setObjectPropFromNode($property, $parameter);
258
			$parameterMap->addProperty($property);
259
		}
260
		return $parameterMap;
261
	}
262
263
	/**
264
	 * Load statement mapping from xml configuration file.
265
	 * @param \SimpleXmlElement $node statement node.
266 2
	 */
267
	protected function loadStatementTag($node)
268 2
	{
269 2
		$statement = new TSqlMapStatement();
270 1
		$this->setObjectPropFromNode($statement, $node);
271 1
		$this->processSqlStatement($statement, $node);
272 1
		$mappedStatement = new TMappedStatement($this->_manager, $statement);
273 1
		$this->_manager->addMappedStatement($mappedStatement);
274
	}
275
276
	/**
277
	 * Load extended SQL statements if application. Replaces global properties
278
	 * in the sql text. Extracts inline parameter maps.
279
	 * @param TSqlMapStatement $statement mapped statement.
280
	 * @param \SimpleXmlElement $node statement node.
281 2
	 */
282
	protected function processSqlStatement($statement, $node)
283 2
	{
284 2
		$commandText = (string) $node;
285 1
		$extend = $statement->getExtends();
286 1
		if ($extend !== null && strlen($extend) > 0) {
287 1
			$superNode = $this->getElementByIdValue($this->_document, '*', $extend);
288
			if ($superNode !== null) {
289
				$commandText = (string) $superNode . $commandText;
290
			} else {
291
				throw new TSqlMapConfigurationException(
292
					'sqlmap_unable_to_find_parent_sql',
293
					$extend,
294
					$this->_configFile,
295
					$node
296
				);
297
			}
298 2
		}
299 2
		//$commandText = $this->_xmlConfig->replaceProperties($commandText);
300 2
		$statement->initialize($this->_manager);
301
		$this->applyInlineParameterMap($statement, $commandText, $node);
302
	}
303
304
	/**
305
	 * Extract inline parameter maps.
306
	 * @param TSqlMapStatement $statement statement object.
307
	 * @param string $sqlStatement sql text
308 2
	 * @param \SimpleXmlElement $node statement node.
309
	 */
310 2
	protected function applyInlineParameterMap($statement, $sqlStatement, $node)
311 2
	{
312
		$scope['file'] = $this->_configFile;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$scope was never initialized. Although not strictly required by PHP, it is generally a good practice to add $scope = array(); before regardless.
Loading history...
313 2
		$scope['node'] = $node;
314 2
315
		$sqlStatement = preg_replace(self::ESCAPED_INLINE_SYMBOL_REGEXP, self::INLINE_PLACEHOLDER, $sqlStatement);
316
		if ($statement->parameterMap() === null) {
317 2
			// Build a Parametermap with the inline parameters.
318 2
			// if they exist. Then delete inline infos from sqltext.
319 2
			$parameterParser = new TInlineParameterMapParser;
320 2
			$sqlText = $parameterParser->parse($sqlStatement, $scope);
321 2
			if (count($sqlText['parameters']) > 0) {
322 2
				$map = new TParameterMap();
323 2
				$map->setID($statement->getID() . '-InLineParameterMap');
324 2
				$statement->setInlineParameterMap($map);
325
				foreach ($sqlText['parameters'] as $property) {
326
					$map->addProperty($property);
327 2
				}
328
			}
329 2
			$sqlStatement = $sqlText['sql'];
330
		}
331 2
		$sqlStatement = preg_replace('/' . self::INLINE_PLACEHOLDER . '/', self::INLINE_SYMBOL, $sqlStatement);
332 2
333
		$this->prepareSql($statement, $sqlStatement, $node);
334
	}
335
336
	/**
337
	 * Prepare the sql text (may extend to dynamic sql).
338
	 * @param TSqlMapStatement $statement mapped statement.
339
	 * @param string $sqlStatement sql text.
340
	 * @param \SimpleXmlElement $node statement node.
341 2
	 * @todo Extend to dynamic sql.
342
	 */
343 2
	protected function prepareSql($statement, $sqlStatement, $node)
344 2
	{
345 2
		$simpleDynamic = new TSimpleDynamicParser;
346 2
		$sqlStatement = preg_replace(self::ESCAPED_SIMPLE_MARK_REGEXP, self::SIMPLE_PLACEHOLDER, $sqlStatement);
347 1
		$dynamics = $simpleDynamic->parse($sqlStatement);
348 1
		if (count($dynamics['parameters']) > 0) {
349
			$sql = new TSimpleDynamicSql($dynamics['parameters']);
350 2
			$sqlStatement = $dynamics['sql'];
351
		} else {
352 2
			$sql = new TStaticSql();
353 2
		}
354 2
		$sqlStatement = preg_replace('/' . self::SIMPLE_PLACEHOLDER . '/', self::SIMPLE_MARK, $sqlStatement);
355 2
		$sql->buildPreparedStatement($statement, $sqlStatement);
356
		$statement->setSqlText($sql);
357
	}
358
359
	/**
360
	 * Load select statement from xml mapping.
361 2
	 * @param \SimpleXmlElement $node select node.
362
	 */
363 2
	protected function loadSelectTag($node)
364 2
	{
365 2
		$select = new TSqlMapSelect;
366 2
		$this->setObjectPropFromNode($select, $node);
367 2
		$this->processSqlStatement($select, $node);
368
		$mappedStatement = new TMappedStatement($this->_manager, $select);
369
		$cacheModel = $select->getCacheModel();
370
		if ($cacheModel !== null && strlen($cacheModel) > 0) {
371 2
			$mappedStatement = new TCachingStatement($mappedStatement);
372 2
		}
373
374
		$this->_manager->addMappedStatement($mappedStatement);
375
	}
376
377
	/**
378
	 * Load insert statement from xml mapping.
379
	 * @param \SimpleXmlElement $node insert node.
380
	 */
381
	protected function loadInsertTag($node)
382
	{
383
		$insert = $this->createInsertStatement($node);
384
		$this->processSqlStatement($insert, $node);
385
		$mappedStatement = new TInsertMappedStatement($this->_manager, $insert);
386
		$this->_manager->addMappedStatement($mappedStatement);
387
	}
388
389
	/**
390
	 * Create new insert statement from xml node.
391
	 * @param \SimpleXmlElement $node insert node.
392
	 * @return TSqlMapInsert insert statement.
393
	 */
394
	protected function createInsertStatement($node)
395
	{
396
		$insert = new TSqlMapInsert;
397
		$this->setObjectPropFromNode($insert, $node);
398
		if (isset($node->selectKey)) {
399
			$this->loadSelectKeyTag($insert, $node->selectKey);
400
		}
401
		return $insert;
402
	}
403
404
	/**
405
	 * Load the selectKey statement from xml mapping.
406
	 * @param mixed $insert
407
	 * @param \SimpleXmlElement $node selectkey node
408
	 */
409
	protected function loadSelectKeyTag($insert, $node)
410
	{
411
		$selectKey = new TSqlMapSelectKey;
412
		$this->setObjectPropFromNode($selectKey, $node);
413
		$selectKey->setID($insert->getID());
414
		$selectKey->setID($insert->getID() . '.SelectKey');
415
		$this->processSqlStatement($selectKey, $node);
416
		$insert->setSelectKey($selectKey);
417
		$mappedStatement = new TMappedStatement($this->_manager, $selectKey);
418
		$this->_manager->addMappedStatement($mappedStatement);
419
	}
420
421
	/**
422
	 * Load update statement from xml mapping.
423
	 * @param \SimpleXmlElement $node update node.
424
	 */
425
	protected function loadUpdateTag($node)
426
	{
427
		$update = new TSqlMapUpdate;
428
		$this->setObjectPropFromNode($update, $node);
429
		$this->processSqlStatement($update, $node);
430
		$mappedStatement = new TUpdateMappedStatement($this->_manager, $update);
431
		$this->_manager->addMappedStatement($mappedStatement);
432
	}
433
434
	/**
435
	 * Load delete statement from xml mapping.
436
	 * @param \SimpleXmlElement $node delete node.
437
	 */
438
	protected function loadDeleteTag($node)
439
	{
440
		$delete = new TSqlMapDelete;
441
		$this->setObjectPropFromNode($delete, $node);
442
		$this->processSqlStatement($delete, $node);
443
		$mappedStatement = new TDeleteMappedStatement($this->_manager, $delete);
444
		$this->_manager->addMappedStatement($mappedStatement);
445
	}
446
447
	/**
448
	 * Load procedure statement from xml mapping.
449
	 * @todo Implement loading procedure
450
	 * @param \SimpleXmlElement $node procedure node
451
	 */
452
	protected function loadProcedureTag($node)
453
	{
454
		//var_dump('todo: add load procedure');
455
	}
456
457
	/**
458
	 * Load cache models from xml mapping.
459
	 * @param \SimpleXmlElement $node cache node.
460
	 */
461
	protected function loadCacheModel($node)
462
	{
463
		$cacheModel = new TSqlMapCacheModel;
464
		$properties = ['id', 'implementation'];
465
		foreach ($node->attributes() as $name => $value) {
466
			if (in_array(strtolower($name), $properties)) {
467
				$cacheModel->{'set' . $name}((string) $value);
468
			}
469
		}
470
		$cache = Prado::createComponent($cacheModel->getImplementationClass(), $cacheModel);
471
		$this->setObjectPropFromNode($cache, $node, $properties);
472
473
		foreach ($node->xpath('property') as $propertyNode) {
474
			$name = $propertyNode->attributes()->name;
475
			if ($name === null || $name === '') {
476
				continue;
477
			}
478
479
			$value = $propertyNode->attributes()->value;
480
			if ($value === null || $value === '') {
481
				continue;
482
			}
483
484
			if (!TPropertyAccess::has($cache, $name)) {
485
				continue;
486
			}
487
488
			TPropertyAccess::set($cache, $name, $value);
489
		}
490
491
		$this->loadFlushInterval($cacheModel, $node);
492
493
		$cacheModel->initialize($cache);
494
		$this->_manager->addCacheModel($cacheModel);
495
		foreach ($node->xpath('flushOnExecute') as $flush) {
496
			$this->loadFlushOnCache($cacheModel, $node, $flush);
497
		}
498
	}
499
500
	/**
501
	 * Load the flush interval
502
	 * @param TSqlMapCacheModel $cacheModel cache model
503
	 * @param \SimpleXmlElement $node cache node
504
	 */
505
	protected function loadFlushInterval($cacheModel, $node)
506
	{
507
		$flushInterval = $node->xpath('flushInterval');
508
		if ($flushInterval === null || count($flushInterval) === 0) {
509
			return;
510
		}
511
		$duration = 0;
512
		foreach ($flushInterval[0]->attributes() as $name => $value) {
513
			switch (strToLower($name)) {
514
				case 'seconds':
515
					$duration += (integer) $value;
516
				break;
517
				case 'minutes':
518
					$duration += 60 * (integer) $value;
519
				break;
520
				case 'hours':
521
					$duration += 3600 * (integer) $value;
522
				break;
523
				case 'days':
524
					$duration += 86400 * (integer) $value;
525
				break;
526
				case 'duration':
527
					$duration = (integer) $value;
528
				break 2; // switch, foreach
529
			}
530
		}
531
		$cacheModel->setFlushInterval($duration);
532
	}
533
534
	/**
535
	 * Load the flush on cache properties.
536
	 * @param TSqlMapCacheModel $cacheModel cache model
537
	 * @param \SimpleXmlElement $parent parent node.
538
	 * @param \SimpleXmlElement $node flush node.
539
	 */
540
	protected function loadFlushOnCache($cacheModel, $parent, $node)
541
	{
542
		$id = $cacheModel->getID();
543
		if (!isset($this->_FlushOnExecuteStatements[$id])) {
544
			$this->_FlushOnExecuteStatements[$id] = [];
545
		}
546
		foreach ($node->attributes() as $name => $value) {
547
			if (strtolower($name) === 'statement') {
548
				$this->_FlushOnExecuteStatements[$id][] = (string) $value;
549
			}
550
		}
551
	}
552
553 2
	/**
554
	 * Attach CacheModel to statement and register trigger statements for cache models
555 2
	 */
556
	protected function registerCacheTriggers()
557
	{
558
		foreach ($this->_FlushOnExecuteStatements as $cacheID => $statementIDs) {
559
			$cacheModel = $this->_manager->getCacheModel($cacheID);
560
			foreach ($statementIDs as $statementID) {
561
				$statement = $this->_manager->getMappedStatement($statementID);
562 2
				$cacheModel->registerTriggerStatement($statement);
563
			}
564
		}
565
	}
566
}
567