TSqlMapXmlMappingConfiguration   F
last analyzed

Complexity

Total Complexity 83

Size/Duplication

Total Lines 529
Duplicated Lines 0 %

Test Coverage

Coverage 31.58%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 245
c 1
b 0
f 0
dl 0
loc 529
ccs 84
cts 266
cp 0.3158
rs 2
wmc 83

22 Methods

Rating   Name   Duplication   Size   Complexity  
B createResultMap() 0 39 6
A loadStatementTag() 0 7 1
A createParameterMap() 0 10 2
A loadSelectKeyTag() 0 10 1
A loadDeleteTag() 0 7 1
A createInsertStatement() 0 8 2
A loadUpdateTag() 0 7 1
A __construct() 0 4 1
A loadFlushOnCache() 0 9 4
A processSqlStatement() 0 20 4
A loadProcedureTag() 0 2 1
B loadResultMap() 0 30 7
F configure() 0 54 12
A applyInlineParameterMap() 0 24 4
A registerCacheTriggers() 0 7 3
B loadFlushInterval() 0 27 9
A loadSelectTag() 0 12 3
B loadCacheModel() 0 36 10
A getConfigFile() 0 3 1
B loadParameterMap() 0 27 7
A prepareSql() 0 14 2
A loadInsertTag() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like TSqlMapXmlMappingConfiguration often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TSqlMapXmlMappingConfiguration, and based on these observations, apply Extract Interface, too.

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