Init Repo

This commit is contained in:
root
2019-09-06 23:53:10 +08:00
commit f0ef89dfbb
7905 changed files with 914138 additions and 0 deletions

View File

@ -0,0 +1,236 @@
<?php
/**
* The Action class
*/
abstract class LtAction
{
/**
* The context object
*
* @var object
*/
public $context;
public $viewDir;
public $viewTplDir;
public $viewTplAutoCompile;
/**
* The dtd config for validator
*
* @var array
*/
protected $dtds = array();
/**
* The Access Control List
*
* @var array
*/
protected $acl;
/**
* The current user's roles
*
* @var array
*/
protected $roles = array();
/**
* A flag to indicate if subclass call LtAction::__construct()
*
* @var boolean
*/
protected $constructed = false;
/**
* The response type
*
* @var string
*/
protected $responseType = "html";
/**
* Result properties
*/
protected $code;
protected $message;
public $data;
protected $view;
protected $layout;
/**
* The constructor function, initialize the URI property
*/
public function __construct()
{
$this->constructed = true;
}
public function executeChain()
{
if (!$this->constructed)
{
//DebugHelper::debug('SUBCLASS_NOT_CALL_PARENT_CONSTRUCTOR', array('class' => $actionClassName));
trigger_error('SUBCLASS_NOT_CALL_PARENT_CONSTRUCTOR');
}
$this->afterConstruct();
$validateResult = $this->validateInput();
if (0 == $validateResult["error_total"])
{
if ($this->checkPrivilege())
{
$this->beforeExecute();
$this->execute();
}
else
{
$this->code = 403;
$this->message = "Access denied";
}
}
else
{
$this->code = 407;
$this->message = "Invalid input";
$this->data['error_messages'] = $validateResult["error_messages"];
}
$this->writeResponse();
}
/**
* Do something after subClass::__construct().
*/
protected function afterConstruct()
{
}
/**
* Validate the data from client
*
* @return array
*/
protected function validateInput()
{
$validateResult = array("error_total" => 0, "error_messages" => array());
if (!empty($this->dtds) && class_exists('LtValidator'))
{
$validator = new LtValidator;
$validator->init();
foreach ($this->dtds as $variable => $dtd)
{
$from = isset($dtd->from) ? $dtd->from : 'request';
foreach ($dtd->rules as $ruleKey => $ruleValue)
{
if ($ruleValue instanceof ConfigExpression)
{
eval('$_ruleValue = ' . $ruleValue->__toString());
$dtd->rules[$ruleKey] = $_ruleValue;
}
}
$error_messages = $validator->validate($this->context->$from($variable), $dtd);
if (!empty($error_messages))
{
$validateResult['error_total'] ++;
$validateResult['error_messages'][$variable] = $error_messages;
}
}
}
return $validateResult;
}
/**
* Check if current user have privilege to do this
*
* @return boolen
*/
protected function checkPrivilege()
{
$allow = true;
if (!empty($this->roles) && class_exists('LtRbac'))
{
$module = $this->context->uri["module"];
$action = $this->context->uri["action"];
$roles = array_merge(array("*"), $this->roles);
$rbac = new LtRbac();
$rbac->init();
$allow = $rbac->checkAcl($roles, "$module/$action");
}
return $allow;
}
/**
* Do something before subClass::execute().
*/
protected function beforeExecute()
{
}
protected function execute()
{
}
protected function writeResponse()
{
switch ($this->responseType)
{
case 'json':
echo json_encode(array("code" => $this->code,
"message" => $this->message,
"data" => $this->data
));
exit; //
break;
case 'tpl':
if (null === $this->view)
{
$this->view = new LtTemplateView;
}
$this->view->component = false; // 是否组件
$this->view->context = $this->context;
$this->view->code = $this->code;
$this->view->message = $this->message;
$this->view->data = $this->data;
$this->view->layoutDir = $this->viewDir . "layout/";
$this->view->layout = $this->layout;
$this->view->templateDir = $this->viewDir;
$this->view->compiledDir = $this->viewTplDir;
$this->view->autoCompile = $this->viewTplAutoCompile;
if (empty($this->template))
{
$this->template = $this->context->uri["module"] . "-" . $this->context->uri["action"];
}
$this->view->template = $this->template;
$this->view->render();
break;
case 'html':
case 'wml':
default:
if (null === $this->view)
{
$this->view = new LtView;
}
$this->view->context = $this->context;
$this->view->code = $this->code;
$this->view->message = $this->message;
$this->view->data = $this->data;
$this->view->layoutDir = $this->viewDir . "layout/";
$this->view->layout = $this->layout;
$this->view->templateDir = $this->viewDir;
if (empty($this->template))
{
$this->template = $this->context->uri["module"] . "-" . $this->context->uri["action"];
}
$this->view->template = $this->template;
$this->view->render();
break;
}
}
}

View File

@ -0,0 +1,134 @@
<?php
/**
* The Component class
*/
abstract class LtComponent
{
/**
* The context object
*
* @var object
*/
public $context;
public $viewDir;
public $viewTplDir;
public $viewTplAutoCompile;
/**
* A flag to indicate if subclass call LtComponent::__construct()
*
* @var boolean
*/
public $constructed = false;
/**
* The response type
*
* @var string
*/
protected $responseType = "html";
/**
* Result properties
*/
protected $code;
protected $message;
public $data;
protected $view;
protected $layout;
/**
* The constructor function
*/
public function __construct()
{
$this->constructed = true;
}
public function executeChain()
{
if (!$this->constructed)
{
//DebugHelper::debug('SUBCLASS_NOT_CALL_PARENT_CONSTRUCTOR', array('class' => $actionClassName));
}
$this->afterConstruct();
$this->beforeExecute();
$this->execute();
$this->writeResponse();
}
protected function afterConstruct()
{
}
/**
* Do something before subClass::execute().
*/
protected function beforeExecute()
{
}
protected function execute()
{
}
protected function writeResponse()
{
switch ($this->responseType)
{
case 'json':
echo json_encode(array("code" => $this->code,
"message" => $this->message,
"data" => $this->data
));
exit;
break;
case 'tpl':
if (null === $this->view)
{
$this->view = new LtTemplateView;
}
$this->view->component = true; // 是否组件
$this->view->context = $this->context;
$this->view->code = $this->code;
$this->view->message = $this->message;
$this->view->data = $this->data;
$this->view->layoutDir = $this->viewDir . "layout/";
$this->view->layout = $this->layout;
$this->view->templateDir = $this->viewDir . "component/";
$this->view->compiledDir = $this->viewTplDir . "component/";
$this->view->autoCompile = $this->viewTplAutoCompile;
if (empty($this->template))
{
$this->template = $this->context->uri["module"] . "-" . $this->context->uri["action"];
}
$this->view->template = $this->template;
$this->view->render();
break;
case 'html':
case 'wml':
default:
if (null === $this->view)
{
$this->view = new LtView;
}
$this->view->context = $this->context;
$this->view->code = $this->code;
$this->view->message = $this->message;
$this->view->data = $this->data;
$this->view->layoutDir = $this->viewDir . "layout/";
$this->view->layout = $this->layout;
$this->view->templateDir = $this->viewDir . "component/";
if (empty($this->template))
{
$this->template = $this->context->uri["module"] . "-" . $this->context->uri["action"];
}
$this->view->template = $this->template;
$this->view->render();
break;
}
}
}

View File

@ -0,0 +1,98 @@
<?php
class LtContext
{
/**
* The uri property
*
* @var array
*/
public $uri;
protected $strip;
public function __construct()
{
}
/**
* return the client input in $_SERVER['argv']
*
* @param integer $offset
* @return string
*/
public function argv($offset)
{
return isset($_SERVER['argv']) && isset($_SERVER['argv'][$offset]) ? $_SERVER['argv'][$offset] : null;
}
/**
* return the client input in $_FILES
*
* @param string $name
* @return array
*/
public function file($name)
{
return isset($_FILES[$name]) ? $_FILES[$name] : null;
}
/**
* return the client input in $_GET
*
* @param string $name
* @return string
*/
public function get($name)
{
return isset($_GET[$name]) ? $_GET[$name] : null;
}
/**
* return the client input in $_POST
*
* @param string $name
* @return string
*/
public function post($name)
{
return isset($_POST[$name]) ? $_POST[$name] : null;
}
/**
* return the client input in $_REQUEST
*
* @param string $name
* @return string
*/
public function request($name)
{
return isset($_REQUEST[$name]) ? $_REQUEST[$name] : null;
}
/**
* return the client input in $_SERVER
*
* @param string $name
* @return string
*/
public function server($name)
{
if ('REMOTE_ADDR' == $name)
{
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
{
$clientIp = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
else
{
$clientIp = $_SERVER[$name];
}
return $clientIp;
}
else
{
return isset($_SERVER[$name]) ? $_SERVER[$name] : null;
}
}
}

View File

@ -0,0 +1,74 @@
<?php
/**
* The Dispatcher class
*/
class LtDispatcher
{
public $viewDir;
public $viewTplDir;
public $viewTplAutoCompile;
public $data;
public function __construct()
{
}
protected function _dispatch($module, $action, $context = null, $classType = "Action")
{
$classType = ucfirst($classType);
$actionClassName = $module . $action . $classType;
if (!class_exists($actionClassName))
{
//DebugHelper::debug("{$classType}_CLASS_NOT_FOUND", array(strtolower($classType) => $action));
trigger_error("{$actionClassName} CLASS NOT FOUND! module={$module} action={$action} classType={$classType}");
}
else
{
if (!($context instanceof LtContext))
{
$newContext = new LtContext;
}
else
{
$newContext = clone $context;
}
$newContext->uri['module'] = $module;
$newContext->uri[strtolower($classType)] = $action;
$actionInstance = new $actionClassName();
$actionInstance->context = $newContext;
$actionInstance->viewDir = $this->viewDir;
$actionInstance->viewTplDir = $this->viewTplDir; // 模板编译目录
$actionInstance->viewTplAutoCompile = $this->viewTplAutoCompile;
$actionInstance->executeChain();
$this->data = $actionInstance->data;
}
}
/**
* Disptach the module/action calling.
*
* @param $module string
* @param $action string
* @return void
* @todo allow one action dispatch another action
*/
public function dispatchAction($module, $action, $context = null)
{
$this->_dispatch($module, $action, $context);
}
/**
* Disptach the module/component calling.
*
* @param $module string
* @param $component string
* @param $data mixed
* @return void
*/
public function dispatchComponent($module, $component, $context = null)
{
$cloneOfContext = clone $context;
$this->_dispatch($module, $component, $cloneOfContext, "Component");
}
}

View File

@ -0,0 +1,385 @@
<?php
class LtTemplateView
{
public $layout;
public $layoutDir;
public $template;
public $templateDir;
public $compiledDir;
public $autoCompile; // bool
public $component; // bool
private $tpl_include_files;
public function __construct()
{
/**
* 自动编译通过对比文件修改时间确定是否编译,
* 当禁止自动编译时, 需要手工删除编译后的文件来重新编译.
*
* 支持component include自动编译
*/
$this->autoCompile = true;
$this->component = false;
}
public function render()
{
if (empty($this->compiledDir))
{
$this->compiledDir = dirname($this->templateDir) . "/viewTpl/";
}
if (!empty($this->layout))
{
include $this->template(true);
}
else if ($this->component)
{
return; // 模板内使用{component module action}合并文件
}
else
{
include $this->template();
}
}
/**
* 返回编译后的模板路径, 如果不存在则编译生成并返回路径.
* 如果文件存在且允许自动编译, 则对比模板文件和编译后的文件修改时间
* 当修改模板后自支重新编译
*
* @param bool $islayout 是否使用布局
* @return string 返回编译后的模板路径
*/
public function template($islayout = false)
{
$this->layoutDir = rtrim($this->layoutDir, '\\/') . '/';
$this->compiledDir = rtrim($this->compiledDir, '\\/') . '/';
$this->templateDir = rtrim($this->templateDir, '\\/') . '/';
if ($islayout)
{
$tplfile = $this->layoutDir . $this->layout . '.php';
$objfile = $this->compiledDir . 'layout/' . $this->layout . '@' . $this->template . '.php';
}
else
{
$tplfile = $this->templateDir . $this->template . '.php';
$objfile = $this->compiledDir . $this->template . '.php';
}
if (is_file($objfile))
{
if ($this->autoCompile)
{
$iscompile = true;
$tpl_include_files = include($objfile);
$last_modified_time = array();
foreach($tpl_include_files as $f)
{
$last_modified_time[] = filemtime($f);
}
if (filemtime($objfile) == max($last_modified_time))
{
$iscompile = false;
}
}
else
{
$iscompile = false;
}
}
else
{
// 目标文件不存在,编译模板
$iscompile = true;
}
if ($iscompile)
{
$this->tpl_include_files[] = $objfile;
$this->tpl_include_files[] = $tplfile;
$dir = pathinfo($objfile, PATHINFO_DIRNAME);
if (!is_dir($dir))
{
if (!mkdir($dir, 0777, true))
{
trigger_error("Can not create $dir");
}
}
$str = file_get_contents($tplfile);
if (!$str)
{
trigger_error('Template file Not found or have no access!', E_USER_ERROR);
}
$str = $this->parse($str);
if ($this->autoCompile)
{
$prefix = "<?php\r\nif(isset(\$iscompile)&&true==\$iscompile)\r\nreturn " . var_export(array_unique($this->tpl_include_files), true) . ";?>";
$prefix = preg_replace("/([\r\n])+/", "\r\n", $prefix);
$postfix = "\r\n<!--Template compilation time : " . date('Y-m-d H:i:s') . "-->\r\n";
}
else
{
$prefix = '';
$postfix = '';
}
$str = $prefix . $str . $postfix;
if (!file_put_contents($objfile, $str))
{
if (file_put_contents($objfile . '.tmp', $str))
{
copy($objfile . '.tmp', $objfile); // win下不能重命名已经存在的文件
unlink($objfile . '.tmp');
}
}
@chmod($objfile,0777);
}
return $objfile;
}
/**
* 解析{}内字符串,替换php代码
*
* @param string $str
* @return string
*/
protected function parse($str)
{
$str = $this->removeComments($str);
$str = $this->parseIncludeComponent($str);
// 回车 换行
$str = str_replace("{CR}", "<?php echo \"\\r\";?>", $str);
$str = str_replace("{LF}", "<?php echo \"\\n\";?>", $str);
// if else elseif
$str = preg_replace("/\{if\s+(.+?)\}/", "<?php if(\\1) { ?>", $str);
$str = preg_replace("/\{else\}/", "<?php } else { ?>", $str);
$str = preg_replace("/\{elseif\s+(.+?)\}/", "<?php } elseif (\\1) { ?>", $str);
$str = preg_replace("/\{\/if\}/", "<?php } ?>", $str);
// loop
$str = preg_replace("/\{loop\s+(\S+)\s+(\S+)\}/e", "\$this->addquote('<?php if(isset(\\1) && is_array(\\1)) foreach(\\1 as \\2) { ?>')", $str);
$str = preg_replace("/\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}/e", "\$this->addquote('<?php if(isset(\\1) && is_array(\\1)) foreach(\\1 as \\2=>\\3) { ?>')", $str);
$str = preg_replace("/\{\/loop\}/", "<?php } ?>", $str);
// url生成
$str = preg_replace("/\{url\(([^}]+)\)\}/", "<?php echo LtObjectUtil::singleton('LtUrl')->generate(\\1);?>", $str);
// 函数
$str = preg_replace("/\{([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\s*\(([^{}]*)\))\}/", "<?php echo \\1;?>", $str);
$str = preg_replace("/\{\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\(([^{}]*)\))\}/", "<?php echo \$\\1;?>", $str);
// 变量
/**
* 放弃支持$name.name.name
* $str = preg_replace("/\{(\\\$[a-zA-Z0-9_\[\]\'\"\$\x7f-\xff]+)\.([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", "<?php echo \\1['\\2'];?>", $str);
*/
// 其它变量
$str = preg_replace("/\{(\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", "<?php echo \\1;?>", $str);
$str = preg_replace("/\{(\\$[a-zA-Z0-9_\.\[\]\'\"\$\x7f-\xff]+)\}/e", "\$this->addquote('<?php echo \\1;?>')", $str);
// 类->属性 类->方法
$str = preg_replace("/\{(\\\$[a-zA-Z0-9_\[\]\'\"\$\x7f-\xff][+\-\>\$\'\"\,\[\]\(\)a-zA-Z0-9_\x7f-\xff]+)\}/es", "\$this->addquote('<?php echo \\1;?>')", $str);
// 常量
$str = preg_replace("/\{([A-Z_\x7f-\xff][A-Z0-9_\x7f-\xff]*)\}/", "<?php echo \\1;?>", $str);
// 静态变量
$str = preg_replace("/\{([a-zA-Z0-9_]*::?\\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", "<?php echo \\1;?>", $str);
$str = preg_replace("/\{([a-zA-Z0-9_]*::?\\\$[a-zA-Z0-9_\.\[\]\'\"\$\x7f-\xff]+)\}/e", "\$this->addquote('<?php echo \\1;?>')", $str);
// 合并相邻php标记
$str = preg_replace("/\?\>\s*\<\?php[\r\n\t ]*/", "", $str);
/**
* 删除空行
* Dos和windows采用回车+换行CR/LF表示下一行,
* 而UNIX/Linux采用换行符LF表示下一行
* 苹果机(MAC OS系统)则采用回车符CR表示下一行.
* CR用符号 '\r'表示, 十进制ASCII代码是13, 十六进制代码为0x0D;
* LF使用'\n'符号表示, ASCII代码是10, 十六制为0x0A.
* 所以Windows平台上换行在文本文件中是使用 0d 0a 两个字节表示,
* 而UNIX和苹果平台上换行则是使用0a或0d一个字节表示.
*
* 这里统一替换成windows平台回车换行, 第二参数考虑 \\1 保持原有
*/
$str = preg_replace("/([\r\n])+/", "\r\n", $str);
// 删除第一行
$str = preg_replace("/^[\r\n]+/", "", $str);
// write
$str = trim($str);
return $str;
}
/**
* 变量加上单引号
* 如果是数字就不加单引号, 如果已经加上单引号或者双引号保持不变
*/
protected function addquote($var)
{
preg_match_all("/\[([a-zA-Z0-9_\-\.\x7f-\xff]+)\]/s", $var, $vars);
foreach($vars[1] as $k => $v)
{
if (is_numeric($v))
{
$var = str_replace($vars[0][$k], "[$v]", $var);
}
else
{
$var = str_replace($vars[0][$k], "['$v']", $var);
}
}
return str_replace("\\\"", "\"", $var);
}
/**
* 模板中第一行可以写exit函数防止浏览
* 删除行首尾空白, html javascript css注释
*/
protected function removeComments($str, $clear = false)
{
$str = str_replace(array('<?php exit?>', '<?php exit;?>'), array('', ''), $str);
// 删除行首尾空白
$str = preg_replace("/([\r\n]+)[\t ]+/s", "\\1", $str);
$str = preg_replace("/[\t ]+([\r\n]+)/s", "\\1", $str);
// 删除 {} 前后的 html 注释 <!-- -->
$str = preg_replace("/\<\!\-\-\s*\{(.+?)\}\s*\-\-\>/s", "{\\1}", $str);
$str = preg_replace("/\<\!\-\-\s*\-\-\>/s", "", $str);
// 删除 html注释 存在 < { 就不删除
$str = preg_replace("/\<\!\-\-\s*[^\<\{]*\s*\-\-\>/s", "", $str);
if ($clear)
{
$str = $this->clear($str);
}
return $str;
}
/**
* 清除一部分 style script内的注释
* 多行注释内部存在 / 字符就不会清除
*/
protected function clear($str)
{
preg_match_all("|<script[^>]*>(.*)</script>|Usi", $str, $tvar);
foreach($tvar[0] as $k => $v)
{
// 删除单行注释
$v = preg_replace("/\/\/\s*[a-zA-Z0-9_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/", "", $v);
// 删除多行注释
$v = preg_replace("/\/\*[^\/]*\*\//s", "", $v);
$str = str_replace($tvar[0][$k], $v, $str);
}
preg_match_all("|<style[^>]*>(.*)</style>|Usi", $str, $tvar);
foreach($tvar[0] as $k => $v)
{
// 删除多行注释
$v = preg_replace("/\/\*[^\/]*\*\//s", "", $v);
$str = str_replace($tvar[0][$k], $v, $str);
}
return $str;
}
/**
*
* @todo 注意相互引用的模板嵌套会导致死循环
*/
protected function parseIncludeComponent($str)
{
$count_include_component = preg_match_all("/\{include\s+(.+)\}/", $str, $tvar);
$count_include_component += preg_match_all("/\{component\s+([a-zA-Z0-9\.\-_]+)\s+([a-zA-Z0-9\.\-_]+)\}/", $str, $tvar);
unset($tvar);
while ($count_include_component > 0)
{
$str = $this->parseInclude($str);
$str = $this->parseComponent($str);
$count_include_component = preg_match_all("/\{include\s+(.+)\}/", $str, $tvar);
$count_include_component += preg_match_all("/\{component\s+([a-zA-Z0-9\.\-_]+)\s+([a-zA-Z0-9\.\-_]+)\}/", $str, $tvar);
unset($tvar);
}
$str = $this->removeComments($str);
return $str;
}
/**
* 解析多个{include path/file}合并成一个文件
*
* @example {include 'debug_info'}
* {include 'debug_info.php'}
* {include "debug_info"}
* {include "debug_info.php"}
* {include $this->templateDir . $this->template}
*/
private function parseInclude($str)
{
$countSubTpl = preg_match_all("/\{include\s+(.+)\}/", $str, $tvar);
while ($countSubTpl > 0)
{
foreach($tvar[1] as $k => $subfile)
{
eval("\$subfile = $subfile;");
if (is_file($subfile))
{
$findfile = $subfile;
}
else if (is_file($subfile . '.php'))
{
$findfile = $subfile . '.php';
}
else if (is_file($this->templateDir . $subfile))
{
$findfile = $this->templateDir . $subfile;
}
else if (is_file($this->templateDir . $subfile . '.php'))
{
$findfile = $this->templateDir . $subfile . '.php';
}
else
{
$findfile = '';
}
if (!empty($findfile))
{
$subTpl = file_get_contents($findfile);
$this->tpl_include_files[] = $findfile;
}
else
{
// 找不到文件
$subTpl = 'SubTemplate not found:' . $subfile;
}
$str = str_replace($tvar[0][$k], $subTpl, $str);
}
$countSubTpl = preg_match_all("/\{include\s+(.+)\}/", $str, $tvar);
}
return $str;
}
/**
* 解析多个{component module action}合并成一个文件
*/
private function parseComponent($str)
{
$countCom = preg_match_all("/\{component\s+([a-zA-Z0-9\.\-_]+)\s+([a-zA-Z0-9\.\-_]+)\}/", $str, $tvar);
while ($countCom > 0)
{
$i = 0;
while ($i < $countCom)
{
$comfile = $this->templateDir . "component/" . $tvar[1][$i] . '-' . $tvar[2][$i] . '.php';
if (is_file($comfile))
{
$subTpl = file_get_contents($comfile);
$this->tpl_include_files[] = $comfile;
}
else
{
$subTpl = 'SubTemplate not found:' . $comfile;
}
////////////////////////////////////////////////////////////////////////////
$module = $tvar[1][$i];
$action = $tvar[2][$i];
$subTpl = "<?php
\$dispatcher = LtObjectUtil::singleton('LtDispatcher');
\$dispatcher->dispatchComponent('$module', '$action', \$this->context);
\$comdata = \$dispatcher->data;
unset(\$dispatcher);
?>
" . $subTpl;
////////////////////////////////////////////////////////////////////////////
$str = str_replace($tvar[0][$i], $subTpl, $str);
$i++;
}
$countCom = preg_match_all("/\{component\s+([a-zA-Z0-9\.\-_]+)\s+([a-zA-Z0-9\.\-_]+)\}/", $str, $tvar);
}
return $str;
}
}

View File

@ -0,0 +1,26 @@
<?php
/**
* The View class
*/
class LtView
{
public $layoutDir;
public $templateDir;
public $layout;
public $template;
public function render()
{
if (!empty($this->layout))
{
include($this->layoutDir . $this->layout . '.php');
}
else
{
include($this->templateDir . $this->template . '.php');
}
}
}