ElementParser.class.php 6.79 KB
<?php
class Util_Dao_SqlMap_ElementParser {
	
	/**
	 * 设定Map元素
	 *
	 * @var Array
	 */
	private $mapElementsConfig = array ('Insert', 'Select','Update', 'Delete', 'Command', 'Read_call','Write_call' );
	
	/**
	 * 创建一个临时文件夹
	 *
	 * @var unknown_type
	 */
	private $tmp = 'tmp';
	
	/**
	 * map 文件
	 *
	 * @var String
	 */
	private $_sqlMapXmlFile;
	
	private $_resourceFiles;
	
	/**
	 * 替换列表
	 *
	 * @var Array
	 */
	private $replaceMap = array ();
	
	/**
	 * 元素Map
	 *
	 * @var Array
	 */
	private $elementMap = array ();
	
	/**
	 * Cache
	 *
	 * @var Util_Dao_Cache
	 */
	private $cache;
	
	private $sqlStr;
	
	/**
	 * 读过滤
	 * 
	 * @var array
	 */
	private $readFilters = array('Select','Read_call','Command');
	/**
	 * 替换参数验证
	 *
	 * @var String
	 */
	private $replaceFilters =  '/\bdelete\b|\bupdate\b|\binsert\b/i';
	
	/**
	 * 初始化
	 *
	 * @param String $dbname
	 */
	public function __construct($sqlMapXmlFile) {
		if (! is_string ( $sqlMapXmlFile ) || empty ( $sqlMapXmlFile )) {
			throw new Util_Dao_Exception ( 'ElementParser :SQL Map find' );
		}
		$this->_sqlMapXmlFile = $sqlMapXmlFile;
		$this->parseResource ();
	}
	
	/**
	 * 获取元素
	 *
	 */
	public function element($sqlId) {
		return $this->elementMap [$sqlId]['sql'];
	}
	
	public function elementReadTag($sqlId) {
	   return $this->elementMap [$sqlId]['readTag'];
	}
	/**
	 * 分析元素
	 *
	 */
	private function parseResource() {
		$xmlFile = new Util_Common_Io_File ( $this->_sqlMapXmlFile );
		if (! $xmlFile->exists ()) {
			throw new Util_Dao_Exception ( $this->_sqlMapXmlFile . ' not find' );
		} else {
			$this->resourceFiles = "file://" . $xmlFile->absPath ();
			$lastModified = $xmlFile->lastModified ();
			//echo $this->tmp . DIRECTORY_SEPARATOR . md5 ( $this->resourceFiles ) . '.php';
			$tmpFile = new Util_Common_Io_File ( $this->tmp . DIRECTORY_SEPARATOR . md5 ( $this->resourceFiles ) . '.php' );
			/**
			 * APC Cache
			 */
			if (Util_Cache_Adapter_Apc::isEnabled ()) {
				$apcEntry = Util_Cache_Adapter_Apc::get ( $this->resourceFiles );
				if ($apcEntry != null && $lastModified == $apcEntry ['lastModified']) {
					$this->elementMap = $apcEntry ['elementMap'];
					return;
				}
			} else {
				/**
				 * 文件Cache
				 */
				if ($tmpFile->exists () == true) {
					$entry = json_decode ( $tmpFile->reader ()->read (), true );
					if (! empty ( $entry ) && $lastModified == $entry ['lastModified']) {
						$this->elementMap = $entry ['elementMap'];
						return;
					}
				}
			}
			$resourceDocument = new DOMDocument ( );
			$resourceDocument->load ( $xmlFile->absPath () );
			$resourceNode = new Util_Common_Xml_XmlNode ( );
			$resourceNode->loadDOM ( $resourceDocument );
			$sqlMapNodeList = $resourceNode->namedChildren ( "sqlMap" );
			$namespace = "";
			$sqlMapNode = $sqlMapNodeList [0];
			$namespace = trim ( $sqlMapNode->attr ( "namespace" ) );
			$queryNodeList = $sqlMapNode->children ();
			foreach ( $queryNodeList as $queryNode ) {
				if ($queryNode->type () == XML_ELEMENT_NODE) {
					$tagName = strToLower ( $queryNode->name () );
					$queryId = trim ( $queryNode->attr ( "id" ) );
					if (in_array ( ucwords ( $tagName ), $this->mapElementsConfig ) && $queryId != '' && $namespace != '') {
						$this->elementMap [$namespace . '.' . $queryId]['sql'] = $queryNode->value ();
						$this->elementMap [$namespace . '.' . $queryId]['readTag'] = in_array(ucwords($tagName), $this->readFilters);
					}
				}
			}
			//var_dump($this->elementMap);
			/** 存入 Cache **/
			if (Util_Cache_Adapter_Apc::isEnabled ()) {
				$valArray = array ('elementMap' => $this->elementMap, 'lastModified' => $lastModified );
				Util_Cache_Adapter_Apc::set ( $this->resourceFiles, $valArray );
			} else {
				if ($tmpFile->exists () == true) {
					$valArray = array ('elementMap' => $this->elementMap, 'lastModified' => $lastModified );
					$tmpFile->writer ()->write ( json_encode ( $valArray ) );
				}
			}
		}
	}
	
	/**
	 * 获取所有对象
	 * 
	 * @return Array
	 */
	public function elementMap() {
		return $this->elementMap;
	}
	
	/**
	 * 设置替换列表
	 *
	 * @param array $replaceMap 替换列表(关联数组)
	 */
	public function setReplaceMap($replaceMap) {
		if (is_array ( $replaceMap )) {
			$newMap = array ();
			foreach ( $replaceMap as $key => $value ) {
				if (is_array ( $value )) {
					throw new Util_Dao_Exception ( ' replaceMap value Should not be array ' );
				}
				if (preg_match ( $this->replaceFilters, $value ) == 1) {
					throw new Util_Dao_Exception ( ' replaceMap should not contain select、insert、delete、update ' );
				}
				$newMap ["#" . $key . "#"] = $value;
			}
			$this->replaceMap = $newMap;
		}
	}
	
	/**
	 * 获取Sql
	 *
	 * @return String
	 */
	public function sql($sqlId) {
		if (stristr ( $sqlId, '.' ) === FALSE || empty ( $sqlId )) {
			throw new Util_Dao_Exception ( ' sqlId Incorrect format ' );
		}
		if (! is_array ( $this->replaceMap )) {
			throw new Util_Dao_Exception ( ' replaceMap Incorrect format ' );
		}
		$this->sqlStr = strtr ( $this->element ( $sqlId ), $this->replaceMap );
		$this->strictParse ();
		return $this->sqlStr;
	}
	
	/**
	 * 获取map标签元素配置
	 *
	 * @return Array
	 */
	public function getMapElementsConfig(){
		return $this->mapElementsConfig;
	}
	
	/**
	 * 获取是否调用写存储
	 * @return boolean
	 */
	public function getWriteCallState() {
	   return $this->writeCallState;
	}
	
	/**
	 * 验证SQL是否合法  : 和 ? 不能同时出现在Sql中
	 *
	 */
	private function strictParse() {
		if (empty ( $this->sqlStr )) {
			return;
		}
		$sqlLength = strlen ( $this->sqlStr );
		$lastQuote = null;
		$positionalParams = array ();
		$namedParams = array ();
		for($i = 0; $i < $sqlLength; $i ++) {
			$char = $this->sqlStr {$i};
			if ($char == "'" || $char == "\"") {
				if ($lastQuote != null) {
					if ($lastQuote == $char) {
						$lastQuote = null;
					} else {
						continue; //跳出本次循环 条件正确时继续
					}
				} else {
					$lastQuote = $char;
				}
			} else {
				if (! $lastQuote) {
					if ($char == "?") {
						$positionalParams [$i] = "?";
					} else if ($char == ":") {
						$nameStart = $i;
						$name = "";
						while ( true ) {
							$nameStart ++;
							if ($nameStart > $sqlLength - 1) {
								break;
							}
							$nameChar = $this->sqlStr {$nameStart};
							$ord = ord ( $nameChar ); //48-57 97-122 65-90 95
							if ($ord == 95 || ($ord >= 48 && $ord <= 57) || ($ord >= 97 && $ord <= 122) || ($ord >= 65 && $ord <= 90)) {
								$name .= $nameChar;
							} else {
								break;
							}
						}
						if ($name != "") {
							$namedParams [$i] = $name;
						}
					}
				}
			}
		}
		if (! empty ( $namedParams ) && ! empty ( $positionalParams )) {
			throw new Util_Dao_Exception ( ' ( ? and : ) placeholders can only be either named or positional params ' );
		}
	}
}