<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                [TOC] * * * * * ## 1 視圖模板 ### 視圖模板概覽 ![](https://box.kancloud.cn/2016-03-18_56ebc970a2e86.jpg) ### 模板內容解析 ![](https://box.kancloud.cn/2016-03-21_56efbf923bc3f.jpg) * * * * * ### 視圖模板意義 >[info] 視圖模板是控制器操作的輸出組織 >主要包含視圖模板操作與模板文件解析兩部分 >最終以web頁面的形式組織控制器操作結果數據 **操作部分:視圖模板配置,模板變量準備** **解析部分:模板變量,模板內容,模板標簽解析** * * * * * ### 視圖模板操作層次 **控制器操作:** 處理具體業務邏輯,準備輸出數據 **視圖層操作:** 輸出數據賦值模板變量,模板主題,模板文件定位 **模板層解析:** 模板文件解析, **標簽層解析:** 模板文件標簽解析 **模板緩存層讀寫:** 讀寫模板編譯結果 * * * * * ## 2 視圖模板配置 ### 1 全局配置 thinkphp\convention.php >[info] 定義View配置參數, ~~~ $view->config return [ 'theme_on' => false, 'default_theme' => 'default', 'view_path' => '', 'view_suffix' => '.html', 'view_depr' => DS, 'view_layer' => VIEW_LAYER, 'parse_str' => [], 'namespace' => '\\think\\view\\driver\\', 'template' => [], ]; ~~~ >[info] 定義Template配置參數 ~~~ $Template->config return [ 'view_path' => '', 'view_suffix' => '.html', 'view_depr' => DS, 'cache_suffix' => '.php', 'tpl_deny_func_list' => 'echo,exit', 'tpl_deny_php' => false, 'tpl_begin' => '{', 'tpl_end' => '}', 'strip_space' => false, 'tpl_cache' => true, 'compile_type' => 'file', 'cache_prefix' => '', 'cache_time' => 0, 'layout_on' => false, 'layout_name' => 'layout', 'layout_item' => '{__CONTENT__}', 'taglib_begin' => '{', 'taglib_end' => '}', 'taglib_load' => true, 'taglib_build_in' => 'cx', 'taglib_pre_load' => '', 'display_cache' => false, 'cache_id' => '', 'tpl_replace_string' => [], 'tpl_var_identify' => 'array', 'namespace' => '\\think\\template\\driver\\', ]; ~~~ >[info] tp5.0Rc2中 > 視圖與模板引擎初始化時不再自動獲取配置參數 > 需要在初始化時手動獲取配置參數 ### 2 應用配置 application\config.php 配置參數同全局配置參數設置 >[info] tp5.0Rc2中 > 視圖與模板引擎初始化時不再自動獲取配置參數 > 需要在初始化時手動獲取配置參數 ### 3 初始化配置 配置參數同全局配置參數設置 ~~~ $view->new think\View( think\Config::get()) $controller->engine( think\Config::get()) ~~~ ## 4 控制器層操作 think\Controller.php ### 控制器層模板變量賦值 assign() ### 控制器層模板引擎初始化 engine() `public function engine($engine, $config = [])` > $engine:模板引擎名稱 > $config:模板引擎配置 `$this->view->engine($engine, $config);` 調用think\View.php視圖對象的engine()初始化模板引擎 ## 5 控制器層解析 think\Controller.php ### 控制器層模板解析 fetch() >[info] 模板解析 fetch() `public function fetch($template = '', $vars = [], $cache_id = '')` ~~~ $template :指定模板文件名,默認為**控制器/操作.模板后綴** $vars :模板變量數組 $cache_id :模板緩存id ~~~ ` return $this->view->fetch($template, $vars, $cache_id);` 調用think\View的fetch() 解析模板文件內容 ### 控制器層內容渲染 show() >[info] 渲染內容輸出 show() `public function show($content, $vars = [])` > $content:待解析內容 > $vars: 模板變量 `return $this->view->show($content, $vars);` 調用think\View的show() 渲染內容輸出 ## 6 視圖層操作 think\View.php ### 視圖初始化 instance(), __construct(),config(),engine() >[info] 視圖實例初始化 instance() `public static function instance(array $config = [])` > $config:視圖配置參數 **單例模式對象實例化** ~~~ if (is_null(self::$instance)) { self::$instance = new self($config); } ~~~ 如果沒有實例對象,初始化視圖對象實例 `return self::$instance;` 如果有實例對象,返回實例對象 >[info] 視圖構造函數 __construct() `public function __construct(array $config = [])` > $config: View相關配置參數 ~~~ $this->config($config); ~~~ 調用think\View->config()設置視圖配置參數 ~~~ if (!isset($this->config['template']['type'])) { $this->config['template']['type'] = 'think'; } ~~~ 檢查模板引擎類型配置,默認為think >[info] 視圖參數配置 config() `public function config($config = '', $value = null)` > $config:視圖配置參數名,或視圖配置參數數組 > $value:視圖配置參數名對應值 **讀寫四功能函數:** 數組寫,鍵值讀,鍵值寫,數組讀 ~~~ if (is_array($config)) { foreach ($this->config as $key => $val) { if (isset($config[$key])) { $this->config[$key] = $config[$key]; } } } ~~~ **1 $config為數組,數組寫 覆蓋到think\view->config[]對應參數** ~~~ elseif (is_null($value)) { return $this->config[$config]; } ~~~ **2 $config為字符串,$value為空時,鍵值讀 讀取$config鍵名對應配置值** ~~~ else { $this->config[$config] = $value; } ~~~ **3 $config,$value都為字符串時,鍵值寫 設置$config對應值為$value** `return $this;` **4 $config,$value都為空時,數組讀 讀取整個配置** >[info] 視圖模板引擎初始化 engine() `public function engine($engine, array $config = [])` > $engine:模板引擎名稱 > $config:模板引擎參數 ~~~ if ('php' == $engine) { $this->engine = 'php'; } ~~~ php模板引擎 `$class = $this->config['namespace'] . ucfirst($engine);` 非php模板引擎類名 ~~~ if (empty($this->config['view_path']) && defined('VIEW_PATH')) { $this->config['view_path'] = VIEW_PATH; } ~~~ 視圖文件路徑 默認為base.php的VIEW_PATH ~~~ $config = array_merge($config, [ 'view_path' => $this->config['view_path'], 'view_suffix' => $this->config['view_suffix'], 'view_depr' => $this->config['view_depr'], ]); ~~~ 參數覆蓋,默認配置覆蓋傳入的配置參數? `$this->engine = new $class($config);` 創建模板引擎, 默認為 think\viewdriver\Think類的對象 `return $this;` 返回視圖對象,**實現鏈式操作**。 ### 視圖變量操作 __set() __get() __isset() ~~~ public function __set($name, $value) public function __get($name) public function __isset($name) ~~~ > $name:模板變量名稱 > $value:模板變量值 ### 開啟,關閉,設置模板主題 theme() **讀寫三功能函數:** 開啟,關閉,設置 ~~~ if (true === $theme) { $this->config['theme_on'] = true; } elseif (false === $theme) { $this->config['theme_on'] = false; } ~~~ 開啟與關閉主題設置 ~~~ else { $this->config['theme_on'] = true; $this->theme = $theme; } ~~~ 開啟模板主題,設置模板主題 `return $this;` 返回視圖對象,**實現鏈式操作** 設置,獲取,檢查模板變量 ## 7 視圖層解析 think\View.php ### 模板文件解析 fetch() `public function fetch($template = '', $vars = [], $config = [], $renderContent = false)` > $template:待解析模板文件與模板內容 > $vars:模板變量 > $config:模板解析引擎配置參數 > $renderContent:是否渲染內容控制 true:返回模板文件名,false:解析后輸出 `$vars = array_merge($this->data, $vars);` 合并傳入模板參數$vars到$this->data `if (!$renderContent) {}` **不需要解析時** 返回模板文件名 `$template = $this->parseTemplate($template);` 獲取模板文件名,并返回。 ~~~ if (!is_file($template) || (APP_DEBUG && IS_WIN && realpath($template) != $template)) { throw new Exception('template file not exists:' . $template, 10700); } ~~~ WIN環境模板不存在,拋出異常 `APP_DEBUG && Log::record('[ VIEW ] ' . $template . ' [ ' . var_export($vars, true) . ' ]', 'info');` 開啟調試,記錄日志信息 ~~~ if (is_null($this->engine)) { $this->engine($this->config['template']['type'], $this->config['template']); } ~~~ **需要解析時**,首先檢查模板引擎是否初始化。 ~~~ ob_start(); ob_implicit_flush(0); ~~~ 開啟輸出控制,關閉絕對刷送 有關輸出控制 見 php的輸出控制 ~~~ if ('php' == $this->engine || empty($this->engine)) { extract($vars, EXTR_OVERWRITE); is_file($template) ? include $template : eval('?>' . $template); } ~~~ **原生php引擎處理** 導入模板變量到符號表, 加載或者執行模板文件內容。 ~~~ else { $this->engine->fetch($template, $vars, $config); } ~~~ **非原生php引擎處理** 調用$this->engine->fetch()對模板文件進行解析。 默認使用think\view\driver\Think->fetch() `$content = ob_get_clean();` 獲取緩存內容并清空 `APP_HOOK && Hook::listen('view_filter', $content);` 標簽過濾回調處理 ~~~ if (!empty($this->config['parse_str'])) { $replace = $this->config['parse_str']; $content = str_replace(array_keys($replace), array_values($replace), $content); } ~~~ 用戶自定義的字符串替換 ~~~ if (!Config::get('response_auto_output')) { return Response::send($content, Response::type()); } ~~~ 關閉自動響應輸出?? 直接輸出模板內容字符串到客戶端。 `return $content;` 返回解析后的內容 ### 模板內容解析 show() `public function show($content, $vars = [])` > $content:模板內容 > $vars:模板變量 `return $this->fetch($content, $vars, '', true);` 直接調用think\View->fetch(); 解析模板內容 ### 文件層解析 parseTemplate(),getTemplateTheme() >[info] 模板文件定位 parseTemplate() `private function parseTemplate($template)` $template:模板文件參數 ~~~ if (is_file($template)) { return realpath($template); } ~~~ 如果是文件名,直接返回文件路徑名 **獲取模板目錄** ~~~ if (empty($this->config['view_path']) && defined('VIEW_PATH')) { $this->config['view_path'] = VIEW_PATH; } ~~~ 配置的模板文件目錄獲取。 ~~~ $theme = $this->getTemplateTheme(); $this->config['view_path'] .= $theme; ~~~ 獲取模板文件主題目錄。 ~~~ $depr = $this->config['view_depr']; $template = str_replace(['/', ':'], $depr, $template); ~~~ 替換模板文件路徑中的分隔符 ~~~ if (strpos($template, '@')) { list($module, $template) = explode('@', $template); $path = APP_PATH . (APP_MULTI_MODULE ? $module . DS : '') . $this->config['view_layer'] . DS; } ~~~ 模板文件路徑包含"@"符號時,定位當前模塊模板目錄 ~~~ else { $path = $this->config['view_path']; } ~~~ 沒有包含"@"時,定位默認配置模板目錄 **獲取模板文件名** ~~~ if (defined('CONTROLLER_NAME')) { if ('' == $template) { $template = str_replace('.', DS, CONTROLLER_NAME) . $depr . ACTION_NAME; } elseif (false === strpos($template, $depr)) { $template = str_replace('.', DS, CONTROLLER_NAME) . $depr . $template; } } ~~~ 添加控制器名稱到模板文件名 `return realpath($path) . DS . $template . $this->config['view_suffix'];` 返回模板目錄,模板文件名,模板后綴合并的字符串作為模板文件名。 >[info]獲取模板主題名稱 getTemplateTheme() ~~~ if ($this->config['theme_on']) { if ($this->theme) { $theme = $this->theme; } else { $theme = $this->config['default_theme']; } } ~~~ 開啟模板主題時,獲取模板主題或者默認主題 `return isset($theme) ? $theme . DS : '';` 返回模板主題或者空字符串 ## 8 模板引擎操作 think\view\driver\Think.php,think\Template.php ### 模板引擎初始化 think\View->engine() >[info] 初始化入口 think\View.php 上面模板引擎初始化過程think\View->engine()中 `this->engine = new $class($config);` 這里默認創建think\view\driver\Think類 ### 視圖引擎構造函數 $Think_engine->__construct() `public function __construct($config = [])` > $config:配置參數 ~~~ { $this->template = new Template($config); } ~~~ 創建think\Template.php類的對象實例作為模板引擎。 ### 視圖引擎解析入口 $Think_engine->fetch() `public function fetch($template, $data = [], $config = [])` > $template:模板文件名 > $data:模板變量 > $config:模板配置參數 ~~~ if (is_file($template)) { $this->template->display($template, $data, $config); } ~~~ $template為文件名時, 調用think\Template->display()解析文件 ~~~ else { $this->template->fetch($template, $data); } ~~~ $template為字符串內容時 調用think\Template->fetch()渲染內容 ### 視圖引擎其他操作 $Think_engine->__call() `public function __call($method, $params)` > $method 調用方法 > $params 調用參數 ~~~ { return call_user_func_array([$this->template, $method], $params); } ~~~ 使用魔術方法__call() 調用$this->template->$method($params); ### 模板引擎構造函數$Template->__construct() `public function __construct(array $config = [])` > $config:解析引擎配置參數 `$this->config['cache_path'] = RUNTIME_PATH . 'temp' . DS;` 緩存目錄配置 `$this->config = array_merge($this->config, $config);` 配置參數合并 ~~~ $this->config['taglib_begin'] = $this->stripPreg($this->config['taglib_begin']); $this->config['taglib_end'] = $this->stripPreg($this->config['taglib_end']); $this->config['tpl_begin'] = $this->stripPreg($this->config['tpl_begin']); $this->config['tpl_end'] = $this->stripPreg($this->config['tpl_end']); ~~~ 標簽庫與普通標簽開始結束標記配置 ~~~ $type = $this->config['compile_type'] ? $this->config['compile_type'] : 'File'; $class = $this->config['namespace'] . ucwords($type); ~~~ 模板編譯存儲驅動類型配置 `$this->storage = new $class();` 創建模板編譯緩存驅動對象 ### 模板擎配置讀寫$Template->config() `public function config($config)` > $config:解析引擎配置參數 **讀寫功能函數:** 數組寫,鍵值對讀 ~~~ if (is_array($config)) { $this->config = array_merge($this->config, $config); } elseif (isset($this->config[$config])) { return $this->config[$config]; } ~~~ $config為數組,合并到配置參數 $config為字符串,讀取配置鍵名對應值 ### 模板引擎配置賦值$Template->__set() `public function __set($name, $value)` > $name:配置鍵名 > $value:配置鍵值 `$this->config[$name] = $value;` 設置配置鍵值對關系 ### 模板引擎變量賦值$Template->assign() `public function assign($name, $value = '')` > $name:變量名 > $value:變量值 數組字符串兩寫功能:數組寫,鍵值對 ~~~ if (is_array($name)) { $this->data = array_merge($this->data, $name); } else { $this->data[$name] = $value; } ~~~ 數組,合并到模板變量 鍵值對,鍵值對賦值 ### 模板引擎變量獲取$Template->get() `public function get($name = '')` > $name:待獲取變量名 ~~~ if ('' == $name) { return $this->data; } ~~~ 空變量名,讀取所有模板變量 ~~~ $data = $this->data; foreach (explode('.', $name) as $key => $val) { if (isset($data[$val])) { $data = $data[$val]; } else { $data = null; break; } } ~~~ 變量二級讀取。$test.va1格式變量 `return $data;` 返回讀取的對應變量 ### 模板內容布局操作 $Template->layout() `public function layout($name) ` > $name:布局控制 > true:開啟,false:關閉,字符串:設置layout_name ~~~ if (false === $name) { $this->config['layout_on'] = false; } ~~~ $name為false,關閉布局 ~~~ else { $this->config['layout_on'] = true; if (is_string($name)) { $this->config['layout_name'] = $name; } } ~~~ $name非false,開啟布局,如果是字符串設置布局名稱 `return $this;` 返回對象,實現鏈式操作 ## 9 模板內容層解析 think\Template.php ### 1 模板文件編譯入口 display() `public function display($template, $vars = [], $config = [])` > $tempalte:模板文件名稱 > $vars:模板變量數組 > $config:模板引擎配置參數 ~~~ if ($vars) { $this->data = $vars; } ~~~ 模板變量設置 ~~~ if ($config) { $this->config($config); } ~~~ 模板引擎配置參數設置 其中的cache_id對應每個渲染模板文件 `if (!empty($this->config['cache_id']) && $this->config['display_cache']) {}` 模板渲染緩存檢測, **這里的緩存使用Cache緩存php運行后生成的web頁面形式緩存** 不同于使用$Template->storage->write()緩存的編譯模板。 后者包含php代碼,前者不包含php代碼。 前者是一種更高效的緩存技術。可以實現靜態頁面生成。 ` $cacheContent = Cache::get($this->config['cache_id']);` 調用Cache::get()獲取渲染緩存內容 這里使用了tp框架內置緩存機制, 為了目標引擎插件化可以解耦這里的Cache 將其設置為配置參數。 ~~~ if ($cacheContent !== false) { echo $cacheContent; return; } ~~~ 渲染緩存存在的情況下,輸出渲染緩存。 這里返回到View->fetch(),即控制器中的$view->fetch()。 如果沒有開啟渲染緩存,則查找編譯模板。 `$template = $this->parseTemplateFile($template);` 調用template->parseTemplateFile()獲取對應模板文件名 這里的parseTemplateFile()與View.php的parseTemplate()相似 **后者是在View.php的fetch()的參數$renderContent為false,** **不進行模板編譯時,查找目標文件所用** **進行模板編譯時,查找目標文件時所用。** `if ($template) {}` 獲取模板文件名成功時,繼續解析 `$cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($template) . $this->config['cache_suffix'];` 合成緩存文件名稱 ` if (!$this->checkCache($cacheFile)) {}` 緩存文件檢查,如果緩存失效,進行重新編譯 ~~~ $content = file_get_contents($template); $this->compiler($content, $cacheFile); ~~~ 緩存失效時,讀取模板文件內容,并編譯 緩存可用時,獲取模板編譯緩存 ob_start(); ob_implicit_flush(0); `$this->storage->read($cacheFile, $this->data);` 讀取模板文件編譯存儲 `$content = ob_get_clean();` 讀取模板文件編譯內容 ~~~ if (!empty($this->config['cache_id']) && $this->config['display_cache']) { Cache::set($this->config['cache_id'], $content, $this->config['cache_time']); } ~~~ 檢查是否需要保存編譯生成的web模板文件 調用Cache:set()保存編譯生成的web模板文件 `echo $content;` 輸出解析結果 ### 2 模板內容編譯入口 fetch() `public function fetch($content, $vars = [])` > $contens:待編譯模板內容 > $vars:模板變量 ~~~ if ($vars) { $this->data = $vars; } ~~~ 保存模板變量 `$cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($content) . $this->config['cache_suffix'];` 合成模板編譯緩存文件名 ~~~ if (!$this->checkCache($cacheFile)) { $this->compiler($content, $cacheFile); } ~~~ 模板編譯緩存無效,重新編譯 `$this->storage->read($cacheFile, $this->data);` 讀取存儲的編譯模板緩存 ### 3 模板文件查找 parseTemplateFile() `private function parseTemplateFile($template)` > $template:待查找文件名 `if (false === strpos($template, '.')) {}` 模板文件名中包含符號".",模板文件的后綴 ~~~ if (strpos($template, '@')) { $template = str_replace(['/', ':'], $this->config['view_depr'], $template); $template = APP_PATH . str_replace('@', '/' . basename($this->config['view_path']) . '/', $template) . $this->config['view_suffix']; } ~~~ 跨模塊模板查找解析。 首先替換"/" ":"為配置的模板分隔符 然后替換"@"為"/",并添加應用路徑/模板路徑和模板后綴到文件名 ~~~ else { if (strpos($template, ':')) { list($theme, $template) = explode(':', $template, 2); $path = dirname($this->config['view_path']) . DS . $theme . DS; } else { $path = $this->config['view_path']; } $template = str_replace(['/', ':'], $this->config['view_depr'], $template); $template = $path . $template . $this->config['view_suffix']; } ~~~ 當前模塊模板操作 首先解析模板文件名模板主題符號":", 然后替換掉模板文件分割符為配置的分隔符 最后合并模板路徑,模板文件名,模板文件后綴, ~~~ if (is_file($template)) { $this->includeFile[$template] = filemtime($template); return $template; } ~~~ 對應模板文件存在時,將對應模板文件加載時間保存到$this->includeFile[] 這里用作模板編譯時間標識。 并返回模板文件名 ~~~ else { throw new Exception('template not exist:' . $template, 10700); } ~~~ 文件不存在拋出異常 ### 4 包含文件名解析 parseTemplateName() `private function parseTemplateName($templateName)` $templateName:需要包含的文件名 `$array = explode(',', $templateName);` 多個模板文件 `foreach ($array as $templateName) {}` 遍歷加載文件 ~~~ if (empty($templateName)) { continue; } ~~~ 檢查數組 if (0 === strpos($templateName, '$')) { $templateName = $this->get(substr($templateName, 1)); } 獲取變量文件名 `$template = $this->parseTemplateFile($templateName);` 查找需要加載的文件 ~~~ if ($template) { $parseStr .= file_get_contents($template); } ~~~ 文件存在則進行讀取 `return $parseStr;` 返回讀取的文件內容 ### 5 模板編譯緩存檢查 checkCache() `private function checkCache($cacheFile)` > $cacheFile:緩存文件名 ~~~ if (!$this->config['tpl_cache']) { return false; } ~~~ 未開啟緩存,返回false ~~~ if (!is_file($cacheFile)) { return false; } ~~~ 文件不存在,返回false ~~~ if (!$handle = @fopen($cacheFile, "r")) { return false; } ~~~ 讀取文件失敗,返回false `preg_match('/\/\*(.+?)\*\//', fgets($handle), $matches);` 讀取文件第一行 ~~~ if (!isset($matches[1])) { return false; } ~~~ 讀取內容為空,返回false `$includeFile = unserialize($matches[1]);` 對第一行解序列化 ~~~ if (!is_array($includeFile)) { return false; } ~~~ 檢查是否存在包含文件$Template->$inculdeFile[]中 不存在,返回false ~~~ foreach ($includeFile as $path => $time) { if (is_file($path) && filemtime($path) > $time) { return false; } } ~~~ 對比模板文件更新時間與上次編譯時間, 如果模板文件有更新,則返回false ~~~ return $this->storage->check($cacheFile, $this->config['cache_time']); ~~~ 最后調用$this->storage->check()檢查緩存有效期,并返回檢查結果 ### 6 模板內容編譯過程 compiler() `private function compiler(&$content, $cacheFile)` > $content:待編譯內容引用,避免復制傳參帶來的內存消耗 > $cacheFile:緩存文件名 `if ($this->config['layout_on']) {}` 模板布局啟動檢查 ~~~ if (false !== strpos($content, '{__NOLAYOUT__}')) { $content = str_replace('{__NOLAYOUT__}', '', $content); } ~~~ 將模板內容中設置為不使用布局的部分去除掉。 else { $layoutFile = $this->parseTemplateFile($this->config['layout_name']); if ($layoutFile) { $content = str_replace($this->config['layout_item'], $content, file_get_contents($layoutFile)); } } 讀取布局模板文件 如:index/view/controller_layout.html 讀取布局模板成功時 將布局模板index/view/controller_layout.html中的{__CONTENT__} 替換為模板內容,并獲取替換結果。 分析這里可知道 >[info] 布局模板是模板內容的上層組織 > 通常使用布局模板定義頁面的總體結構,如頁頂,頁腳,頁邊框 > 布局模板中可替換內容對應功能頁面組織, 如內容主體 `$this->parse($content);` 對模板內容進行解析編譯。 >[info] 前面的模板解析在文件層檢查其渲染緩存,編譯緩存,布局文件 > 這里是模板內容解析的總入口 > 下面的模板解析在內容層開始解析模板標簽等內容 ### 7 模板內容解析總入口 parse() `public function parse(&$content)` > $content:待解析模板文件內容 ~~~ if (empty($content)) { return; } ~~~ 進行參數有效檢查如果待解析內容為空,直接返回 ~~~ $this->parseLiteral($content); $this->parseExtend($content); $this->parseLayout($content); $this->parseInclude($content); $this->parseLiteral($content); ~~~ >[info] 1 解析原生標簽literal內容 parseliteral() > 2 解析extend標簽內容 parseExtend() > 3 解析layout標簽內容 parseLayout() > 4 解析include標簽內容 parseInclude() > 5 再次解析包含文件中可能攜帶的原生標簽literal內容 parseliteral() `$this->parsePhp($content);` php的語法檢查 `if ($this->config['taglib_load']) {}` 允許加載其他標簽庫 `$tagLibs = $this->getIncludeTagLib($content);` 對模板內容的標簽列表進行加載。 ~~~ if (!empty($tagLibs)) { foreach ($tagLibs as $tagLibName) { $this->parseTagLib($tagLibName, $content); } } ~~~ 對標簽列表依次進行加載 ### 8 literal標簽內容解析 parseLiteral() `private function parseLiteral(&$content, $restore = false)` > $content:待解析內容引用 > $restore:內容是否需要還原 ~~~ $regex = $this->getRegex($restore ? 'restoreliteral' : 'literal'); ~~~ 獲取literal標簽內容的正則字符串。 `if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {}` 正則匹配內容中的literal標簽,并保存到$matches數組中。 有關preg_match_all()見php的正則表達式 ` if (!$restore) {}` literal原生標簽內容替換處理, 將literal原生標簽內容保存到$this->literal[]數組 并使用特殊字符串替換掉。 `$count = count($this->literal);` 原生標簽內容緩存數組。 對于需要替換的內容進行保存。 這里獲取數組的下個數字鍵值, 對應當前處理的literal原生標簽內容 `foreach ($matches as $match) {}` 遍歷匹配到的literal標簽內容 `$this->literal[] = substr($match[0], strlen($match[1]), -strlen($match[2]));` 緩存需要恢復的literal原生標簽內容 `$content = str_replace($match[0], "<!--###literal{$count}###-->", $content);` 替換需要literal原生標簽內容為對應$count鍵標記 ~~~ $count++; ~~~ 數字鍵自增,保存下個literal原生標簽內容 ~~~ else { foreach ($matches as $match) { $content = str_replace($match[0], $this->literal[$match[1]], $content); } $this->literal = []; } ~~~ else對模板內容中的literal標簽替換字符串 還原為$this->literal[]數組中保存的原生內容 `unset($matches);` 刪除正則內容,減少內存消耗 ` return;` 直接返回調用處, 因為處理的引用字符串,不需要返值 ### 9 extend標簽解析 parseExtend() `private function parseExtend(&$content)` > $content:待解析字符串引用 `$regex = $this->getRegex('extend');` 獲取extend標簽正則表達式字符串 `$array = $blocks = $baseBlocks = [];` `$extend = '';` $blocks block標簽數組,$baseBlocks 頂層block標簽數組 $extend 解析結果字符串聲明 **下面聲明了一個閉包函數,進行extend標簽內容解析** 關于閉包函數 見 php的閉包函數 `$func = function ($template) use (&$func, &$regex, &$array, &$extend, &$blocks, &$baseBlocks) {}` 閉包函數聲明 其中$template作為模板內容變量參數 use 包含父作用域變量參數 `if (preg_match($regex, $template, $matches)) {}` 正則模板內容中的extend標簽字符串 `if (!isset($array[$matches['name']])) {}` 檢查是否解析過這個標簽內容,防止重復解析 extend標簽,讀取繼承模板文件內容 `$array[$matches['name']] = 1;` ?? `$extend = $this->parseTemplateName($matches['name']);` 調用parseTemplateName() 根據文件名讀取繼承模板文件內容 `$func($extend);` 遞歸調用匿名函數$func,處理$extend的內容 `$blocks = array_merge($blocks, $this->parseBlock($template));` 調用parseBlock() 解析模板內容中extend標簽下的的block標簽內容 作為父類模板的的子類實現內容。 `return;` extend標簽解析返回 `else {}` `$baseBlocks = $this->parseBlock($template, true);` 調用parseBlock() 解析模板內容的block標簽內容 作為繼承的父類模板的blcok聲明內容 **$baseblocks,$blocks兩個block標簽內容通過name屬性對應,并替換。** ~~~ if (empty($extend)) { $extend = $template; } ~~~ extend外的block標簽內容,保存到結果 `$func($content);` 匿名函數處理模板內容 `if (!empty($extend)) {}` 返回解析結果內容不為空 `if ($baseBlocks) {}` 父模板block標簽內容數組為不空時 ~~~ foreach ($baseBlocks as $name => $val) {} ~~~ 遍歷父模板block表示內容數組 ~~~ $replace = $val['content']; ~~~ 父模板block標簽內容緩存 `if (!empty($children[$name])) {}` 防止重復遍歷 ~~~ foreach ($children[$name] as $key) { $replace = str_replace($baseBlocks[$key]['begin'] . $baseBlocks[$key]['content'] . $baseBlocks[$key]['end'], $blocks[$key]['content'], $replace); } ~~~ 將父模板block標簽替換為子模板對應block標簽內容 `if (isset($blocks[$name])) {}` 父模板$name的block標簽 在子模板block標簽數組$blocks中存在對應內容 需要進行子模板內容替換處理 `$replace = str_replace(['{__BLOCK__}', '{__block__}'], $replace, $blocks[$name]['content']);` 合并子模板的{__Bblock__}標簽內容 `if (!empty($val['parent'])) {}` 子模板的頂級block標簽 `$parent = $val['parent'];` 獲取其父標簽 ~~~ if (isset($blocks[$parent])) { $blocks[$parent]['content'] = str_replace($blocks[$name]['begin'] . $blocks[$name]['content'] . $blocks[$name]['end'], $replace, $blocks[$parent]['content']); } ~~~ 對子模板block中的內容進行替換處理 `$blocks[$name]['content'] = $replace;` 子模板內容 ` $children[$parent][] = $name;` 子模板解析結果數組$chilldren 在上面的`if (!empty($children[$name])) {}`中使用 ### 10 block標簽解析 parseBlock() `private function parseBlock(&$content, $sort = false)` > $content:待解析內容 > $sort:是否排序 `$regex = $this->getRegex('block');` 獲取block標簽的正則表達式 `if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {}` 匹配$content的block標簽內容到$matches `$right = $keys = [];` 子模板塊內容$right,對應父模板鍵數組$keys? `foreach ($matches as $match) {}` 遍歷匹配的結果數組$matches `if (empty($match['name'][0])) {}` 這里是子模板的block內容解析 `if (count($right) > 0) {}` 這里的$right在下的else語句中添加 ~~~ $tag = array_pop($right); $start = $tag['offset'] + strlen($tag['tag']); $length = $match[0][1] - $start; ~~~ 單個標簽處理,獲取其開始與長度 ~~~ $result[$tag['name']] = [ 'begin' => $tag['tag'], 'content' => substr($content, $start, $length), 'end' => $match[0][0], 'parent' => count($right) ? end($right)['name'] : '', ]; ~~~ 子標簽內容的組合結果 $keys`[$tag['name']] = $match[0][1];` 保存標簽索引到$keys ~~~ $right[] = [ 'name' => $match[2][0], 'offset' => $match[0][1], 'tag' => $match[0][0], ]; ~~~ 父標block簽壓棧處理 `unset($right, $matches);` 注銷無用內存 ~~~ if ($sort) { array_multisort($keys, $result); } ~~~ 排序處理 `return $result;` 返回block標簽內容 ### 11 layout標簽解析 parseLayout() `private function parseLayout(&$content)` $content:待解析內容索引 `if (preg_match($this->getRegex('layout'), $content, $matches)) {}` 正則匹配布局字符串內容 `$content = str_replace($matches[0], '', $content);` Layout標簽內容替換處理 `$array = $this->parseAttr($matches[0]);` Layout表示屬性解析 `if (!$this->config['layout_on'] || $this->config['layout_name'] != $array['name']) {}` 布局檢測?? ~~~ $layoutFile = $this->parseTemplateFile($array['name']); ~~~ 加載布局模板文件 `if ($layoutFile) {}` 加載布局模板成功 `$replace = isset($array['replace']) ? $array['replace'] : $this->config['layout_item'];` 獲取替換目標 `$content = str_replace($replace, $content, file_get_contents($layoutFile));` 對布局模板內容進行替換 ~~~ else { $content = str_replace('{__NOLAYOUT__}', '', $content); } ~~~ 刪除模板內容中的{__NOLAYOUT__} ### 12 include標簽解析 parseInclude() `private function parseInclude(&$content)` > $content:待解析內容 `$regex = $this->getRegex('include');` 獲取include標簽字符串正則表達式 下面聲明閉包函數遞歸處理include標簽內容 `$func = function ($template) use (&$func, &$regex, &$content) {}` 閉包函數聲明 `if (preg_match_all($regex, $template, $matches, PREG_SET_ORDER)) {}` 正則匹配include標簽內容 ~~~ $array = $this->parseAttr($match[0]); $file = $array['file']; unset($array['file']); ~~~ 獲取其中的file屬性 `$parseStr = $this->parseTemplateName($file);` 加載需要包含的文件 ### 13 php語法檢查 parsePhp() `private function parsePhp(&$content)` > $content:待解析模板內容 `$content = preg_replace('/(<\?(?!php|=|$))/i', '<?php echo \'\\1\'; ?>' . "\n", $content);` 對短標簽進行替換處理 ~~~ if ($this->config['tpl_deny_php'] && false !== strpos($content, '<?php')) { throw new Exception('not allow php tag', 11600); } ~~~ 禁止php代碼的模板檢測后拋出異常 ### 14 標簽庫加載 getIncludeTagLib() `private function getIncludeTagLib(&$content)` > $content:待解析內容 `preg_match($this->getRegex('taglib'), $content, $matches)` 獲取Taglib標簽內容 ~~~ $content = str_replace($matches[0], '', $content); ~~~ 將匹配的內容去除 `return explode(',', $matches['name']);` 以逗號分割標簽庫名稱 `return null;` 如果匹配失敗,返回null。 ### 15 標簽庫標簽解析 parseTagLib() `public function parseTagLib($tagLib, &$content, $hide = false)` > $tagLibName:標簽庫名稱 > $content:待解析內容引用 ~~~ if (strpos($tagLib, '\\')) { $className = $tagLib; $tagLib = substr($tagLib, strrpos($tagLib, '\\') + 1); } ~~~ 根命名空間下的標簽庫類名$className ~~~ else { $className = '\\think\\template\\taglib\\' . ucwords($tagLib); } ~~~ 標簽庫命名空間下的類名 `$tLib = new $className($this);` 創建標簽庫類實例對象 默認為think\template\taglib\Cx.php類 `$tLib->parseTag($content, $hide ? '' : $tagLib);` 調用對應標簽庫類實例對象對模板內容進行解析 這里進入標簽庫標簽解析 ### 16 標簽屬性分析 parseAttr() `public function parseAttr($str, $name = null)` > $str:標簽字符串 > $name: 屬性名稱 `$regex = '/\s+(?>(?<name>[\w-]+)\s*)=(?>\s*)([\"\'])(?<value>(?:(?!\\2).)*)\\2/is';` 屬性正則表達式 ~~~ if (preg_match_all($regex, $str, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $array[$match['name']] = $match['value']; } unset($matches); } ~~~ 對標簽字符串$str中的屬性進行正則匹配, 以鍵值對形式保存到$array中。 ~~~ if (!empty($name) && isset($array[$name])) { return $array[$name]; } else { return $array; } ~~~ 從$array中獲取$name對應屬性值,并返回 ### 17 標簽正則字符串生成 getRegex() `private function getRegex($tagName)` > $tagName:標簽名 `$regex = '';` 生成結果字符串 `if ('tag' == $tagName) {}` 普通標簽生成 ~~~ $begin = $this->config['tpl_begin']; $end = $this->config['tpl_end']; ~~~ 獲取普通標簽開始與結束標記 ~~~ if (strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1) {} ~~~ ?? ~~~ $regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>[^' . $end . ']*))' . $end; ~~~ ??生成的普通標簽正則字符串 `$regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>(?:(?!' . $end . ').)*))' . $end;` ??生成的普通標簽正則字符串 `else {}` else的內容為標簽庫標簽生成 ~~~ $begin = $this->config['taglib_begin']; $end = $this->config['taglib_end']; ~~~ 標簽庫標簽開始與結束標記 `$single = strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1 ? true : false;` ?? `switch ($tagName) {}` 根據標簽名參數$tagName,生成不同標簽 包括 literal,restoreliteral,include, taglib,layout,extend,block 分析可知這里的標簽都是內容塊標簽。 ~~~ $begin = $this->config['taglib_begin']; $end = $this->config['taglib_end']; ~~~ 標簽庫標簽的開始與結束標記 正則表達式相關見 php的正則表達式 ## 10 模板普通標簽解析 Template\Template.php ### 1 普通標簽解析 parseTag() `private function parseTag(&$content)` > $content:待解析模板內容 ### 2 模板變量解析 parseVar() `public function parseVar(&$varStr)` > $varStr:變量的內容 > 格式為{$varname|function1|function2=arg1,arg2} ### 3 模板變量過濾函數解析 parseVarFunction() `public function parseVarFunction(&$varStr)` > $varStr:變量的內容 > 格式為{$varname|function1|function2=arg1,arg2} ###4 框架模板變量解析 parseThinkVar() `public function parseThinkVar(&$vars)` > $vars: 變量內容 > 格式 以 $Think. 打頭的變量屬于特殊模板變量 ## 11 模板標簽庫操作 Template\TagLib.php ### 標簽庫初始化 `public function __construct($template)` ### 獲取標簽列表 `public function getTags()` ### 標簽添加?? 增加標簽格式 增加標簽解析 修改標簽解析 刪除標簽解析 ## 12 模板標簽庫解析 template\TagLib.php,\taglib\Cx.php >[info] TagLib.php ### 模板內容中標簽庫標簽解析 parseTag() `public function parseTag(&$content, $lib = '')` > $cotent:待解析內容 > $lib:標簽庫類 標簽分閉合與非閉合兩種解析 ### 標簽庫標簽的屬性解析 parseAttr() `public function parseAttr($str, $name, $alias = '')` > $str: 標簽屬性字符串 > $name:屬性名 > $alias:屬性別名 ### 標簽中條件表達式解析 parseCondition() `public function parseCondition($condition)` `public function autoBuildVar(&$name)` `private function getRegex($tags, $close)` >[info] template\taglib\Cx.php ### php標簽解析 _php() `public function _php($tag, $content)` ### volist標簽解析 _volist() `public function _volist($tag, $content)` ### foreach標簽解析 _foreach() `public function _foreach($tag, $content)` ### if標簽解析 _if() `public function _if($tag, $content)` ### elseif標簽解析 _elseif() `public function _elseif($tag, $content)` ### else標簽解析 _else() `public function _else($tag)` ### switc標簽解析 _switc() `public function _switch($tag, $content)` ### case標簽解析 _case() `public function _case($tag, $content)` ### defaul標簽解析 _defaul() `public function _default($tag)` ### compare標簽解析 _compare() `public function _compare($tag, $content)` ### range標簽解析 _range() `public function _range($tag, $content)` ### present標簽解析 _present() `public function _present($tag, $content)` ### notpresent標簽解析 _notpresent() `public function _notpresent($tag, $content)` ### empty標簽解析 _empty() `public function _empty($tag, $content)` ### notempty標簽解析 _notempty() `public function _notempty($tag, $content)` ### defined標簽解析 _defined() `public function _defined($tag, $content)` ### notdefined標簽解析 _notdefined() `public function _notdefined($tag, $content)` ### import標簽解析 _import() `public function _import($tag, $content, $isFile = false)` ### load標簽解析 _load() `public function _load($tag, $content)` ### assign標簽解析 _assign() `public function _assign($tag, $content)` ### define標簽解析 _define() `public function _define($tag, $content)` ### for標簽解析 _for() `public function _for($tag, $content)` ### url標簽解析 _url() `public function _url($tag, $content)` ### functio標簽解析 _functio() `public function _function($tag, $content)` ## 13 模板緩存讀寫 template\driver\File.php `public function write($cacheFile, $content)` `public function read($cacheFile, $vars = [])` `public function check($cacheFile, $cacheTime)`
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看