Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/*** TSqlMapXmlConfigBuilder, TSqlMapXmlConfiguration, TSqlMapXmlMappingConfiguration classes file.** @author Wei Zhuo <weizhuo[at]gmail[dot]com>* @link http://www.pradosoft.com/* @copyright Copyright © 2005-2008 PradoSoft* @license http://www.pradosoft.com/license/* @version $Id: TSqlMapXmlConfiguration.php 2541 2008-10-21 15:05:13Z qiang.xue $* @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: TSqlMapXmlConfiguration.php 2541 2008-10-21 15:05:13Z qiang.xue $* @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;elsethrow 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;elsethrow 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(!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: TSqlMapXmlConfiguration.php 2541 2008-10-21 15:05:13Z qiang.xue $* @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){$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]);elsethrow new TSqlMapConfigurationException('sqlmap_unable_to_find_result_mapping',$resultMap, $this->_configFile, $entry->getID());}}if(!is_null($entry->getDiscriminator()))$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: TSqlMapXmlConfiguration.php 2541 2008-10-21 15:05:13Z qiang.xue $* @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;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 mapif(!$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 nodesforeach($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(is_null($discriminator))throw new TSqlMapConfigurationException('sqlmap_undefined_discriminator', $node, $this->_configFile,$subMapNode);$subMap = new TSubMap;$this->setObjectPropFromNode($subMap,$subMapNode);$discriminator->addSubMap($subMap);}if(!is_null($discriminator))$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;elsethrow 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());$this->setObjectPropFromNode($cache,$node,$properties);$cacheModel->initialize($cache);$this->_manager->addCacheModel($cacheModel);foreach($node->xpath('flushOnExecute') as $flush)$this->loadFlushOnCache($cacheModel,$node,$flush);}/*** 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);}}}}