TSqlMapXmlMappingConfiguration::configure()   F
last analyzed

Complexity

Conditions 12
Paths 2048

Size

Total Lines 54
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 15.0242

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 21
cts 29
cp 0.7241
crap 15.0242
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
/**
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