PRADO_DIR); private static $_usings=array(); private static $_application=null; private static $_logger=null; protected static $classExists = array(); public static function getVersion() { return '3.3.0'; } public static function initErrorHandlers() { set_error_handler(array('PradoBase','phpErrorHandler')); register_shutdown_function(array('PradoBase','phpFatalErrorHandler')); set_exception_handler(array('PradoBase','exceptionHandler')); } public static function autoload($className) { include_once($className.self::CLASS_FILE_EXT); if(!class_exists($className,false) && !interface_exists($className,false)) self::fatalError("Class file for '$className' cannot be found."); } public static function poweredByPrado($logoType=0) { $logoName=$logoType==1?'powered2':'powered'; if(self::$_application!==null) { $am=self::$_application->getAssetManager(); $url=$am->publishFilePath(self::getPathOfNamespace('System.'.$logoName,'.gif')); } else $url='http://pradosoft.github.io/docs/'.$logoName.'.gif'; return 'Powered by PRADO'; } public static function phpErrorHandler($errno,$errstr,$errfile,$errline) { if(error_reporting() & $errno) throw new TPhpErrorException($errno,$errstr,$errfile,$errline); } public static function phpFatalErrorHandler() { $error = error_get_last(); if($error && TPhpErrorException::isFatalError($error) && error_reporting() & $error['type']) { self::exceptionHandler(new TPhpErrorException($error['type'],$error['message'],$error['file'],$error['line'])); } } public static function exceptionHandler($exception) { if(self::$_application!==null && ($errorHandler=self::$_application->getErrorHandler())!==null) { $errorHandler->handleError(null,$exception); } else { echo $exception; } exit(1); } public static function setApplication($application) { if(self::$_application!==null && !defined('PRADO_TEST_RUN')) throw new TInvalidOperationException('prado_application_singleton_required'); self::$_application=$application; } public static function getApplication() { return self::$_application; } public static function getFrameworkPath() { return PRADO_DIR; } public static function createComponent($type) { if(!isset(self::$classExists[$type])) self::$classExists[$type] = class_exists($type, false); if( !isset(self::$_usings[$type]) && !self::$classExists[$type]) { self::using($type); self::$classExists[$type] = class_exists($type, false); } if( ($pos = strrpos($type, '.')) !== false) $type = substr($type,$pos+1); if(($n=func_num_args())>1) { $args = func_get_args(); switch($n) { case 2: return new $type($args[1]); break; case 3: return new $type($args[1], $args[2]); break; case 4: return new $type($args[1], $args[2], $args[3]); break; case 5: return new $type($args[1], $args[2], $args[3], $args[4]); break; default: $s='$args[1]'; for($i=2;$i<$n;++$i) $s.=",\$args[$i]"; eval("\$component=new $type($s);"); return $component; break; } } else return new $type; } public static function using($namespace,$checkClassExistence=true) { if(isset(self::$_usings[$namespace]) || class_exists($namespace,false)) return; if(($pos=strrpos($namespace,'.'))===false) { try { include_once($namespace.self::CLASS_FILE_EXT); } catch(Exception $e) { if($checkClassExistence && !class_exists($namespace,false)) throw new TInvalidOperationException('prado_component_unknown',$namespace,$e->getMessage()); else throw $e; } } else if(($path=self::getPathOfNamespace($namespace,self::CLASS_FILE_EXT))!==null) { $className=substr($namespace,$pos+1); if($className==='*') { self::$_usings[$namespace]=$path; set_include_path(get_include_path().PATH_SEPARATOR.$path); } else { self::$_usings[$namespace]=$path; if(!$checkClassExistence || !class_exists($className,false)) { try { include_once($path); } catch(Exception $e) { if($checkClassExistence && !class_exists($className,false)) throw new TInvalidOperationException('prado_component_unknown',$className,$e->getMessage()); else throw $e; } } } } else throw new TInvalidDataValueException('prado_using_invalid',$namespace); } public static function getPathOfNamespace($namespace, $ext='') { if(self::CLASS_FILE_EXT === $ext || empty($ext)) { if(isset(self::$_usings[$namespace])) return self::$_usings[$namespace]; if(isset(self::$_aliases[$namespace])) return self::$_aliases[$namespace]; } $segs = explode('.',$namespace); $alias = array_shift($segs); if(null !== ($file = array_pop($segs)) && null !== ($root = self::getPathOfAlias($alias))) return rtrim($root.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR ,$segs),'/\\').(($file === '*') ? '' : DIRECTORY_SEPARATOR.$file.$ext); return null; } public static function getPathOfAlias($alias) { return isset(self::$_aliases[$alias])?self::$_aliases[$alias]:null; } protected static function getPathAliases() { return self::$_aliases; } public static function setPathOfAlias($alias,$path) { if(isset(self::$_aliases[$alias]) && !defined('PRADO_TEST_RUN')) throw new TInvalidOperationException('prado_alias_redefined',$alias); else if(($rp=realpath($path))!==false && is_dir($rp)) { if(strpos($alias,'.')===false) self::$_aliases[$alias]=$rp; else throw new TInvalidDataValueException('prado_aliasname_invalid',$alias); } else throw new TInvalidDataValueException('prado_alias_invalid',$alias,$path); } public static function fatalError($msg) { echo '

Fatal Error

'; echo '

'.$msg.'

'; if(!function_exists('debug_backtrace')) return; echo '

Debug Backtrace

'; echo '
';
		$index=-1;
		foreach(debug_backtrace() as $t)
		{
			$index++;
			if($index==0)  				continue;
			echo '#'.$index.' ';
			if(isset($t['file']))
				echo basename($t['file']) . ':' . $t['line'];
			else
				 echo '';
			echo ' -- ';
			if(isset($t['class']))
				echo $t['class'] . $t['type'];
			echo $t['function'] . '(';
			if(isset($t['args']) && sizeof($t['args']) > 0)
			{
				$count=0;
				foreach($t['args'] as $item)
				{
					if(is_string($item))
					{
						$str=htmlentities(str_replace("\r\n", "", $item), ENT_QUOTES);
						if (strlen($item) > 70)
							echo "'". substr($str, 0, 70) . "...'";
						else
							echo "'" . $str . "'";
					}
					else if (is_int($item) || is_float($item))
						echo $item;
					else if (is_object($item))
						echo get_class($item);
					else if (is_array($item))
						echo 'array(' . count($item) . ')';
					else if (is_bool($item))
						echo $item ? 'true' : 'false';
					else if ($item === null)
						echo 'NULL';
					else if (is_resource($item))
						echo get_resource_type($item);
					$count++;
					if (count($t['args']) > $count)
						echo ', ';
				}
			}
			echo ")\n";
		}
		echo '
'; exit(1); } public static function getUserLanguages() { static $languages=null; if($languages===null) { if(!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) $languages[0]='en'; else { $languages=array(); foreach(explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']) as $language) { $array=explode(';q=',trim($language)); $languages[trim($array[0])]=isset($array[1])?(float)$array[1]:1.0; } arsort($languages); $languages=array_keys($languages); if(empty($languages)) $languages[0]='en'; } } return $languages; } public static function getPreferredLanguage() { static $language=null; if($language===null) { $langs=Prado::getUserLanguages(); $lang=explode('-',$langs[0]); if(empty($lang[0]) || !ctype_alpha($lang[0])) $language='en'; else $language=$lang[0]; } return $language; } public static function trace($msg,$category='Uncategorized',$ctl=null) { if(self::$_application && self::$_application->getMode()===TApplicationMode::Performance) return; if(!self::$_application || self::$_application->getMode()===TApplicationMode::Debug) { $trace=debug_backtrace(); if(isset($trace[0]['file']) && isset($trace[0]['line'])) $msg.=" (line {$trace[0]['line']}, {$trace[0]['file']})"; $level=TLogger::DEBUG; } else $level=TLogger::INFO; self::log($msg,$level,$category,$ctl); } public static function log($msg,$level=TLogger::INFO,$category='Uncategorized',$ctl=null) { if(self::$_logger===null) self::$_logger=new TLogger; self::$_logger->log($msg,$level,$category,$ctl); } public static function getLogger() { if(self::$_logger===null) self::$_logger=new TLogger; return self::$_logger; } public static function varDump($var,$depth=10,$highlight=false) { Prado::using('System.Util.TVarDumper'); return TVarDumper::dump($var,$depth,$highlight); } public static function localize($text, $parameters=array(), $catalogue=null, $charset=null) { Prado::using('System.I18N.Translation'); $app = Prado::getApplication()->getGlobalization(false); $params = array(); foreach($parameters as $key => $value) $params['{'.$key.'}'] = $value; if($app===null || ($config = $app->getTranslationConfiguration())===null) return strtr($text, $params); if ($catalogue===null) $catalogue=isset($config['catalogue'])?$config['catalogue']:'messages'; Translation::init($catalogue); $appCharset = $app===null ? '' : $app->getCharset(); $defaultCharset = ($app===null) ? 'UTF-8' : $app->getDefaultCharset(); if(empty($charset)) $charset = $appCharset; if(empty($charset)) $charset = $defaultCharset; return Translation::formatter($catalogue)->format($text,$params,$catalogue,$charset); } } PradoBase::using('System.TComponent'); PradoBase::using('System.Exceptions.TException'); PradoBase::using('System.Util.TLogger'); if(!class_exists('Prado',false)) { class Prado extends PradoBase { } } spl_autoload_register(array('Prado','autoload')); Prado::initErrorHandlers(); interface IModule { public function init($config); public function getID(); public function setID($id); } interface IService { public function init($config); public function getID(); public function setID($id); public function getEnabled(); public function setEnabled($value); public function run(); } interface ITextWriter { public function write($str); public function flush(); } interface IUser { public function getName(); public function setName($value); public function getIsGuest(); public function setIsGuest($value); public function getRoles(); public function setRoles($value); public function isInRole($role); public function saveToString(); public function loadFromString($string); } interface IStatePersister { public function load(); public function save($state); } interface ICache { public function get($id); public function set($id,$value,$expire=0,$dependency=null); public function add($id,$value,$expire=0,$dependency=null); public function delete($id); public function flush(); } interface ICacheDependency { public function getHasChanged(); } interface IRenderable { public function render($writer); } interface IBindable { public function dataBind(); } interface IStyleable { public function getHasStyle(); public function getStyle(); public function clearStyle(); } interface IActiveControl { public function getActiveControl(); } interface ICallbackEventHandler { public function raiseCallbackEvent($eventArgument); } interface IDataRenderer { public function getData(); public function setData($value); } class TApplicationComponent extends TComponent { public function getApplication() { return Prado::getApplication(); } public function getService() { return Prado::getApplication()->getService(); } public function getRequest() { return Prado::getApplication()->getRequest(); } public function getResponse() { return Prado::getApplication()->getResponse(); } public function getSession() { return Prado::getApplication()->getSession(); } public function getUser() { return Prado::getApplication()->getUser(); } public function publishAsset($assetPath,$className=null) { if($className===null) $className=get_class($this); $class=new ReflectionClass($className); $fullPath=dirname($class->getFileName()).DIRECTORY_SEPARATOR.$assetPath; return $this->publishFilePath($fullPath); } public function publishFilePath($fullPath, $checkTimestamp=false) { return Prado::getApplication()->getAssetManager()->publishFilePath($fullPath, $checkTimestamp); } } abstract class TModule extends TApplicationComponent implements IModule { private $_id; public function init($config) { } public function getID() { return $this->_id; } public function setID($value) { $this->_id=$value; } } abstract class TService extends TApplicationComponent implements IService { private $_id; private $_enabled=true; public function init($config) { } public function getID() { return $this->_id; } public function setID($value) { $this->_id=$value; } public function getEnabled() { return $this->_enabled; } public function setEnabled($value) { $this->_enabled=TPropertyValue::ensureBoolean($value); } public function run() { } } class TErrorHandler extends TModule { const ERROR_FILE_NAME='error'; const EXCEPTION_FILE_NAME='exception'; const SOURCE_LINES=12; private $_templatePath=null; public function init($config) { $this->getApplication()->setErrorHandler($this); } public function getErrorTemplatePath() { if($this->_templatePath===null) $this->_templatePath=Prado::getFrameworkPath().'/Exceptions/templates'; return $this->_templatePath; } public function setErrorTemplatePath($value) { if(($templatePath=Prado::getPathOfNamespace($value))!==null && is_dir($templatePath)) $this->_templatePath=$templatePath; else throw new TConfigurationException('errorhandler_errortemplatepath_invalid',$value); } public function handleError($sender,$param) { static $handling=false; restore_error_handler(); restore_exception_handler(); if($handling) $this->handleRecursiveError($param); else { $handling=true; if(($response=$this->getResponse())!==null) $response->clear(); if(!headers_sent()) header('Content-Type: text/html; charset=UTF-8'); if($param instanceof THttpException) $this->handleExternalError($param->getStatusCode(),$param); else if($this->getApplication()->getMode()===TApplicationMode::Debug) $this->displayException($param); else $this->handleExternalError(500,$param); } } protected static function hideSecurityRelated($value, $exception=null) { $aRpl = array(); if($exception !== null && $exception instanceof Exception) { $aTrace = $exception->getTrace(); foreach($aTrace as $item) { if(isset($item['file'])) $aRpl[dirname($item['file']) . DIRECTORY_SEPARATOR] = '' . DIRECTORY_SEPARATOR; } } $aRpl[$_SERVER['DOCUMENT_ROOT']] = '${DocumentRoot}'; $aRpl[str_replace('/', DIRECTORY_SEPARATOR, $_SERVER['DOCUMENT_ROOT'])] = '${DocumentRoot}'; $aRpl[PRADO_DIR . DIRECTORY_SEPARATOR] = '${PradoFramework}' . DIRECTORY_SEPARATOR; if(isset($aRpl[DIRECTORY_SEPARATOR])) unset($aRpl[DIRECTORY_SEPARATOR]); $aRpl = array_reverse($aRpl, true); return str_replace(array_keys($aRpl), $aRpl, $value); } protected function handleExternalError($statusCode,$exception) { if(!($exception instanceof THttpException)) error_log($exception->__toString()); $content=$this->getErrorTemplate($statusCode,$exception); $serverAdmin=isset($_SERVER['SERVER_ADMIN'])?$_SERVER['SERVER_ADMIN']:''; $isDebug = $this->getApplication()->getMode()===TApplicationMode::Debug; $errorMessage = $exception->getMessage(); if($isDebug) $version=$_SERVER['SERVER_SOFTWARE'].' PRADO/'.Prado::getVersion(); else { $version=''; $errorMessage = self::hideSecurityRelated($errorMessage, $exception); } $tokens=array( '%%StatusCode%%' => "$statusCode", '%%ErrorMessage%%' => htmlspecialchars($errorMessage), '%%ServerAdmin%%' => $serverAdmin, '%%Version%%' => $version, '%%Time%%' => @strftime('%Y-%m-%d %H:%M',time()) ); $this->getApplication()->getResponse()->setStatusCode($statusCode, $isDebug ? $exception->getMessage() : null); echo strtr($content,$tokens); } protected function handleRecursiveError($exception) { if($this->getApplication()->getMode()===TApplicationMode::Debug) { echo "Recursive Error\n"; echo "

Recursive Error

\n"; echo "
".$exception->__toString()."
\n"; echo ""; } else { error_log("Error happened while processing an existing error:\n".$exception->__toString()); header('HTTP/1.0 500 Internal Error'); } } protected function displayException($exception) { if(php_sapi_name()==='cli') { echo $exception->getMessage()."\n"; echo $exception->getTraceAsString(); return; } if($exception instanceof TTemplateException) { $fileName=$exception->getTemplateFile(); $lines=empty($fileName)?explode("\n",$exception->getTemplateSource()):@file($fileName); $source=$this->getSourceCode($lines,$exception->getLineNumber()); if($fileName==='') $fileName='---embedded template---'; $errorLine=$exception->getLineNumber(); } else { if(($trace=$this->getExactTrace($exception))!==null) { $fileName=$trace['file']; $errorLine=$trace['line']; } else { $fileName=$exception->getFile(); $errorLine=$exception->getLine(); } $source=$this->getSourceCode(@file($fileName),$errorLine); } if($this->getApplication()->getMode()===TApplicationMode::Debug) $version=$_SERVER['SERVER_SOFTWARE'].' PRADO/'.Prado::getVersion(); else $version=''; $tokens=array( '%%ErrorType%%' => get_class($exception), '%%ErrorMessage%%' => $this->addLink(htmlspecialchars($exception->getMessage())), '%%SourceFile%%' => htmlspecialchars($fileName).' ('.$errorLine.')', '%%SourceCode%%' => $source, '%%StackTrace%%' => htmlspecialchars($exception->getTraceAsString()), '%%Version%%' => $version, '%%Time%%' => @strftime('%Y-%m-%d %H:%M',time()) ); $content=$this->getExceptionTemplate($exception); echo strtr($content,$tokens); } protected function getExceptionTemplate($exception) { $lang=Prado::getPreferredLanguage(); $exceptionFile=Prado::getFrameworkPath().'/Exceptions/templates/'.self::EXCEPTION_FILE_NAME.'-'.$lang.'.html'; if(!is_file($exceptionFile)) $exceptionFile=Prado::getFrameworkPath().'/Exceptions/templates/'.self::EXCEPTION_FILE_NAME.'.html'; if(($content=@file_get_contents($exceptionFile))===false) die("Unable to open exception template file '$exceptionFile'."); return $content; } protected function getErrorTemplate($statusCode,$exception) { $base=$this->getErrorTemplatePath().DIRECTORY_SEPARATOR.self::ERROR_FILE_NAME; $lang=Prado::getPreferredLanguage(); if(is_file("$base$statusCode-$lang.html")) $errorFile="$base$statusCode-$lang.html"; else if(is_file("$base$statusCode.html")) $errorFile="$base$statusCode.html"; else if(is_file("$base-$lang.html")) $errorFile="$base-$lang.html"; else $errorFile="$base.html"; if(($content=@file_get_contents($errorFile))===false) die("Unable to open error template file '$errorFile'."); return $content; } private function getExactTrace($exception) { $trace=$exception->getTrace(); $result=null; if($exception instanceof TPhpErrorException) { if(isset($trace[0]['file'])) $result=$trace[0]; elseif(isset($trace[1])) $result=$trace[1]; } else if($exception instanceof TInvalidOperationException) { if(($result=$this->getPropertyAccessTrace($trace,'__get'))===null) $result=$this->getPropertyAccessTrace($trace,'__set'); } if($result!==null && strpos($result['file'],': eval()\'d code')!==false) return null; return $result; } private function getPropertyAccessTrace($trace,$pattern) { $result=null; foreach($trace as $t) { if(isset($t['function']) && $t['function']===$pattern) $result=$t; else break; } return $result; } private function getSourceCode($lines,$errorLine) { $beginLine=$errorLine-self::SOURCE_LINES>=0?$errorLine-self::SOURCE_LINES:0; $endLine=$errorLine+self::SOURCE_LINES<=count($lines)?$errorLine+self::SOURCE_LINES:count($lines); $source=''; for($i=$beginLine;$i<$endLine;++$i) { if($i===$errorLine-1) { $line=htmlspecialchars(sprintf("%04d: %s",$i+1,str_replace("\t",' ',$lines[$i]))); $source.="
".$line."
"; } else $source.=htmlspecialchars(sprintf("%04d: %s",$i+1,str_replace("\t",' ',$lines[$i]))); } return $source; } private function addLink($message) { $baseUrl='http://pradosoft.github.io/docs/manual/class-'; return preg_replace('/\b(T[A-Z]\w+)\b/',"\${1}",$message); } } class TList extends TComponent implements IteratorAggregate,ArrayAccess,Countable { private $_d=array(); private $_c=0; private $_r=false; public function __construct($data=null,$readOnly=false) { if($data!==null) $this->copyFrom($data); $this->setReadOnly($readOnly); } public function getReadOnly() { return $this->_r; } protected function setReadOnly($value) { $this->_r=TPropertyValue::ensureBoolean($value); } public function getIterator() { return new ArrayIterator( $this->_d ); } public function count() { return $this->getCount(); } public function getCount() { return $this->_c; } public function itemAt($index) { if($index>=0 && $index<$this->_c) return $this->_d[$index]; else throw new TInvalidDataValueException('list_index_invalid',$index); } public function add($item) { $this->insertAt($this->_c,$item); return $this->_c-1; } public function insertAt($index,$item) { if(!$this->_r) { if($index===$this->_c) $this->_d[$this->_c++]=$item; else if($index>=0 && $index<$this->_c) { array_splice($this->_d,$index,0,array($item)); $this->_c++; } else throw new TInvalidDataValueException('list_index_invalid',$index); } else throw new TInvalidOperationException('list_readonly',get_class($this)); } public function remove($item) { if(!$this->_r) { if(($index=$this->indexOf($item))>=0) { $this->removeAt($index); return $index; } else throw new TInvalidDataValueException('list_item_inexistent'); } else throw new TInvalidOperationException('list_readonly',get_class($this)); } public function removeAt($index) { if(!$this->_r) { if($index>=0 && $index<$this->_c) { $this->_c--; if($index===$this->_c) return array_pop($this->_d); else { $item=$this->_d[$index]; array_splice($this->_d,$index,1); return $item; } } else throw new TInvalidDataValueException('list_index_invalid',$index); } else throw new TInvalidOperationException('list_readonly',get_class($this)); } public function clear() { for($i=$this->_c-1;$i>=0;--$i) $this->removeAt($i); } public function contains($item) { return $this->indexOf($item)>=0; } public function indexOf($item) { if(($index=array_search($item,$this->_d,true))===false) return -1; else return $index; } public function insertBefore($baseitem, $item) { if(!$this->_r) { if(($index = $this->indexOf($baseitem)) == -1) throw new TInvalidDataValueException('list_item_inexistent'); $this->insertAt($index, $item); return $index; } else throw new TInvalidOperationException('list_readonly',get_class($this)); } public function insertAfter($baseitem, $item) { if(!$this->_r) { if(($index = $this->indexOf($baseitem)) == -1) throw new TInvalidDataValueException('list_item_inexistent'); $this->insertAt($index + 1, $item); return $index + 1; } else throw new TInvalidOperationException('list_readonly',get_class($this)); } public function toArray() { return $this->_d; } public function copyFrom($data) { if(is_array($data) || ($data instanceof Traversable)) { if($this->_c>0) $this->clear(); foreach($data as $item) $this->add($item); } else if($data!==null) throw new TInvalidDataTypeException('list_data_not_iterable'); } public function mergeWith($data) { if(is_array($data) || ($data instanceof Traversable)) { foreach($data as $item) $this->add($item); } else if($data!==null) throw new TInvalidDataTypeException('list_data_not_iterable'); } public function offsetExists($offset) { return ($offset>=0 && $offset<$this->_c); } public function offsetGet($offset) { return $this->itemAt($offset); } public function offsetSet($offset,$item) { if($offset===null || $offset===$this->_c) $this->insertAt($this->_c,$item); else { $this->removeAt($offset); $this->insertAt($offset,$item); } } public function offsetUnset($offset) { $this->removeAt($offset); } } class TListIterator extends ArrayIterator { } abstract class TCache extends TModule implements ICache, ArrayAccess { private $_prefix=null; private $_primary=true; public function init($config) { if($this->_prefix===null) $this->_prefix=$this->getApplication()->getUniqueID(); if($this->_primary) { if($this->getApplication()->getCache()===null) $this->getApplication()->setCache($this); else throw new TConfigurationException('cache_primary_duplicated',get_class($this)); } } public function getPrimaryCache() { return $this->_primary; } public function setPrimaryCache($value) { $this->_primary=TPropertyValue::ensureBoolean($value); } public function getKeyPrefix() { return $this->_prefix; } public function setKeyPrefix($value) { $this->_prefix=$value; } protected function generateUniqueKey($key) { return md5($this->_prefix.$key); } public function get($id) { if(($data=$this->getValue($this->generateUniqueKey($id)))!==false) { if(!is_array($data)) return false; if(!($data[1] instanceof ICacheDependency) || !$data[1]->getHasChanged()) return $data[0]; } return false; } public function set($id,$value,$expire=0,$dependency=null) { if(empty($value) && $expire === 0) $this->delete($id); else { $data=array($value,$dependency); return $this->setValue($this->generateUniqueKey($id),$data,$expire); } } public function add($id,$value,$expire=0,$dependency=null) { if(empty($value) && $expire === 0) return false; $data=array($value,$dependency); return $this->addValue($this->generateUniqueKey($id),$data,$expire); } public function delete($id) { return $this->deleteValue($this->generateUniqueKey($id)); } public function flush() { throw new TNotSupportedException('cache_flush_unsupported'); } abstract protected function getValue($key); abstract protected function setValue($key,$value,$expire); abstract protected function addValue($key,$value,$expire); abstract protected function deleteValue($key); public function offsetExists($id) { return $this->get($id) !== false; } public function offsetGet($id) { return $this->get($id); } public function offsetSet($id, $value) { $this->set($id, $value); } public function offsetUnset($id) { $this->delete($id); } } abstract class TCacheDependency extends TComponent implements ICacheDependency { } class TFileCacheDependency extends TCacheDependency { private $_fileName; private $_timestamp; public function __construct($fileName) { $this->setFileName($fileName); } public function getFileName() { return $this->_fileName; } public function setFileName($value) { $this->_fileName=$value; $this->_timestamp=@filemtime($value); } public function getTimestamp() { return $this->_timestamp; } public function getHasChanged() { return @filemtime($this->_fileName)!==$this->_timestamp; } } class TDirectoryCacheDependency extends TCacheDependency { private $_recursiveCheck=true; private $_recursiveLevel=-1; private $_timestamps; private $_directory; public function __construct($directory) { $this->setDirectory($directory); } public function getDirectory() { return $this->_directory; } public function setDirectory($directory) { if(($path=realpath($directory))===false || !is_dir($path)) throw new TInvalidDataValueException('directorycachedependency_directory_invalid',$directory); $this->_directory=$path; $this->_timestamps=$this->generateTimestamps($path); } public function getRecursiveCheck() { return $this->_recursiveCheck; } public function setRecursiveCheck($value) { $this->_recursiveCheck=TPropertyValue::ensureBoolean($value); } public function getRecursiveLevel() { return $this->_recursiveLevel; } public function setRecursiveLevel($value) { $this->_recursiveLevel=TPropertyValue::ensureInteger($value); } public function getHasChanged() { return $this->generateTimestamps($this->_directory)!=$this->_timestamps; } protected function validateFile($fileName) { return true; } protected function validateDirectory($directory) { return true; } protected function generateTimestamps($directory,$level=0) { if(($dir=opendir($directory))===false) throw new TIOException('directorycachedependency_directory_invalid',$directory); $timestamps=array(); while(($file=readdir($dir))!==false) { $path=$directory.DIRECTORY_SEPARATOR.$file; if($file==='.' || $file==='..') continue; else if(is_dir($path)) { if(($this->_recursiveLevel<0 || $level<$this->_recursiveLevel) && $this->validateDirectory($path)) $timestamps=array_merge($this->generateTimestamps($path,$level+1)); } else if($this->validateFile($path)) $timestamps[$path]=filemtime($path); } closedir($dir); return $timestamps; } } class TGlobalStateCacheDependency extends TCacheDependency { private $_stateName; private $_stateValue; public function __construct($name) { $this->setStateName($name); } public function getStateName() { return $this->_stateName; } public function setStateName($value) { $this->_stateName=$value; $this->_stateValue=Prado::getApplication()->getGlobalState($value); } public function getHasChanged() { return $this->_stateValue!==Prado::getApplication()->getGlobalState($this->_stateName); } } class TChainedCacheDependency extends TCacheDependency { private $_dependencies=null; public function getDependencies() { if($this->_dependencies===null) $this->_dependencies=new TCacheDependencyList; return $this->_dependencies; } public function getHasChanged() { if($this->_dependencies!==null) { foreach($this->_dependencies as $dependency) if($dependency->getHasChanged()) return true; } return false; } } class TApplicationStateCacheDependency extends TCacheDependency { public function getHasChanged() { return Prado::getApplication()->getMode()!==TApplicationMode::Performance; } } class TCacheDependencyList extends TList { public function insertAt($index,$item) { if($item instanceof ICacheDependency) parent::insertAt($index,$item); else throw new TInvalidDataTypeException('cachedependencylist_cachedependency_required'); } } class TTextWriter extends TComponent implements ITextWriter { private $_str=''; public function flush() { $str=$this->_str; $this->_str=''; return $str; } public function write($str) { $this->_str.=$str; } public function writeLine($str='') { $this->write($str."\n"); } } class TPriorityList extends TList { private $_d=array(); private $_o=false; private $_fd=null; private $_c=0; private $_dp=10; private $_p=8; public function __construct($data=null,$readOnly=false,$defaultPriority=10,$precision=8) { parent::__construct(); if($data!==null) $this->copyFrom($data); $this->setReadOnly($readOnly); $this->setPrecision($precision); $this->setDefaultPriority($defaultPriority); } public function count() { return $this->getCount(); } public function getCount() { return $this->_c; } public function getPriorityCount($priority=null) { if($priority===null) $priority=$this->getDefaultPriority(); $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); if(!isset($this->_d[$priority]) || !is_array($this->_d[$priority])) return false; return count($this->_d[$priority]); } public function getDefaultPriority() { return $this->_dp; } protected function setDefaultPriority($value) { $this->_dp=(string)round(TPropertyValue::ensureFloat($value),$this->_p); } public function getPrecision() { return $this->_p; } protected function setPrecision($value) { $this->_p=TPropertyValue::ensureInteger($value); } public function getIterator() { return new ArrayIterator($this->flattenPriorities()); } public function getPriorities() { $this->sortPriorities(); return array_keys($this->_d); } protected function sortPriorities() { if(!$this->_o) { ksort($this->_d,SORT_NUMERIC); $this->_o=true; } } protected function flattenPriorities() { if(is_array($this->_fd)) return $this->_fd; $this->sortPriorities(); $this->_fd=array(); foreach($this->_d as $priority => $itemsatpriority) $this->_fd=array_merge($this->_fd,$itemsatpriority); return $this->_fd; } public function itemAt($index) { if($index>=0&&$index<$this->getCount()) { $arr=$this->flattenPriorities(); return $arr[$index]; } else throw new TInvalidDataValueException('list_index_invalid',$index); } public function itemsAtPriority($priority=null) { if($priority===null) $priority=$this->getDefaultPriority(); $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); return isset($this->_d[$priority])?$this->_d[$priority]:null; } public function itemAtIndexInPriority($index,$priority=null) { if($priority===null) $priority=$this->getDefaultPriority(); $priority=(string)round(TPropertyValue::ensureFloat($priority), $this->_p); return !isset($this->_d[$priority])?false:( isset($this->_d[$priority][$index])?$this->_d[$priority][$index]:false ); } public function add($item,$priority=null) { if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); return $this->insertAtIndexInPriority($item,false,$priority,true); } public function insertAt($index,$item) { if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); if(($priority=$this->priorityAt($index,true))!==false) $this->insertAtIndexInPriority($item,$priority[1],$priority[0]); else throw new TInvalidDataValueException('list_index_invalid',$index); } public function insertAtIndexInPriority($item,$index=false,$priority=null,$preserveCache=false) { if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); if($priority===null) $priority=$this->getDefaultPriority(); $priority=(string)round(TPropertyValue::ensureFloat($priority), $this->_p); if($preserveCache) { $this->sortPriorities(); $cc=0; foreach($this->_d as $prioritykey=>$items) if($prioritykey>=$priority) break; else $cc+=count($items); if($index===false&&isset($this->_d[$priority])) { $c=count($this->_d[$priority]); $c+=$cc; $this->_d[$priority][]=$item; } else if(isset($this->_d[$priority])) { $c=$index+$cc; array_splice($this->_d[$priority],$index,0,array($item)); } else { $c = $cc; $this->_o = false; $this->_d[$priority]=array($item); } if($this->_fd&&is_array($this->_fd)) array_splice($this->_fd,$c,0,array($item)); } else { $c=null; if($index===false&&isset($this->_d[$priority])) { $cc=count($this->_d[$priority]); $this->_d[$priority][]=$item; } else if(isset($this->_d[$priority])) { $cc=$index; array_splice($this->_d[$priority],$index,0,array($item)); } else { $cc=0; $this->_o=false; $this->_d[$priority]=array($item); } if($this->_fd&&is_array($this->_fd)&&count($this->_d)==1) array_splice($this->_fd,$cc,0,array($item)); else $this->_fd=null; } $this->_c++; return $c; } public function remove($item,$priority=false) { if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); if(($p=$this->priorityOf($item,true))!==false) { if($priority!==false) { if($priority===null) $priority=$this->getDefaultPriority(); $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); if($p[0]!=$priority) throw new TInvalidDataValueException('list_item_inexistent'); } $this->removeAtIndexInPriority($p[1],$p[0]); return $p[2]; } else throw new TInvalidDataValueException('list_item_inexistent'); } public function removeAt($index) { if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); if(($priority=$this->priorityAt($index, true))!==false) return $this->removeAtIndexInPriority($priority[1],$priority[0]); throw new TInvalidDataValueException('list_index_invalid',$index); } public function removeAtIndexInPriority($index, $priority=null) { if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); if($priority===null) $priority=$this->getDefaultPriority(); $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); if(!isset($this->_d[$priority])||$index<0||$index>=count($this->_d[$priority])) throw new TInvalidDataValueException('list_item_inexistent'); $value=array_splice($this->_d[$priority],$index,1); $value=$value[0]; if(!count($this->_d[$priority])) unset($this->_d[$priority]); $this->_c--; $this->_fd=null; return $value; } public function clear() { if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); $d=array_reverse($this->_d,true); foreach($this->_d as $priority=>$items) { for($index=count($items)-1;$index>=0;$index--) $this->removeAtIndexInPriority($index,$priority); unset($this->_d[$priority]); } } public function contains($item) { return $this->indexOf($item)>=0; } public function indexOf($item) { if(($index=array_search($item,$this->flattenPriorities(),true))===false) return -1; else return $index; } public function priorityOf($item,$withindex = false) { $this->sortPriorities(); $absindex = 0; foreach($this->_d as $priority=>$items) { if(($index=array_search($item,$items,true))!==false) { $absindex+=$index; return $withindex?array($priority,$index,$absindex, 'priority'=>$priority,'index'=>$index,'absindex'=>$absindex):$priority; } else $absindex+=count($items); } return false; } public function priorityAt($index,$withindex = false) { if($index<0||$index>=$this->getCount()) throw new TInvalidDataValueException('list_index_invalid',$index); $absindex=$index; $this->sortPriorities(); foreach($this->_d as $priority=>$items) { if($index>=($c=count($items))) $index-=$c; else return $withindex?array($priority,$index,$absindex, 'priority'=>$priority,'index'=>$index,'absindex'=>$absindex):$priority; } return false; } public function insertBefore($indexitem, $item) { if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); if(($priority=$this->priorityOf($indexitem,true))===false) throw new TInvalidDataValueException('list_item_inexistent'); $this->insertAtIndexInPriority($item,$priority[1],$priority[0]); return $priority[2]; } public function insertAfter($indexitem, $item) { if($this->getReadOnly()) throw new TInvalidOperationException('list_readonly',get_class($this)); if(($priority=$this->priorityOf($indexitem,true))===false) throw new TInvalidDataValueException('list_item_inexistent'); $this->insertAtIndexInPriority($item,$priority[1]+1,$priority[0]); return $priority[2]+1; } public function toArray() { return $this->flattenPriorities(); } public function toPriorityArray() { $this->sortPriorities(); return $this->_d; } public function toArrayBelowPriority($priority,$inclusive=false) { $this->sortPriorities(); $items=array(); foreach($this->_d as $itemspriority=>$itemsatpriority) { if((!$inclusive&&$itemspriority>=$priority)||$itemspriority>$priority) break; $items=array_merge($items,$itemsatpriority); } return $items; } public function toArrayAbovePriority($priority,$inclusive=true) { $this->sortPriorities(); $items=array(); foreach($this->_d as $itemspriority=>$itemsatpriority) { if((!$inclusive&&$itemspriority<=$priority)||$itemspriority<$priority) continue; $items=array_merge($items,$itemsatpriority); } return $items; } public function copyFrom($data) { if($data instanceof TPriorityList) { if($this->getCount()>0) $this->clear(); foreach($data->getPriorities() as $priority) { foreach($data->itemsAtPriority($priority) as $index=>$item) $this->insertAtIndexInPriority($item,$index,$priority); } } else if(is_array($data)||$data instanceof Traversable) { if($this->getCount()>0) $this->clear(); foreach($data as $key=>$item) $this->add($item); } else if($data!==null) throw new TInvalidDataTypeException('map_data_not_iterable'); } public function mergeWith($data) { if($data instanceof TPriorityList) { foreach($data->getPriorities() as $priority) { foreach($data->itemsAtPriority($priority) as $index=>$item) $this->insertAtIndexInPriority($item,false,$priority); } } else if(is_array($data)||$data instanceof Traversable) { foreach($data as $priority=>$item) $this->add($item); } else if($data!==null) throw new TInvalidDataTypeException('map_data_not_iterable'); } public function offsetExists($offset) { return ($offset>=0&&$offset<$this->getCount()); } public function offsetGet($offset) { return $this->itemAt($offset); } public function offsetSet($offset,$item) { if($offset===null) return $this->add($item); if($offset===$this->getCount()) { $priority=$this->priorityAt($offset-1,true); $priority[1]++; } else { $priority=$this->priorityAt($offset,true); $this->removeAtIndexInPriority($priority[1],$priority[0]); } $this->insertAtIndexInPriority($item,$priority[1],$priority[0]); } public function offsetUnset($offset) { $this->removeAt($offset); } } class TMap extends TComponent implements IteratorAggregate,ArrayAccess,Countable { private $_d=array(); private $_r=false; protected function __getZappableSleepProps(&$exprops) { parent::__getZappableSleepProps($exprops); if ($this->_d===array()) $exprops[] = "\0TMap\0_d"; if ($this->_r===false) $exprops[] = "\0TMap\0_r"; } public function __construct($data=null,$readOnly=false) { if($data!==null) $this->copyFrom($data); $this->setReadOnly($readOnly); } public function getReadOnly() { return $this->_r; } protected function setReadOnly($value) { $this->_r=TPropertyValue::ensureBoolean($value); } public function getIterator() { return new ArrayIterator( $this->_d ); } public function count() { return $this->getCount(); } public function getCount() { return count($this->_d); } public function getKeys() { return array_keys($this->_d); } public function itemAt($key) { return isset($this->_d[$key]) ? $this->_d[$key] : null; } public function add($key,$value) { if(!$this->_r) $this->_d[$key]=$value; else throw new TInvalidOperationException('map_readonly',get_class($this)); } public function remove($key) { if(!$this->_r) { if(isset($this->_d[$key]) || array_key_exists($key,$this->_d)) { $value=$this->_d[$key]; unset($this->_d[$key]); return $value; } else return null; } else throw new TInvalidOperationException('map_readonly',get_class($this)); } public function clear() { foreach(array_keys($this->_d) as $key) $this->remove($key); } public function contains($key) { return isset($this->_d[$key]) || array_key_exists($key,$this->_d); } public function toArray() { return $this->_d; } public function copyFrom($data) { if(is_array($data) || $data instanceof Traversable) { if($this->getCount()>0) $this->clear(); foreach($data as $key=>$value) $this->add($key,$value); } else if($data!==null) throw new TInvalidDataTypeException('map_data_not_iterable'); } public function mergeWith($data) { if(is_array($data) || $data instanceof Traversable) { foreach($data as $key=>$value) $this->add($key,$value); } else if($data!==null) throw new TInvalidDataTypeException('map_data_not_iterable'); } public function offsetExists($offset) { return $this->contains($offset); } public function offsetGet($offset) { return $this->itemAt($offset); } public function offsetSet($offset,$item) { $this->add($offset,$item); } public function offsetUnset($offset) { $this->remove($offset); } } class TMapIterator extends ArrayIterator { } class TPriorityMap extends TMap { private $_d=array(); private $_r=false; private $_o=false; private $_fd=null; private $_c=0; private $_dp=10; private $_p=8; public function __construct($data=null,$readOnly=false,$defaultPriority=10,$precision=8) { if($data!==null) $this->copyFrom($data); $this->setReadOnly($readOnly); $this->setPrecision($precision); $this->setDefaultPriority($defaultPriority); } public function getReadOnly() { return $this->_r; } protected function setReadOnly($value) { $this->_r=TPropertyValue::ensureBoolean($value); } public function getDefaultPriority() { return $this->_dp; } protected function setDefaultPriority($value) { $this->_dp = (string)round(TPropertyValue::ensureFloat($value), $this->_p); } public function getPrecision() { return $this->_p; } protected function setPrecision($value) { $this->_p=TPropertyValue::ensureInteger($value); } public function getIterator() { return new ArrayIterator($this->flattenPriorities()); } protected function sortPriorities() { if(!$this->_o) { ksort($this->_d, SORT_NUMERIC); $this->_o=true; } } protected function flattenPriorities() { if(is_array($this->_fd)) return $this->_fd; $this->sortPriorities(); $this->_fd = array(); foreach($this->_d as $priority => $itemsatpriority) $this->_fd = array_merge($this->_fd, $itemsatpriority); return $this->_fd; } public function count() { return $this->getCount(); } public function getCount() { return $this->_c; } public function getPriorityCount($priority=null) { if($priority===null) $priority=$this->getDefaultPriority(); $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); if(!isset($this->_d[$priority])||!is_array($this->_d[$priority])) return false; return count($this->_d[$priority]); } public function getPriorities() { $this->sortPriorities(); return array_keys($this->_d); } public function getKeys() { return array_keys($this->flattenPriorities()); } public function itemAt($key,$priority=false) { if($priority===false){ $map=$this->flattenPriorities(); return isset($map[$key])?$map[$key]:null; } else { if($priority===null) $priority=$this->getDefaultPriority(); $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); return (isset($this->_d[$priority])&&isset($this->_d[$priority][$key]))?$this->_d[$priority][$key]:null; } } public function setPriorityAt($key,$priority=null) { if($priority===null) $priority=$this->getDefaultPriority(); $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); $oldpriority=$this->priorityAt($key); if($oldpriority!==false&&$oldpriority!=$priority) { $value=$this->remove($key,$oldpriority); $this->add($key,$value,$priority); } return $oldpriority; } public function itemsAtPriority($priority=null) { if($priority===null) $priority=$this->getDefaultPriority(); $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); return isset($this->_d[$priority])?$this->_d[$priority]:null; } public function priorityOf($item) { $this->sortPriorities(); foreach($this->_d as $priority=>$items) if(($index=array_search($item,$items,true))!==false) return $priority; return false; } public function priorityAt($key) { $this->sortPriorities(); foreach($this->_d as $priority=>$items) if(array_key_exists($key,$items)) return $priority; return false; } public function add($key,$value,$priority=null) { if($priority===null) $priority=$this->getDefaultPriority(); $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); if(!$this->_r) { foreach($this->_d as $innerpriority=>$items) if(array_key_exists($key,$items)) { unset($this->_d[$innerpriority][$key]); $this->_c--; if(count($this->_d[$innerpriority])===0) unset($this->_d[$innerpriority]); } if(!isset($this->_d[$priority])) { $this->_d[$priority]=array($key=>$value); $this->_o=false; } else $this->_d[$priority][$key]=$value; $this->_c++; $this->_fd=null; } else throw new TInvalidOperationException('map_readonly',get_class($this)); return $priority; } public function remove($key,$priority=false) { if(!$this->_r) { if($priority===null) $priority=$this->getDefaultPriority(); if($priority===false) { $this->sortPriorities(); foreach($this->_d as $priority=>$items) if(array_key_exists($key,$items)) { $value=$this->_d[$priority][$key]; unset($this->_d[$priority][$key]); $this->_c--; if(count($this->_d[$priority])===0) { unset($this->_d[$priority]); $this->_o=false; } $this->_fd=null; return $value; } return null; } else { $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); if(isset($this->_d[$priority])&&(isset($this->_d[$priority][$key])||array_key_exists($key,$this->_d[$priority]))) { $value=$this->_d[$priority][$key]; unset($this->_d[$priority][$key]); $this->_c--; if(count($this->_d[$priority])===0) { unset($this->_d[$priority]); $this->_o=false; } $this->_fd=null; return $value; } else return null; } } else throw new TInvalidOperationException('map_readonly',get_class($this)); } public function clear() { foreach($this->_d as $priority=>$items) foreach(array_keys($items) as $key) $this->remove($key); } public function contains($key) { $map=$this->flattenPriorities(); return isset($map[$key])||array_key_exists($key,$map); } public function toArray() { return $this->flattenPriorities(); } public function toArrayBelowPriority($priority,$inclusive=false) { $this->sortPriorities(); $items=array(); foreach($this->_d as $itemspriority=>$itemsatpriority) { if((!$inclusive&&$itemspriority>=$priority)||$itemspriority>$priority) break; $items=array_merge($items,$itemsatpriority); } return $items; } public function toArrayAbovePriority($priority,$inclusive=true) { $this->sortPriorities(); $items=array(); foreach($this->_d as $itemspriority=>$itemsatpriority) { if((!$inclusive&&$itemspriority<=$priority)||$itemspriority<$priority) continue; $items=array_merge($items,$itemsatpriority); } return $items; } public function copyFrom($data) { if($data instanceof TPriorityMap) { if($this->getCount()>0) $this->clear(); foreach($data->getPriorities() as $priority) { foreach($data->itemsAtPriority($priority) as $key => $value) { $this->add($key,$value,$priority); } } } else if(is_array($data)||$data instanceof Traversable) { if($this->getCount()>0) $this->clear(); foreach($data as $key=>$value) $this->add($key,$value); } else if($data!==null) throw new TInvalidDataTypeException('map_data_not_iterable'); } public function mergeWith($data) { if($data instanceof TPriorityMap) { foreach($data->getPriorities() as $priority) { foreach($data->itemsAtPriority($priority) as $key => $value) $this->add($key,$value,$priority); } } else if(is_array($data)||$data instanceof Traversable) { foreach($data as $key=>$value) $this->add($key,$value); } else if($data!==null) throw new TInvalidDataTypeException('map_data_not_iterable'); } public function offsetExists($offset) { return $this->contains($offset); } public function offsetGet($offset) { return $this->itemAt($offset); } public function offsetSet($offset,$item) { $this->add($offset,$item); } public function offsetUnset($offset) { $this->remove($offset); } } class TStack extends TComponent implements IteratorAggregate,Countable { private $_d=array(); private $_c=0; public function __construct($data=null) { if($data!==null) $this->copyFrom($data); } public function toArray() { return $this->_d; } public function copyFrom($data) { if(is_array($data) || ($data instanceof Traversable)) { $this->clear(); foreach($data as $item) { $this->_d[]=$item; ++$this->_c; } } else if($data!==null) throw new TInvalidDataTypeException('stack_data_not_iterable'); } public function clear() { $this->_c=0; $this->_d=array(); } public function contains($item) { return array_search($item,$this->_d,true)!==false; } public function peek() { if($this->_c===0) throw new TInvalidOperationException('stack_empty'); else return $this->_d[$this->_c-1]; } public function pop() { if($this->_c===0) throw new TInvalidOperationException('stack_empty'); else { --$this->_c; return array_pop($this->_d); } } public function push($item) { ++$this->_c; $this->_d[] = $item; } public function getIterator() { return new ArrayIterator( $this->_d ); } public function getCount() { return $this->_c; } public function count() { return $this->getCount(); } } class TStackIterator implements Iterator { private $_d; private $_i; private $_c; public function __construct(&$data) { $this->_d=&$data; $this->_i=0; $this->_c=count($this->_d); } public function rewind() { $this->_i=0; } public function key() { return $this->_i; } public function current() { return $this->_d[$this->_i]; } public function next() { $this->_i++; } public function valid() { return $this->_i<$this->_c; } } class TXmlElement extends TComponent { private $_parent=null; private $_tagName='unknown'; private $_value=''; private $_elements=null; private $_attributes=null; public function __construct($tagName) { $this->setTagName($tagName); } public function getParent() { return $this->_parent; } public function setParent($parent) { $this->_parent=$parent; } public function getTagName() { return $this->_tagName; } public function setTagName($tagName) { $this->_tagName=$tagName; } public function getValue() { return $this->_value; } public function setValue($value) { $this->_value=TPropertyValue::ensureString($value); } public function getHasElement() { return $this->_elements!==null && $this->_elements->getCount()>0; } public function getHasAttribute() { return $this->_attributes!==null && $this->_attributes->getCount()>0; } public function getAttribute($name) { if($this->_attributes!==null) return $this->_attributes->itemAt($name); else return null; } public function setAttribute($name,$value) { $this->getAttributes()->add($name,TPropertyValue::ensureString($value)); } public function getElements() { if(!$this->_elements) $this->_elements=new TXmlElementList($this); return $this->_elements; } public function getAttributes() { if(!$this->_attributes) $this->_attributes=new TMap; return $this->_attributes; } public function getElementByTagName($tagName) { if($this->_elements) { foreach($this->_elements as $element) if($element->_tagName===$tagName) return $element; } return null; } public function getElementsByTagName($tagName) { $list=new TList; if($this->_elements) { foreach($this->_elements as $element) if($element->_tagName===$tagName) $list->add($element); } return $list; } public function toString($indent=0) { $attr=''; if($this->_attributes!==null) { foreach($this->_attributes as $name=>$value) { $value=$this->xmlEncode($value); $attr.=" $name=\"$value\""; } } $prefix=str_repeat(' ',$indent*4); if($this->getHasElement()) { $str=$prefix."<{$this->_tagName}$attr>\n"; foreach($this->getElements() as $element) $str.=$element->toString($indent+1)."\n"; $str.=$prefix."_tagName}>"; return $str; } else if(($value=$this->getValue())!=='') { $value=$this->xmlEncode($value); return $prefix."<{$this->_tagName}$attr>$value_tagName}>"; } else return $prefix."<{$this->_tagName}$attr />"; } public function __toString() { return $this->toString(); } private function xmlEncode($str) { return strtr($str,array( '>'=>'>', '<'=>'<', '&'=>'&', '"'=>'"', "\r"=>' ', "\t"=>' ', "\n"=>' ')); } } class TXmlDocument extends TXmlElement { private $_version; private $_encoding; public function __construct($version='1.0',$encoding='') { parent::__construct(''); $this->setVersion($version); $this->setEncoding($encoding); } public function getVersion() { return $this->_version; } public function setVersion($version) { $this->_version=$version; } public function getEncoding() { return $this->_encoding; } public function setEncoding($encoding) { $this->_encoding=$encoding; } public function loadFromFile($file) { if(($str=@file_get_contents($file))!==false) return $this->loadFromString($str); else throw new TIOException('xmldocument_file_read_failed',$file); } public function loadFromString($string) { $doc=new DOMDocument(); if($doc->loadXML($string)===false) return false; $this->setEncoding($doc->encoding); $this->setVersion($doc->xmlVersion); $element=$doc->documentElement; $this->setTagName($element->tagName); $this->setValue($element->nodeValue); $elements=$this->getElements(); $attributes=$this->getAttributes(); $elements->clear(); $attributes->clear(); static $bSimpleXml; if($bSimpleXml === null) $bSimpleXml = (boolean)function_exists('simplexml_load_string'); if($bSimpleXml) { $simpleDoc = simplexml_load_string($string); $docNamespaces = $simpleDoc->getDocNamespaces(false); $simpleDoc = null; foreach($docNamespaces as $prefix => $uri) { if($prefix === '') $attributes->add('xmlns', $uri); else $attributes->add('xmlns:'.$prefix, $uri); } } foreach($element->attributes as $name=>$attr) $attributes->add(($attr->prefix === '' ? '' : $attr->prefix . ':') .$name,$attr->value); foreach($element->childNodes as $child) { if($child instanceof DOMElement) $elements->add($this->buildElement($child)); } return true; } public function saveToFile($file) { if(($fw=fopen($file,'w'))!==false) { fwrite($fw,$this->saveToString()); fclose($fw); } else throw new TIOException('xmldocument_file_write_failed',$file); } public function saveToString() { $version=empty($this->_version)?' version="1.0"':' version="'.$this->_version.'"'; $encoding=empty($this->_encoding)?'':' encoding="'.$this->_encoding.'"'; return "\n".$this->toString(0); } public function __toString() { return $this->saveToString(); } protected function buildElement($node) { $element=new TXmlElement($node->tagName); $element->setValue($node->nodeValue); foreach($node->attributes as $name=>$attr) $element->getAttributes()->add(($attr->prefix === '' ? '' : $attr->prefix . ':') . $name,$attr->value); foreach($node->childNodes as $child) { if($child instanceof DOMElement) $element->getElements()->add($this->buildElement($child)); } return $element; } } class TXmlElementList extends TList { private $_o; public function __construct(TXmlElement $owner) { $this->_o=$owner; } protected function getOwner() { return $this->_o; } public function insertAt($index,$item) { if($item instanceof TXmlElement) { parent::insertAt($index,$item); if($item->getParent()!==null) $item->getParent()->getElements()->remove($item); $item->setParent($this->_o); } else throw new TInvalidDataTypeException('xmlelementlist_xmlelement_required'); } public function removeAt($index) { $item=parent::removeAt($index); if($item instanceof TXmlElement) $item->setParent(null); return $item; } } class TAuthorizationRule extends TComponent { private $_action; private $_users; private $_roles; private $_verb; private $_ipRules; private $_everyone; private $_guest; private $_authenticated; public function __construct($action,$users,$roles,$verb='',$ipRules='') { $action=strtolower(trim($action)); if($action==='allow' || $action==='deny') $this->_action=$action; else throw new TInvalidDataValueException('authorizationrule_action_invalid',$action); $this->_users=array(); $this->_roles=array(); $this->_ipRules=array(); $this->_everyone=false; $this->_guest=false; $this->_authenticated=false; if(trim($users)==='') $users='*'; foreach(explode(',',$users) as $user) { if(($user=trim(strtolower($user)))!=='') { if($user==='*') { $this->_everyone=true; break; } else if($user==='?') $this->_guest=true; else if($user==='@') $this->_authenticated=true; else $this->_users[]=$user; } } if(trim($roles)==='') $roles='*'; foreach(explode(',',$roles) as $role) { if(($role=trim(strtolower($role)))!=='') $this->_roles[]=$role; } if(($verb=trim(strtolower($verb)))==='') $verb='*'; if($verb==='*' || $verb==='get' || $verb==='post') $this->_verb=$verb; else throw new TInvalidDataValueException('authorizationrule_verb_invalid',$verb); if(trim($ipRules)==='') $ipRules='*'; foreach(explode(',',$ipRules) as $ipRule) { if(($ipRule=trim($ipRule))!=='') $this->_ipRules[]=$ipRule; } } public function getAction() { return $this->_action; } public function getUsers() { return $this->_users; } public function getRoles() { return $this->_roles; } public function getVerb() { return $this->_verb; } public function getIPRules() { return $this->_ipRules; } public function getGuestApplied() { return $this->_guest || $this->_everyone; } public function getEveryoneApplied() { return $this->_everyone; } public function getAuthenticatedApplied() { return $this->_authenticated || $this->_everyone; } public function isUserAllowed(IUser $user,$verb,$ip) { if($this->isVerbMatched($verb) && $this->isIpMatched($ip) && $this->isUserMatched($user) && $this->isRoleMatched($user)) return ($this->_action==='allow')?1:-1; else return 0; } private function isIpMatched($ip) { if(empty($this->_ipRules)) return 1; foreach($this->_ipRules as $rule) { if($rule==='*' || $rule===$ip || (($pos=strpos($rule,'*'))!==false && strncmp($ip,$rule,$pos)===0)) return 1; } return 0; } private function isUserMatched($user) { return ($this->_everyone || ($this->_guest && $user->getIsGuest()) || ($this->_authenticated && !$user->getIsGuest()) || in_array(strtolower($user->getName()),$this->_users)); } private function isRoleMatched($user) { foreach($this->_roles as $role) { if($role==='*' || $user->isInRole($role)) return true; } return false; } private function isVerbMatched($verb) { return ($this->_verb==='*' || strcasecmp($verb,$this->_verb)===0); } } class TAuthorizationRuleCollection extends TList { public function isUserAllowed($user,$verb,$ip) { if($user instanceof IUser) { $verb=strtolower(trim($verb)); foreach($this as $rule) { if(($decision=$rule->isUserAllowed($user,$verb,$ip))!==0) return ($decision>0); } return true; } else return false; } public function insertAt($index,$item) { if($item instanceof TAuthorizationRule) parent::insertAt($index,$item); else throw new TInvalidDataTypeException('authorizationrulecollection_authorizationrule_required'); } } class TSecurityManager extends TModule { const STATE_VALIDATION_KEY = 'prado:securitymanager:validationkey'; const STATE_ENCRYPTION_KEY = 'prado:securitymanager:encryptionkey'; private $_validationKey = null; private $_encryptionKey = null; private $_hashAlgorithm = 'sha1'; private $_cryptAlgorithm = 'rijndael-256'; private $_mbstring; public function init($config) { $this->_mbstring=extension_loaded('mbstring'); $this->getApplication()->setSecurityManager($this); } protected function generateRandomKey() { return sprintf('%08x%08x%08x%08x',mt_rand(),mt_rand(),mt_rand(),mt_rand()); } public function getValidationKey() { if(null === $this->_validationKey) { if(null === ($this->_validationKey = $this->getApplication()->getGlobalState(self::STATE_VALIDATION_KEY))) { $this->_validationKey = $this->generateRandomKey(); $this->getApplication()->setGlobalState(self::STATE_VALIDATION_KEY, $this->_validationKey, null, true); } } return $this->_validationKey; } public function setValidationKey($value) { if('' === $value) throw new TInvalidDataValueException('securitymanager_validationkey_invalid'); $this->_validationKey = $value; } public function getEncryptionKey() { if(null === $this->_encryptionKey) { if(null === ($this->_encryptionKey = $this->getApplication()->getGlobalState(self::STATE_ENCRYPTION_KEY))) { $this->_encryptionKey = $this->generateRandomKey(); $this->getApplication()->setGlobalState(self::STATE_ENCRYPTION_KEY, $this->_encryptionKey, null, true); } } return $this->_encryptionKey; } public function setEncryptionKey($value) { if('' === $value) throw new TInvalidDataValueException('securitymanager_encryptionkey_invalid'); $this->_encryptionKey = $value; } public function getValidation() { return $this->_hashAlgorithm; } public function getHashAlgorithm() { return $this->_hashAlgorithm; } public function setValidation($value) { $this->_hashAlgorithm = TPropertyValue::ensureEnum($value, 'TSecurityManagerValidationMode'); } public function setHashAlgorithm($value) { $this->_hashAlgorithm = TPropertyValue::ensureString($value); } public function getEncryption() { if(is_string($this->_cryptAlgorithm)) return $this->_cryptAlgorithm; return "3DES"; } public function setEncryption($value) { $this->_cryptAlgorithm = $value; } public function getCryptAlgorithm() { return $this->_cryptAlgorithm; } public function setCryptAlgorithm($value) { $this->_cryptAlgorithm = $value; } public function encrypt($data) { $module=$this->openCryptModule(); $key = $this->substr(md5($this->getEncryptionKey()), 0, mcrypt_enc_get_key_size($module)); srand(); $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND); mcrypt_generic_init($module, $key, $iv); $encrypted = $iv.mcrypt_generic($module, $data); mcrypt_generic_deinit($module); mcrypt_module_close($module); return $encrypted; } public function decrypt($data) { $module=$this->openCryptModule(); $key = $this->substr(md5($this->getEncryptionKey()), 0, mcrypt_enc_get_key_size($module)); $ivSize = mcrypt_enc_get_iv_size($module); $iv = $this->substr($data, 0, $ivSize); mcrypt_generic_init($module, $key, $iv); $decrypted = mdecrypt_generic($module, $this->substr($data, $ivSize, $this->strlen($data))); mcrypt_generic_deinit($module); mcrypt_module_close($module); return $decrypted; } protected function openCryptModule() { if(extension_loaded('mcrypt')) { if(is_array($this->_cryptAlgorithm)) $module=@call_user_func_array('mcrypt_module_open',$this->_cryptAlgorithm); else $module=@mcrypt_module_open($this->_cryptAlgorithm,'', MCRYPT_MODE_CBC,''); if($module===false) throw new TNotSupportedException('securitymanager_mcryptextension_initfailed'); return $module; } else throw new TNotSupportedException('securitymanager_mcryptextension_required'); } public function hashData($data) { $hmac = $this->computeHMAC($data); return $hmac.$data; } public function validateData($data) { $len=$this->strlen($this->computeHMAC('test')); if($this->strlen($data) < $len) return false; $hmac = $this->substr($data, 0, $len); $data2=$this->substr($data, $len, $this->strlen($data)); return $hmac === $this->computeHMAC($data2) ? $data2 : false; } protected function computeHMAC($data) { $key = $this->getValidationKey(); if(function_exists('hash_hmac')) return hash_hmac($this->_hashAlgorithm, $data, $key); if(!strcasecmp($this->_hashAlgorithm,'sha1')) { $pack = 'H40'; $func = 'sha1'; } else { $pack = 'H32'; $func = 'md5'; } $key = str_pad($func($key), 64, chr(0)); return $func((str_repeat(chr(0x5C), 64) ^ substr($key, 0, 64)) . pack($pack, $func((str_repeat(chr(0x36), 64) ^ substr($key, 0, 64)) . $data))); } private function strlen($string) { return $this->_mbstring ? mb_strlen($string,'8bit') : strlen($string); } private function substr($string,$start,$length) { return $this->_mbstring ? mb_substr($string,$start,$length,'8bit') : substr($string,$start,$length); } } class TSecurityManagerValidationMode extends TEnumerable { const MD5 = 'MD5'; const SHA1 = 'SHA1'; } class THttpUtility { private static $_encodeTable=array('<'=>'<','>'=>'>','"'=>'"'); private static $_decodeTable=array('<'=>'<','>'=>'>','"'=>'"'); private static $_stripTable=array('<'=>'','>'=>'','"'=>''); public static function htmlEncode($s) { return strtr($s,self::$_encodeTable); } public static function htmlDecode($s) { return strtr($s,self::$_decodeTable); } public static function htmlStrip($s) { return strtr($s,self::$_stripTable); } } class TJavaScript { public static function renderScriptFiles($files) { $str=''; foreach($files as $file) $str.= self::renderScriptFile($file); return $str; } public static function renderScriptFile($file) { return '\n"; } public static function renderScriptBlocks($scripts) { if(count($scripts)) return "\n"; else return ''; } public static function renderScriptBlocksCallback($scripts) { if(count($scripts)) return implode("\n",$scripts)."\n"; else return ''; } public static function renderScriptBlock($script) { return "\n"; } public static function quoteString($js) { return self::jsonEncode($js,JSON_HEX_QUOT | JSON_HEX_APOS | JSON_HEX_TAG); } public static function quoteJsLiteral($js) { if($js instanceof TJavaScriptLiteral) return $js; else return new TJavaScriptLiteral($js); } public static function quoteFunction($js) { return self::quoteJsLiteral($js); } public static function isJsLiteral($js) { return ($js instanceof TJavaScriptLiteral); } public static function isFunction($js) { return self::isJsLiteral($js); } public static function encode($value,$toMap=true,$encodeEmptyStrings=false) { if(is_string($value)) return self::quoteString($value); else if(is_bool($value)) return $value?'true':'false'; else if(is_array($value)) { $results=''; if(($n=count($value))>0 && array_keys($value)!==range(0,$n-1)) { foreach($value as $k=>$v) { if($v!=='' || $encodeEmptyStrings) { if($results!=='') $results.=','; $results.="'$k':".self::encode($v,$toMap,$encodeEmptyStrings); } } return '{'.$results.'}'; } else { foreach($value as $v) { if($v!=='' || $encodeEmptyStrings) { if($results!=='') $results.=','; $results.=self::encode($v,$toMap, $encodeEmptyStrings); } } return '['.$results.']'; } } else if(is_integer($value)) return "$value"; else if(is_float($value)) { switch($value) { case -INF: return 'Number.NEGATIVE_INFINITY'; break; case INF: return 'Number.POSITIVE_INFINITY'; break; default: $locale=localeConv(); if($locale['decimal_point']=='.') return "$value"; else return str_replace($locale['decimal_point'], '.', "$value"); break; } } else if(is_object($value)) if ($value instanceof TJavaScriptLiteral) return $value->toJavaScriptLiteral(); else return self::encode(get_object_vars($value),$toMap); else if($value===null) return 'null'; else return ''; } public static function jsonEncode($value, $options = 0) { if (($g=Prado::getApplication()->getGlobalization(false))!==null && strtoupper($enc=$g->getCharset())!='UTF-8') { self::convertToUtf8($value, $enc); } $s = @json_encode($value,$options); self::checkJsonError(); return $s; } private static function convertToUtf8(&$value, $sourceEncoding) { if(is_string($value)) $value=iconv($sourceEncoding, 'UTF-8', $value); else if (is_array($value)) { foreach($value as &$element) self::convertToUtf8($element, $sourceEncoding); } } public static function jsonDecode($value, $assoc = false, $depth = 512) { $s= @json_decode($value, $assoc, $depth); self::checkJsonError(); return $s; } private static function checkJsonError() { switch ($err = json_last_error()) { case JSON_ERROR_NONE: return; break; case JSON_ERROR_DEPTH: $msg = 'Maximum stack depth exceeded'; break; case JSON_ERROR_STATE_MISMATCH: $msg = 'Underflow or the modes mismatch'; break; case JSON_ERROR_CTRL_CHAR: $msg = 'Unexpected control character found'; break; case JSON_ERROR_SYNTAX: $msg = 'Syntax error, malformed JSON'; break; case JSON_ERROR_UTF8: $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; break; default: $msg = 'Unknown error'; break; } throw new Exception("JSON error ($err): $msg"); } public static function JSMin($code) { Prado::using('System.Web.Javascripts.JSMin'); return JSMin::minify($code); } } class TUrlManager extends TModule { public function constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems) { $url=$serviceID.'='.urlencode($serviceParam); $amp=$encodeAmpersand?'&':'&'; $request=$this->getRequest(); if(is_array($getItems) || $getItems instanceof Traversable) { if($encodeGetItems) { foreach($getItems as $name=>$value) { if(is_array($value)) { $name=urlencode($name.'[]'); foreach($value as $v) $url.=$amp.$name.'='.urlencode($v); } else $url.=$amp.urlencode($name).'='.urlencode($value); } } else { foreach($getItems as $name=>$value) { if(is_array($value)) { foreach($value as $v) $url.=$amp.$name.'[]='.$v; } else $url.=$amp.$name.'='.$value; } } } switch($request->getUrlFormat()) { case THttpRequestUrlFormat::Path: return $request->getApplicationUrl().'/'.strtr($url,array($amp=>'/','?'=>'/','='=>$request->getUrlParamSeparator())); case THttpRequestUrlFormat::HiddenPath: return rtrim(dirname($request->getApplicationUrl()), '/').'/'.strtr($url,array($amp=>'/','?'=>'/','='=>$request->getUrlParamSeparator())); default: return $request->getApplicationUrl().'?'.$url; } } public function parseUrl() { $request=$this->getRequest(); $pathInfo=trim($request->getPathInfo(),'/'); if(($request->getUrlFormat()===THttpRequestUrlFormat::Path || $request->getUrlFormat()===THttpRequestUrlFormat::HiddenPath) && $pathInfo!=='') { $separator=$request->getUrlParamSeparator(); $paths=explode('/',$pathInfo); $getVariables=array(); foreach($paths as $path) { if(($path=trim($path))!=='') { if(($pos=strpos($path,$separator))!==false) { $name=substr($path,0,$pos); $value=substr($path,$pos+1); if(($pos=strpos($name,'[]'))!==false) $getVariables[substr($name,0,$pos)][]=$value; else $getVariables[$name]=$value; } else $getVariables[$path]=''; } } return $getVariables; } else return array(); } } class THttpRequest extends TApplicationComponent implements IteratorAggregate,ArrayAccess,Countable,IModule { const CGIFIX__PATH_INFO = 1; const CGIFIX__SCRIPT_NAME = 2; private $_urlManager=null; private $_urlManagerID=''; private $_separator=','; private $_serviceID=null; private $_serviceParam=null; private $_cookies=null; private $_requestUri; private $_pathInfo; private $_cookieOnly=null; private $_urlFormat=THttpRequestUrlFormat::Get; private $_services; private $_requestResolved=false; private $_enableCookieValidation=false; private $_cgiFix=0; private $_enableCache=false; private $_url=null; private $_id; private $_items=array(); public function getID() { return $this->_id; } public function setID($value) { $this->_id=$value; } public function init($config) { if(php_sapi_name()==='cli') { $_SERVER['REMOTE_ADDR']='127.0.0.1'; $_SERVER['REQUEST_METHOD']='GET'; $_SERVER['SERVER_NAME']='localhost'; $_SERVER['SERVER_PORT']=80; $_SERVER['HTTP_USER_AGENT']=''; } if(isset($_SERVER['REQUEST_URI'])) $this->_requestUri=$_SERVER['REQUEST_URI']; else $this->_requestUri=$_SERVER['SCRIPT_NAME'].(empty($_SERVER['QUERY_STRING'])?'':'?'.$_SERVER['QUERY_STRING']); if($this->_cgiFix&self::CGIFIX__PATH_INFO && isset($_SERVER['ORIG_PATH_INFO'])) $this->_pathInfo=substr($_SERVER['ORIG_PATH_INFO'], strlen($_SERVER['SCRIPT_NAME'])); elseif(isset($_SERVER['PATH_INFO'])) $this->_pathInfo=$_SERVER['PATH_INFO']; else if(strpos($_SERVER['PHP_SELF'],$_SERVER['SCRIPT_NAME'])===0 && $_SERVER['PHP_SELF']!==$_SERVER['SCRIPT_NAME']) $this->_pathInfo=substr($_SERVER['PHP_SELF'],strlen($_SERVER['SCRIPT_NAME'])); else $this->_pathInfo=''; if(get_magic_quotes_gpc()) { if(isset($_GET)) $_GET=$this->stripSlashes($_GET); if(isset($_POST)) $_POST=$this->stripSlashes($_POST); if(isset($_REQUEST)) $_REQUEST=$this->stripSlashes($_REQUEST); if(isset($_COOKIE)) $_COOKIE=$this->stripSlashes($_COOKIE); } $this->getApplication()->setRequest($this); } public function stripSlashes(&$data) { return is_array($data)?array_map(array($this,'stripSlashes'),$data):stripslashes($data); } public function getUrl() { if($this->_url===null) { $secure=$this->getIsSecureConnection(); $url=$secure?'https://':'http://'; if(empty($_SERVER['HTTP_HOST'])) { $url.=$_SERVER['SERVER_NAME']; $port=$_SERVER['SERVER_PORT']; if(($port!=80 && !$secure) || ($port!=443 && $secure)) $url.=':'.$port; } else $url.=$_SERVER['HTTP_HOST']; $url.=$this->getRequestUri(); $this->_url=new TUri($url); } return $this->_url; } public function setEnableCache($value) { $this->_enableCache = TPropertyValue::ensureBoolean($value); } public function getEnableCache() { return $this->_enableCache; } protected function getCacheKey() { return $this->getID(); } protected function cacheUrlManager($manager) { if($this->getEnableCache()) { $cache = $this->getApplication()->getCache(); if($cache !== null) { $dependencies = null; if($this->getApplication()->getMode() !== TApplicationMode::Performance) if ($manager instanceof TUrlMapping && $fn = $manager->getConfigFile()) { $fn = Prado::getPathOfNamespace($fn,$this->getApplication()->getConfigurationFileExt()); $dependencies = new TFileCacheDependency($fn); } return $cache->set($this->getCacheKey(), $manager, 0, $dependencies); } } return false; } protected function loadCachedUrlManager() { if($this->getEnableCache()) { $cache = $this->getApplication()->getCache(); if($cache !== null) { $manager = $cache->get($this->getCacheKey()); if($manager instanceof TUrlManager) return $manager; } } return null; } public function getUrlManager() { return $this->_urlManagerID; } public function setUrlManager($value) { $this->_urlManagerID=$value; } public function getUrlManagerModule() { if($this->_urlManager===null) { if(($this->_urlManager = $this->loadCachedUrlManager())===null) { if(empty($this->_urlManagerID)) { $this->_urlManager=new TUrlManager; $this->_urlManager->init(null); } else { $this->_urlManager=$this->getApplication()->getModule($this->_urlManagerID); if($this->_urlManager===null) throw new TConfigurationException('httprequest_urlmanager_inexist',$this->_urlManagerID); if(!($this->_urlManager instanceof TUrlManager)) throw new TConfigurationException('httprequest_urlmanager_invalid',$this->_urlManagerID); } $this->cacheUrlManager($this->_urlManager); } } return $this->_urlManager; } public function getUrlFormat() { return $this->_urlFormat; } public function setUrlFormat($value) { $this->_urlFormat=TPropertyValue::ensureEnum($value,'THttpRequestUrlFormat'); } public function getUrlParamSeparator() { return $this->_separator; } public function setUrlParamSeparator($value) { if(strlen($value)===1) $this->_separator=$value; else throw new TInvalidDataValueException('httprequest_separator_invalid'); } public function getRequestType() { return isset($_SERVER['REQUEST_METHOD'])?$_SERVER['REQUEST_METHOD']:null; } public function getContentType($mimetypeOnly = true) { if(!isset($_SERVER['CONTENT_TYPE'])) return null; if($mimetypeOnly === true && ($_pos = strpos(';', $_SERVER['CONTENT_TYPE'])) !== false) return substr($_SERVER['CONTENT_TYPE'], 0, $_pos); return $_SERVER['CONTENT_TYPE']; } public function getIsSecureConnection() { return isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'],'off'); } public function getPathInfo() { return $this->_pathInfo; } public function getQueryString() { return isset($_SERVER['QUERY_STRING'])?$_SERVER['QUERY_STRING']:null; } public function getHttpProtocolVersion() { return isset($_SERVER['SERVER_PROTOCOL'])?$_SERVER['SERVER_PROTOCOL']:null; } public function getHeaders($case=null) { static $result; if($result === null && function_exists('apache_request_headers')) { $result = apache_request_headers(); } elseif($result === null) { $result = array(); foreach($_SERVER as $key=>$value) { if(strncasecmp($key, 'HTTP_', 5) !== 0) continue; $key = str_replace(' ','-', ucwords(strtolower(str_replace('_',' ', substr($key, 5))))); $result[$key] = $value; } } if($case !== null) return array_change_key_case($result, $case); return $result; } public function getRequestUri() { return $this->_requestUri; } public function getBaseUrl($forceSecureConnection=null) { $url=$this->getUrl(); $scheme=($forceSecureConnection)?"https": (($forceSecureConnection === null)?$url->getScheme():'http'); $host=$url->getHost(); if (($port=$url->getPort())) $host.=':'.$port; return $scheme.'://'.$host; } public function getApplicationUrl() { if($this->_cgiFix&self::CGIFIX__SCRIPT_NAME && isset($_SERVER['ORIG_SCRIPT_NAME'])) return $_SERVER['ORIG_SCRIPT_NAME']; return isset($_SERVER['SCRIPT_NAME'])?$_SERVER['SCRIPT_NAME']:null; } public function getAbsoluteApplicationUrl($forceSecureConnection=null) { return $this->getBaseUrl($forceSecureConnection) . $this->getApplicationUrl(); } public function getApplicationFilePath() { return realpath(isset($_SERVER['SCRIPT_FILENAME'])?$_SERVER['SCRIPT_FILENAME']:null); } public function getServerName() { return isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:null; } public function getServerPort() { return isset($_SERVER['SERVER_PORT'])?$_SERVER['SERVER_PORT']:null; } public function getUrlReferrer() { return isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:null; } public function getBrowser() { try { return get_browser(); } catch(TPhpErrorException $e) { throw new TConfigurationException('httprequest_browscap_required'); } } public function getUserAgent() { return isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:null; } public function getUserHostAddress() { return isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:null; } public function getUserHost() { return isset($_SERVER['REMOTE_HOST'])?$_SERVER['REMOTE_HOST']:null; } public function getAcceptTypes() { return isset($_SERVER['HTTP_ACCEPT'])?$_SERVER['HTTP_ACCEPT']:null; } public function getUserLanguages() { return Prado::getUserLanguages(); } public function getEnableCookieValidation() { return $this->_enableCookieValidation; } public function setEnableCookieValidation($value) { $this->_enableCookieValidation=TPropertyValue::ensureBoolean($value); } public function getCgiFix() { return $this->_cgiFix; } public function setCgiFix($value) { $this->_cgiFix=TPropertyValue::ensureInteger($value); } public function getCookies() { if($this->_cookies===null) { $this->_cookies=new THttpCookieCollection; if($this->getEnableCookieValidation()) { $sm=$this->getApplication()->getSecurityManager(); foreach($_COOKIE as $key=>$value) { if(($value=$sm->validateData($value))!==false) $this->_cookies->add(new THttpCookie($key,$value)); } } else { foreach($_COOKIE as $key=>$value) $this->_cookies->add(new THttpCookie($key,$value)); } } return $this->_cookies; } public function getUploadedFiles() { return $_FILES; } public function getServerVariables() { return $_SERVER; } public function getEnvironmentVariables() { return $_ENV; } public function constructUrl($serviceID,$serviceParam,$getItems=null,$encodeAmpersand=true,$encodeGetItems=true) { if ($this->_cookieOnly===null) $this->_cookieOnly=(int)ini_get('session.use_cookies') && (int)ini_get('session.use_only_cookies'); $url=$this->getUrlManagerModule()->constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems); if(defined('SID') && SID != '' && !$this->_cookieOnly) return $url . (strpos($url,'?')===false? '?' : ($encodeAmpersand?'&':'&')) . SID; else return $url; } protected function parseUrl() { return $this->getUrlManagerModule()->parseUrl(); } public function resolveRequest($serviceIDs) { $getParams=$this->parseUrl(); foreach($getParams as $name=>$value) $_GET[$name]=$value; $this->_items=array_merge($_GET,$_POST); $this->_requestResolved=true; foreach($serviceIDs as $serviceID) { if($this->contains($serviceID)) { $this->setServiceID($serviceID); $this->setServiceParameter($this->itemAt($serviceID)); return $serviceID; } } return null; } public function getRequestResolved() { return $this->_requestResolved; } public function getServiceID() { return $this->_serviceID; } public function setServiceID($value) { $this->_serviceID=$value; } public function getServiceParameter() { return $this->_serviceParam; } public function setServiceParameter($value) { $this->_serviceParam=$value; } public function getIterator() { return new ArrayIterator($this->_items); } public function getCount() { return count($this->_items); } public function count() { return $this->getCount(); } public function getKeys() { return array_keys($this->_items); } public function itemAt($key) { return isset($this->_items[$key]) ? $this->_items[$key] : null; } public function add($key,$value) { $this->_items[$key]=$value; } public function remove($key) { if(isset($this->_items[$key]) || array_key_exists($key,$this->_items)) { $value=$this->_items[$key]; unset($this->_items[$key]); return $value; } else return null; } public function clear() { foreach(array_keys($this->_items) as $key) $this->remove($key); } public function contains($key) { return isset($this->_items[$key]) || array_key_exists($key,$this->_items); } public function toArray() { return $this->_items; } public function offsetExists($offset) { return $this->contains($offset); } public function offsetGet($offset) { return $this->itemAt($offset); } public function offsetSet($offset,$item) { $this->add($offset,$item); } public function offsetUnset($offset) { $this->remove($offset); } } class THttpCookieCollection extends TList { private $_o; public function __construct($owner=null) { $this->_o=$owner; } public function insertAt($index,$item) { if($item instanceof THttpCookie) { parent::insertAt($index,$item); if($this->_o instanceof THttpResponse) $this->_o->addCookie($item); } else throw new TInvalidDataTypeException('httpcookiecollection_httpcookie_required'); } public function removeAt($index) { $item=parent::removeAt($index); if($this->_o instanceof THttpResponse) $this->_o->removeCookie($item); return $item; } public function itemAt($index) { if(is_integer($index)) return parent::itemAt($index); else return $this->findCookieByName($index); } public function findCookieByName($name) { foreach($this as $cookie) if($cookie->getName()===$name) return $cookie; return null; } } class THttpCookie extends TComponent { private $_domain=''; private $_name; private $_value=''; private $_expire=0; private $_path='/'; private $_secure=false; private $_httpOnly=false; public function __construct($name,$value) { $this->_name=$name; $this->_value=$value; } public function getDomain() { return $this->_domain; } public function setDomain($value) { $this->_domain=$value; } public function getExpire() { return $this->_expire; } public function setExpire($value) { $this->_expire=TPropertyValue::ensureInteger($value); } public function getHttpOnly() { return $this->_httpOnly; } public function setHttpOnly($value) { $this->_httpOnly = TPropertyValue::ensureBoolean($value); } public function getName() { return $this->_name; } public function setName($value) { $this->_name=$value; } public function getValue() { return $this->_value; } public function setValue($value) { $this->_value=$value; } public function getPath() { return $this->_path; } public function setPath($value) { $this->_path=$value; } public function getSecure() { return $this->_secure; } public function setSecure($value) { $this->_secure=TPropertyValue::ensureBoolean($value); } } class TUri extends TComponent { private static $_defaultPort=array( 'ftp'=>21, 'gopher'=>70, 'http'=>80, 'https'=>443, 'news'=>119, 'nntp'=>119, 'wais'=>210, 'telnet'=>23 ); private $_scheme; private $_host; private $_port; private $_user; private $_pass; private $_path; private $_query; private $_fragment; private $_uri; public function __construct($uri) { if(($ret=@parse_url($uri))!==false) { $this->_scheme=isset($ret['scheme'])?$ret['scheme']:''; $this->_host=isset($ret['host'])?$ret['host']:''; $this->_port=isset($ret['port'])?$ret['port']:''; $this->_user=isset($ret['user'])?$ret['user']:''; $this->_pass=isset($ret['pass'])?$ret['pass']:''; $this->_path=isset($ret['path'])?$ret['path']:''; $this->_query=isset($ret['query'])?$ret['query']:''; $this->_fragment=isset($ret['fragment'])?$ret['fragment']:''; $this->_uri=$uri; } else { throw new TInvalidDataValueException('uri_format_invalid',$uri); } } public function getUri() { return $this->_uri; } public function getScheme() { return $this->_scheme; } public function getHost() { return $this->_host; } public function getPort() { return $this->_port; } public function getUser() { return $this->_user; } public function getPassword() { return $this->_pass; } public function getPath() { return $this->_path; } public function getQuery() { return $this->_query; } public function getFragment() { return $this->_fragment; } } class THttpRequestUrlFormat extends TEnumerable { const Get='Get'; const Path='Path'; const HiddenPath='HiddenPath'; } class THttpResponseAdapter extends TApplicationComponent { private $_response; public function __construct($response) { $this->_response=$response; } public function getResponse() { return $this->_response; } public function flushContent() { $this->_response->flushContent(); } public function httpRedirect($url) { $this->_response->httpRedirect($url); } public function createNewHtmlWriter($type, $writer) { return $this->_response->createNewHtmlWriter($type,$writer); } } class THttpResponse extends TModule implements ITextWriter { const DEFAULT_CONTENTTYPE = 'text/html'; const DEFAULT_CHARSET = 'UTF-8'; private static $HTTP_STATUS_CODES = array( 100 => 'Continue', 101 => 'Switching Protocols', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported' ); private $_bufferOutput=true; private $_initialized=false; private $_cookies=null; private $_status=200; private $_reason='OK'; private $_htmlWriterType='System.Web.UI.THtmlWriter'; private $_contentType=null; private $_charset=''; private $_adapter; private $_httpHeaderSent; private $_contentTypeHeaderSent; public function __destruct() { } public function setAdapter(THttpResponseAdapter $adapter) { $this->_adapter=$adapter; } public function getAdapter() { return $this->_adapter; } public function getHasAdapter() { return $this->_adapter!==null; } public function init($config) { if($this->_bufferOutput) ob_start(); $this->_initialized=true; $this->getApplication()->setResponse($this); } public function getCacheExpire() { return session_cache_expire(); } public function setCacheExpire($value) { session_cache_expire(TPropertyValue::ensureInteger($value)); } public function getCacheControl() { return session_cache_limiter(); } public function setCacheControl($value) { session_cache_limiter(TPropertyValue::ensureEnum($value,array('none','nocache','private','private_no_expire','public'))); } public function setContentType($type) { if ($this->_contentTypeHeaderSent) throw new Exception('Unable to alter content-type as it has been already sent'); $this->_contentType = $type; } public function getContentType() { return $this->_contentType; } public function getCharset() { return $this->_charset; } public function setCharset($charset) { $this->_charset = (strToLower($charset) === 'false') ? false : (string)$charset; } public function getBufferOutput() { return $this->_bufferOutput; } public function setBufferOutput($value) { if($this->_initialized) throw new TInvalidOperationException('httpresponse_bufferoutput_unchangeable'); else $this->_bufferOutput=TPropertyValue::ensureBoolean($value); } public function getStatusCode() { return $this->_status; } public function setStatusCode($status, $reason=null) { if ($this->_httpHeaderSent) throw new Exception('Unable to alter response as HTTP header already sent'); $status=TPropertyValue::ensureInteger($status); if(isset(self::$HTTP_STATUS_CODES[$status])) { $this->_reason=self::$HTTP_STATUS_CODES[$status]; }else{ if($reason===null || $reason==='') { throw new TInvalidDataValueException("response_status_reason_missing"); } $reason=TPropertyValue::ensureString($reason); if(strpos($reason, "\r")!=false || strpos($reason, "\n")!=false) { throw new TInvalidDataValueException("response_status_reason_barchars"); } $this->_reason=$reason; } $this->_status=$status; } public function getStatusReason() { return $this->_reason; } public function getCookies() { if($this->_cookies===null) $this->_cookies=new THttpCookieCollection($this); return $this->_cookies; } public function write($str) { if (!$this->_bufferOutput and !$this->_httpHeaderSent) $this->ensureHeadersSent(); echo $str; } public function writeFile($fileName,$content=null,$mimeType=null,$headers=null,$forceDownload=true,$clientFileName=null,$fileSize=null) { static $defaultMimeTypes=array( 'css'=>'text/css', 'gif'=>'image/gif', 'png'=>'image/png', 'jpg'=>'image/jpeg', 'jpeg'=>'image/jpeg', 'htm'=>'text/html', 'html'=>'text/html', 'js'=>'javascript/js', 'pdf'=>'application/pdf', 'xls'=>'application/vnd.ms-excel', ); if($mimeType===null) { $mimeType='text/plain'; if(function_exists('mime_content_type')) $mimeType=mime_content_type($fileName); else if(($ext=strrchr($fileName,'.'))!==false) { $ext=substr($ext,1); if(isset($defaultMimeTypes[$ext])) $mimeType=$defaultMimeTypes[$ext]; } } if($clientFileName===null) $clientFileName=basename($fileName); else $clientFileName=basename($clientFileName); if($fileSize===null || $fileSize < 0) $fileSize = ($content===null?filesize($fileName):strlen($content)); $this->sendHttpHeader(); if(is_array($headers)) { foreach($headers as $h) header($h); } else { header('Pragma: public'); header('Expires: 0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header("Content-Type: $mimeType"); $this->_contentTypeHeaderSent = true; } header('Content-Length: '.$fileSize); header("Content-Disposition: " . ($forceDownload ? 'attachment' : 'inline') . "; filename=\"$clientFileName\""); header('Content-Transfer-Encoding: binary'); if($content===null) readfile($fileName); else echo $content; } public function redirect($url) { if($this->getHasAdapter()) $this->_adapter->httpRedirect($url); else $this->httpRedirect($url); } public function httpRedirect($url) { $this->ensureHeadersSent(); if($url[0]==='/') $url=$this->getRequest()->getBaseUrl().$url; if ($this->_status >= 300 && $this->_status < 400) header('Location: '.str_replace('&','&',$url), true, $this->_status); else header('Location: '.str_replace('&','&',$url)); if(!$this->getApplication()->getRequestCompleted()) $this->getApplication()->onEndRequest(); exit(); } public function reload() { $this->redirect($this->getRequest()->getRequestUri()); } public function flush($continueBuffering = true) { if($this->getHasAdapter()) $this->_adapter->flushContent($continueBuffering); else $this->flushContent($continueBuffering); } public function ensureHeadersSent() { $this->ensureHttpHeaderSent(); $this->ensureContentTypeHeaderSent(); } public function flushContent($continueBuffering = true) { $this->ensureHeadersSent(); if($this->_bufferOutput) { if (ob_get_length()>0) { if (!$continueBuffering) { $this->_bufferOutput = false; ob_end_flush(); } else ob_flush(); flush(); } } else flush(); } protected function ensureHttpHeaderSent() { if (!$this->_httpHeaderSent) $this->sendHttpHeader(); } protected function sendHttpHeader() { $protocol=$this->getRequest()->getHttpProtocolVersion(); if($this->getRequest()->getHttpProtocolVersion() === null) $protocol='HTTP/1.1'; $phpSapiName = substr(php_sapi_name(), 0, 3); $cgi = $phpSapiName == 'cgi' || $phpSapiName == 'fpm'; header(($cgi ? 'Status:' : $protocol).' '.$this->_status.' '.$this->_reason, true, TPropertyValue::ensureInteger($this->_status)); $this->_httpHeaderSent = true; } protected function ensureContentTypeHeaderSent() { if (!$this->_contentTypeHeaderSent) $this->sendContentTypeHeader(); } protected function sendContentTypeHeader() { $contentType=$this->_contentType===null?self::DEFAULT_CONTENTTYPE:$this->_contentType; $charset=$this->getCharset(); if($charset === false) { $this->appendHeader('Content-Type: '.$contentType); return; } if($charset==='' && ($globalization=$this->getApplication()->getGlobalization(false))!==null) $charset=$globalization->getCharset(); if($charset==='') $charset = self::DEFAULT_CHARSET; $this->appendHeader('Content-Type: '.$contentType.';charset='.$charset); $this->_contentTypeHeaderSent = true; } public function getContents() { return $this->_bufferOutput?ob_get_contents():''; } public function clear() { if($this->_bufferOutput) ob_clean(); } public function getHeaders($case=null) { $result = array(); $headers = headers_list(); foreach($headers as $header) { $tmp = explode(':', $header); $key = trim(array_shift($tmp)); $value = trim(implode(':', $tmp)); if(isset($result[$key])) $result[$key] .= ', ' . $value; else $result[$key] = $value; } if($case !== null) return array_change_key_case($result, $case); return $result; } public function appendHeader($value, $replace=true) { header($value, $replace); } public function appendLog($message,$messageType=0,$destination='',$extraHeaders='') { error_log($message,$messageType,$destination,$extraHeaders); } public function addCookie($cookie) { $request=$this->getRequest(); if($request->getEnableCookieValidation()) { $value=$this->getApplication()->getSecurityManager()->hashData($cookie->getValue()); setcookie( $cookie->getName(), $value, $cookie->getExpire(), $cookie->getPath(), $cookie->getDomain(), $cookie->getSecure(), $cookie->getHttpOnly() ); } else { setcookie( $cookie->getName(), $cookie->getValue(), $cookie->getExpire(), $cookie->getPath(), $cookie->getDomain(), $cookie->getSecure(), $cookie->getHttpOnly() ); } } public function removeCookie($cookie) { setcookie( $cookie->getName(), null, 0, $cookie->getPath(), $cookie->getDomain(), $cookie->getSecure(), $cookie->getHttpOnly() ); } public function getHtmlWriterType() { return $this->_htmlWriterType; } public function setHtmlWriterType($value) { $this->_htmlWriterType=$value; } public function createHtmlWriter($type=null) { if($type===null) $type=$this->getHtmlWriterType(); if($this->getHasAdapter()) return $this->_adapter->createNewHtmlWriter($type, $this); else return $this->createNewHtmlWriter($type, $this); } public function createNewHtmlWriter($type, $writer) { return Prado::createComponent($type, $writer); } } class THttpSession extends TApplicationComponent implements IteratorAggregate,ArrayAccess,Countable,IModule { private $_initialized=false; private $_started=false; private $_autoStart=false; private $_cookie=null; private $_id; private $_customStorage=false; public function getID() { return $this->_id; } public function setID($value) { $this->_id=$value; } public function init($config) { if($this->_autoStart) $this->open(); $this->_initialized=true; $this->getApplication()->setSession($this); register_shutdown_function(array($this, "close")); } public function open() { if(!$this->_started) { if($this->_customStorage) session_set_save_handler(array($this,'_open'),array($this,'_close'),array($this,'_read'),array($this,'_write'),array($this,'_destroy'),array($this,'_gc')); if($this->_cookie!==null) session_set_cookie_params($this->_cookie->getExpire(),$this->_cookie->getPath(),$this->_cookie->getDomain(),$this->_cookie->getSecure(),$this->_cookie->getHttpOnly()); if(ini_get('session.auto_start')!=='1') session_start(); $this->_started=true; } } public function close() { if($this->_started) { session_write_close(); $this->_started=false; } } public function destroy() { if($this->_started) { session_destroy(); $this->_started=false; } } public function regenerate($deleteOld=false) { $old = $this->getSessionID(); session_regenerate_id($deleteOld); return $old; } public function getIsStarted() { return $this->_started; } public function getSessionID() { return session_id(); } public function setSessionID($value) { if($this->_started) throw new TInvalidOperationException('httpsession_sessionid_unchangeable'); else session_id($value); } public function getSessionName() { return session_name(); } public function setSessionName($value) { if($this->_started) throw new TInvalidOperationException('httpsession_sessionname_unchangeable'); else if(ctype_alnum($value)) session_name($value); else throw new TInvalidDataValueException('httpsession_sessionname_invalid',$value); } public function getSavePath() { return session_save_path(); } public function setSavePath($value) { if($this->_started) throw new TInvalidOperationException('httpsession_savepath_unchangeable'); else if(is_dir($value)) session_save_path($value); else throw new TInvalidDataValueException('httpsession_savepath_invalid',$value); } public function getUseCustomStorage() { return $this->_customStorage; } public function setUseCustomStorage($value) { $this->_customStorage=TPropertyValue::ensureBoolean($value); } public function getCookie() { if($this->_cookie===null) $this->_cookie=new THttpCookie($this->getSessionName(),$this->getSessionID()); return $this->_cookie; } public function getCookieMode() { if(ini_get('session.use_cookies')==='0') return THttpSessionCookieMode::None; else if(ini_get('session.use_only_cookies')==='0') return THttpSessionCookieMode::Allow; else return THttpSessionCookieMode::Only; } public function setCookieMode($value) { if($this->_started) throw new TInvalidOperationException('httpsession_cookiemode_unchangeable'); else { $value=TPropertyValue::ensureEnum($value,'THttpSessionCookieMode'); if($value===THttpSessionCookieMode::None) { ini_set('session.use_cookies','0'); ini_set('session.use_only_cookies','0'); } else if($value===THttpSessionCookieMode::Allow) { ini_set('session.use_cookies','1'); ini_set('session.use_only_cookies','0'); } else { ini_set('session.use_cookies','1'); ini_set('session.use_only_cookies','1'); ini_set('session.use_trans_sid', 0); } } } public function getAutoStart() { return $this->_autoStart; } public function setAutoStart($value) { if($this->_initialized) throw new TInvalidOperationException('httpsession_autostart_unchangeable'); else $this->_autoStart=TPropertyValue::ensureBoolean($value); } public function getGCProbability() { return TPropertyValue::ensureInteger(ini_get('session.gc_probability')); } public function setGCProbability($value) { if($this->_started) throw new TInvalidOperationException('httpsession_gcprobability_unchangeable'); else { $value=TPropertyValue::ensureInteger($value); if($value>=0 && $value<=100) { ini_set('session.gc_probability',$value); ini_set('session.gc_divisor','100'); } else throw new TInvalidDataValueException('httpsession_gcprobability_invalid',$value); } } public function getUseTransparentSessionID() { return ini_get('session.use_trans_sid')==='1'; } public function setUseTransparentSessionID($value) { if($this->_started) throw new TInvalidOperationException('httpsession_transid_unchangeable'); else { $value=TPropertyValue::ensureBoolean($value); if ($value && $this->getCookieMode()==THttpSessionCookieMode::Only) throw new TInvalidOperationException('httpsession_transid_cookieonly'); ini_set('session.use_trans_sid',$value?'1':'0'); } } public function getTimeout() { return TPropertyValue::ensureInteger(ini_get('session.gc_maxlifetime')); } public function setTimeout($value) { if($this->_started) throw new TInvalidOperationException('httpsession_maxlifetime_unchangeable'); else ini_set('session.gc_maxlifetime',$value); } public function _open($savePath,$sessionName) { return true; } public function _close() { return true; } public function _read($id) { return ''; } public function _write($id,$data) { return true; } public function _destroy($id) { return true; } public function _gc($maxLifetime) { return true; } public function getIterator() { return new TSessionIterator; } public function getCount() { return count($_SESSION); } public function count() { return $this->getCount(); } public function getKeys() { return array_keys($_SESSION); } public function itemAt($key) { return isset($_SESSION[$key]) ? $_SESSION[$key] : null; } public function add($key,$value) { $_SESSION[$key]=$value; } public function remove($key) { if(isset($_SESSION[$key])) { $value=$_SESSION[$key]; unset($_SESSION[$key]); return $value; } else return null; } public function clear() { foreach(array_keys($_SESSION) as $key) unset($_SESSION[$key]); } public function contains($key) { return isset($_SESSION[$key]); } public function toArray() { return $_SESSION; } public function offsetExists($offset) { return isset($_SESSION[$offset]); } public function offsetGet($offset) { return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null; } public function offsetSet($offset,$item) { $_SESSION[$offset]=$item; } public function offsetUnset($offset) { unset($_SESSION[$offset]); } } class TSessionIterator implements Iterator { private $_keys; private $_key; public function __construct() { $this->_keys=array_keys($_SESSION); } public function rewind() { $this->_key=reset($this->_keys); } public function key() { return $this->_key; } public function current() { return isset($_SESSION[$this->_key])?$_SESSION[$this->_key]:null; } public function next() { do { $this->_key=next($this->_keys); } while(!isset($_SESSION[$this->_key]) && $this->_key!==false); } public function valid() { return $this->_key!==false; } } class THttpSessionCookieMode extends TEnumerable { const None='None'; const Allow='Allow'; const Only='Only'; } Prado::using('System.Web.UI.WebControls.*'); class TAttributeCollection extends TMap { private $_caseSensitive=false; protected function __getZappableSleepProps(&$exprops) { parent::__getZappableSleepProps($exprops); if ($this->_caseSensitive===false) $exprops[] = "\0TAttributeCollection\0_caseSensitive"; } public function __get($name) { return $this->contains($name)?$this->itemAt($name):parent::__get($name); } public function __set($name,$value) { $this->add($name,$value); } public function getCaseSensitive() { return $this->_caseSensitive; } public function setCaseSensitive($value) { $this->_caseSensitive=TPropertyValue::ensureBoolean($value); } public function itemAt($key) { return parent::itemAt($this->_caseSensitive?$key:strtolower($key)); } public function add($key,$value) { parent::add($this->_caseSensitive?$key:strtolower($key),$value); } public function remove($key) { return parent::remove($this->_caseSensitive?$key:strtolower($key)); } public function contains($key) { return parent::contains($this->_caseSensitive?$key:strtolower($key)); } public function hasProperty($name) { return $this->contains($name) || parent::canGetProperty($name) || parent::canSetProperty($name); } public function canGetProperty($name) { return $this->contains($name) || parent::canGetProperty($name); } public function canSetProperty($name) { return true; } } class TControlAdapter extends TApplicationComponent { protected $_control; public function __construct($control) { $this->_control=$control; } public function getControl() { return $this->_control; } public function getPage() { return $this->_control?$this->_control->getPage():null; } public function createChildControls() { $this->_control->createChildControls(); } public function loadState() { $this->_control->loadState(); } public function saveState() { $this->_control->saveState(); } public function onInit($param) { $this->_control->onInit($param); } public function onLoad($param) { $this->_control->onLoad($param); } public function onPreRender($param) { $this->_control->onPreRender($param); } public function onUnload($param) { $this->_control->onUnload($param); } public function render($writer) { $this->_control->render($writer); } public function renderChildren($writer) { $this->_control->renderChildren($writer); } } class TControl extends TApplicationComponent implements IRenderable, IBindable { const ID_FORMAT='/^[a-zA-Z_]\\w*$/'; const ID_SEPARATOR='$'; const CLIENT_ID_SEPARATOR='_'; const AUTOMATIC_ID_PREFIX='ctl'; const CS_CONSTRUCTED=0; const CS_CHILD_INITIALIZED=1; const CS_INITIALIZED=2; const CS_STATE_LOADED=3; const CS_LOADED=4; const CS_PRERENDERED=5; const IS_ID_SET=0x01; const IS_DISABLE_VIEWSTATE=0x02; const IS_SKIN_APPLIED=0x04; const IS_STYLESHEET_APPLIED=0x08; const IS_DISABLE_THEMING=0x10; const IS_CHILD_CREATED=0x20; const IS_CREATING_CHILD=0x40; const RF_CONTROLS=0; const RF_CHILD_STATE=1; const RF_NAMED_CONTROLS=2; const RF_NAMED_CONTROLS_ID=3; const RF_SKIN_ID=4; const RF_DATA_BINDINGS=5; const RF_EVENTS=6; const RF_CONTROLSTATE=7; const RF_NAMED_OBJECTS=8; const RF_ADAPTER=9; const RF_AUTO_BINDINGS=10; private $_id=''; private $_uid; private $_parent; private $_page; private $_namingContainer; private $_tplControl; private $_viewState=array(); private $_tempState=array(); private $_trackViewState=true; private $_stage=0; private $_flags=0; private $_rf=array(); public function __get($name) { if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name])) return $this->_rf[self::RF_NAMED_OBJECTS][$name]; else return parent::__get($name); } public function getHasAdapter() { return isset($this->_rf[self::RF_ADAPTER]); } public function getAdapter() { return isset($this->_rf[self::RF_ADAPTER])?$this->_rf[self::RF_ADAPTER]:null; } public function setAdapter(TControlAdapter $adapter) { $this->_rf[self::RF_ADAPTER]=$adapter; } public function getParent() { return $this->_parent; } public function getNamingContainer() { if(!$this->_namingContainer && $this->_parent) { if($this->_parent instanceof INamingContainer) $this->_namingContainer=$this->_parent; else $this->_namingContainer=$this->_parent->getNamingContainer(); } return $this->_namingContainer; } public function getPage() { if(!$this->_page) { if($this->_parent) $this->_page=$this->_parent->getPage(); else if($this->_tplControl) $this->_page=$this->_tplControl->getPage(); } return $this->_page; } public function setPage($page) { $this->_page=$page; } public function setTemplateControl($control) { $this->_tplControl=$control; } public function getTemplateControl() { if(!$this->_tplControl && $this->_parent) $this->_tplControl=$this->_parent->getTemplateControl(); return $this->_tplControl; } public function getSourceTemplateControl() { $control=$this; while(($control instanceof TControl) && ($control=$control->getTemplateControl())!==null) { if(($control instanceof TTemplateControl) && $control->getIsSourceTemplateControl()) return $control; } return $this->getPage(); } protected function getControlStage() { return $this->_stage; } protected function setControlStage($value) { $this->_stage=$value; } public function getID($hideAutoID=true) { if($hideAutoID) return ($this->_flags & self::IS_ID_SET) ? $this->_id : ''; else return $this->_id; } public function setID($id) { if(!preg_match(self::ID_FORMAT,$id)) throw new TInvalidDataValueException('control_id_invalid',get_class($this),$id); $this->_id=$id; $this->_flags |= self::IS_ID_SET; $this->clearCachedUniqueID($this instanceof INamingContainer); if($this->_namingContainer) $this->_namingContainer->clearNameTable(); } public function getUniqueID() { if($this->_uid==='' || $this->_uid===null) { $this->_uid=''; if($namingContainer=$this->getNamingContainer()) { if($this->getPage()===$namingContainer) return ($this->_uid=$this->_id); else if(($prefix=$namingContainer->getUniqueID())==='') return $this->_id; else return ($this->_uid=$prefix.self::ID_SEPARATOR.$this->_id); } else return $this->_id; } else return $this->_uid; } public function focus() { $this->getPage()->setFocus($this); } public function getClientID() { return strtr($this->getUniqueID(),self::ID_SEPARATOR,self::CLIENT_ID_SEPARATOR); } public static function convertUniqueIdToClientId($uniqueID) { return strtr($uniqueID,self::ID_SEPARATOR,self::CLIENT_ID_SEPARATOR); } public function getSkinID() { return isset($this->_rf[self::RF_SKIN_ID])?$this->_rf[self::RF_SKIN_ID]:''; } public function setSkinID($value) { if(($this->_flags & self::IS_SKIN_APPLIED) || $this->_stage>=self::CS_CHILD_INITIALIZED) throw new TInvalidOperationException('control_skinid_unchangeable',get_class($this)); else $this->_rf[self::RF_SKIN_ID]=$value; } public function getIsSkinApplied() { return ($this->_flags & self::IS_SKIN_APPLIED); } public function getEnableTheming() { if($this->_flags & self::IS_DISABLE_THEMING) return false; else return $this->_parent?$this->_parent->getEnableTheming():true; } public function setEnableTheming($value) { if($this->_stage>=self::CS_CHILD_INITIALIZED) throw new TInvalidOperationException('control_enabletheming_unchangeable',get_class($this),$this->getUniqueID()); else if(TPropertyValue::ensureBoolean($value)) $this->_flags &= ~self::IS_DISABLE_THEMING; else $this->_flags |= self::IS_DISABLE_THEMING; } public function getCustomData() { return $this->getViewState('CustomData',null); } public function setCustomData($value) { $this->setViewState('CustomData',$value,null); } public function getHasControls() { return isset($this->_rf[self::RF_CONTROLS]) && $this->_rf[self::RF_CONTROLS]->getCount()>0; } public function getControls() { if(!isset($this->_rf[self::RF_CONTROLS])) $this->_rf[self::RF_CONTROLS]=$this->createControlCollection(); return $this->_rf[self::RF_CONTROLS]; } protected function createControlCollection() { return $this->getAllowChildControls()?new TControlCollection($this):new TEmptyControlCollection($this); } public function getVisible($checkParents=true) { if($checkParents) { for($control=$this;$control;$control=$control->_parent) if(!$control->getVisible(false)) return false; return true; } else return $this->getViewState('Visible',true); } public function setVisible($value) { $this->setViewState('Visible',TPropertyValue::ensureBoolean($value),true); } public function getEnabled($checkParents=false) { if($checkParents) { for($control=$this;$control;$control=$control->_parent) if(!$control->getViewState('Enabled',true)) return false; return true; } else return $this->getViewState('Enabled',true); } public function setEnabled($value) { $this->setViewState('Enabled',TPropertyValue::ensureBoolean($value),true); } public function getHasAttributes() { if($attributes=$this->getViewState('Attributes',null)) return $attributes->getCount()>0; else return false; } public function getAttributes() { if($attributes=$this->getViewState('Attributes',null)) return $attributes; else { $attributes=new TAttributeCollection; $this->setViewState('Attributes',$attributes,null); return $attributes; } } public function hasAttribute($name) { if($attributes=$this->getViewState('Attributes',null)) return $attributes->contains($name); else return false; } public function getAttribute($name) { if($attributes=$this->getViewState('Attributes',null)) return $attributes->itemAt($name); else return null; } public function setAttribute($name,$value) { $this->getAttributes()->add($name,$value); } public function removeAttribute($name) { if($attributes=$this->getViewState('Attributes',null)) return $attributes->remove($name); else return null; } public function getEnableViewState($checkParents=false) { if($checkParents) { for($control=$this;$control!==null;$control=$control->getParent()) if($control->_flags & self::IS_DISABLE_VIEWSTATE) return false; return true; } else return !($this->_flags & self::IS_DISABLE_VIEWSTATE); } public function setEnableViewState($value) { if(TPropertyValue::ensureBoolean($value)) $this->_flags &= ~self::IS_DISABLE_VIEWSTATE; else $this->_flags |= self::IS_DISABLE_VIEWSTATE; } protected function getControlState($key,$defaultValue=null) { return isset($this->_rf[self::RF_CONTROLSTATE][$key])?$this->_rf[self::RF_CONTROLSTATE][$key]:$defaultValue; } protected function setControlState($key,$value,$defaultValue=null) { if($value===$defaultValue) unset($this->_rf[self::RF_CONTROLSTATE][$key]); else $this->_rf[self::RF_CONTROLSTATE][$key]=$value; } protected function clearControlState($key) { unset($this->_rf[self::RF_CONTROLSTATE][$key]); } public function trackViewState($enabled) { $this->_trackViewState=TPropertyValue::ensureBoolean($enabled); } public function getViewState($key,$defaultValue=null) { if(isset($this->_viewState[$key])) return $this->_viewState[$key]!==null?$this->_viewState[$key]:$defaultValue; else if(isset($this->_tempState[$key])) { if(is_object($this->_tempState[$key]) && $this->_trackViewState) $this->_viewState[$key]=$this->_tempState[$key]; return $this->_tempState[$key]; } else return $defaultValue; } public function setViewState($key,$value,$defaultValue=null) { if($this->_trackViewState) { unset($this->_tempState[$key]); if($value===$defaultValue) unset($this->_viewState[$key]); else $this->_viewState[$key]=$value; } else { unset($this->_viewState[$key]); if($value===$defaultValue) unset($this->_tempState[$key]); else $this->_tempState[$key]=$value; } } public function clearViewState($key) { unset($this->_viewState[$key]); unset($this->_tempState[$key]); } public function bindProperty($name,$expression) { $this->_rf[self::RF_DATA_BINDINGS][$name]=$expression; } public function unbindProperty($name) { unset($this->_rf[self::RF_DATA_BINDINGS][$name]); } public function autoBindProperty($name,$expression) { $this->_rf[self::RF_AUTO_BINDINGS][$name]=$expression; } public function dataBind() { $this->dataBindProperties(); $this->onDataBinding(null); $this->dataBindChildren(); } protected function dataBindProperties() { if(isset($this->_rf[self::RF_DATA_BINDINGS])) { if(($context=$this->getTemplateControl())===null) $context=$this; foreach($this->_rf[self::RF_DATA_BINDINGS] as $property=>$expression) $this->setSubProperty($property,$context->evaluateExpression($expression)); } } protected function autoDataBindProperties() { if(isset($this->_rf[self::RF_AUTO_BINDINGS])) { if(($context=$this->getTemplateControl())===null) $context=$this; foreach($this->_rf[self::RF_AUTO_BINDINGS] as $property=>$expression) $this->setSubProperty($property,$context->evaluateExpression($expression)); } } protected function dataBindChildren() { if(isset($this->_rf[self::RF_CONTROLS])) { foreach($this->_rf[self::RF_CONTROLS] as $control) if($control instanceof IBindable) $control->dataBind(); } } final protected function getChildControlsCreated() { return ($this->_flags & self::IS_CHILD_CREATED)!==0; } final protected function setChildControlsCreated($value) { if($value) $this->_flags |= self::IS_CHILD_CREATED; else { if($this->getHasControls() && ($this->_flags & self::IS_CHILD_CREATED)) $this->getControls()->clear(); $this->_flags &= ~self::IS_CHILD_CREATED; } } public function ensureChildControls() { if(!($this->_flags & self::IS_CHILD_CREATED) && !($this->_flags & self::IS_CREATING_CHILD)) { try { $this->_flags |= self::IS_CREATING_CHILD; if(isset($this->_rf[self::RF_ADAPTER])) $this->_rf[self::RF_ADAPTER]->createChildControls(); else $this->createChildControls(); $this->_flags &= ~self::IS_CREATING_CHILD; $this->_flags |= self::IS_CHILD_CREATED; } catch(Exception $e) { $this->_flags &= ~self::IS_CREATING_CHILD; $this->_flags |= self::IS_CHILD_CREATED; throw $e; } } } public function createChildControls() { } public function findControl($id) { $id=strtr($id,'.',self::ID_SEPARATOR); $container=($this instanceof INamingContainer)?$this:$this->getNamingContainer(); if(!$container || !$container->getHasControls()) return null; if(!isset($container->_rf[self::RF_NAMED_CONTROLS])) { $container->_rf[self::RF_NAMED_CONTROLS]=array(); $container->fillNameTable($container,$container->_rf[self::RF_CONTROLS]); } if(($pos=strpos($id,self::ID_SEPARATOR))===false) return isset($container->_rf[self::RF_NAMED_CONTROLS][$id])?$container->_rf[self::RF_NAMED_CONTROLS][$id]:null; else { $cid=substr($id,0,$pos); $sid=substr($id,$pos+1); if(isset($container->_rf[self::RF_NAMED_CONTROLS][$cid])) return $container->_rf[self::RF_NAMED_CONTROLS][$cid]->findControl($sid); else return null; } } public function findControlsByType($type,$strict=true) { $controls=array(); if($this->getHasControls()) { foreach($this->_rf[self::RF_CONTROLS] as $control) { if(is_object($control) && (get_class($control)===$type || (!$strict && ($control instanceof $type)))) $controls[]=$control; if(($control instanceof TControl) && $control->getHasControls()) $controls=array_merge($controls,$control->findControlsByType($type,$strict)); } } return $controls; } public function findControlsByID($id) { $controls=array(); if($this->getHasControls()) { foreach($this->_rf[self::RF_CONTROLS] as $control) { if($control instanceof TControl) { if($control->_id===$id) $controls[]=$control; $controls=array_merge($controls,$control->findControlsByID($id)); } } } return $controls; } public function clearNamingContainer() { unset($this->_rf[self::RF_NAMED_CONTROLS_ID]); $this->clearNameTable(); } public function registerObject($name,$object) { if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name])) throw new TInvalidOperationException('control_object_reregistered',$name); $this->_rf[self::RF_NAMED_OBJECTS][$name]=$object; } public function unregisterObject($name) { unset($this->_rf[self::RF_NAMED_OBJECTS][$name]); } public function isObjectRegistered($name) { return isset($this->_rf[self::RF_NAMED_OBJECTS][$name]); } public function getHasChildInitialized() { return $this->getControlStage() >= self::CS_CHILD_INITIALIZED; } public function getHasInitialized() { return $this->getControlStage() >= self::CS_INITIALIZED; } public function getHasLoadedPostData() { return $this->getControlStage() >= self::CS_STATE_LOADED; } public function getHasLoaded() { return $this->getControlStage() >= self::CS_LOADED; } public function getHasPreRendered() { return $this->getControlStage() >= self::CS_PRERENDERED; } public function getRegisteredObject($name) { return isset($this->_rf[self::RF_NAMED_OBJECTS][$name])?$this->_rf[self::RF_NAMED_OBJECTS][$name]:null; } public function getAllowChildControls() { return true; } public function addParsedObject($object) { $this->getControls()->add($object); } final protected function clearChildState() { unset($this->_rf[self::RF_CHILD_STATE]); } final protected function isDescendentOf($ancestor) { $control=$this; while($control!==$ancestor && $control->_parent) $control=$control->_parent; return $control===$ancestor; } public function addedControl($control) { if($control->_parent) $control->_parent->getControls()->remove($control); $control->_parent=$this; $control->_page=$this->getPage(); $namingContainer=($this instanceof INamingContainer)?$this:$this->_namingContainer; if($namingContainer) { $control->_namingContainer=$namingContainer; if($control->_id==='') $control->generateAutomaticID(); else $namingContainer->clearNameTable(); $control->clearCachedUniqueID($control instanceof INamingContainer); } if($this->_stage>=self::CS_CHILD_INITIALIZED) { $control->initRecursive($namingContainer); if($this->_stage>=self::CS_STATE_LOADED) { if(isset($this->_rf[self::RF_CHILD_STATE][$control->_id])) { $state=$this->_rf[self::RF_CHILD_STATE][$control->_id]; unset($this->_rf[self::RF_CHILD_STATE][$control->_id]); } else $state=null; $control->loadStateRecursive($state,!($this->_flags & self::IS_DISABLE_VIEWSTATE)); if($this->_stage>=self::CS_LOADED) { $control->loadRecursive(); if($this->_stage>=self::CS_PRERENDERED) $control->preRenderRecursive(); } } } } public function removedControl($control) { if($this->_namingContainer) $this->_namingContainer->clearNameTable(); $control->unloadRecursive(); $control->_parent=null; $control->_page=null; $control->_namingContainer=null; $control->_tplControl=null; if(!($control->_flags & self::IS_ID_SET)) $control->_id=''; else unset($this->_rf[self::RF_NAMED_OBJECTS][$control->_id]); $control->clearCachedUniqueID(true); } protected function initRecursive($namingContainer=null) { $this->ensureChildControls(); if($this->getHasControls()) { if($this instanceof INamingContainer) $namingContainer=$this; $page=$this->getPage(); foreach($this->_rf[self::RF_CONTROLS] as $control) { if($control instanceof TControl) { $control->_namingContainer=$namingContainer; $control->_page=$page; if($control->_id==='' && $namingContainer) $control->generateAutomaticID(); $control->initRecursive($namingContainer); } } } if($this->_stage_stage=self::CS_CHILD_INITIALIZED; if(($page=$this->getPage()) && $this->getEnableTheming() && !($this->_flags & self::IS_SKIN_APPLIED)) { $page->applyControlSkin($this); $this->_flags |= self::IS_SKIN_APPLIED; } if(isset($this->_rf[self::RF_ADAPTER])) $this->_rf[self::RF_ADAPTER]->onInit(null); else $this->onInit(null); $this->_stage=self::CS_INITIALIZED; } } protected function loadRecursive() { if($this->_stage_rf[self::RF_ADAPTER])) $this->_rf[self::RF_ADAPTER]->onLoad(null); else $this->onLoad(null); } if($this->getHasControls()) { foreach($this->_rf[self::RF_CONTROLS] as $control) { if($control instanceof TControl) $control->loadRecursive(); } } if($this->_stage_stage=self::CS_LOADED; } protected function preRenderRecursive() { $this->autoDataBindProperties(); if($this->getVisible(false)) { if(isset($this->_rf[self::RF_ADAPTER])) $this->_rf[self::RF_ADAPTER]->onPreRender(null); else $this->onPreRender(null); if($this->getHasControls()) { foreach($this->_rf[self::RF_CONTROLS] as $control) { if($control instanceof TControl) $control->preRenderRecursive(); else if($control instanceof TCompositeLiteral) $control->evaluateDynamicContent(); } } } $this->_stage=self::CS_PRERENDERED; } protected function unloadRecursive() { if(!($this->_flags & self::IS_ID_SET)) $this->_id=''; if($this->getHasControls()) { foreach($this->_rf[self::RF_CONTROLS] as $control) if($control instanceof TControl) $control->unloadRecursive(); } if(isset($this->_rf[self::RF_ADAPTER])) $this->_rf[self::RF_ADAPTER]->onUnload(null); else $this->onUnload(null); } public function onInit($param) { $this->raiseEvent('OnInit',$this,$param); } public function onLoad($param) { $this->raiseEvent('OnLoad',$this,$param); } public function onDataBinding($param) { $this->raiseEvent('OnDataBinding',$this,$param); } public function onUnload($param) { $this->raiseEvent('OnUnload',$this,$param); } public function onPreRender($param) { $this->raiseEvent('OnPreRender',$this,$param); } protected function raiseBubbleEvent($sender,$param) { $control=$this; while($control=$control->_parent) { if($control->bubbleEvent($sender,$param)) break; } } public function bubbleEvent($sender,$param) { return false; } public function broadcastEvent($name,$sender,$param) { $rootControl=(($page=$this->getPage())===null)?$this:$page; $rootControl->broadcastEventInternal($name,$sender,new TBroadcastEventParameter($name,$param)); } private function broadcastEventInternal($name,$sender,$param) { if($this->hasEvent($name)) $this->raiseEvent($name,$sender,$param->getParameter()); if($this instanceof IBroadcastEventReceiver) $this->broadcastEventReceived($sender,$param); if($this->getHasControls()) { foreach($this->_rf[self::RF_CONTROLS] as $control) { if($control instanceof TControl) $control->broadcastEventInternal($name,$sender,$param); } } } protected function traverseChildControls($param,$preCallback=null,$postCallback=null) { if($preCallback!==null) call_user_func($preCallback,$this,$param); if($this->getHasControls()) { foreach($this->_rf[self::RF_CONTROLS] as $control) { if($control instanceof TControl) { $control->traverseChildControls($param,$preCallback,$postCallback); } } } if($postCallback!==null) call_user_func($postCallback,$this,$param); } public function renderControl($writer) { if($this instanceof IActiveControl || $this->getVisible(false)) { if(isset($this->_rf[self::RF_ADAPTER])) $this->_rf[self::RF_ADAPTER]->render($writer); else $this->render($writer); } } public function render($writer) { $this->renderChildren($writer); } public function renderChildren($writer) { if($this->getHasControls()) { foreach($this->_rf[self::RF_CONTROLS] as $control) { if(is_string($control)) $writer->write($control); else if($control instanceof TControl) $control->renderControl($writer); else if($control instanceof IRenderable) $control->render($writer); } } } public function saveState() { } public function loadState() { } protected function loadStateRecursive(&$state,$needViewState=true) { if(is_array($state)) { $needViewState=($needViewState && !($this->_flags & self::IS_DISABLE_VIEWSTATE)); if(isset($state[1])) { $this->_rf[self::RF_CONTROLSTATE]=&$state[1]; unset($state[1]); } else unset($this->_rf[self::RF_CONTROLSTATE]); if($needViewState) { if(isset($state[0])) $this->_viewState=&$state[0]; else $this->_viewState=array(); } unset($state[0]); if($this->getHasControls()) { foreach($this->_rf[self::RF_CONTROLS] as $control) { if($control instanceof TControl) { if(isset($state[$control->_id])) { $control->loadStateRecursive($state[$control->_id],$needViewState); unset($state[$control->_id]); } } } } if(!empty($state)) $this->_rf[self::RF_CHILD_STATE]=&$state; } $this->_stage=self::CS_STATE_LOADED; if(isset($this->_rf[self::RF_ADAPTER])) $this->_rf[self::RF_ADAPTER]->loadState(); else $this->loadState(); } protected function &saveStateRecursive($needViewState=true) { if(isset($this->_rf[self::RF_ADAPTER])) $this->_rf[self::RF_ADAPTER]->saveState(); else $this->saveState(); $needViewState=($needViewState && !($this->_flags & self::IS_DISABLE_VIEWSTATE)); $state=array(); if($this->getHasControls()) { foreach($this->_rf[self::RF_CONTROLS] as $control) { if($control instanceof TControl) { if(count($tmp = &$control->saveStateRecursive($needViewState))) $state[$control->_id]=$tmp; } } } if($needViewState && !empty($this->_viewState)) $state[0]=&$this->_viewState; if(isset($this->_rf[self::RF_CONTROLSTATE])) $state[1]=&$this->_rf[self::RF_CONTROLSTATE]; return $state; } public function applyStyleSheetSkin($page) { if($page && !($this->_flags & self::IS_STYLESHEET_APPLIED)) { $page->applyControlStyleSheet($this); $this->_flags |= self::IS_STYLESHEET_APPLIED; } else if($this->_flags & self::IS_STYLESHEET_APPLIED) throw new TInvalidOperationException('control_stylesheet_applied',get_class($this)); } private function clearCachedUniqueID($recursive) { if($recursive && $this->_uid!==null && isset($this->_rf[self::RF_CONTROLS])) { foreach($this->_rf[self::RF_CONTROLS] as $control) if($control instanceof TControl) $control->clearCachedUniqueID($recursive); } $this->_uid=null; } private function generateAutomaticID() { $this->_flags &= ~self::IS_ID_SET; if(!isset($this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID])) $this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]=0; $id=$this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]++; $this->_id=self::AUTOMATIC_ID_PREFIX . $id; $this->_namingContainer->clearNameTable(); } private function clearNameTable() { unset($this->_rf[self::RF_NAMED_CONTROLS]); } private function fillNameTable($container,$controls) { foreach($controls as $control) { if($control instanceof TControl) { if($control->_id!=='') { if(isset($container->_rf[self::RF_NAMED_CONTROLS][$control->_id])) throw new TInvalidDataValueException('control_id_nonunique',get_class($control),$control->_id); else $container->_rf[self::RF_NAMED_CONTROLS][$control->_id]=$control; } if(!($control instanceof INamingContainer) && $control->getHasControls()) $this->fillNameTable($container,$control->_rf[self::RF_CONTROLS]); } } } } class TControlCollection extends TList { private $_o; public function __construct(TControl $owner,$readOnly=false) { $this->_o=$owner; parent::__construct(null,$readOnly); } protected function getOwner() { return $this->_o; } public function insertAt($index,$item) { if($item instanceof TControl) { parent::insertAt($index,$item); $this->_o->addedControl($item); } else if(is_string($item) || ($item instanceof IRenderable)) parent::insertAt($index,$item); else throw new TInvalidDataTypeException('controlcollection_control_required'); } public function removeAt($index) { $item=parent::removeAt($index); if($item instanceof TControl) $this->_o->removedControl($item); return $item; } public function clear() { parent::clear(); if($this->_o instanceof INamingContainer) $this->_o->clearNamingContainer(); } } class TEmptyControlCollection extends TControlCollection { public function __construct(TControl $owner) { parent::__construct($owner,true); } public function insertAt($index,$item) { if(!is_string($item)) parent::insertAt($index,$item); } } interface INamingContainer { } interface IPostBackEventHandler { public function raisePostBackEvent($param); } interface IPostBackDataHandler { public function loadPostData($key,$values); public function raisePostDataChangedEvent(); public function getDataChanged(); } interface IValidator { public function validate(); public function getIsValid(); public function setIsValid($value); public function getErrorMessage(); public function setErrorMessage($value); } interface IValidatable { public function getValidationPropertyValue(); public function getIsValid(); public function setIsValid($value); } interface IBroadcastEventReceiver { public function broadcastEventReceived($sender,$param); } interface ITheme { public function applySkin($control); } interface ITemplate { public function instantiateIn($parent); } interface IButtonControl { public function getText(); public function setText($value); public function getCausesValidation(); public function setCausesValidation($value); public function getCommandName(); public function setCommandName($value); public function getCommandParameter(); public function setCommandParameter($value); public function getValidationGroup(); public function setValidationGroup($value); public function onClick($param); public function onCommand($param); public function setIsDefaultButton($value); public function getIsDefaultButton(); } interface ISurroundable { public function getSurroundingTag(); public function getSurroundingTagID(); } class TBroadcastEventParameter extends TEventParameter { private $_name; private $_param; public function __construct($name='',$parameter=null) { $this->_name=$name; $this->_param=$parameter; } public function getName() { return $this->_name; } public function setName($value) { $this->_name=$value; } public function getParameter() { return $this->_param; } public function setParameter($value) { $this->_param=$value; } } class TCommandEventParameter extends TEventParameter { private $_name; private $_param; public function __construct($name='',$parameter='') { $this->_name=$name; $this->_param=$parameter; } public function getCommandName() { return $this->_name; } public function getCommandParameter() { return $this->_param; } } class TCompositeLiteral extends TComponent implements IRenderable, IBindable { const TYPE_EXPRESSION=0; const TYPE_STATEMENTS=1; const TYPE_DATABINDING=2; private $_container=null; private $_items=array(); private $_expressions=array(); private $_statements=array(); private $_bindings=array(); public function __construct($items) { $this->_items=array(); $this->_expressions=array(); $this->_statements=array(); foreach($items as $id=>$item) { if(is_array($item)) { if($item[0]===self::TYPE_EXPRESSION) $this->_expressions[$id]=$item[1]; else if($item[0]===self::TYPE_STATEMENTS) $this->_statements[$id]=$item[1]; else if($item[0]===self::TYPE_DATABINDING) $this->_bindings[$id]=$item[1]; $this->_items[$id]=''; } else $this->_items[$id]=$item; } } public function getContainer() { return $this->_container; } public function setContainer(TComponent $value) { $this->_container=$value; } public function evaluateDynamicContent() { $context=$this->_container===null?$this:$this->_container; foreach($this->_expressions as $id=>$expression) $this->_items[$id]=$context->evaluateExpression($expression); foreach($this->_statements as $id=>$statement) $this->_items[$id]=$context->evaluateStatements($statement); } public function dataBind() { $context=$this->_container===null?$this:$this->_container; foreach($this->_bindings as $id=>$binding) $this->_items[$id]=$context->evaluateExpression($binding); } public function render($writer) { $writer->write(implode('',$this->_items)); } } class TFont extends TComponent { const IS_BOLD=0x01; const IS_ITALIC=0x02; const IS_OVERLINE=0x04; const IS_STRIKEOUT=0x08; const IS_UNDERLINE=0x10; const IS_SET_BOLD=0x01000; const IS_SET_ITALIC=0x02000; const IS_SET_OVERLINE=0x04000; const IS_SET_STRIKEOUT=0x08000; const IS_SET_UNDERLINE=0x10000; const IS_SET_SIZE=0x20000; const IS_SET_NAME=0x40000; private $_flags=0; private $_name=''; private $_size=''; protected function __getZappableSleepProps(&$exprops) { parent::__getZappableSleepProps($exprops); if ($this->_flags===0) $exprops[] = "\0TFont\0_flags"; if ($this->_name==='') $exprops[] = "\0TFont\0_name"; if ($this->_size==='') $exprops[] = "\0TFont\0_size"; } public function getBold() { return ($this->_flags & self::IS_BOLD)!==0; } public function setBold($value) { $this->_flags |= self::IS_SET_BOLD; if(TPropertyValue::ensureBoolean($value)) $this->_flags |= self::IS_BOLD; else $this->_flags &= ~self::IS_BOLD; } public function getItalic() { return ($this->_flags & self::IS_ITALIC)!==0; } public function setItalic($value) { $this->_flags |= self::IS_SET_ITALIC; if(TPropertyValue::ensureBoolean($value)) $this->_flags |= self::IS_ITALIC; else $this->_flags &= ~self::IS_ITALIC; } public function getOverline() { return ($this->_flags & self::IS_OVERLINE)!==0; } public function setOverline($value) { $this->_flags |= self::IS_SET_OVERLINE; if(TPropertyValue::ensureBoolean($value)) $this->_flags |= self::IS_OVERLINE; else $this->_flags &= ~self::IS_OVERLINE; } public function getSize() { return $this->_size; } public function setSize($value) { $this->_flags |= self::IS_SET_SIZE; $this->_size=$value; } public function getStrikeout() { return ($this->_flags & self::IS_STRIKEOUT)!==0; } public function setStrikeout($value) { $this->_flags |= self::IS_SET_STRIKEOUT; if(TPropertyValue::ensureBoolean($value)) $this->_flags |= self::IS_STRIKEOUT; else $this->_flags &= ~self::IS_STRIKEOUT; } public function getUnderline() { return ($this->_flags & self::IS_UNDERLINE)!==0; } public function setUnderline($value) { $this->_flags |= self::IS_SET_UNDERLINE; if(TPropertyValue::ensureBoolean($value)) $this->_flags |= self::IS_UNDERLINE; else $this->_flags &= ~self::IS_UNDERLINE; } public function getName() { return $this->_name; } public function setName($value) { $this->_flags |= self::IS_SET_NAME; $this->_name=$value; } public function getIsEmpty() { return !$this->_flags; } public function reset() { $this->_flags=0; $this->_name=''; $this->_size=''; } public function mergeWith($font) { if($font===null || $font->_flags===0) return; if(!($this->_flags & self::IS_SET_BOLD) && ($font->_flags & self::IS_SET_BOLD)) $this->setBold($font->getBold()); if(!($this->_flags & self::IS_SET_ITALIC) && ($font->_flags & self::IS_SET_ITALIC)) $this->setItalic($font->getItalic()); if(!($this->_flags & self::IS_SET_OVERLINE) && ($font->_flags & self::IS_SET_OVERLINE)) $this->setOverline($font->getOverline()); if(!($this->_flags & self::IS_SET_STRIKEOUT) && ($font->_flags & self::IS_SET_STRIKEOUT)) $this->setStrikeout($font->getStrikeout()); if(!($this->_flags & self::IS_SET_UNDERLINE) && ($font->_flags & self::IS_SET_UNDERLINE)) $this->setUnderline($font->getUnderline()); if(!($this->_flags & self::IS_SET_SIZE) && ($font->_flags & self::IS_SET_SIZE)) $this->setSize($font->getSize()); if(!($this->_flags & self::IS_SET_NAME) && ($font->_flags & self::IS_SET_NAME)) $this->setName($font->getName()); } public function copyFrom($font) { if($font===null || $font->_flags===0) return; if($font->_flags & self::IS_SET_BOLD) $this->setBold($font->getBold()); if($font->_flags & self::IS_SET_ITALIC) $this->setItalic($font->getItalic()); if($font->_flags & self::IS_SET_OVERLINE) $this->setOverline($font->getOverline()); if($font->_flags & self::IS_SET_STRIKEOUT) $this->setStrikeout($font->getStrikeout()); if($font->_flags & self::IS_SET_UNDERLINE) $this->setUnderline($font->getUnderline()); if($font->_flags & self::IS_SET_SIZE) $this->setSize($font->getSize()); if($font->_flags & self::IS_SET_NAME) $this->setName($font->getName()); } public function toString() { if($this->_flags===0) return ''; $str=''; if($this->_flags & self::IS_SET_BOLD) $str.='font-weight:'.(($this->_flags & self::IS_BOLD)?'bold;':'normal;'); if($this->_flags & self::IS_SET_ITALIC) $str.='font-style:'.(($this->_flags & self::IS_ITALIC)?'italic;':'normal;'); $textDec=''; if($this->_flags & self::IS_UNDERLINE) $textDec.='underline'; if($this->_flags & self::IS_OVERLINE) $textDec.=' overline'; if($this->_flags & self::IS_STRIKEOUT) $textDec.=' line-through'; $textDec=ltrim($textDec); if($textDec!=='') $str.='text-decoration:'.$textDec.';'; if($this->_size!=='') $str.='font-size:'.$this->_size.';'; if($this->_name!=='') $str.='font-family:'.$this->_name.';'; return $str; } public function addAttributesToRender($writer) { if($this->_flags===0) return; if($this->_flags & self::IS_SET_BOLD) $writer->addStyleAttribute('font-weight',(($this->_flags & self::IS_BOLD)?'bold':'normal')); if($this->_flags & self::IS_SET_ITALIC) $writer->addStyleAttribute('font-style',(($this->_flags & self::IS_ITALIC)?'italic':'normal')); $textDec=''; if($this->_flags & self::IS_UNDERLINE) $textDec.='underline'; if($this->_flags & self::IS_OVERLINE) $textDec.=' overline'; if($this->_flags & self::IS_STRIKEOUT) $textDec.=' line-through'; $textDec=ltrim($textDec); if($textDec!=='') $writer->addStyleAttribute('text-decoration',$textDec); if($this->_size!=='') $writer->addStyleAttribute('font-size',$this->_size); if($this->_name!=='') $writer->addStyleAttribute('font-family',$this->_name); } } class TStyle extends TComponent { private $_fields=array(); private $_font=null; private $_class=null; private $_customStyle=null; private $_displayStyle='Fixed'; protected function __getZappableSleepProps(&$exprops) { parent::__getZappableSleepProps($exprops); if ($this->_fields===array()) $exprops[] = "\0TStyle\0_fields"; if($this->_font===null) $exprops[] = "\0TStyle\0_font"; if($this->_class===null) $exprops[] = "\0TStyle\0_class"; if ($this->_customStyle===null) $exprops[] = "\0TStyle\0_customStyle"; if ($this->_displayStyle==='Fixed') $exprops[] = "\0TStyle\0_displayStyle"; } public function __construct($style=null) { if($style!==null) $this->copyFrom($style); } public function __clone() { if($this->_font!==null) $this->_font = clone($this->_font); } public function getBackColor() { return isset($this->_fields['background-color'])?$this->_fields['background-color']:''; } public function setBackColor($value) { if(trim($value)==='') unset($this->_fields['background-color']); else $this->_fields['background-color']=$value; } public function getBorderColor() { return isset($this->_fields['border-color'])?$this->_fields['border-color']:''; } public function setBorderColor($value) { if(trim($value)==='') unset($this->_fields['border-color']); else $this->_fields['border-color']=$value; } public function getBorderStyle() { return isset($this->_fields['border-style'])?$this->_fields['border-style']:''; } public function setBorderStyle($value) { if(trim($value)==='') unset($this->_fields['border-style']); else $this->_fields['border-style']=$value; } public function getBorderWidth() { return isset($this->_fields['border-width'])?$this->_fields['border-width']:''; } public function setBorderWidth($value) { if(trim($value)==='') unset($this->_fields['border-width']); else $this->_fields['border-width']=$value; } public function getCssClass() { return $this->_class===null?'':$this->_class; } public function hasCssClass() { return ($this->_class!==null); } public function setCssClass($value) { $this->_class=$value; } public function getFont() { if($this->_font===null) $this->_font=new TFont; return $this->_font; } public function hasFont() { return $this->_font !== null; } public function setDisplayStyle($value) { $this->_displayStyle = TPropertyValue::ensureEnum($value, 'TDisplayStyle'); switch($this->_displayStyle) { case TDisplayStyle::None: $this->_fields['display'] = 'none'; break; case TDisplayStyle::Dynamic: $this->_fields['display'] = ''; break; case TDisplayStyle::Fixed: $this->_fields['visibility'] = 'visible'; break; case TDisplayStyle::Hidden: $this->_fields['visibility'] = 'hidden'; break; } } public function getDisplayStyle() { return $this->_displayStyle; } public function getForeColor() { return isset($this->_fields['color'])?$this->_fields['color']:''; } public function setForeColor($value) { if(trim($value)==='') unset($this->_fields['color']); else $this->_fields['color']=$value; } public function getHeight() { return isset($this->_fields['height'])?$this->_fields['height']:''; } public function setHeight($value) { if(trim($value)==='') unset($this->_fields['height']); else $this->_fields['height']=$value; } public function getCustomStyle() { return $this->_customStyle===null?'':$this->_customStyle; } public function setCustomStyle($value) { $this->_customStyle=$value; } public function getStyleField($name) { return isset($this->_fields[$name])?$this->_fields[$name]:''; } public function setStyleField($name,$value) { $this->_fields[$name]=$value; } public function clearStyleField($name) { unset($this->_fields[$name]); } public function hasStyleField($name) { return isset($this->_fields[$name]); } public function getWidth() { return isset($this->_fields['width'])?$this->_fields['width']:''; } public function setWidth($value) { $this->_fields['width']=$value; } public function reset() { $this->_fields=array(); $this->_font=null; $this->_class=null; $this->_customStyle=null; } public function copyFrom($style) { if($style instanceof TStyle) { $this->_fields=array_merge($this->_fields,$style->_fields); if($style->_class!==null) $this->_class=$style->_class; if($style->_customStyle!==null) $this->_customStyle=$style->_customStyle; if($style->_font!==null) $this->getFont()->copyFrom($style->_font); } } public function mergeWith($style) { if($style instanceof TStyle) { $this->_fields=array_merge($style->_fields,$this->_fields); if($this->_class===null) $this->_class=$style->_class; if($this->_customStyle===null) $this->_customStyle=$style->_customStyle; if($style->_font!==null) $this->getFont()->mergeWith($style->_font); } } public function addAttributesToRender($writer) { if($this->_customStyle!==null) { foreach(explode(';',$this->_customStyle) as $style) { $arr=explode(':',$style,2); if(isset($arr[1]) && trim($arr[0])!=='') $writer->addStyleAttribute(trim($arr[0]),trim($arr[1])); } } $writer->addStyleAttributes($this->_fields); if($this->_font!==null) $this->_font->addAttributesToRender($writer); if($this->_class!==null) $writer->addAttribute('class',$this->_class); } public function getStyleFields() { return $this->_fields; } } class TDisplayStyle extends TEnumerable { const None='None'; const Dynamic='Dynamic'; const Fixed='Fixed'; const Hidden='Hidden'; } class TTableStyle extends TStyle { private $_backImageUrl=null; private $_horizontalAlign=null; private $_cellPadding=null; private $_cellSpacing=null; private $_gridLines=null; private $_borderCollapse=null; protected function __getZappableSleepProps(&$exprops) { parent::__getZappableSleepProps($exprops); if ($this->_backImageUrl===null) $exprops[] = "\0TTableStyle\0_backImageUrl"; if ($this->_horizontalAlign===null) $exprops[] = "\0TTableStyle\0_horizontalAlign"; if ($this->_cellPadding===null) $exprops[] = "\0TTableStyle\0_cellPadding"; if ($this->_cellSpacing===null) $exprops[] = "\0TTableStyle\0_cellSpacing"; if ($this->_gridLines===null) $exprops[] = "\0TTableStyle\0_gridLines"; if ($this->_borderCollapse===null) $exprops[] = "\0TTableStyle\0_borderCollapse"; } public function reset() { $this->_backImageUrl=null; $this->_horizontalAlign=null; $this->_cellPadding=null; $this->_cellSpacing=null; $this->_gridLines=null; $this->_borderCollapse=null; } public function copyFrom($style) { parent::copyFrom($style); if($style instanceof TTableStyle) { if($style->_backImageUrl!==null) $this->_backImageUrl=$style->_backImageUrl; if($style->_horizontalAlign!==null) $this->_horizontalAlign=$style->_horizontalAlign; if($style->_cellPadding!==null) $this->_cellPadding=$style->_cellPadding; if($style->_cellSpacing!==null) $this->_cellSpacing=$style->_cellSpacing; if($style->_gridLines!==null) $this->_gridLines=$style->_gridLines; if($style->_borderCollapse!==null) $this->_borderCollapse=$style->_borderCollapse; } } public function mergeWith($style) { parent::mergeWith($style); if($style instanceof TTableStyle) { if($this->_backImageUrl===null && $style->_backImageUrl!==null) $this->_backImageUrl=$style->_backImageUrl; if($this->_horizontalAlign===null && $style->_horizontalAlign!==null) $this->_horizontalAlign=$style->_horizontalAlign; if($this->_cellPadding===null && $style->_cellPadding!==null) $this->_cellPadding=$style->_cellPadding; if($this->_cellSpacing===null && $style->_cellSpacing!==null) $this->_cellSpacing=$style->_cellSpacing; if($this->_gridLines===null && $style->_gridLines!==null) $this->_gridLines=$style->_gridLines; if($this->_borderCollapse===null && $style->_borderCollapse!==null) $this->_borderCollapse=$style->_borderCollapse; } } public function addAttributesToRender($writer) { if(($url=trim($this->getBackImageUrl()))!=='') $writer->addStyleAttribute('background-image','url('.$url.')'); if(($horizontalAlign=$this->getHorizontalAlign())!==THorizontalAlign::NotSet) $writer->addStyleAttribute('text-align',strtolower($horizontalAlign)); if(($cellPadding=$this->getCellPadding())>=0) $writer->addAttribute('cellpadding',"$cellPadding"); if(($cellSpacing=$this->getCellSpacing())>=0) $writer->addAttribute('cellspacing',"$cellSpacing"); if($this->getBorderCollapse()) $writer->addStyleAttribute('border-collapse','collapse'); switch($this->getGridLines()) { case TTableGridLines::Horizontal : $writer->addAttribute('rules','rows'); break; case TTableGridLines::Vertical : $writer->addAttribute('rules','cols'); break; case TTableGridLines::Both : $writer->addAttribute('rules','all'); break; } parent::addAttributesToRender($writer); } public function getBackImageUrl() { return $this->_backImageUrl===null?'':$this->_backImageUrl; } public function setBackImageUrl($value) { $this->_backImageUrl=$value; } public function getHorizontalAlign() { return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign; } public function setHorizontalAlign($value) { $this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign'); } public function getCellPadding() { return $this->_cellPadding===null?-1:$this->_cellPadding; } public function setCellPadding($value) { if(($this->_cellPadding=TPropertyValue::ensureInteger($value))<-1) throw new TInvalidDataValueException('tablestyle_cellpadding_invalid'); } public function getCellSpacing() { return $this->_cellSpacing===null?-1:$this->_cellSpacing; } public function setCellSpacing($value) { if(($this->_cellSpacing=TPropertyValue::ensureInteger($value))<-1) throw new TInvalidDataValueException('tablestyle_cellspacing_invalid'); } public function getGridLines() { return $this->_gridLines===null?TTableGridLines::None:$this->_gridLines; } public function setGridLines($value) { $this->_gridLines=TPropertyValue::ensureEnum($value,'TTableGridLines'); } public function getBorderCollapse() { return $this->_borderCollapse===null?false:$this->_borderCollapse; } public function setBorderCollapse($value) { $this->_borderCollapse=TPropertyValue::ensureBoolean($value); } } class TTableItemStyle extends TStyle { private $_horizontalAlign=null; private $_verticalAlign=null; private $_wrap=null; protected function __getZappableSleepProps(&$exprops) { parent::__getZappableSleepProps($exprops); if ($this->_horizontalAlign===null) $exprops[] = "\0TTableItemStyle\0_horizontalAlign"; if ($this->_verticalAlign===null) $exprops[] = "\0TTableItemStyle\0_verticalAlign"; if ($this->_wrap===null) $exprops[] = "\0TTableItemStyle\0_wrap"; } public function reset() { parent::reset(); $this->_verticalAlign=null; $this->_horizontalAlign=null; $this->_wrap=null; } public function copyFrom($style) { parent::copyFrom($style); if($style instanceof TTableItemStyle) { if($this->_verticalAlign===null && $style->_verticalAlign!==null) $this->_verticalAlign=$style->_verticalAlign; if($this->_horizontalAlign===null && $style->_horizontalAlign!==null) $this->_horizontalAlign=$style->_horizontalAlign; if($this->_wrap===null && $style->_wrap!==null) $this->_wrap=$style->_wrap; } } public function mergeWith($style) { parent::mergeWith($style); if($style instanceof TTableItemStyle) { if($style->_verticalAlign!==null) $this->_verticalAlign=$style->_verticalAlign; if($style->_horizontalAlign!==null) $this->_horizontalAlign=$style->_horizontalAlign; if($style->_wrap!==null) $this->_wrap=$style->_wrap; } } public function addAttributesToRender($writer) { if(!$this->getWrap()) $writer->addStyleAttribute('white-space','nowrap'); if(($horizontalAlign=$this->getHorizontalAlign())!==THorizontalAlign::NotSet) $writer->addAttribute('align',strtolower($horizontalAlign)); if(($verticalAlign=$this->getVerticalAlign())!==TVerticalAlign::NotSet) $writer->addAttribute('valign',strtolower($verticalAlign)); parent::addAttributesToRender($writer); } public function getHorizontalAlign() { return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign; } public function setHorizontalAlign($value) { $this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign'); } public function getVerticalAlign() { return $this->_verticalAlign===null?TVerticalAlign::NotSet:$this->_verticalAlign; } public function setVerticalAlign($value) { $this->_verticalAlign=TPropertyValue::ensureEnum($value,'TVerticalAlign'); } public function getWrap() { return $this->_wrap===null?true:$this->_wrap; } public function setWrap($value) { $this->_wrap=TPropertyValue::ensureBoolean($value); } } class THorizontalAlign extends TEnumerable { const NotSet='NotSet'; const Left='Left'; const Right='Right'; const Center='Center'; const Justify='Justify'; } class TVerticalAlign extends TEnumerable { const NotSet='NotSet'; const Top='Top'; const Bottom='Bottom'; const Middle='Middle'; } class TTableGridLines extends TEnumerable { const None='None'; const Horizontal='Horizontal'; const Vertical='Vertical'; const Both='Both'; } class TWebControlAdapter extends TControlAdapter { public function render($writer) { $this->renderBeginTag($writer); $this->renderContents($writer); $this->renderEndTag($writer); } public function renderBeginTag($writer) { $this->getControl()->renderBeginTag($writer); } public function renderContents($writer) { $this->getControl()->renderContents($writer); } public function renderEndTag($writer) { $this->getControl()->renderEndTag($writer); } } class TWebControlDecorator extends TComponent { private $_internalonly; private $_usestate = false; private $_control; private $_outercontrol; private $_addedTemplateDecoration=false; private $_pretagtext = ''; private $_precontentstext = ''; private $_postcontentstext = ''; private $_posttagtext = ''; private $_pretagtemplate; private $_precontentstemplate; private $_postcontentstemplate; private $_posttagtemplate; public function __construct($control, $onlyinternal = false) { $this->_control = $control; $this->_internalonly = $onlyinternal; } public function getUseState() { return $this->_usestate; } public function setUseState($value) { $this->_usestate = TPropertyValue::ensureBoolean($value); } public function getPreTagText() { return $this->_pretagtext; } public function setPreTagText($value) { if(!$this->_internalonly && !$this->_control->getIsSkinApplied()) $this->_pretagtext = TPropertyValue::ensureString($value); } public function getPreContentsText() { return $this->_precontentstext; } public function setPreContentsText($value) { if(!$this->_control->getIsSkinApplied()) $this->_precontentstext = TPropertyValue::ensureString($value); } public function getPostContentsText() { return $this->_postcontentstext; } public function setPostContentsText($value) { if(!$this->_control->getIsSkinApplied()) $this->_postcontentstext = TPropertyValue::ensureString($value); } public function getPostTagText() { return $this->_posttagtext; } public function setPostTagText($value) { if(!$this->_internalonly && !$this->_control->getIsSkinApplied()) $this->_posttagtext = TPropertyValue::ensureString($value); } public function getPreTagTemplate() { return $this->_pretagtemplate; } public function setPreTagTemplate($value) { if(!$this->_internalonly && !$this->_control->getIsSkinApplied()) $this->_pretagtemplate = $value; } public function getPreContentsTemplate() { return $this->_precontentstemplate; } public function setPreContentsTemplate($value) { if(!$this->_control->getIsSkinApplied()) $this->_precontentstemplate = $value; } public function getPostContentsTemplate() { return $this->_postcontentstemplate; } public function setPostContentsTemplate($value) { if(!$this->_control->getIsSkinApplied()) $this->_postcontentstemplate = $value; } public function getPostTagTemplate() { return $this->_posttagtemplate; } public function setPostTagTemplate($value) { if(!$this->_internalonly && !$this->_control->getIsSkinApplied()) $this->_posttagtemplate = $value; } public function instantiate($outercontrol = null) { if($this->getPreTagTemplate() || $this->getPreContentsTemplate() || $this->getPostContentsTemplate() || $this->getPostTagTemplate()) { $this->_outercontrol = $outercontrol; if($this->getUseState()) $this->ensureTemplateDecoration(); else $this->_control->getPage()->onSaveStateComplete[] = array($this, 'ensureTemplateDecoration'); } } public function ensureTemplateDecoration($sender=null, $param=null) { $control = $this->_control; $outercontrol = $this->_outercontrol; if($outercontrol === null) $outercontrol = $control; if($this->_addedTemplateDecoration) return $this->_addedTemplateDecoration; $this->_addedTemplateDecoration = true; if($this->getPreContentsTemplate()) { $precontents = Prado::createComponent('TCompositeControl'); $this->getPreContentsTemplate()->instantiateIn($precontents); $control->getControls()->insertAt(0, $precontents); } if($this->getPostContentsTemplate()) { $postcontents = Prado::createComponent('TCompositeControl'); $this->getPostContentsTemplate()->instantiateIn($postcontents); $control->getControls()->add($postcontents); } if(!$outercontrol->getParent()) return $this->_addedTemplateDecoration; if($this->getPreTagTemplate()) { $pretag = Prado::createComponent('TCompositeControl'); $this->getPreTagTemplate()->instantiateIn($pretag); $outercontrol->getParent()->getControls()->insertBefore($outercontrol, $pretag); } if($this->getPostTagTemplate()) { $posttag = Prado::createComponent('TCompositeControl'); $this->getPostTagTemplate()->instantiateIn($posttag); $outercontrol->getParent()->getControls()->insertAfter($outercontrol, $posttag); } return true; } public function renderPreTagText($writer) { $writer->write($this->getPreTagText()); } public function renderPreContentsText($writer) { $writer->write($this->getPreContentsText()); } public function renderPostContentsText($writer) { $writer->write($this->getPostContentsText()); } public function renderPostTagText($writer) { $writer->write($this->getPostTagText()); } } class TWebControl extends TControl implements IStyleable { private $_ensureid=false; protected $_decorator; public function setEnsureId($value) { $this->_ensureid |= TPropertyValue::ensureBoolean($value); } public function getEnsureId() { return $this->_ensureid; } public function getDecorator($create=true) { if($create && !$this->_decorator) $this->_decorator = Prado::createComponent('TWebControlDecorator', $this); return $this->_decorator; } public function copyBaseAttributes(TWebControl $control) { $this->setAccessKey($control->getAccessKey()); $this->setToolTip($control->getToolTip()); $this->setTabIndex($control->getTabIndex()); if(!$control->getEnabled()) $this->setEnabled(false); if($control->getHasAttributes()) $this->getAttributes()->copyFrom($control->getAttributes()); } public function getAccessKey() { return $this->getViewState('AccessKey',''); } public function setAccessKey($value) { if(strlen($value)>1) throw new TInvalidDataValueException('webcontrol_accesskey_invalid',get_class($this),$value); $this->setViewState('AccessKey',$value,''); } public function getBackColor() { if($style=$this->getViewState('Style',null)) return $style->getBackColor(); else return ''; } public function setBackColor($value) { $this->getStyle()->setBackColor($value); } public function getBorderColor() { if($style=$this->getViewState('Style',null)) return $style->getBorderColor(); else return ''; } public function setBorderColor($value) { $this->getStyle()->setBorderColor($value); } public function getBorderStyle() { if($style=$this->getViewState('Style',null)) return $style->getBorderStyle(); else return ''; } public function setBorderStyle($value) { $this->getStyle()->setBorderStyle($value); } public function getBorderWidth() { if($style=$this->getViewState('Style',null)) return $style->getBorderWidth(); else return ''; } public function setBorderWidth($value) { $this->getStyle()->setBorderWidth($value); } public function getFont() { return $this->getStyle()->getFont(); } public function getForeColor() { if($style=$this->getViewState('Style',null)) return $style->getForeColor(); else return ''; } public function setForeColor($value) { $this->getStyle()->setForeColor($value); } public function getHeight() { if($style=$this->getViewState('Style',null)) return $style->getHeight(); else return ''; } public function setDisplay($value) { $this->getStyle()->setDisplayStyle($value); } public function getDisplay() { return $this->getStyle()->getDisplayStyle(); } public function setCssClass($value) { $this->getStyle()->setCssClass($value); } public function getCssClass() { if($style=$this->getViewState('Style',null)) return $style->getCssClass(); else return ''; } public function setHeight($value) { $this->getStyle()->setHeight($value); } public function getHasStyle() { return $this->getViewState('Style',null)!==null; } protected function createStyle() { return new TStyle; } public function getStyle() { if($style=$this->getViewState('Style',null)) return $style; else { $style=$this->createStyle(); $this->setViewState('Style',$style,null); return $style; } } public function setStyle($value) { if(is_string($value)) $this->getStyle()->setCustomStyle($value); else throw new TInvalidDataValueException('webcontrol_style_invalid',get_class($this)); } public function clearStyle() { $this->clearViewState('Style'); } public function getTabIndex() { return $this->getViewState('TabIndex',0); } public function setTabIndex($value) { $this->setViewState('TabIndex',TPropertyValue::ensureInteger($value),0); } protected function getTagName() { return 'span'; } public function getToolTip() { return $this->getViewState('ToolTip',''); } public function setToolTip($value) { $this->setViewState('ToolTip',$value,''); } public function getWidth() { if($style=$this->getViewState('Style',null)) return $style->getWidth(); else return ''; } public function setWidth($value) { $this->getStyle()->setWidth($value); } public function onPreRender($param) { if($decorator = $this->getDecorator(false)) $decorator->instantiate(); parent::onPreRender($param); } protected function addAttributesToRender($writer) { if($this->getID()!=='' || $this->getEnsureId()) $writer->addAttribute('id',$this->getClientID()); if(($accessKey=$this->getAccessKey())!=='') $writer->addAttribute('accesskey',$accessKey); if(!$this->getEnabled()) $writer->addAttribute('disabled','disabled'); if(($tabIndex=$this->getTabIndex())>0) $writer->addAttribute('tabindex',"$tabIndex"); if(($toolTip=$this->getToolTip())!=='') $writer->addAttribute('title',$toolTip); if($style=$this->getViewState('Style',null)) $style->addAttributesToRender($writer); if($this->getHasAttributes()) { foreach($this->getAttributes() as $name=>$value) $writer->addAttribute($name,$value); } } public function render($writer) { $this->renderBeginTag($writer); $this->renderContents($writer); $this->renderEndTag($writer); } public function renderBeginTag($writer) { if($decorator = $this->getDecorator(false)) { $decorator->renderPreTagText($writer); $this->addAttributesToRender($writer); $writer->renderBeginTag($this->getTagName()); $decorator->renderPreContentsText($writer); } else { $this->addAttributesToRender($writer); $writer->renderBeginTag($this->getTagName()); } } public function renderContents($writer) { parent::renderChildren($writer); } public function renderEndTag($writer) { if($decorator = $this->getDecorator(false)) { $decorator->renderPostContentsText($writer); $writer->renderEndTag(); $decorator->renderPostTagText($writer); } else $writer->renderEndTag($writer); } } class TCompositeControl extends TControl implements INamingContainer { protected function initRecursive($namingContainer=null) { $this->ensureChildControls(); parent::initRecursive($namingContainer); } } class TTemplateControl extends TCompositeControl { const EXT_TEMPLATE='.tpl'; private static $_template=array(); private $_localTemplate=null; private $_master=null; private $_masterClass=''; private $_contents=array(); private $_placeholders=array(); public function getTemplate() { if($this->_localTemplate===null) { $class=get_class($this); if(!isset(self::$_template[$class])) self::$_template[$class]=$this->loadTemplate(); return self::$_template[$class]; } else return $this->_localTemplate; } public function setTemplate($value) { $this->_localTemplate=$value; } public function getIsSourceTemplateControl() { if(($template=$this->getTemplate())!==null) return $template->getIsSourceTemplate(); else return false; } public function getTemplateDirectory() { if(($template=$this->getTemplate())!==null) return $template->getContextPath(); else return ''; } protected function loadTemplate() { $template=$this->getService()->getTemplateManager()->getTemplateByClassName(get_class($this)); return $template; } public function createChildControls() { if($tpl=$this->getTemplate()) { foreach($tpl->getDirective() as $name=>$value) { if(is_string($value)) $this->setSubProperty($name,$value); else throw new TConfigurationException('templatecontrol_directive_invalid',get_class($this),$name); } $tpl->instantiateIn($this); } } public function registerContent($id,TContent $object) { if(isset($this->_contents[$id])) throw new TConfigurationException('templatecontrol_contentid_duplicated',$id); else $this->_contents[$id]=$object; } public function registerContentPlaceHolder($id,TContentPlaceHolder $object) { if(isset($this->_placeholders[$id])) throw new TConfigurationException('templatecontrol_placeholderid_duplicated',$id); else $this->_placeholders[$id]=$object; } public function getMasterClass() { return $this->_masterClass; } public function setMasterClass($value) { $this->_masterClass=$value; } public function getMaster() { return $this->_master; } public function injectContent($id,$content) { if(isset($this->_placeholders[$id])) { $placeholder=$this->_placeholders[$id]; $controls=$placeholder->getParent()->getControls(); $loc=$controls->remove($placeholder); $controls->insertAt($loc,$content); } else throw new TConfigurationException('templatecontrol_placeholder_inexistent',$id); } protected function initRecursive($namingContainer=null) { $this->ensureChildControls(); if($this->_masterClass!=='') { $master=Prado::createComponent($this->_masterClass); if(!($master instanceof TTemplateControl)) throw new TInvalidDataValueException('templatecontrol_mastercontrol_invalid'); $this->_master=$master; $this->getControls()->clear(); $this->getControls()->add($master); $master->ensureChildControls(); foreach($this->_contents as $id=>$content) $master->injectContent($id,$content); } else if(!empty($this->_contents)) throw new TConfigurationException('templatecontrol_mastercontrol_required',get_class($this)); parent::initRecursive($namingContainer); } public function tryToUpdateView($arObj, $throwExceptions = false) { $objAttrs = get_class_vars(get_class($arObj)); foreach (array_keys($objAttrs) as $key) { try { if ($key != "RELATIONS") { $control = $this->{$key}; if ($control instanceof TTextBox) $control->Text = $arObj->{$key}; elseif ($control instanceof TCheckBox) $control->Checked = (boolean) $arObj->{$key}; elseif ($control instanceof TDatePicker) $control->Date = $arObj->{$key}; } else { foreach ($objAttrs["RELATIONS"] as $relKey => $relValues) { $relControl = $this->{$relKey}; switch ($relValues[0]) { case TActiveRecord::BELONGS_TO: case TActiveRecord::HAS_ONE: $relControl->Text = $arObj->{$relKey}; break; case TActiveRecord::HAS_MANY: if ($relControl instanceof TListControl) { $relControl->DataSource = $arObj->{$relKey}; $relControl->dataBind(); } break; } } break; } } catch (Exception $ex) { if ($throwExceptions) throw $ex; } } } public function tryToUpdateAR($arObj, $throwExceptions = false) { $objAttrs = get_class_vars(get_class($arObj)); foreach (array_keys($objAttrs) as $key) { try { if ($key == "RELATIONS") break; $control = $this->{$key}; if ($control instanceof TTextBox) $arObj->{$key} = $control->Text; elseif ($control instanceof TCheckBox) $arObj->{$key} = $control->Checked; elseif ($control instanceof TDatePicker) $arObj->{$key} = $control->Date; } catch (Exception $ex) { if ($throwExceptions) throw $ex; } } } } class TForm extends TControl { public function onInit($param) { parent::onInit($param); $this->getPage()->setForm($this); } protected function addAttributesToRender($writer) { $writer->addAttribute('id',$this->getClientID()); $writer->addAttribute('method',$this->getMethod()); $uri=$this->getRequest()->getRequestURI(); $writer->addAttribute('action',str_replace('&','&',str_replace('&','&',$uri))); if(($enctype=$this->getEnctype())!=='') $writer->addAttribute('enctype',$enctype); $attributes=$this->getAttributes(); $attributes->remove('action'); $writer->addAttributes($attributes); if(($butt=$this->getDefaultButton())!=='') { if(($button=$this->findControl($butt))!==null) $this->getPage()->getClientScript()->registerDefaultButton($this, $button); else throw new TInvalidDataValueException('form_defaultbutton_invalid',$butt); } } public function render($writer) { $page=$this->getPage(); $this->addAttributesToRender($writer); $writer->renderBeginTag('form'); $cs=$page->getClientScript(); if($page->getClientSupportsJavaScript()) { $cs->renderHiddenFieldsBegin($writer); $cs->renderScriptFilesBegin($writer); $cs->renderBeginScripts($writer); $page->beginFormRender($writer); $this->renderChildren($writer); $cs->renderHiddenFieldsEnd($writer); $page->endFormRender($writer); $cs->renderScriptFilesEnd($writer); $cs->renderEndScripts($writer); } else { $cs->renderHiddenFieldsBegin($writer); $page->beginFormRender($writer); $this->renderChildren($writer); $page->endFormRender($writer); $cs->renderHiddenFieldsEnd($writer); } $writer->renderEndTag(); } public function getDefaultButton() { return $this->getViewState('DefaultButton',''); } public function setDefaultButton($value) { $this->setViewState('DefaultButton',$value,''); } public function getMethod() { return $this->getViewState('Method','post'); } public function setMethod($value) { $this->setViewState('Method',TPropertyValue::ensureEnum($value,'post','get'),'post'); } public function getEnctype() { return $this->getViewState('Enctype',''); } public function setEnctype($value) { $this->setViewState('Enctype',$value,''); } public function getName() { return $this->getUniqueID(); } } class TClientScriptManager extends TApplicationComponent { const SCRIPT_PATH='Web/Javascripts/source'; const PACKAGES_FILE='Web/Javascripts/packages.php'; private $_page; private $_hiddenFields=array(); private $_beginScripts=array(); private $_endScripts=array(); private $_scriptFiles=array(); private $_headScriptFiles=array(); private $_headScripts=array(); private $_styleSheetFiles=array(); private $_styleSheets=array(); private $_registeredPradoScripts=array(); private static $_pradoScripts; private static $_pradoPackages; private $_renderedHiddenFields; private $_renderedScriptFiles=array(); private $_expandedPradoScripts; public function __construct(TPage $owner) { $this->_page=$owner; } public function getRequiresHead() { return count($this->_styleSheetFiles) || count($this->_styleSheets) || count($this->_headScriptFiles) || count($this->_headScripts); } public static function getPradoPackages() { return self::$_pradoPackages; } public static function getPradoScripts() { return self::$_pradoScripts; } public function registerPradoScript($name) { $this->registerPradoScriptInternal($name); $params=func_get_args(); $this->_page->registerCachingAction('Page.ClientScript','registerPradoScript',$params); } protected function registerPradoScriptInternal($name) { if(!isset($this->_registeredPradoScripts[$name])) { if(self::$_pradoScripts === null) { $packageFile = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::PACKAGES_FILE; list($packages,$deps)= include($packageFile); self::$_pradoScripts = $deps; self::$_pradoPackages = $packages; } if (isset(self::$_pradoScripts[$name])) $this->_registeredPradoScripts[$name]=true; else throw new TInvalidOperationException('csmanager_pradoscript_invalid',$name); if(($packages=array_keys($this->_registeredPradoScripts))!==array()) { $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH; list($path,$baseUrl)=$this->getPackagePathUrl($base); $packagesUrl=array(); $isDebug=$this->getApplication()->getMode()===TApplicationMode::Debug; foreach ($packages as $p) { foreach (self::$_pradoScripts[$p] as $dep) { foreach (self::$_pradoPackages[$dep] as $script) if (!isset($this->_expandedPradoScripts[$script])) { $this->_expandedPradoScripts[$script] = true; if($isDebug) { if (!in_array($url=$baseUrl.'/'.$script,$packagesUrl)) $packagesUrl[]=$url; } else { if (!in_array($url=$baseUrl.'/min/'.$script,$packagesUrl)) { if(!is_file($filePath=$path.'/min/'.$script)) { $dirPath=dirname($filePath); if(!is_dir($dirPath)) mkdir($dirPath, PRADO_CHMOD, true); file_put_contents($filePath, TJavaScript::JSMin(file_get_contents($base.'/'.$script))); chmod($filePath, PRADO_CHMOD); } $packagesUrl[]=$url; } } } } } foreach($packagesUrl as $url) $this->registerScriptFile($url,$url); } } } public function getPradoScriptAssetUrl() { $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH; $assets = Prado::getApplication()->getAssetManager(); return $assets->getPublishedUrl($base); } public function getScriptUrls() { $scripts = array_values($this->_headScriptFiles); $scripts = array_merge($scripts, array_values($this->_scriptFiles)); $scripts = array_unique($scripts); return $scripts; } protected function getPackagePathUrl($base) { $assets = Prado::getApplication()->getAssetManager(); if(strpos($base, $assets->getBaseUrl())===false) { if(($dir = Prado::getPathOfNameSpace($base)) !== null) { $base = $dir; } return array($assets->getPublishedPath($base), $assets->publishFilePath($base)); } else { return array($assets->getBasePath().str_replace($assets->getBaseUrl(),'',$base), $base); } } public function getCallbackReference(ICallbackEventHandler $callbackHandler, $options=null) { $options = !is_array($options) ? array() : $options; $class = new ReflectionClass($callbackHandler); $clientSide = $callbackHandler->getActiveControl()->getClientSide(); $options = array_merge($options, $clientSide->getOptions()->toArray()); $optionString = TJavaScript::encode($options); $this->registerPradoScriptInternal('ajax'); $id = $callbackHandler->getUniqueID(); return "new Prado.CallbackRequest('{$id}',{$optionString})"; } public function registerCallbackControl($class, $options) { $optionString=TJavaScript::encode($options); $code="new {$class}({$optionString});"; $this->_endScripts[sprintf('%08X', crc32($code))]=$code; $this->registerPradoScriptInternal('ajax'); $params=func_get_args(); $this->_page->registerCachingAction('Page.ClientScript','registerCallbackControl',$params); } public function registerPostBackControl($class,$options) { if($class === null) { return; } if(!isset($options['FormID']) && ($form=$this->_page->getForm())!==null) $options['FormID']=$form->getClientID(); $optionString=TJavaScript::encode($options); $code="new {$class}({$optionString});"; $this->_endScripts[sprintf('%08X', crc32($code))]=$code; $this->registerPradoScriptInternal('prado'); $params=func_get_args(); $this->_page->registerCachingAction('Page.ClientScript','registerPostBackControl',$params); } public function registerDefaultButton($panel, $button) { $panelID=is_string($panel)?$panel:$panel->getUniqueID(); if(is_string($button)) $buttonID=$button; else { $button->setIsDefaultButton(true); $buttonID=$button->getUniqueID(); } $options = TJavaScript::encode($this->getDefaultButtonOptions($panelID, $buttonID)); $code = "new Prado.WebUI.DefaultButton($options);"; $this->_endScripts['prado:'.$panelID]=$code; $this->registerPradoScriptInternal('prado'); $params=array($panelID,$buttonID); $this->_page->registerCachingAction('Page.ClientScript','registerDefaultButton',$params); } protected function getDefaultButtonOptions($panelID, $buttonID) { $options['ID'] = TControl::convertUniqueIdToClientId($panelID); $options['Panel'] = TControl::convertUniqueIdToClientId($panelID); $options['Target'] = TControl::convertUniqueIdToClientId($buttonID); $options['EventTarget'] = $buttonID; $options['Event'] = 'click'; return $options; } public function registerFocusControl($target) { $this->registerPradoScriptInternal('jquery'); if($target instanceof TControl) $target=$target->getClientID(); $this->_endScripts['prado:focus'] = 'jQuery(\'#'.$target.'\').focus();'; $params=func_get_args(); $this->_page->registerCachingAction('Page.ClientScript','registerFocusControl',$params); } public function registerStyleSheetFile($key,$url,$media='') { if($media==='') $this->_styleSheetFiles[$key]=$url; else $this->_styleSheetFiles[$key]=array($url,$media); $params=func_get_args(); $this->_page->registerCachingAction('Page.ClientScript','registerStyleSheetFile',$params); } public function registerStyleSheet($key,$css,$media='') { $this->_styleSheets[$key]=$css; $params=func_get_args(); $this->_page->registerCachingAction('Page.ClientScript','registerStyleSheet',$params); } public function getStyleSheetUrls() { $stylesheets = array_values( array_map(function($e) { return is_array($e) ? $e[0] : $e; }, $this->_styleSheetFiles) ); foreach(Prado::getApplication()->getAssetManager()->getPublished() as $path=>$url) if (substr($url,strlen($url)-4)=='.css') $stylesheets[] = $url; $stylesheets = array_unique($stylesheets); return $stylesheets; } public function getStyleSheetCodes() { return array_unique(array_values($this->_styleSheets)); } public function registerHeadScriptFile($key,$url) { $this->checkIfNotInRender(); $this->_headScriptFiles[$key]=$url; $params=func_get_args(); $this->_page->registerCachingAction('Page.ClientScript','registerHeadScriptFile',$params); } public function registerHeadScript($key,$script) { $this->checkIfNotInRender(); $this->_headScripts[$key]=$script; $params=func_get_args(); $this->_page->registerCachingAction('Page.ClientScript','registerHeadScript',$params); } public function registerScriptFile($key, $url) { $this->_scriptFiles[$key]=$url; $params=func_get_args(); $this->_page->registerCachingAction('Page.ClientScript','registerScriptFile',$params); } public function registerBeginScript($key,$script) { $this->checkIfNotInRender(); $this->_beginScripts[$key]=$script; $params=func_get_args(); $this->_page->registerCachingAction('Page.ClientScript','registerBeginScript',$params); } public function registerEndScript($key,$script) { $this->_endScripts[$key]=$script; $params=func_get_args(); $this->_page->registerCachingAction('Page.ClientScript','registerEndScript',$params); } public function registerHiddenField($name,$value) { $this->_hiddenFields[$name]=$value; $params=func_get_args(); $this->_page->registerCachingAction('Page.ClientScript','registerHiddenField',$params); } public function isStyleSheetFileRegistered($key) { return isset($this->_styleSheetFiles[$key]); } public function isStyleSheetRegistered($key) { return isset($this->_styleSheets[$key]); } public function isHeadScriptFileRegistered($key) { return isset($this->_headScriptFiles[$key]); } public function isHeadScriptRegistered($key) { return isset($this->_headScripts[$key]); } public function isScriptFileRegistered($key) { return isset($this->_scriptFiles[$key]); } public function isBeginScriptRegistered($key) { return isset($this->_beginScripts[$key]); } public function isEndScriptRegistered($key) { return isset($this->_endScripts[$key]); } public function hasEndScripts() { return count($this->_endScripts) > 0; } public function hasBeginScripts() { return count($this->_beginScripts) > 0; } public function isHiddenFieldRegistered($key) { return isset($this->_hiddenFields[$key]); } public function renderStyleSheetFiles($writer) { $str=''; foreach($this->_styleSheetFiles as $url) { if(is_array($url)) $str.="\n"; else $str.="\n"; } $writer->write($str); } public function renderStyleSheets($writer) { if(count($this->_styleSheets)) $writer->write("\n"); } public function renderHeadScriptFiles($writer) { $this->renderScriptFiles($writer,$this->_headScriptFiles); } public function renderHeadScripts($writer) { $writer->write(TJavaScript::renderScriptBlocks($this->_headScripts)); } public function renderScriptFilesBegin($writer) { $this->renderAllPendingScriptFiles($writer); } public function renderScriptFilesEnd($writer) { $this->renderAllPendingScriptFiles($writer); } public function markScriptFileAsRendered($url) { $this->_renderedScriptFiles[$url] = $url; $params=func_get_args(); $this->_page->registerCachingAction('Page.ClientScript','markScriptFileAsRendered',$params); } protected function renderScriptFiles($writer, Array $scripts) { foreach($scripts as $script) { $writer->write(TJavaScript::renderScriptFile($script)); $this->markScriptFileAsRendered($script); } } protected function getRenderedScriptFiles() { return $this->_renderedScriptFiles; } public function renderAllPendingScriptFiles($writer) { if(!empty($this->_scriptFiles)) { $addedScripts = array_diff($this->_scriptFiles,$this->getRenderedScriptFiles()); $this->renderScriptFiles($writer,$addedScripts); } } public function renderBeginScripts($writer) { $writer->write(TJavaScript::renderScriptBlocks($this->_beginScripts)); } public function renderEndScripts($writer) { $writer->write(TJavaScript::renderScriptBlocks($this->_endScripts)); } public function renderBeginScriptsCallback($writer) { $writer->write(TJavaScript::renderScriptBlocksCallback($this->_beginScripts)); } public function renderEndScriptsCallback($writer) { $writer->write(TJavaScript::renderScriptBlocksCallback($this->_endScripts)); } public function renderHiddenFieldsBegin($writer) { $this->renderHiddenFieldsInt($writer,true); } public function renderHiddenFieldsEnd($writer) { $this->renderHiddenFieldsInt($writer,false); } public function flushScriptFiles($writer, $control=null) { if(!$this->_page->getIsCallback()) { $this->_page->ensureRenderInForm($control); $this->renderAllPendingScriptFiles($writer); } } protected function renderHiddenFieldsInt($writer, $initial) { if ($initial) $this->_renderedHiddenFields = array(); $str=''; foreach($this->_hiddenFields as $name=>$value) { if (in_array($name,$this->_renderedHiddenFields)) continue; $id=strtr($name,':','_'); if(is_array($value)) { foreach($value as $v) $str.='\n"; } else { $str.='\n"; } $this->_renderedHiddenFields[] = $name; } if($str!=='') $writer->write("
\n".$str."
\n"); } public function getHiddenFields() { return $this->_hiddenFields; } protected function checkIfNotInRender() { if ($form = $this->_page->InFormRender) throw new Exception('Operation invalid when page is already rendering'); } } abstract class TClientSideOptions extends TComponent { private $_options; protected function setFunction($name, $code) { if(!TJavaScript::isJsLiteral($code)) $code = TJavaScript::quoteJsLiteral($this->ensureFunction($code)); $this->setOption($name, $code); } protected function getOption($name) { if ($this->_options) return $this->_options->itemAt($name); else return null; } protected function setOption($name, $value) { $this->getOptions()->add($name, $value); } public function getOptions() { if (!$this->_options) $this->_options = Prado::createComponent('System.Collections.TMap'); return $this->_options; } protected function ensureFunction($javascript) { return "function(sender, parameter){ {$javascript} }"; } } class TPage extends TTemplateControl { const FIELD_POSTBACK_TARGET='PRADO_POSTBACK_TARGET'; const FIELD_POSTBACK_PARAMETER='PRADO_POSTBACK_PARAMETER'; const FIELD_LASTFOCUS='PRADO_LASTFOCUS'; const FIELD_PAGESTATE='PRADO_PAGESTATE'; const FIELD_CALLBACK_TARGET='PRADO_CALLBACK_TARGET'; const FIELD_CALLBACK_PARAMETER='PRADO_CALLBACK_PARAMETER'; private static $_systemPostFields=array( 'PRADO_POSTBACK_TARGET'=>true, 'PRADO_POSTBACK_PARAMETER'=>true, 'PRADO_LASTFOCUS'=>true, 'PRADO_PAGESTATE'=>true, 'PRADO_CALLBACK_TARGET'=>true, 'PRADO_CALLBACK_PARAMETER'=>true ); private $_form; private $_head; private $_validators=array(); private $_validated=false; private $_theme; private $_title; private $_styleSheet; private $_clientScript; protected $_postData; protected $_restPostData; protected $_controlsPostDataChanged=array(); protected $_controlsRequiringPostData=array(); protected $_controlsRegisteredForPostData=array(); private $_postBackEventTarget; private $_postBackEventParameter; protected $_formRendered=false; protected $_inFormRender=false; private $_focus; private $_pagePath=''; private $_enableStateValidation=true; private $_enableStateEncryption=false; private $_enableStateCompression=true; private $_statePersisterClass='System.Web.UI.TPageStatePersister'; private $_statePersister; private $_cachingStack; private $_clientState=''; protected $_isLoadingPostData=false; private $_enableJavaScript=true; private $_writer; public function __construct() { $this->setPage($this); } public function run($writer) { $this->_writer = $writer; $this->determinePostBackMode(); if($this->getIsPostBack()) { if($this->getIsCallback()) $this->processCallbackRequest($writer); else $this->processPostBackRequest($writer); } else $this->processNormalRequest($writer); $this->_writer = null; } protected function processNormalRequest($writer) { $this->onPreInit(null); $this->initRecursive(); $this->onInitComplete(null); $this->onPreLoad(null); $this->loadRecursive(); $this->onLoadComplete(null); $this->preRenderRecursive(); $this->onPreRenderComplete(null); $this->savePageState(); $this->onSaveStateComplete(null); $this->renderControl($writer); $this->unloadRecursive(); } protected function processPostBackRequest($writer) { $this->onPreInit(null); $this->initRecursive(); $this->onInitComplete(null); $this->_restPostData=new TMap; $this->loadPageState(); $this->processPostData($this->_postData,true); $this->onPreLoad(null); $this->loadRecursive(); $this->processPostData($this->_restPostData,false); $this->raiseChangedEvents(); $this->raisePostBackEvent(); $this->onLoadComplete(null); $this->preRenderRecursive(); $this->onPreRenderComplete(null); $this->savePageState(); $this->onSaveStateComplete(null); $this->renderControl($writer); $this->unloadRecursive(); } protected static function decodeUTF8($data, $enc) { if(is_array($data)) { foreach($data as $k=>$v) $data[$k]=self::decodeUTF8($v, $enc); return $data; } elseif(is_string($data)) { return iconv('UTF-8',$enc.'//IGNORE',$data); } else { return $data; } } protected function processCallbackRequest($writer) { Prado::using('System.Web.UI.ActiveControls.TActivePageAdapter'); Prado::using('System.Web.UI.JuiControls.TJuiControlOptions'); $this->setAdapter(new TActivePageAdapter($this)); $callbackEventParameter = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_PARAMETER); if(strlen($callbackEventParameter) > 0) $this->_postData[TPage::FIELD_CALLBACK_PARAMETER]=TJavaScript::jsonDecode((string)$callbackEventParameter); if (($g=$this->getApplication()->getGlobalization(false))!==null && strtoupper($enc=$g->getCharset())!='UTF-8') foreach ($this->_postData as $k=>$v) $this->_postData[$k]=self::decodeUTF8($v, $enc); $this->onPreInit(null); $this->initRecursive(); $this->onInitComplete(null); $this->_restPostData=new TMap; $this->loadPageState(); $this->processPostData($this->_postData,true); $this->onPreLoad(null); $this->loadRecursive(); $this->processPostData($this->_restPostData,false); $this->raiseChangedEvents(); $this->getAdapter()->processCallbackEvent($writer); $this->onLoadComplete(null); $this->preRenderRecursive(); $this->onPreRenderComplete(null); $this->savePageState(); $this->onSaveStateComplete(null); $this->getAdapter()->renderCallbackResponse($writer); $this->unloadRecursive(); } public function getCallbackClient() { if($this->getAdapter() !== null) return $this->getAdapter()->getCallbackClientHandler(); else return new TCallbackClientScript(); } public function setCallbackClient($client) { $this->getAdapter()->setCallbackClientHandler($client); } public function getCallbackEventTarget() { return $this->getAdapter()->getCallbackEventTarget(); } public function setCallbackEventTarget(TControl $control) { $this->getAdapter()->setCallbackEventTarget($control); } public function getCallbackEventParameter() { return $this->getAdapter()->getCallbackEventParameter(); } public function setCallbackEventParameter($value) { $this->getAdapter()->setCallbackEventParameter($value); } public function getForm() { return $this->_form; } public function setForm(TForm $form) { if($this->_form===null) $this->_form=$form; else throw new TInvalidOperationException('page_form_duplicated'); } public function getValidators($validationGroup=null) { if(!$this->_validators) $this->_validators=new TList; if(empty($validationGroup) === true) return $this->_validators; else { $list=new TList; foreach($this->_validators as $validator) if($validator->getValidationGroup()===$validationGroup) $list->add($validator); return $list; } } public function validate($validationGroup=null) { $this->_validated=true; if($this->_validators && $this->_validators->getCount()) { if($validationGroup===null) { foreach($this->_validators as $validator) $validator->validate(); } else { foreach($this->_validators as $validator) { if($validator->getValidationGroup()===$validationGroup) $validator->validate(); } } } } public function getIsValid() { if($this->_validated) { if($this->_validators && $this->_validators->getCount()) { foreach($this->_validators as $validator) if(!$validator->getIsValid()) return false; } return true; } else throw new TInvalidOperationException('page_isvalid_unknown'); } public function getTheme() { if(is_string($this->_theme)) $this->_theme=$this->getService()->getThemeManager()->getTheme($this->_theme); return $this->_theme; } public function setTheme($value) { $this->_theme=empty($value)?null:$value; } public function getStyleSheetTheme() { if(is_string($this->_styleSheet)) $this->_styleSheet=$this->getService()->getThemeManager()->getTheme($this->_styleSheet); return $this->_styleSheet; } public function setStyleSheetTheme($value) { $this->_styleSheet=empty($value)?null:$value; } public function applyControlSkin($control) { if(($theme=$this->getTheme())!==null) $theme->applySkin($control); } public function applyControlStyleSheet($control) { if(($theme=$this->getStyleSheetTheme())!==null) $theme->applySkin($control); } public function getClientScript() { if(!$this->_clientScript) { $className = $classPath = $this->getService()->getClientScriptManagerClass(); Prado::using($className); if(($pos=strrpos($className,'.'))!==false) $className=substr($className,$pos+1); if(!class_exists($className,false) || ($className!=='TClientScriptManager' && !is_subclass_of($className,'TClientScriptManager'))) throw new THttpException(404,'page_csmanagerclass_invalid',$classPath); $this->_clientScript=new $className($this); } return $this->_clientScript; } public function onPreInit($param) { $this->raiseEvent('OnPreInit',$this,$param); } public function onInitComplete($param) { $this->raiseEvent('OnInitComplete',$this,$param); } public function onPreLoad($param) { $this->raiseEvent('OnPreLoad',$this,$param); } public function onLoadComplete($param) { $this->raiseEvent('OnLoadComplete',$this,$param); } public function onPreRenderComplete($param) { $this->raiseEvent('OnPreRenderComplete',$this,$param); $cs=$this->getClientScript(); $theme=$this->getTheme(); if($theme instanceof ITheme) { foreach($theme->getStyleSheetFiles() as $url) $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url)); foreach($theme->getJavaScriptFiles() as $url) $cs->registerHeadScriptFile($url,$url); } $styleSheet=$this->getStyleSheetTheme(); if($styleSheet instanceof ITheme) { foreach($styleSheet->getStyleSheetFiles() as $url) $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url)); foreach($styleSheet->getJavaScriptFiles() as $url) $cs->registerHeadScriptFile($url,$url); } if($cs->getRequiresHead() && $this->getHead()===null) throw new TConfigurationException('page_head_required'); } private function getCssMediaType($url) { $segs=explode('.',basename($url)); if(isset($segs[2])) return $segs[count($segs)-2]; else return ''; } public function onSaveStateComplete($param) { $this->raiseEvent('OnSaveStateComplete',$this,$param); } private function determinePostBackMode() { $postData=$this->getRequest(); if($postData->contains(self::FIELD_PAGESTATE) || $postData->contains(self::FIELD_POSTBACK_TARGET)) $this->_postData=$postData; } public function getIsPostBack() { return $this->_postData!==null; } public function getIsCallback() { return $this->getIsPostBack() && $this->getRequest()->contains(self::FIELD_CALLBACK_TARGET); } public function saveState() { parent::saveState(); $this->setViewState('ControlsRequiringPostBack',$this->_controlsRegisteredForPostData,array()); } public function loadState() { parent::loadState(); $this->_controlsRequiringPostData=$this->getViewState('ControlsRequiringPostBack',array()); } protected function loadPageState() { $state=$this->getStatePersister()->load(); $this->loadStateRecursive($state,$this->getEnableViewState()); } protected function savePageState() { $state=&$this->saveStateRecursive($this->getEnableViewState()); $this->getStatePersister()->save($state); } protected function isSystemPostField($field) { return isset(self::$_systemPostFields[$field]); } public function registerRequiresPostData($control) { $id=is_string($control)?$control:$control->getUniqueID(); $this->_controlsRegisteredForPostData[$id]=true; $params=func_get_args(); foreach($this->getCachingStack() as $item) $item->registerAction('Page','registerRequiresPostData',array($id)); } public function getPostBackEventTarget() { if($this->_postBackEventTarget===null && $this->_postData!==null) { $eventTarget=$this->_postData->itemAt(self::FIELD_POSTBACK_TARGET); if(!empty($eventTarget)) $this->_postBackEventTarget=$this->findControl($eventTarget); } return $this->_postBackEventTarget; } public function setPostBackEventTarget(TControl $control) { $this->_postBackEventTarget=$control; } public function getPostBackEventParameter() { if($this->_postBackEventParameter===null && $this->_postData!==null) { if(($this->_postBackEventParameter=$this->_postData->itemAt(self::FIELD_POSTBACK_PARAMETER))===null) $this->_postBackEventParameter=''; } return $this->_postBackEventParameter; } public function setPostBackEventParameter($value) { $this->_postBackEventParameter=$value; } protected function processPostData($postData,$beforeLoad) { $this->_isLoadingPostData=true; if($beforeLoad) $this->_restPostData=new TMap; foreach($postData as $key=>$value) { if($this->isSystemPostField($key)) continue; else if($control=$this->findControl($key)) { if($control instanceof IPostBackDataHandler) { if($control->loadPostData($key,$postData)) $this->_controlsPostDataChanged[]=$control; } else if($control instanceof IPostBackEventHandler && empty($this->_postData[self::FIELD_POSTBACK_TARGET])) { $this->_postData->add(self::FIELD_POSTBACK_TARGET,$key); } unset($this->_controlsRequiringPostData[$key]); } else if($beforeLoad) $this->_restPostData->add($key,$value); } foreach($this->_controlsRequiringPostData as $key=>$value) { if($control=$this->findControl($key)) { if($control instanceof IPostBackDataHandler) { if($control->loadPostData($key,$this->_postData)) $this->_controlsPostDataChanged[]=$control; } else throw new TInvalidDataValueException('page_postbackcontrol_invalid',$key); unset($this->_controlsRequiringPostData[$key]); } } $this->_isLoadingPostData=false; } public function getIsLoadingPostData() { return $this->_isLoadingPostData; } protected function raiseChangedEvents() { foreach($this->_controlsPostDataChanged as $control) $control->raisePostDataChangedEvent(); } protected function raisePostBackEvent() { if(($postBackHandler=$this->getPostBackEventTarget())===null) $this->validate(); else if($postBackHandler instanceof IPostBackEventHandler) $postBackHandler->raisePostBackEvent($this->getPostBackEventParameter()); } public function getInFormRender() { return $this->_inFormRender; } public function ensureRenderInForm($control) { if(!$this->getIsCallback() && !$this->_inFormRender) throw new TConfigurationException('page_control_outofform',get_class($control), $control ? $control->getUniqueID() : null); } public function beginFormRender($writer) { if($this->_formRendered) throw new TConfigurationException('page_form_duplicated'); $this->_formRendered=true; $this->getClientScript()->registerHiddenField(self::FIELD_PAGESTATE,$this->getClientState()); $this->_inFormRender=true; } public function endFormRender($writer) { if($this->_focus) { if(($this->_focus instanceof TControl) && $this->_focus->getVisible(true)) $focus=$this->_focus->getClientID(); else $focus=$this->_focus; $this->getClientScript()->registerFocusControl($focus); } else if($this->_postData && ($lastFocus=$this->_postData->itemAt(self::FIELD_LASTFOCUS))!==null) $this->getClientScript()->registerFocusControl($lastFocus); $this->_inFormRender=false; } public function setFocus($value) { $this->_focus=$value; } public function getClientSupportsJavaScript() { return $this->_enableJavaScript; } public function setClientSupportsJavaScript($value) { $this->_enableJavaScript=TPropertyValue::ensureBoolean($value); } public function getHead() { return $this->_head; } public function setHead(THead $value) { if($this->_head) throw new TInvalidOperationException('page_head_duplicated'); $this->_head=$value; if($this->_title!==null) { $this->_head->setTitle($this->_title); $this->_title=null; } } public function getTitle() { if($this->_head) return $this->_head->getTitle(); else return $this->_title===null ? '' : $this->_title; } public function setTitle($value) { if($this->_head) $this->_head->setTitle($value); else $this->_title=$value; } public function getClientState() { return $this->_clientState; } public function setClientState($state) { $this->_clientState=$state; } public function getRequestClientState() { return $this->getRequest()->itemAt(self::FIELD_PAGESTATE); } public function getStatePersisterClass() { return $this->_statePersisterClass; } public function setStatePersisterClass($value) { $this->_statePersisterClass=$value; } public function getStatePersister() { if($this->_statePersister===null) { $this->_statePersister=Prado::createComponent($this->_statePersisterClass); if(!($this->_statePersister instanceof IPageStatePersister)) throw new TInvalidDataTypeException('page_statepersister_invalid'); $this->_statePersister->setPage($this); } return $this->_statePersister; } public function getEnableStateValidation() { return $this->_enableStateValidation; } public function setEnableStateValidation($value) { $this->_enableStateValidation=TPropertyValue::ensureBoolean($value); } public function getEnableStateEncryption() { return $this->_enableStateEncryption; } public function setEnableStateEncryption($value) { $this->_enableStateEncryption=TPropertyValue::ensureBoolean($value); } public function getEnableStateCompression() { return $this->_enableStateCompression; } public function setEnableStateCompression($value) { $this->_enableStateCompression=TPropertyValue::ensureBoolean($value); } public function getPagePath() { return $this->_pagePath; } public function setPagePath($value) { $this->_pagePath=$value; } public function registerCachingAction($context,$funcName,$funcParams) { if($this->_cachingStack) { foreach($this->_cachingStack as $cache) $cache->registerAction($context,$funcName,$funcParams); } } public function getCachingStack() { if(!$this->_cachingStack) $this->_cachingStack=new TStack; return $this->_cachingStack; } public function flushWriter() { if ($this->_writer) $this->Response->write($this->_writer->flush()); } } interface IPageStatePersister { public function getPage(); public function setPage(TPage $page); public function save($state); public function load(); } class TPageStateFormatter { public static function serialize($page,$data) { $sm=$page->getApplication()->getSecurityManager(); if($page->getEnableStateValidation()) $str=$sm->hashData(serialize($data)); else $str=serialize($data); if($page->getEnableStateCompression() && extension_loaded('zlib')) $str=gzcompress($str); if($page->getEnableStateEncryption()) $str=$sm->encrypt($str); return base64_encode($str); } public static function unserialize($page,$data) { $str=base64_decode($data); if($str==='') return null; if($str!==false) { $sm=$page->getApplication()->getSecurityManager(); if($page->getEnableStateEncryption()) $str=$sm->decrypt($str); if($page->getEnableStateCompression() && extension_loaded('zlib')) $str=@gzuncompress($str); if($page->getEnableStateValidation()) { if(($str=$sm->validateData($str))!==false) return unserialize($str); } else return unserialize($str); } return null; } } class TOutputCache extends TControl implements INamingContainer { const CACHE_ID_PREFIX='prado:outputcache'; private $_cacheModuleID=''; private $_dataCached=false; private $_cacheAvailable=false; private $_cacheChecked=false; private $_cacheKey=null; private $_duration=60; private $_cache=null; private $_contents; private $_state; private $_actions=array(); private $_varyByParam=''; private $_keyPrefix=''; private $_varyBySession=false; private $_cachePostBack=false; private $_cacheTime=0; public function getAllowChildControls() { $this->determineCacheability(); return !$this->_dataCached; } private function determineCacheability() { if(!$this->_cacheChecked) { $this->_cacheChecked=true; if($this->_duration>0 && ($this->_cachePostBack || !$this->getPage()->getIsPostBack())) { if($this->_cacheModuleID!=='') { $this->_cache=$this->getApplication()->getModule($this->_cacheModuleID); if(!($this->_cache instanceof ICache)) throw new TConfigurationException('outputcache_cachemoduleid_invalid',$this->_cacheModuleID); } else $this->_cache=$this->getApplication()->getCache(); if($this->_cache!==null) { $this->_cacheAvailable=true; $data=$this->_cache->get($this->getCacheKey()); if(is_array($data)) { $param=new TOutputCacheCheckDependencyEventParameter; $param->setCacheTime(isset($data[3])?$data[3]:0); $this->onCheckDependency($param); $this->_dataCached=$param->getIsValid(); } else $this->_dataCached=false; if($this->_dataCached) list($this->_contents,$this->_state,$this->_actions,$this->_cacheTime)=$data; } } } } protected function initRecursive($namingContainer=null) { if($this->_cacheAvailable && !$this->_dataCached) { $stack=$this->getPage()->getCachingStack(); $stack->push($this); parent::initRecursive($namingContainer); $stack->pop(); } else parent::initRecursive($namingContainer); } protected function loadRecursive() { if($this->_cacheAvailable && !$this->_dataCached) { $stack=$this->getPage()->getCachingStack(); $stack->push($this); parent::loadRecursive(); $stack->pop(); } else { if($this->_dataCached) $this->performActions(); parent::loadRecursive(); } } private function performActions() { $page=$this->getPage(); $cs=$page->getClientScript(); foreach($this->_actions as $action) { if($action[0]==='Page.ClientScript') call_user_func_array(array($cs,$action[1]),$action[2]); else if($action[0]==='Page') call_user_func_array(array($page,$action[1]),$action[2]); else call_user_func_array(array($this->getSubProperty($action[0]),$action[1]),$action[2]); } } protected function preRenderRecursive() { if($this->_cacheAvailable && !$this->_dataCached) { $stack=$this->getPage()->getCachingStack(); $stack->push($this); parent::preRenderRecursive(); $stack->pop(); } else parent::preRenderRecursive(); } protected function loadStateRecursive(&$state,$needViewState=true) { $st=unserialize($state); parent::loadStateRecursive($st,$needViewState); } protected function &saveStateRecursive($needViewState=true) { if($this->_dataCached) return $this->_state; else { $st=parent::saveStateRecursive($needViewState); $this->_state=serialize($st); return $this->_state; } } public function registerAction($context,$funcName,$funcParams) { $this->_actions[]=array($context,$funcName,$funcParams); } public function getCacheKey() { if($this->_cacheKey===null) $this->_cacheKey=$this->calculateCacheKey(); return $this->_cacheKey; } protected function calculateCacheKey() { $key=$this->getBaseCacheKey(); if($this->_varyBySession) $key.=$this->getSession()->getSessionID(); if($this->_varyByParam!=='') { $params=array(); $request=$this->getRequest(); foreach(explode(',',$this->_varyByParam) as $name) { $name=trim($name); $params[$name]=$request->itemAt($name); } $key.=serialize($params); } $param=new TOutputCacheCalculateKeyEventParameter; $this->onCalculateKey($param); $key.=$param->getCacheKey(); return $key; } protected function getBaseCacheKey() { return self::CACHE_ID_PREFIX.$this->_keyPrefix.$this->getPage()->getPagePath().$this->getUniqueID(); } public function getCacheModuleID() { return $this->_cacheModuleID; } public function setCacheModuleID($value) { $this->_cacheModuleID=$value; } public function setCacheKeyPrefix($value) { $this->_keyPrefix=$value; } public function getCacheTime() { return $this->_cacheTime; } protected function getCacheDependency() { return null; } public function getContentCached() { return $this->_dataCached; } public function getDuration() { return $this->_duration; } public function setDuration($value) { if(($value=TPropertyValue::ensureInteger($value))<0) throw new TInvalidDataValueException('outputcache_duration_invalid',get_class($this)); $this->_duration=$value; } public function getVaryByParam() { return $this->_varyByParam; } public function setVaryByParam($value) { $this->_varyByParam=trim($value); } public function getVaryBySession() { return $this->_varyBySession; } public function setVaryBySession($value) { $this->_varyBySession=TPropertyValue::ensureBoolean($value); } public function getCachingPostBack() { return $this->_cachePostBack; } public function setCachingPostBack($value) { $this->_cachePostBack=TPropertyValue::ensureBoolean($value); } public function onCheckDependency($param) { $this->raiseEvent('OnCheckDependency',$this,$param); } public function onCalculateKey($param) { $this->raiseEvent('OnCalculateKey',$this,$param); } public function render($writer) { if($this->_dataCached) $writer->write($this->_contents); else if($this->_cacheAvailable) { $textwriter = new TTextWriter(); $multiwriter = new TOutputCacheTextWriterMulti(array($writer->getWriter(),$textwriter)); $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), $multiwriter); $stack=$this->getPage()->getCachingStack(); $stack->push($this); parent::render($htmlWriter); $stack->pop(); $content=$textwriter->flush(); $data=array($content,$this->_state,$this->_actions,time()); $this->_cache->set($this->getCacheKey(),$data,$this->getDuration(),$this->getCacheDependency()); } else parent::render($writer); } } class TOutputCacheCheckDependencyEventParameter extends TEventParameter { private $_isValid=true; private $_cacheTime=0; public function getIsValid() { return $this->_isValid; } public function setIsValid($value) { $this->_isValid=TPropertyValue::ensureBoolean($value); } public function getCacheTime() { return $this->_cacheTime; } public function setCacheTime($value) { $this->_cacheTime=TPropertyValue::ensureInteger($value); } } class TOutputCacheCalculateKeyEventParameter extends TEventParameter { private $_cacheKey=''; public function getCacheKey() { return $this->_cacheKey; } public function setCacheKey($value) { $this->_cacheKey=TPropertyValue::ensureString($value); } } class TOutputCacheTextWriterMulti extends TTextWriter { protected $_writers; public function __construct(Array $writers) { $this->_writers = $writers; } public function write($s) { foreach($this->_writers as $writer) $writer->write($s); } public function flush() { foreach($this->_writers as $writer) $s = $writer->flush(); return $s; } } class TTemplateManager extends TModule { const TEMPLATE_FILE_EXT='.tpl'; const TEMPLATE_CACHE_PREFIX='prado:template:'; public function init($config) { $this->getService()->setTemplateManager($this); } public function getTemplateByClassName($className) { $class=new ReflectionClass($className); $tplFile=dirname($class->getFileName()).DIRECTORY_SEPARATOR.$className.self::TEMPLATE_FILE_EXT; return $this->getTemplateByFileName($tplFile); } public function getTemplateByFileName($fileName) { if(($fileName=$this->getLocalizedTemplate($fileName))!==null) { if(($cache=$this->getApplication()->getCache())===null) return new TTemplate(file_get_contents($fileName),dirname($fileName),$fileName); else { $array=$cache->get(self::TEMPLATE_CACHE_PREFIX.$fileName); if(is_array($array)) { list($template,$timestamps)=$array; if($this->getApplication()->getMode()===TApplicationMode::Performance) return $template; $cacheValid=true; foreach($timestamps as $tplFile=>$timestamp) { if(!is_file($tplFile) || filemtime($tplFile)>$timestamp) { $cacheValid=false; break; } } if($cacheValid) return $template; } $template=new TTemplate(file_get_contents($fileName),dirname($fileName),$fileName); $includedFiles=$template->getIncludedFiles(); $timestamps=array(); $timestamps[$fileName]=filemtime($fileName); foreach($includedFiles as $includedFile) $timestamps[$includedFile]=filemtime($includedFile); $cache->set(self::TEMPLATE_CACHE_PREFIX.$fileName,array($template,$timestamps)); return $template; } } else return null; } protected function getLocalizedTemplate($filename) { if(($app=$this->getApplication()->getGlobalization(false))===null) return is_file($filename)?$filename:null; foreach($app->getLocalizedResource($filename) as $file) { if(($file=realpath($file))!==false && is_file($file)) return $file; } return null; } } class TTemplate extends TApplicationComponent implements ITemplate { const REGEX_RULES='/||<\/?com:([\w\.]+)((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?"|\s*[\w\.]+\s*=\s*<%.*?%>)*)\s*\/?>|<\/?prop:([\w\.]+)\s*>|<%@\s*((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?")*)\s*%>|<%[%#~\/\\$=\\[](.*?)%>|)*)\s*\/>/msS'; const CONFIG_DATABIND=0; const CONFIG_EXPRESSION=1; const CONFIG_ASSET=2; const CONFIG_PARAMETER=3; const CONFIG_LOCALIZATION=4; const CONFIG_TEMPLATE=5; private $_tpl=array(); private $_directive=array(); private $_contextPath; private $_tplFile=null; private $_startingLine=0; private $_content; private $_sourceTemplate=true; private $_hashCode=''; private $_tplControl=null; private $_includedFiles=array(); private $_includeAtLine=array(); private $_includeLines=array(); public function __construct($template,$contextPath,$tplFile=null,$startingLine=0,$sourceTemplate=true) { $this->_sourceTemplate=$sourceTemplate; $this->_contextPath=$contextPath; $this->_tplFile=$tplFile; $this->_startingLine=$startingLine; $this->_content=$template; $this->_hashCode=md5($template); $this->parse($template); $this->_content=null; } public function getTemplateFile() { return $this->_tplFile; } public function getIsSourceTemplate() { return $this->_sourceTemplate; } public function getContextPath() { return $this->_contextPath; } public function getDirective() { return $this->_directive; } public function getHashCode() { return $this->_hashCode; } public function &getItems() { return $this->_tpl; } public function instantiateIn($tplControl,$parentControl=null) { $this->_tplControl=$tplControl; if($parentControl===null) $parentControl=$tplControl; if(($page=$tplControl->getPage())===null) $page=$this->getService()->getRequestedPage(); $controls=array(); $directChildren=array(); foreach($this->_tpl as $key=>$object) { if($object[0]===-1) $parent=$parentControl; else if(isset($controls[$object[0]])) $parent=$controls[$object[0]]; else continue; if(isset($object[2])) { $component=Prado::createComponent($object[1]); $properties=&$object[2]; if($component instanceof TControl) { if($component instanceof TOutputCache) $component->setCacheKeyPrefix($this->_hashCode.$key); $component->setTemplateControl($tplControl); if(isset($properties['id'])) { if(is_array($properties['id'])) $properties['id']=$component->evaluateExpression($properties['id'][1]); $tplControl->registerObject($properties['id'],$component); } if(isset($properties['skinid'])) { if(is_array($properties['skinid'])) $component->setSkinID($component->evaluateExpression($properties['skinid'][1])); else $component->setSkinID($properties['skinid']); unset($properties['skinid']); } $component->trackViewState(false); $component->applyStyleSheetSkin($page); foreach($properties as $name=>$value) $this->configureControl($component,$name,$value); $component->trackViewState(true); if($parent===$parentControl) $directChildren[]=$component; else $component->createdOnTemplate($parent); if($component->getAllowChildControls()) $controls[$key]=$component; } else if($component instanceof TComponent) { $controls[$key]=$component; if(isset($properties['id'])) { if(is_array($properties['id'])) $properties['id']=$component->evaluateExpression($properties['id'][1]); $tplControl->registerObject($properties['id'],$component); if(!$component->hasProperty('id')) unset($properties['id']); } foreach($properties as $name=>$value) $this->configureComponent($component,$name,$value); if($parent===$parentControl) $directChildren[]=$component; else $component->createdOnTemplate($parent); } } else { if($object[1] instanceof TCompositeLiteral) { $o=clone $object[1]; $o->setContainer($tplControl); if($parent===$parentControl) $directChildren[]=$o; else $parent->addParsedObject($o); } else { if($parent===$parentControl) $directChildren[]=$object[1]; else $parent->addParsedObject($object[1]); } } } foreach($directChildren as $control) { if($control instanceof TComponent) $control->createdOnTemplate($parentControl); else $parentControl->addParsedObject($control); } } protected function configureControl($control,$name,$value) { if(strncasecmp($name,'on',2)===0) $this->configureEvent($control,$name,$value,$control); else if(($pos=strrpos($name,'.'))===false) $this->configureProperty($control,$name,$value); else $this->configureSubProperty($control,$name,$value); } protected function configureComponent($component,$name,$value) { if(strpos($name,'.')===false) $this->configureProperty($component,$name,$value); else $this->configureSubProperty($component,$name,$value); } protected function configureEvent($control,$name,$value,$contextControl) { if(strpos($value,'.')===false) $control->attachEventHandler($name,array($contextControl,'TemplateControl.'.$value)); else $control->attachEventHandler($name,array($contextControl,$value)); } protected function configureProperty($component,$name,$value) { if(is_array($value)) { switch($value[0]) { case self::CONFIG_DATABIND: $component->bindProperty($name,$value[1]); break; case self::CONFIG_EXPRESSION: if($component instanceof TControl) $component->autoBindProperty($name,$value[1]); else { $setter='set'.$name; $component->$setter($this->_tplControl->evaluateExpression($value[1])); } break; case self::CONFIG_TEMPLATE: $setter='set'.$name; $component->$setter($value[1]); break; case self::CONFIG_ASSET: $setter='set'.$name; $url=$this->publishFilePath($this->_contextPath.DIRECTORY_SEPARATOR.$value[1]); $component->$setter($url); break; case self::CONFIG_PARAMETER: $setter='set'.$name; $component->$setter($this->getApplication()->getParameters()->itemAt($value[1])); break; case self::CONFIG_LOCALIZATION: $setter='set'.$name; $component->$setter(Prado::localize($value[1])); break; default: throw new TConfigurationException('template_tag_unexpected',$name,$value[1]); break; } } else { if (substr($name,0,2)=='js') if ($value and !($value instanceof TJavaScriptLiteral)) $value = new TJavaScriptLiteral($value); $setter='set'.$name; $component->$setter($value); } } protected function configureSubProperty($component,$name,$value) { if(is_array($value)) { switch($value[0]) { case self::CONFIG_DATABIND: $component->bindProperty($name,$value[1]); break; case self::CONFIG_EXPRESSION: if($component instanceof TControl) $component->autoBindProperty($name,$value[1]); else $component->setSubProperty($name,$this->_tplControl->evaluateExpression($value[1])); break; case self::CONFIG_TEMPLATE: $component->setSubProperty($name,$value[1]); break; case self::CONFIG_ASSET: $url=$this->publishFilePath($this->_contextPath.DIRECTORY_SEPARATOR.$value[1]); $component->setSubProperty($name,$url); break; case self::CONFIG_PARAMETER: $component->setSubProperty($name,$this->getApplication()->getParameters()->itemAt($value[1])); break; case self::CONFIG_LOCALIZATION: $component->setSubProperty($name,Prado::localize($value[1])); break; default: throw new TConfigurationException('template_tag_unexpected',$name,$value[1]); break; } } else $component->setSubProperty($name,$value); } protected function parse($input) { $input=$this->preprocess($input); $tpl=&$this->_tpl; $n=preg_match_all(self::REGEX_RULES,$input,$matches,PREG_SET_ORDER|PREG_OFFSET_CAPTURE); $expectPropEnd=false; $textStart=0; $stack=array(); $container=-1; $matchEnd=0; $c=0; $this->_directive=null; try { for($i=0;$i<$n;++$i) { $match=&$matches[$i]; $str=$match[0][0]; $matchStart=$match[0][1]; $matchEnd=$matchStart+strlen($str)-1; if(strpos($str,'$textStart) $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); $textStart=$matchEnd+1; $type=$match[1][0]; $attributes=$this->parseAttributes($match[2][0],$match[2][1]); $this->validateAttributes($type,$attributes); $tpl[$c++]=array($container,$type,$attributes); if($str[strlen($str)-2]!=='/') { $stack[] = $type; $container=$c-1; } } else if(strpos($str,'$textStart) $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); $textStart=$matchEnd+1; $type=$match[1][0]; if(empty($stack)) throw new TConfigurationException('template_closingtag_unexpected',""); $name=array_pop($stack); if($name!==$type) { $tag=$name[0]==='@' ? '' : ""; throw new TConfigurationException('template_closingtag_expected',$tag); } $container=$tpl[$container][0]; } else if(strpos($str,'<%@')===0) { if($expectPropEnd) continue; if($matchStart>$textStart) $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); $textStart=$matchEnd+1; if(isset($tpl[0]) || $this->_directive!==null) throw new TConfigurationException('template_directive_nonunique'); $this->_directive=$this->parseAttributes($match[4][0],$match[4][1]); } else if(strpos($str,'<%')===0) { if($expectPropEnd) continue; if($matchStart>$textStart) $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); $textStart=$matchEnd+1; $literal=trim($match[5][0]); if($str[2]==='=') $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,$literal)); else if($str[2]==='%') $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_STATEMENTS,$literal)); else if($str[2]==='#') $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_DATABINDING,$literal)); else if($str[2]==='$') $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"\$this->getApplication()->getParameters()->itemAt('$literal')")); else if($str[2]==='~') $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"\$this->publishFilePath('$this->_contextPath/$literal')")); else if($str[2]==='/') $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"rtrim(dirname(\$this->getApplication()->getRequest()->getApplicationUrl()), '/').'/$literal'")); else if($str[2]==='[') { $literal=strtr(trim(substr($literal,0,strlen($literal)-1)),array("'"=>"\'","\\"=>"\\\\")); $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"Prado::localize('$literal')")); } } else if(strpos($str,'')===strlen($str)-2) { if($expectPropEnd) continue; if($matchStart>$textStart) $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); $textStart=$matchEnd+1; $prop=strtolower($match[6][0]); $attrs=$this->parseAttributes($match[7][0],$match[7][1]); $attributes=array(); foreach($attrs as $name=>$value) $attributes[$prop.'.'.$name]=$value; $type=$tpl[$container][1]; $this->validateAttributes($type,$attributes); foreach($attributes as $name=>$value) { if(isset($tpl[$container][2][$name])) throw new TConfigurationException('template_property_duplicated',$name); $tpl[$container][2][$name]=$value; } } else { $prop=strtolower($match[3][0]); $stack[] = '@'.$prop; if(!$expectPropEnd) { if($matchStart>$textStart) $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); $textStart=$matchEnd+1; $expectPropEnd=true; } } } else if(strpos($str,'"); $name=array_pop($stack); if($name!=='@'.$prop) { $tag=$name[0]==='@' ? '' : ""; throw new TConfigurationException('template_closingtag_expected',$tag); } if(($last=count($stack))<1 || $stack[$last-1][0]!=='@') { if($matchStart>$textStart) { $value=substr($input,$textStart,$matchStart-$textStart); if(substr($prop,-8,8)==='template') $value=$this->parseTemplateProperty($value,$textStart); else $value=$this->parseAttribute($value); if($container>=0) { $type=$tpl[$container][1]; $this->validateAttributes($type,array($prop=>$value)); if(isset($tpl[$container][2][$prop])) throw new TConfigurationException('template_property_duplicated',$prop); $tpl[$container][2][$prop]=$value; } else $this->_directive[$prop]=$value; $textStart=$matchEnd+1; } $expectPropEnd=false; } } else if(strpos($str,'