1. 程式人生 > >讓你的thinkphp支援巢狀繼承模板

讓你的thinkphp支援巢狀繼承模板

最近想把後臺重寫一下,不想再用iframe了,用整頁跳轉的,當然想到的就是把之前那些固定的選單做成一個模板,然後挖坑,內容頁就填充到坑裡

thinkphp裡面有模板繼承的功能 extend,可我發現它居然不支援巢狀繼承

比如我寫一個只加載bootstrap框架的basic頁basic.html(放在Common/View/Boot/basic.html)

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<!--<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />-->
		<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,minimum-scale =1,user-scalable=no" />
		<link rel="stylesheet" href="{$Think.PLUGIN_PATH}bootstrap/css/bootstrap.min.css" />
		<link rel="stylesheet" href="{$Think.CSS_PATH}lc.css" />	
		<block name="head"></block>
	</head>
	<body>
		<block name="body"></block>
		<script src="{$Think.PLUGIN_PATH}jquery/jquery-1.11.3.min.js"></script>
		<script src="{$Think.PLUGIN_PATH}bootstrap/js/bootstrap.min.js"></script>		
		<block name="foot"></block>		
	</body>
</html>

然後我再寫一個我後臺的admin_basic.html,繼承剛才的basic頁(放在Admin/View/Public/admin_basic.html),然後寫導航、選單,然後再挖坑。
<extend name="[email protected]:basic" />
<block name="body">	
<div id="warpper">
	<nav>頂部的導航</nav>
        <!-- 這兒用tp的Widget擴充套件讀取選單 -->
        <div id="page_menu">{:W("Admin/getAction")}</div>
	<div id="page-warpper">
		<block name="body">內容頁</block>
	</div>
</div>
</block>
<block name="foot">
	<block name="foot"></block>
</block>

然後我就寫後臺管理系統的首頁 index.html
<extend name="Public:admin_basic" />
<block name="body">	
	這是我的後臺系統管理
</block>
<block name="foot">
<script type="text/javascript">
	$(function(){
		alert("hello,world");
	});
</script>
</block>

tp它居然預設無法這樣實現,我覺得這個功能應該是非常重要的呀,沒辦法,它不實現,只能我來實現了。

當然首先就是百度了,非常幸運,讓我找到下面這個大溼的部落格

具體是找到Thinkphp/Libray/Think/Template.class.php檔案,在245行附近的parseExtend方法中 $content = $this->replaceBlock($content);這句後面增加 $content = $this->parseExtend($content); 讓tp遞迴解析extend,然後再到340行附近的replaceBlock方法中,將  $content = $content[3]; 索引改為0。

請看下在程式碼,註釋有[2develop]

protected function parseExtend($content) {
        $begin      =   $this->config['taglib_begin'];
        $end        =   $this->config['taglib_end'];        
        // 讀取模板中的繼承標籤
        $find       =   preg_match('/'.$begin.'extend\s(.+?)\s*?\/'.$end.'/is',$content,$matches);
        if($find) {
            //替換extend標籤
            $content    =   str_replace($matches[0],'',$content);
            // 記錄頁面中的block標籤
            preg_replace_callback('/'.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.'(.*?)'.$begin.'\/block'.$end.'/is', array($this, 'parseBlock'),$content);
            // 讀取繼承模板
            $array      =   $this->parseXmlAttrs($matches[1]);
            $content    =   $this->parseTemplateName($array['name']);
            $content    =   $this->parseInclude($content, false); //對繼承模板中的include進行分析
            // 替換block標籤
            $content = $this->replaceBlock($content);

            // [2DEVELOP] 支援多層繼承
            $content = $this->parseExtend($content);
        }else{
            $content    =   preg_replace_callback('/'.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.'(.*?)'.$begin.'\/block'.$end.'/is', function($match){return stripslashes($match[2]);}, $content);
        }
        return $content;
    }
  private function replaceBlock($content){
        
        static $parse = 0;
        $begin = $this->config['taglib_begin'];
        $end   = $this->config['taglib_end'];
        $reg   = '/('.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.')(.*?)'.$begin.'\/block'.$end.'/is';      

        if(is_string($content)){
            do{
                $content = preg_replace_callback($reg, array($this, 'replaceBlock'), $content);
                
            } while ($parse && $parse--);

            return $content;
        } elseif(is_array($content)){
            if(preg_match('/'.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.'/is', $content[3])){ //存在巢狀,進一步解析
                $parse = 1;
                $content[3] = preg_replace_callback($reg, array($this, 'replaceBlock'), "{$content[3]}{$begin}/block{$end}");
                
                return $content[1] . $content[3];
            } else {
                $name    = $content[2];
                //[2DEVELOP] 支援多層繼承,將下面原來的索引值3改為0
                $content = $content[0];
                $content = isset($this->block[$name]) ? $this->block[$name] : $content;
                return $content;
            }
        }
    }

我用它上面寫的方法後,發現真的成功了,但是!可能我比較糾結,我發現了一個小問題。

我發現如果我basic裡面的block的name名為body的話,我在admin_basic裡如果也挖了一個坑name也為body的話,它就會覆蓋admin_basic裡我在body的block裡寫的內容了,要不就得避免block的name值一樣。

我比較龜毛,一般主體我一直喜歡用body,所以我不能妥協!

讓我研究了一上午,終於讓我解決了

到Thinkphp/Libray/Think/Template.class.php

將345附近的$reg   = '/('.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.')(.*?)'.$begin.'\/block'.$end.'/is';

改成下面這個

$reg   = '/('.$begin.'block\sname=[\'"]([^\'"]+?)[\'"]\s*?'.$end.')([^(?!'.$begin.'block)]*?)'.$begin.'\/block'.$end.'/is';

</pre><pre name="code" class="php"> private function replaceBlock($content){
        
        static $parse = 0;
        $begin = $this->config['taglib_begin'];
        $end   = $this->config['taglib_end'];
        //$reg   = '/('.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.')(.*?)'.$begin.'\/block'.$end.'/is';
        // [2DEVELOP]支援多層繼承,上面是原來的正則
        $reg   = '/('.$begin.'block\sname=[\'"]([^\'"]+?)[\'"]\s*?'.$end.')([^(?!'.$begin.'block)]*?)'.$begin.'\/block'.$end.'/is';

        if(is_string($content)){
            do{
                $content = preg_replace_callback($reg, array($this, 'replaceBlock'), $content);
                
            } while ($parse && $parse--);

            return $content;
        } elseif(is_array($content)){
            if(preg_match('/'.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.'/is', $content[3])){ //存在巢狀,進一步解析
                $parse = 1;
                $content[3] = preg_replace_callback($reg, array($this, 'replaceBlock'), "{$content[3]}{$begin}/block{$end}");
                
                return $content[1] . $content[3];
            } else {
                $name    = $content[2];

                $content = $content[3];
                $content = isset($this->block[$name]) ? $this->block[$name] : $content;
                return $content;
            }
        }
    }

我也不知這樣改會不會有什麼錯誤,不過我看了我其它頁面都還沒發現有錯誤的現象,僅供參考學習。

相關推薦

thinkphp支援繼承模板

最近想把後臺重寫一下,不想再用iframe了,用整頁跳轉的,當然想到的就是把之前那些固定的選單做成一個模板,然後挖坑,內容頁就填充到坑裡 thinkphp裡面有模板繼承的功能 extend,可我發現它居然不支援巢狀繼承 比如我寫一個只加載bootstrap框架的basi

Proxmox VE支援虛擬化

目前公司的測試環境使用Proxmox VE(PVE),PVE虛擬出來的主機CPU預設不支援vmx,即不支援巢狀虛擬化,在虛擬機器中使用egrep "vmx|svm" /proc/cpuinfo驗證,無輸出,那麼如何讓他支援呢?其實PVE的核心還是採用了KVM+Qemu的方式模擬,那麼參照如何讓KVM支援巢狀虛

使ESXI host支援虛擬化hyperv

1.物理機要確保開啟支援巢狀虛擬化 vi /etc/vmware/config libdir = "/usr/lib/vmware" authd.proxy.vim = "vmware-hostd:hostd-vmdb" authd.proxy.nfc = "vmware

C++ 的模板的特化定義不允許寫在類定義的範圍內

最近在使用在使用模板特化寫一段程式時發現一個奇怪的問題,比如像如下程式碼: #include <iostream>using namespace std;class CMyClass{public: template <typename T> struct test { 

一張圖搞懂JavaScript的繼承與原型鏈

前面的話   javascript裡的關係又多又亂。作用域鏈是一種單向的鏈式關係,還算簡單清晰;this機制的呼叫關係,稍微有些複雜;而關於原型,則是prototype、proto和constructor的三角關係。本文先用一張圖開宗明義,然後詳細解釋原型的三

Fragment懶載入(支援) 友盟統計Fragment時長最佳實踐

前言 在專案中我們可能需要獲取Fragment可見或者不可見時的回撥。(回撥這個詞在這裡用的可能並不準確,這裡理解為當Fragment可見或者不可見時能夠觸發一些方法,下同)。 Fragmeng生命週期中有onResume,onPause,這兩個生命週期是跟

三目運算支援

上原始碼:case DCH_MS: /* millisecond */ len = from_char_parse_int_len(&out->ms, &s, 3, n);

織夢channelartlist標籤裡的channel也支援currentstyle高亮

巢狀標籤 <div class="nav"> <ul> {dede:channelartlist row=7 typeid=top currentstyle=current} <li class="{dede:field.currents

MongoDB學習筆記~大叔框架實體更新支援N層~遞迴遞迴我愛

回到目錄 遞迴遞迴我愛你!只要你想做,就一定能成功! 從一到二,從二到三,它是容易的,也是沒什麼可搞的,或者說,它是一種流水線的方式,而從三到十,從十到百,它註定要有一個質的突破,否則,它會把你累死,程式碼寫的讓你自己都覺得想吐!有時,我們是被逼出來的,對於一種功能的實現,我們有時需要有從三到十的態度中,

Thinkphp獲取單個列陣列並用foreach和eq

1.Thinkphp 獲取單個列的陣列 $coins = M('coin')->getField('name_en',true); 2. foreach巢狀eq <foreach name="coins" item="vo">     &l

Python中函式以及函式繼承

# a = 10 # b = 0 # c = 5 # try: # print("a的值是:%d,b的值是:%d"%(a,b)) # # f = c.open("a.txt") # print(f) # d = a / b # print("%d除以%d的值為

程式碼講解:面向物件下——程式碼塊、內部類、繼承(保證全部都會噢)

//一、程式碼塊(筆試、面試常考知識點) //1.普通程式碼塊:普通程式碼塊就是定義在方法中的程式碼塊 // public class Lesson6{ // public static void main(String[] args){ // {//定義在main方法中的程式碼塊——

C++抽象類的繼承方式

今天在工作中遇到了一個問題,需要繼承一個巢狀的抽象類,廢了很大的功夫才成功建立了物件 抽象巢狀類如下: class A { class B { public: B* GetInstance() = 0; } }; 繼承類如下:

不同程式語言在發生stackoverflow之前支援的呼叫棧最大層數

今天我的一位同事在微信群裡發了一張圖片,勾起了我的好奇心:不同程式語言支援的函式遞迴呼叫的最大巢狀層數是? Java 1.8 private static void recur(int i){ System.out.println("Stack level: " +

模板類別名作為函式返回型別,可能會提示的編譯錯誤

  模板巢狀類別名作為函式返回型別,可能會提示的編譯錯誤 1 #include <iostream> 2 using namespace std; 3 4 template<typename ElementType> 5 class B 6 { 7

的ubuntu系統支援CPU動態調頻

首先,你得確保你的電腦是支援並且打開了CPU動態調頻功能的,方法如下: (我用的是的DELL機) (1)開機後進入BIOS,找到performance選項,在speedset子選項中你會看到你的CPU動態調頻是否開啟了,如果未開啟,要開啟。 (2)檢視你的核心是否安裝了

配置tomcatshtml檔案顯示

之前,我知道tomcat可以直接解析shtml檔案,在瀏覽器中顯示效果來,後來由於需求發生改變,比如說 在做靜態化生成的時候一個網站的頭部和底部都是一樣的,如果每個頁面都生成一次,顯然很浪費時間,所有我們可以把 這個共同的頭部 和底部挖出來單獨生成,然後通過sh

所見即所得,使用Java將HTML解析為Excel,支援多級表頭、單元格合併

最近專案需要實現如題“所見即所得”的功能,之前每次生成Excel都需要重新從資料庫查詢一遍,降低效率不說,那些巢狀的表頭實在是很難用Sql巢狀拼接實現。而且這樣做還沒有通用性,不同的表格需要寫不同的Sql實現,非常繁瑣。         在網上找了很

RecyclerView列表如此簡單

平常開發時,相信像這樣的頁面,大家一定是遇到過的。這裡比較坑爹的地方在於呢:列表巢狀。訂單列表中的每一項,都包含一個商品列表。像這種需求,大家會如何實現呢? 這裡呢,說一下我自己的思路,我沒有使用列表巢狀,而是,將原有的Order拆分成了三個佈局型別:Head、Body

手把手教怎麼解析多層的JSON資料(使用JSONModel)

使用API API介紹 參考的JSON資料(可能與你看到的不同) { "date": "20181020", "stories": [ { "title": "每週一吸 · 狸花貓",