diff options
Diffstat (limited to 'framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php')
-rw-r--r-- | framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php | 1610 |
1 files changed, 805 insertions, 805 deletions
diff --git a/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php b/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php index a60827fe..988d00db 100644 --- a/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php +++ b/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php @@ -1,805 +1,805 @@ -<?php
-/**
- * TSqlMapXmlConfigBuilder, TSqlMapXmlConfiguration, TSqlMapXmlMappingConfiguration classes file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-Prado::using('System.Data.SqlMap.Configuration.TSqlMapStatement');
-
-/**
- * TSqlMapXmlConfig class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-abstract class TSqlMapXmlConfigBuilder
-{
- /**
- * Create an instance of an object give by the attribute named 'class' in the
- * node and set the properties on the object given by attribute names and values.
- * @param SimpleXmlNode property node
- * @return Object new instance of class with class name given by 'class' attribute value.
- */
- protected function createObjectFromNode($node)
- {
- if(isset($node['class']))
- {
- $obj = Prado::createComponent((string)$node['class']);
- $this->setObjectPropFromNode($obj,$node,array('class'));
- return $obj;
- }
- throw new TSqlMapConfigurationException(
- 'sqlmap_node_class_undef', $node, $this->getConfigFile());
- }
-
- /**
- * For each attributes (excluding attribute named in $except) set the
- * property of the $obj given by the name of the attribute with the value
- * of the attribute.
- * @param Object object instance
- * @param SimpleXmlNode property node
- * @param array exception property name
- */
- protected function setObjectPropFromNode($obj,$node,$except=array())
- {
- foreach($node->attributes() as $name=>$value)
- {
- if(!in_array($name,$except))
- {
- if($obj->canSetProperty($name))
- $obj->{$name} = (string)$value;
- else
- throw new TSqlMapConfigurationException(
- 'sqlmap_invalid_property', $name, get_class($obj),
- $node, $this->getConfigFile());
- }
- }
- }
-
- /**
- * Gets the filename relative to the basefile.
- * @param string base filename
- * @param string relative filename
- * @return string absolute filename.
- */
- protected function getAbsoluteFilePath($basefile,$resource)
- {
- $basedir = dirname($basefile);
- $file = realpath($basedir.DIRECTORY_SEPARATOR.$resource);
- if(!is_string($file) || !is_file($file))
- $file = realpath($resource);
- if(is_string($file) && is_file($file))
- return $file;
- else
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_resource', $resource);
- }
-
- /**
- * Load document using simple xml.
- * @param string filename.
- * @return SimpleXmlElement xml document.
- */
- protected function loadXmlDocument($filename,TSqlMapXmlConfiguration $config)
- {
- if( strpos($filename, '${') !== false)
- $filename = $config->replaceProperties($filename);
-
- if(!is_file($filename))
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_config', $filename);
- return simplexml_load_string($config->replaceProperties(file_get_contents($filename)));
- }
-
- /**
- * Get element node by ID value (try for attribute name ID as case insensitive).
- * @param SimpleXmlDocument $document
- * @param string tag name.
- * @param string id value.
- * @return SimpleXmlElement node if found, null otherwise.
- */
- protected function getElementByIdValue($document, $tag, $value)
- {
- //hack to allow upper case and lower case attribute names.
- foreach(array('id','ID','Id', 'iD') as $id)
- {
- $xpath = "//{$tag}[@{$id}='{$value}']";
- foreach($document->xpath($xpath) as $node)
- return $node;
- }
- }
-
- /**
- * @return string configuration file.
- */
- protected abstract function getConfigFile();
-}
-
-/**
- * TSqlMapXmlConfig class.
- *
- * Configures the TSqlMapManager using xml configuration file.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapXmlConfiguration extends TSqlMapXmlConfigBuilder
-{
- /**
- * @var TSqlMapManager manager
- */
- private $_manager;
- /**
- * @var string configuration file.
- */
- private $_configFile;
- /**
- * @var array global properties.
- */
- private $_properties=array();
-
- /**
- * @param TSqlMapManager manager instance.
- */
- public function __construct($manager)
- {
- $this->_manager=$manager;
- }
-
- public function getManager()
- {
- return $this->_manager;
- }
-
- protected function getConfigFile()
- {
- return $this->_configFile;
- }
-
- /**
- * Configure the TSqlMapManager using the given xml file.
- * @param string SqlMap configuration xml file.
- */
- public function configure($filename=null)
- {
- $this->_configFile=$filename;
- $document = $this->loadXmlDocument($filename,$this);
-
- foreach($document->xpath('//property') as $property)
- $this->loadGlobalProperty($property);
-
- foreach($document->xpath('//typeHandler') as $handler)
- $this->loadTypeHandler($handler);
-
- foreach($document->xpath('//connection[last()]') as $conn)
- $this->loadDatabaseConnection($conn);
-
- //try to load configuration in the current config file.
- $mapping = new TSqlMapXmlMappingConfiguration($this);
- $mapping->configure($filename);
-
- foreach($document->xpath('//sqlMap') as $sqlmap)
- $this->loadSqlMappingFiles($sqlmap);
-
- $this->resolveResultMapping();
- $this->attachCacheModels();
- }
-
- /**
- * Load global replacement property.
- * @param SimpleXmlElement property node.
- */
- protected function loadGlobalProperty($node)
- {
- $this->_properties[(string)$node['name']] = (string)$node['value'];
- }
-
- /**
- * Load the type handler configurations.
- * @param SimpleXmlElement type handler node
- */
- protected function loadTypeHandler($node)
- {
- $handler = $this->createObjectFromNode($node);
- $this->_manager->getTypeHandlers()->registerTypeHandler($handler);
- }
-
- /**
- * Load the database connection tag.
- * @param SimpleXmlElement connection node.
- */
- protected function loadDatabaseConnection($node)
- {
- $conn = $this->createObjectFromNode($node);
- $this->_manager->setDbConnection($conn);
- }
-
- /**
- * Load SqlMap mapping configuration.
- * @param unknown_type $node
- */
- protected function loadSqlMappingFiles($node)
- {
- if(strlen($resource = (string)$node['resource']) > 0)
- {
- if( strpos($resource, '${') !== false)
- $resource = $this->replaceProperties($resource);
-
- $mapping = new TSqlMapXmlMappingConfiguration($this);
- $filename = $this->getAbsoluteFilePath($this->_configFile, $resource);
- $mapping->configure($filename);
- }
- }
-
- /**
- * Resolve nest result mappings.
- */
- protected function resolveResultMapping()
- {
- $maps = $this->_manager->getResultMaps();
- foreach($maps as $entry)
- {
- foreach($entry->getColumns() as $item)
- {
- $resultMap = $item->getResultMapping();
- if(strlen($resultMap) > 0)
- {
- if($maps->contains($resultMap))
- $item->setNestedResultMap($maps[$resultMap]);
- else
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_result_mapping',
- $resultMap, $this->_configFile, $entry->getID());
- }
- }
- if($entry->getDiscriminator()!==null)
- $entry->getDiscriminator()->initialize($this->_manager);
- }
- }
-
- /**
- * Set the cache for each statement having a cache model property.
- */
- protected function attachCacheModels()
- {
- foreach($this->_manager->getMappedStatements() as $mappedStatement)
- {
- if(strlen($model = $mappedStatement->getStatement()->getCacheModel()) > 0)
- {
- $cache = $this->_manager->getCacheModel($model);
- $mappedStatement->getStatement()->setCache($cache);
- }
- }
- }
-
- /**
- * Replace the place holders ${name} in text with properties the
- * corresponding global property value.
- * @param string original string.
- * @return string string with global property replacement.
- */
- public function replaceProperties($string)
- {
- foreach($this->_properties as $find => $replace)
- $string = str_replace('${'.$find.'}', $replace, $string);
- return $string;
- }
-}
-
-/**
- * Loads the statements, result maps, parameters maps from xml configuration.
- *
- * description
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder
-{
- private $_xmlConfig;
- private $_configFile;
- private $_manager;
-
- private $_document;
-
- private $_FlushOnExecuteStatements=array();
-
- /**
- * Regular expressions for escaping simple/inline parameter symbols
- */
- const SIMPLE_MARK='$';
- const INLINE_SYMBOL='#';
- const ESCAPED_SIMPLE_MARK_REGEXP='/\$\$/';
- const ESCAPED_INLINE_SYMBOL_REGEXP='/\#\#/';
- const SIMPLE_PLACEHOLDER='`!!`';
- const INLINE_PLACEHOLDER='`!!!`';
-
- /**
- * @param TSqlMapXmlConfiguration parent xml configuration.
- */
- public function __construct(TSqlMapXmlConfiguration $xmlConfig)
- {
- $this->_xmlConfig=$xmlConfig;
- $this->_manager=$xmlConfig->getManager();
- }
-
- protected function getConfigFile()
- {
- return $this->_configFile;
- }
-
- /**
- * Configure an XML mapping.
- * @param string xml mapping filename.
- */
- public function configure($filename)
- {
- $this->_configFile=$filename;
- $document = $this->loadXmlDocument($filename,$this->_xmlConfig);
- $this->_document=$document;
-
- static $bCacheDependencies;
- if($bCacheDependencies === null)
- $bCacheDependencies = Prado::getApplication()->getMode() !== TApplicationMode::Performance;
-
- if($bCacheDependencies)
- $this->_manager->getCacheDependencies()
- ->getDependencies()
- ->add(new TFileCacheDependency($filename));
-
- foreach($document->xpath('//resultMap') as $node)
- $this->loadResultMap($node);
-
- foreach($document->xpath('//parameterMap') as $node)
- $this->loadParameterMap($node);
-
- foreach($document->xpath('//statement') as $node)
- $this->loadStatementTag($node);
-
- foreach($document->xpath('//select') as $node)
- $this->loadSelectTag($node);
-
- foreach($document->xpath('//insert') as $node)
- $this->loadInsertTag($node);
-
- foreach($document->xpath('//update') as $node)
- $this->loadUpdateTag($node);
-
- foreach($document->xpath('//delete') as $node)
- $this->loadDeleteTag($node);
-
- foreach($document->xpath('//procedure') as $node)
- $this->loadProcedureTag($node);
-
- foreach($document->xpath('//cacheModel') as $node)
- $this->loadCacheModel($node);
-
- $this->registerCacheTriggers();
- }
-
- /**
- * Load the result maps.
- * @param SimpleXmlElement result map node.
- */
- protected function loadResultMap($node)
- {
- $resultMap = $this->createResultMap($node);
-
- //find extended result map.
- if(strlen($extendMap = $resultMap->getExtends()) > 0)
- {
- if(!$this->_manager->getResultMaps()->contains($extendMap))
- {
- $extendNode=$this->getElementByIdValue($this->_document,'resultMap',$extendMap);
- if($extendNode!==null)
- $this->loadResultMap($extendNode);
- }
-
- if(!$this->_manager->getResultMaps()->contains($extendMap))
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_parent_result_map', $node, $this->_configFile, $extendMap);
-
- $superMap = $this->_manager->getResultMap($extendMap);
- $resultMap->getColumns()->mergeWith($superMap->getColumns());
- }
-
- //add the result map
- if(!$this->_manager->getResultMaps()->contains($resultMap->getID()))
- $this->_manager->addResultMap($resultMap);
- }
-
- /**
- * Create a new result map and its associated result properties,
- * disciminiator and sub maps.
- * @param SimpleXmlElement result map node
- * @return TResultMap SqlMap result mapping.
- */
- protected function createResultMap($node)
- {
- $resultMap = new TResultMap();
- $this->setObjectPropFromNode($resultMap,$node);
-
- //result nodes
- foreach($node->result as $result)
- {
- $property = new TResultProperty($resultMap);
- $this->setObjectPropFromNode($property,$result);
- $resultMap->addResultProperty($property);
- }
-
- //create the discriminator
- $discriminator = null;
- if(isset($node->discriminator))
- {
- $discriminator = new TDiscriminator();
- $this->setObjectPropFromNode($discriminator, $node->discriminator);
- $discriminator->initMapping($resultMap);
- }
-
- foreach($node->xpath('subMap') as $subMapNode)
- {
- if($discriminator===null)
- throw new TSqlMapConfigurationException(
- 'sqlmap_undefined_discriminator', $node, $this->_configFile,$subMapNode);
- $subMap = new TSubMap;
- $this->setObjectPropFromNode($subMap,$subMapNode);
- $discriminator->addSubMap($subMap);
- }
-
- if($discriminator!==null)
- $resultMap->setDiscriminator($discriminator);
-
- return $resultMap;
- }
-
- /**
- * Load parameter map from xml.
- *
- * @param SimpleXmlElement parameter map node.
- */
- protected function loadParameterMap($node)
- {
- $parameterMap = $this->createParameterMap($node);
-
- if(strlen($extendMap = $parameterMap->getExtends()) > 0)
- {
- if(!$this->_manager->getParameterMaps()->contains($extendMap))
- {
- $extendNode=$this->getElementByIdValue($this->_document,'parameterMap',$extendMap);
- if($extendNode!==null)
- $this->loadParameterMap($extendNode);
- }
-
- if(!$this->_manager->getParameterMaps()->contains($extendMap))
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_parent_parameter_map', $node, $this->_configFile,$extendMap);
- $superMap = $this->_manager->getParameterMap($extendMap);
- $index = 0;
- foreach($superMap->getPropertyNames() as $propertyName)
- $parameterMap->insertProperty($index++,$superMap->getProperty($propertyName));
- }
- $this->_manager->addParameterMap($parameterMap);
- }
-
- /**
- * Create a new parameter map from xml node.
- * @param SimpleXmlElement parameter map node.
- * @return TParameterMap new parameter mapping.
- */
- protected function createParameterMap($node)
- {
- $parameterMap = new TParameterMap();
- $this->setObjectPropFromNode($parameterMap,$node);
- foreach($node->parameter as $parameter)
- {
- $property = new TParameterProperty();
- $this->setObjectPropFromNode($property,$parameter);
- $parameterMap->addProperty($property);
- }
- return $parameterMap;
- }
-
- /**
- * Load statement mapping from xml configuration file.
- * @param SimpleXmlElement statement node.
- */
- protected function loadStatementTag($node)
- {
- $statement = new TSqlMapStatement();
- $this->setObjectPropFromNode($statement,$node);
- $this->processSqlStatement($statement, $node);
- $mappedStatement = new TMappedStatement($this->_manager, $statement);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load extended SQL statements if application. Replaces global properties
- * in the sql text. Extracts inline parameter maps.
- * @param TSqlMapStatement mapped statement.
- * @param SimpleXmlElement statement node.
- */
- protected function processSqlStatement($statement, $node)
- {
- $commandText = (string)$node;
- if(strlen($extend = $statement->getExtends()) > 0)
- {
- $superNode = $this->getElementByIdValue($this->_document,'*',$extend);
- if($superNode!==null)
- $commandText = (string)$superNode . $commandText;
- else
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_parent_sql', $extend, $this->_configFile,$node);
- }
- //$commandText = $this->_xmlConfig->replaceProperties($commandText);
- $statement->initialize($this->_manager);
- $this->applyInlineParameterMap($statement, $commandText, $node);
- }
-
- /**
- * Extract inline parameter maps.
- * @param TSqlMapStatement statement object.
- * @param string sql text
- * @param SimpleXmlElement statement node.
- */
- protected function applyInlineParameterMap($statement, $sqlStatement, $node)
- {
- $scope['file'] = $this->_configFile;
- $scope['node'] = $node;
-
- $sqlStatement=preg_replace(self::ESCAPED_INLINE_SYMBOL_REGEXP,self::INLINE_PLACEHOLDER,$sqlStatement);
- if($statement->parameterMap() === null)
- {
- // Build a Parametermap with the inline parameters.
- // if they exist. Then delete inline infos from sqltext.
- $parameterParser = new TInlineParameterMapParser;
- $sqlText = $parameterParser->parse($sqlStatement, $scope);
- if(count($sqlText['parameters']) > 0)
- {
- $map = new TParameterMap();
- $map->setID($statement->getID().'-InLineParameterMap');
- $statement->setInlineParameterMap($map);
- foreach($sqlText['parameters'] as $property)
- $map->addProperty($property);
- }
- $sqlStatement = $sqlText['sql'];
- }
- $sqlStatement=preg_replace('/'.self::INLINE_PLACEHOLDER.'/',self::INLINE_SYMBOL,$sqlStatement);
-
- $this->prepareSql($statement, $sqlStatement, $node);
- }
-
- /**
- * Prepare the sql text (may extend to dynamic sql).
- * @param TSqlMapStatement mapped statement.
- * @param string sql text.
- * @param SimpleXmlElement statement node.
- * @todo Extend to dynamic sql.
- */
- protected function prepareSql($statement,$sqlStatement, $node)
- {
- $simpleDynamic = new TSimpleDynamicParser;
- $sqlStatement=preg_replace(self::ESCAPED_SIMPLE_MARK_REGEXP,self::SIMPLE_PLACEHOLDER,$sqlStatement);
- $dynamics = $simpleDynamic->parse($sqlStatement);
- if(count($dynamics['parameters']) > 0)
- {
- $sql = new TSimpleDynamicSql($dynamics['parameters']);
- $sqlStatement = $dynamics['sql'];
- }
- else
- $sql = new TStaticSql();
- $sqlStatement=preg_replace('/'.self::SIMPLE_PLACEHOLDER.'/',self::SIMPLE_MARK,$sqlStatement);
- $sql->buildPreparedStatement($statement, $sqlStatement);
- $statement->setSqlText($sql);
- }
-
- /**
- * Load select statement from xml mapping.
- * @param SimpleXmlElement select node.
- */
- protected function loadSelectTag($node)
- {
- $select = new TSqlMapSelect;
- $this->setObjectPropFromNode($select,$node);
- $this->processSqlStatement($select,$node);
- $mappedStatement = new TMappedStatement($this->_manager, $select);
- if(strlen($select->getCacheModel()) > 0)
- $mappedStatement = new TCachingStatement($mappedStatement);
-
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load insert statement from xml mapping.
- * @param SimpleXmlElement insert node.
- */
- protected function loadInsertTag($node)
- {
- $insert = $this->createInsertStatement($node);
- $this->processSqlStatement($insert, $node);
- $mappedStatement = new TInsertMappedStatement($this->_manager, $insert);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Create new insert statement from xml node.
- * @param SimpleXmlElement insert node.
- * @return TSqlMapInsert insert statement.
- */
- protected function createInsertStatement($node)
- {
- $insert = new TSqlMapInsert;
- $this->setObjectPropFromNode($insert,$node);
- if(isset($node->selectKey))
- $this->loadSelectKeyTag($insert,$node->selectKey);
- return $insert;
- }
-
- /**
- * Load the selectKey statement from xml mapping.
- * @param SimpleXmlElement selectkey node
- */
- protected function loadSelectKeyTag($insert, $node)
- {
- $selectKey = new TSqlMapSelectKey;
- $this->setObjectPropFromNode($selectKey,$node);
- $selectKey->setID($insert->getID());
- $selectKey->setID($insert->getID().'.SelectKey');
- $this->processSqlStatement($selectKey,$node);
- $insert->setSelectKey($selectKey);
- $mappedStatement = new TMappedStatement($this->_manager, $selectKey);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load update statement from xml mapping.
- * @param SimpleXmlElement update node.
- */
- protected function loadUpdateTag($node)
- {
- $update = new TSqlMapUpdate;
- $this->setObjectPropFromNode($update,$node);
- $this->processSqlStatement($update, $node);
- $mappedStatement = new TUpdateMappedStatement($this->_manager, $update);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load delete statement from xml mapping.
- * @param SimpleXmlElement delete node.
- */
- protected function loadDeleteTag($node)
- {
- $delete = new TSqlMapDelete;
- $this->setObjectPropFromNode($delete,$node);
- $this->processSqlStatement($delete, $node);
- $mappedStatement = new TDeleteMappedStatement($this->_manager, $delete);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load procedure statement from xml mapping.
- * @todo Implement loading procedure
- * @param SimpleXmlElement procedure node
- */
- protected function loadProcedureTag($node)
- {
- //var_dump('todo: add load procedure');
- }
-
- /**
- * Load cache models from xml mapping.
- * @param SimpleXmlElement cache node.
- */
- protected function loadCacheModel($node)
- {
- $cacheModel = new TSqlMapCacheModel;
- $properties = array('id','implementation');
- foreach($node->attributes() as $name=>$value)
- {
- if(in_array(strtolower($name), $properties))
- $cacheModel->{'set'.$name}((string)$value);
- }
- $cache = Prado::createComponent($cacheModel->getImplementationClass(), $cacheModel);
- $this->setObjectPropFromNode($cache,$node,$properties);
-
- foreach($node->xpath('property') as $propertyNode)
- {
- $name = $propertyNode->attributes()->name;
- if($name===null || $name==='') continue;
-
- $value = $propertyNode->attributes()->value;
- if($value===null || $value==='') continue;
-
- if( !TPropertyAccess::has($cache, $name) ) continue;
-
- TPropertyAccess::set($cache, $name, $value);
- }
-
- $this->loadFlushInterval($cacheModel,$node);
-
- $cacheModel->initialize($cache);
- $this->_manager->addCacheModel($cacheModel);
- foreach($node->xpath('flushOnExecute') as $flush)
- $this->loadFlushOnCache($cacheModel,$node,$flush);
- }
-
- /**
- * Load the flush interval
- * @param TSqlMapCacheModel cache model
- * @param SimpleXmlElement cache node
- */
- protected function loadFlushInterval($cacheModel, $node)
- {
- $flushInterval = $node->xpath('flushInterval');
- if($flushInterval === null || count($flushInterval) === 0) return;
- $duration = 0;
- foreach($flushInterval[0]->attributes() as $name=>$value)
- {
- switch(strToLower($name))
- {
- case 'seconds':
- $duration += (integer)$value;
- break;
- case 'minutes':
- $duration += 60 * (integer)$value;
- break;
- case 'hours':
- $duration += 3600 * (integer)$value;
- break;
- case 'days':
- $duration += 86400 * (integer)$value;
- break;
- case 'duration':
- $duration = (integer)$value;
- break 2; // switch, foreach
- }
- }
- $cacheModel->setFlushInterval($duration);
- }
-
- /**
- * Load the flush on cache properties.
- * @param TSqlMapCacheModel cache model
- * @param SimpleXmlElement parent node.
- * @param SimpleXmlElement flush node.
- */
- protected function loadFlushOnCache($cacheModel,$parent,$node)
- {
- $id = $cacheModel->getID();
- if(!isset($this->_FlushOnExecuteStatements[$id]))
- $this->_FlushOnExecuteStatements[$id] = array();
- foreach($node->attributes() as $name=>$value)
- {
- if(strtolower($name)==='statement')
- $this->_FlushOnExecuteStatements[$id][] = (string)$value;
- }
- }
-
- /**
- * Attach CacheModel to statement and register trigger statements for cache models
- */
- protected function registerCacheTriggers()
- {
- foreach($this->_FlushOnExecuteStatements as $cacheID => $statementIDs)
- {
- $cacheModel = $this->_manager->getCacheModel($cacheID);
- foreach($statementIDs as $statementID)
- {
- $statement = $this->_manager->getMappedStatement($statementID);
- $cacheModel->registerTriggerStatement($statement);
- }
- }
- }
-}
-
+<?php +/** + * TSqlMapXmlConfigBuilder, TSqlMapXmlConfiguration, TSqlMapXmlMappingConfiguration classes file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.SqlMap.Configuration + */ + +Prado::using('System.Data.SqlMap.Configuration.TSqlMapStatement'); + +/** + * TSqlMapXmlConfig class file. + * + * @author Wei Zhuo <weizhuo[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + */ +abstract class TSqlMapXmlConfigBuilder +{ + /** + * Create an instance of an object give by the attribute named 'class' in the + * node and set the properties on the object given by attribute names and values. + * @param SimpleXmlNode property node + * @return Object new instance of class with class name given by 'class' attribute value. + */ + protected function createObjectFromNode($node) + { + if(isset($node['class'])) + { + $obj = Prado::createComponent((string)$node['class']); + $this->setObjectPropFromNode($obj,$node,array('class')); + return $obj; + } + throw new TSqlMapConfigurationException( + 'sqlmap_node_class_undef', $node, $this->getConfigFile()); + } + + /** + * For each attributes (excluding attribute named in $except) set the + * property of the $obj given by the name of the attribute with the value + * of the attribute. + * @param Object object instance + * @param SimpleXmlNode property node + * @param array exception property name + */ + protected function setObjectPropFromNode($obj,$node,$except=array()) + { + foreach($node->attributes() as $name=>$value) + { + if(!in_array($name,$except)) + { + if($obj->canSetProperty($name)) + $obj->{$name} = (string)$value; + else + throw new TSqlMapConfigurationException( + 'sqlmap_invalid_property', $name, get_class($obj), + $node, $this->getConfigFile()); + } + } + } + + /** + * Gets the filename relative to the basefile. + * @param string base filename + * @param string relative filename + * @return string absolute filename. + */ + protected function getAbsoluteFilePath($basefile,$resource) + { + $basedir = dirname($basefile); + $file = realpath($basedir.DIRECTORY_SEPARATOR.$resource); + if(!is_string($file) || !is_file($file)) + $file = realpath($resource); + if(is_string($file) && is_file($file)) + return $file; + else + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_resource', $resource); + } + + /** + * Load document using simple xml. + * @param string filename. + * @return SimpleXmlElement xml document. + */ + protected function loadXmlDocument($filename,TSqlMapXmlConfiguration $config) + { + if( strpos($filename, '${') !== false) + $filename = $config->replaceProperties($filename); + + if(!is_file($filename)) + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_config', $filename); + return simplexml_load_string($config->replaceProperties(file_get_contents($filename))); + } + + /** + * Get element node by ID value (try for attribute name ID as case insensitive). + * @param SimpleXmlDocument $document + * @param string tag name. + * @param string id value. + * @return SimpleXmlElement node if found, null otherwise. + */ + protected function getElementByIdValue($document, $tag, $value) + { + //hack to allow upper case and lower case attribute names. + foreach(array('id','ID','Id', 'iD') as $id) + { + $xpath = "//{$tag}[@{$id}='{$value}']"; + foreach($document->xpath($xpath) as $node) + return $node; + } + } + + /** + * @return string configuration file. + */ + protected abstract function getConfigFile(); +} + +/** + * TSqlMapXmlConfig class. + * + * Configures the TSqlMapManager using xml configuration file. + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapXmlConfiguration extends TSqlMapXmlConfigBuilder +{ + /** + * @var TSqlMapManager manager + */ + private $_manager; + /** + * @var string configuration file. + */ + private $_configFile; + /** + * @var array global properties. + */ + private $_properties=array(); + + /** + * @param TSqlMapManager manager instance. + */ + public function __construct($manager) + { + $this->_manager=$manager; + } + + public function getManager() + { + return $this->_manager; + } + + protected function getConfigFile() + { + return $this->_configFile; + } + + /** + * Configure the TSqlMapManager using the given xml file. + * @param string SqlMap configuration xml file. + */ + public function configure($filename=null) + { + $this->_configFile=$filename; + $document = $this->loadXmlDocument($filename,$this); + + foreach($document->xpath('//property') as $property) + $this->loadGlobalProperty($property); + + foreach($document->xpath('//typeHandler') as $handler) + $this->loadTypeHandler($handler); + + foreach($document->xpath('//connection[last()]') as $conn) + $this->loadDatabaseConnection($conn); + + //try to load configuration in the current config file. + $mapping = new TSqlMapXmlMappingConfiguration($this); + $mapping->configure($filename); + + foreach($document->xpath('//sqlMap') as $sqlmap) + $this->loadSqlMappingFiles($sqlmap); + + $this->resolveResultMapping(); + $this->attachCacheModels(); + } + + /** + * Load global replacement property. + * @param SimpleXmlElement property node. + */ + protected function loadGlobalProperty($node) + { + $this->_properties[(string)$node['name']] = (string)$node['value']; + } + + /** + * Load the type handler configurations. + * @param SimpleXmlElement type handler node + */ + protected function loadTypeHandler($node) + { + $handler = $this->createObjectFromNode($node); + $this->_manager->getTypeHandlers()->registerTypeHandler($handler); + } + + /** + * Load the database connection tag. + * @param SimpleXmlElement connection node. + */ + protected function loadDatabaseConnection($node) + { + $conn = $this->createObjectFromNode($node); + $this->_manager->setDbConnection($conn); + } + + /** + * Load SqlMap mapping configuration. + * @param unknown_type $node + */ + protected function loadSqlMappingFiles($node) + { + if(strlen($resource = (string)$node['resource']) > 0) + { + if( strpos($resource, '${') !== false) + $resource = $this->replaceProperties($resource); + + $mapping = new TSqlMapXmlMappingConfiguration($this); + $filename = $this->getAbsoluteFilePath($this->_configFile, $resource); + $mapping->configure($filename); + } + } + + /** + * Resolve nest result mappings. + */ + protected function resolveResultMapping() + { + $maps = $this->_manager->getResultMaps(); + foreach($maps as $entry) + { + foreach($entry->getColumns() as $item) + { + $resultMap = $item->getResultMapping(); + if(strlen($resultMap) > 0) + { + if($maps->contains($resultMap)) + $item->setNestedResultMap($maps[$resultMap]); + else + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_result_mapping', + $resultMap, $this->_configFile, $entry->getID()); + } + } + if($entry->getDiscriminator()!==null) + $entry->getDiscriminator()->initialize($this->_manager); + } + } + + /** + * Set the cache for each statement having a cache model property. + */ + protected function attachCacheModels() + { + foreach($this->_manager->getMappedStatements() as $mappedStatement) + { + if(strlen($model = $mappedStatement->getStatement()->getCacheModel()) > 0) + { + $cache = $this->_manager->getCacheModel($model); + $mappedStatement->getStatement()->setCache($cache); + } + } + } + + /** + * Replace the place holders ${name} in text with properties the + * corresponding global property value. + * @param string original string. + * @return string string with global property replacement. + */ + public function replaceProperties($string) + { + foreach($this->_properties as $find => $replace) + $string = str_replace('${'.$find.'}', $replace, $string); + return $string; + } +} + +/** + * Loads the statements, result maps, parameters maps from xml configuration. + * + * description + * + * @author Wei Zhuo <weizho[at]gmail[dot]com> + * @version $Id$ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder +{ + private $_xmlConfig; + private $_configFile; + private $_manager; + + private $_document; + + private $_FlushOnExecuteStatements=array(); + + /** + * Regular expressions for escaping simple/inline parameter symbols + */ + const SIMPLE_MARK='$'; + const INLINE_SYMBOL='#'; + const ESCAPED_SIMPLE_MARK_REGEXP='/\$\$/'; + const ESCAPED_INLINE_SYMBOL_REGEXP='/\#\#/'; + const SIMPLE_PLACEHOLDER='`!!`'; + const INLINE_PLACEHOLDER='`!!!`'; + + /** + * @param TSqlMapXmlConfiguration parent xml configuration. + */ + public function __construct(TSqlMapXmlConfiguration $xmlConfig) + { + $this->_xmlConfig=$xmlConfig; + $this->_manager=$xmlConfig->getManager(); + } + + protected function getConfigFile() + { + return $this->_configFile; + } + + /** + * Configure an XML mapping. + * @param string xml mapping filename. + */ + public function configure($filename) + { + $this->_configFile=$filename; + $document = $this->loadXmlDocument($filename,$this->_xmlConfig); + $this->_document=$document; + + static $bCacheDependencies; + if($bCacheDependencies === null) + $bCacheDependencies = Prado::getApplication()->getMode() !== TApplicationMode::Performance; + + if($bCacheDependencies) + $this->_manager->getCacheDependencies() + ->getDependencies() + ->add(new TFileCacheDependency($filename)); + + foreach($document->xpath('//resultMap') as $node) + $this->loadResultMap($node); + + foreach($document->xpath('//parameterMap') as $node) + $this->loadParameterMap($node); + + foreach($document->xpath('//statement') as $node) + $this->loadStatementTag($node); + + foreach($document->xpath('//select') as $node) + $this->loadSelectTag($node); + + foreach($document->xpath('//insert') as $node) + $this->loadInsertTag($node); + + foreach($document->xpath('//update') as $node) + $this->loadUpdateTag($node); + + foreach($document->xpath('//delete') as $node) + $this->loadDeleteTag($node); + + foreach($document->xpath('//procedure') as $node) + $this->loadProcedureTag($node); + + foreach($document->xpath('//cacheModel') as $node) + $this->loadCacheModel($node); + + $this->registerCacheTriggers(); + } + + /** + * Load the result maps. + * @param SimpleXmlElement result map node. + */ + protected function loadResultMap($node) + { + $resultMap = $this->createResultMap($node); + + //find extended result map. + if(strlen($extendMap = $resultMap->getExtends()) > 0) + { + if(!$this->_manager->getResultMaps()->contains($extendMap)) + { + $extendNode=$this->getElementByIdValue($this->_document,'resultMap',$extendMap); + if($extendNode!==null) + $this->loadResultMap($extendNode); + } + + if(!$this->_manager->getResultMaps()->contains($extendMap)) + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_parent_result_map', $node, $this->_configFile, $extendMap); + + $superMap = $this->_manager->getResultMap($extendMap); + $resultMap->getColumns()->mergeWith($superMap->getColumns()); + } + + //add the result map + if(!$this->_manager->getResultMaps()->contains($resultMap->getID())) + $this->_manager->addResultMap($resultMap); + } + + /** + * Create a new result map and its associated result properties, + * disciminiator and sub maps. + * @param SimpleXmlElement result map node + * @return TResultMap SqlMap result mapping. + */ + protected function createResultMap($node) + { + $resultMap = new TResultMap(); + $this->setObjectPropFromNode($resultMap,$node); + + //result nodes + foreach($node->result as $result) + { + $property = new TResultProperty($resultMap); + $this->setObjectPropFromNode($property,$result); + $resultMap->addResultProperty($property); + } + + //create the discriminator + $discriminator = null; + if(isset($node->discriminator)) + { + $discriminator = new TDiscriminator(); + $this->setObjectPropFromNode($discriminator, $node->discriminator); + $discriminator->initMapping($resultMap); + } + + foreach($node->xpath('subMap') as $subMapNode) + { + if($discriminator===null) + throw new TSqlMapConfigurationException( + 'sqlmap_undefined_discriminator', $node, $this->_configFile,$subMapNode); + $subMap = new TSubMap; + $this->setObjectPropFromNode($subMap,$subMapNode); + $discriminator->addSubMap($subMap); + } + + if($discriminator!==null) + $resultMap->setDiscriminator($discriminator); + + return $resultMap; + } + + /** + * Load parameter map from xml. + * + * @param SimpleXmlElement parameter map node. + */ + protected function loadParameterMap($node) + { + $parameterMap = $this->createParameterMap($node); + + if(strlen($extendMap = $parameterMap->getExtends()) > 0) + { + if(!$this->_manager->getParameterMaps()->contains($extendMap)) + { + $extendNode=$this->getElementByIdValue($this->_document,'parameterMap',$extendMap); + if($extendNode!==null) + $this->loadParameterMap($extendNode); + } + + if(!$this->_manager->getParameterMaps()->contains($extendMap)) + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_parent_parameter_map', $node, $this->_configFile,$extendMap); + $superMap = $this->_manager->getParameterMap($extendMap); + $index = 0; + foreach($superMap->getPropertyNames() as $propertyName) + $parameterMap->insertProperty($index++,$superMap->getProperty($propertyName)); + } + $this->_manager->addParameterMap($parameterMap); + } + + /** + * Create a new parameter map from xml node. + * @param SimpleXmlElement parameter map node. + * @return TParameterMap new parameter mapping. + */ + protected function createParameterMap($node) + { + $parameterMap = new TParameterMap(); + $this->setObjectPropFromNode($parameterMap,$node); + foreach($node->parameter as $parameter) + { + $property = new TParameterProperty(); + $this->setObjectPropFromNode($property,$parameter); + $parameterMap->addProperty($property); + } + return $parameterMap; + } + + /** + * Load statement mapping from xml configuration file. + * @param SimpleXmlElement statement node. + */ + protected function loadStatementTag($node) + { + $statement = new TSqlMapStatement(); + $this->setObjectPropFromNode($statement,$node); + $this->processSqlStatement($statement, $node); + $mappedStatement = new TMappedStatement($this->_manager, $statement); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load extended SQL statements if application. Replaces global properties + * in the sql text. Extracts inline parameter maps. + * @param TSqlMapStatement mapped statement. + * @param SimpleXmlElement statement node. + */ + protected function processSqlStatement($statement, $node) + { + $commandText = (string)$node; + if(strlen($extend = $statement->getExtends()) > 0) + { + $superNode = $this->getElementByIdValue($this->_document,'*',$extend); + if($superNode!==null) + $commandText = (string)$superNode . $commandText; + else + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_parent_sql', $extend, $this->_configFile,$node); + } + //$commandText = $this->_xmlConfig->replaceProperties($commandText); + $statement->initialize($this->_manager); + $this->applyInlineParameterMap($statement, $commandText, $node); + } + + /** + * Extract inline parameter maps. + * @param TSqlMapStatement statement object. + * @param string sql text + * @param SimpleXmlElement statement node. + */ + protected function applyInlineParameterMap($statement, $sqlStatement, $node) + { + $scope['file'] = $this->_configFile; + $scope['node'] = $node; + + $sqlStatement=preg_replace(self::ESCAPED_INLINE_SYMBOL_REGEXP,self::INLINE_PLACEHOLDER,$sqlStatement); + if($statement->parameterMap() === null) + { + // Build a Parametermap with the inline parameters. + // if they exist. Then delete inline infos from sqltext. + $parameterParser = new TInlineParameterMapParser; + $sqlText = $parameterParser->parse($sqlStatement, $scope); + if(count($sqlText['parameters']) > 0) + { + $map = new TParameterMap(); + $map->setID($statement->getID().'-InLineParameterMap'); + $statement->setInlineParameterMap($map); + foreach($sqlText['parameters'] as $property) + $map->addProperty($property); + } + $sqlStatement = $sqlText['sql']; + } + $sqlStatement=preg_replace('/'.self::INLINE_PLACEHOLDER.'/',self::INLINE_SYMBOL,$sqlStatement); + + $this->prepareSql($statement, $sqlStatement, $node); + } + + /** + * Prepare the sql text (may extend to dynamic sql). + * @param TSqlMapStatement mapped statement. + * @param string sql text. + * @param SimpleXmlElement statement node. + * @todo Extend to dynamic sql. + */ + protected function prepareSql($statement,$sqlStatement, $node) + { + $simpleDynamic = new TSimpleDynamicParser; + $sqlStatement=preg_replace(self::ESCAPED_SIMPLE_MARK_REGEXP,self::SIMPLE_PLACEHOLDER,$sqlStatement); + $dynamics = $simpleDynamic->parse($sqlStatement); + if(count($dynamics['parameters']) > 0) + { + $sql = new TSimpleDynamicSql($dynamics['parameters']); + $sqlStatement = $dynamics['sql']; + } + else + $sql = new TStaticSql(); + $sqlStatement=preg_replace('/'.self::SIMPLE_PLACEHOLDER.'/',self::SIMPLE_MARK,$sqlStatement); + $sql->buildPreparedStatement($statement, $sqlStatement); + $statement->setSqlText($sql); + } + + /** + * Load select statement from xml mapping. + * @param SimpleXmlElement select node. + */ + protected function loadSelectTag($node) + { + $select = new TSqlMapSelect; + $this->setObjectPropFromNode($select,$node); + $this->processSqlStatement($select,$node); + $mappedStatement = new TMappedStatement($this->_manager, $select); + if(strlen($select->getCacheModel()) > 0) + $mappedStatement = new TCachingStatement($mappedStatement); + + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load insert statement from xml mapping. + * @param SimpleXmlElement insert node. + */ + protected function loadInsertTag($node) + { + $insert = $this->createInsertStatement($node); + $this->processSqlStatement($insert, $node); + $mappedStatement = new TInsertMappedStatement($this->_manager, $insert); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Create new insert statement from xml node. + * @param SimpleXmlElement insert node. + * @return TSqlMapInsert insert statement. + */ + protected function createInsertStatement($node) + { + $insert = new TSqlMapInsert; + $this->setObjectPropFromNode($insert,$node); + if(isset($node->selectKey)) + $this->loadSelectKeyTag($insert,$node->selectKey); + return $insert; + } + + /** + * Load the selectKey statement from xml mapping. + * @param SimpleXmlElement selectkey node + */ + protected function loadSelectKeyTag($insert, $node) + { + $selectKey = new TSqlMapSelectKey; + $this->setObjectPropFromNode($selectKey,$node); + $selectKey->setID($insert->getID()); + $selectKey->setID($insert->getID().'.SelectKey'); + $this->processSqlStatement($selectKey,$node); + $insert->setSelectKey($selectKey); + $mappedStatement = new TMappedStatement($this->_manager, $selectKey); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load update statement from xml mapping. + * @param SimpleXmlElement update node. + */ + protected function loadUpdateTag($node) + { + $update = new TSqlMapUpdate; + $this->setObjectPropFromNode($update,$node); + $this->processSqlStatement($update, $node); + $mappedStatement = new TUpdateMappedStatement($this->_manager, $update); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load delete statement from xml mapping. + * @param SimpleXmlElement delete node. + */ + protected function loadDeleteTag($node) + { + $delete = new TSqlMapDelete; + $this->setObjectPropFromNode($delete,$node); + $this->processSqlStatement($delete, $node); + $mappedStatement = new TDeleteMappedStatement($this->_manager, $delete); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load procedure statement from xml mapping. + * @todo Implement loading procedure + * @param SimpleXmlElement procedure node + */ + protected function loadProcedureTag($node) + { + //var_dump('todo: add load procedure'); + } + + /** + * Load cache models from xml mapping. + * @param SimpleXmlElement cache node. + */ + protected function loadCacheModel($node) + { + $cacheModel = new TSqlMapCacheModel; + $properties = array('id','implementation'); + foreach($node->attributes() as $name=>$value) + { + if(in_array(strtolower($name), $properties)) + $cacheModel->{'set'.$name}((string)$value); + } + $cache = Prado::createComponent($cacheModel->getImplementationClass(), $cacheModel); + $this->setObjectPropFromNode($cache,$node,$properties); + + foreach($node->xpath('property') as $propertyNode) + { + $name = $propertyNode->attributes()->name; + if($name===null || $name==='') continue; + + $value = $propertyNode->attributes()->value; + if($value===null || $value==='') continue; + + if( !TPropertyAccess::has($cache, $name) ) continue; + + TPropertyAccess::set($cache, $name, $value); + } + + $this->loadFlushInterval($cacheModel,$node); + + $cacheModel->initialize($cache); + $this->_manager->addCacheModel($cacheModel); + foreach($node->xpath('flushOnExecute') as $flush) + $this->loadFlushOnCache($cacheModel,$node,$flush); + } + + /** + * Load the flush interval + * @param TSqlMapCacheModel cache model + * @param SimpleXmlElement cache node + */ + protected function loadFlushInterval($cacheModel, $node) + { + $flushInterval = $node->xpath('flushInterval'); + if($flushInterval === null || count($flushInterval) === 0) return; + $duration = 0; + foreach($flushInterval[0]->attributes() as $name=>$value) + { + switch(strToLower($name)) + { + case 'seconds': + $duration += (integer)$value; + break; + case 'minutes': + $duration += 60 * (integer)$value; + break; + case 'hours': + $duration += 3600 * (integer)$value; + break; + case 'days': + $duration += 86400 * (integer)$value; + break; + case 'duration': + $duration = (integer)$value; + break 2; // switch, foreach + } + } + $cacheModel->setFlushInterval($duration); + } + + /** + * Load the flush on cache properties. + * @param TSqlMapCacheModel cache model + * @param SimpleXmlElement parent node. + * @param SimpleXmlElement flush node. + */ + protected function loadFlushOnCache($cacheModel,$parent,$node) + { + $id = $cacheModel->getID(); + if(!isset($this->_FlushOnExecuteStatements[$id])) + $this->_FlushOnExecuteStatements[$id] = array(); + foreach($node->attributes() as $name=>$value) + { + if(strtolower($name)==='statement') + $this->_FlushOnExecuteStatements[$id][] = (string)$value; + } + } + + /** + * Attach CacheModel to statement and register trigger statements for cache models + */ + protected function registerCacheTriggers() + { + foreach($this->_FlushOnExecuteStatements as $cacheID => $statementIDs) + { + $cacheModel = $this->_manager->getCacheModel($cacheID); + foreach($statementIDs as $statementID) + { + $statement = $this->_manager->getMappedStatement($statementID); + $cacheModel->registerTriggerStatement($statement); + } + } + } +} + |