Thinkphp 5 學習筆記

Triats

 
namespace app\index\controller;

// 引入Traits类(PHP5.5以上版本可以不需要)
T('controller/View');
T('controller/Jump');
class Index {
    
    use \traits\controller\View;
    use \traits\controller\Jump;
    public function index(){
        if(!IS_AJAX){
            $this->error('非法操作');
        }else{
            $this->assign('name','thinkphp');
            return $this->fetch('hello');
        }
    }
}

PHP Trait

Trait是從PHP 5.4加入的一種細粒度代碼復用的語法。以下是官方手冊對Trait的描述:

Trait 是為類似PHP 的單繼承語言而準備的一種代碼復用機制。Trait 為了減少單繼承語言的限制,使開發人員能夠自由地在不同層次結構內獨立的類中復用method。Trait 和Class 組合的語義定義了一種減少複雜性的方式,避免傳統多繼承和Mixin 類相關典型問題。

Trait 和Class 相似,但僅僅旨在用細粒度和一致的方式來組合功能。無法通過trait 自身來實例化。它為傳統繼承增加了水平特性的組合;也就是說,應用的幾個Class 之間不需要繼承。

什麼是Trait ?

其實說通俗一點,就是能把重複的方法拆分到一個文件,通過use 引入以達到代碼復用的目的。

那麼,我們應該怎麼樣去拆分我們的代碼才是合適的呢?我的看法是這樣的:

Trait,譯作“特性”、“特徵”、“特點”。那麼問題就來了:什麼才是特性?

一個銷售公司有很多種產品:電視,電腦與鼠標墊,卡通手辦等。其中鼠標墊與卡通手辦是非賣品,只用於贈送。

那麼這裡的“可賣性” 就是一個特性,非賣品是沒有價格的。我們便可以抽像出“可賣性” 這個Trait 來:

trait Sellable
{
    protected $price = 0;

    public function getPrice()
    {
        return $this->price;
    }

    public function setPrice(int $price)
    {
        $this->price = $price;
    }
}

當然我們所有的產品都會有品牌與其它基本屬性,所以我們通常會定義一個產品類:

class Pruduct
{
    protected $brand;
    //...

    public function __construct($brand)
    {
        $this->brand = $brand;
    }

    public function getBrand()
    {
        return $this->brand;
    }

    //...
}

我們的電視與電腦類:

class TV extends Pruduct
{
    use Sellable;
    //...

    public function play()
    {
        echo "一台 {$this->brand} 电视在播放中...";
    }

    //...
}

class Computer extends Pruduct
{
    use Sellable;

    protected $cores = 8;
    //...

    public function getNumberOfCores()
    {
        return $this->cores;
    }

    //...
}

而鼠標墊與手辦等禮品是不可賣的:

class Gift extends Pruduct
{
    protected $name;

    function __construct($brand, $name)
    {
        parent::__construct($brand);
        $this->name = $name;
    }

    //...
}

上面的這個例子中,“可賣性” 便是部分商品的一個特性,也可以理解為商品的一個歸類。你也許會說,我也可以再添加一個Goods 類來完成上面的例子啊,Goods 繼承Product,再讓所有可賣的商品繼承於Goods 類,把價格屬性與方法寫到Goods 裡,同樣可以代碼复用啊。的確,這沒啥問題。但是你會發現:你有多個需要區別的特性時,由於PHP 只有單繼承的原因,你不得不組合很多個基類出來,將他們層疊,最終得到的樹狀結構是很複雜的。這也是Trait 所帶來的優勢:隨意組合,代碼清晰。

其實還有很多例子,比如可飛行的,那麼把飛行這個特性所具有的屬性(如:高度,距離)與方法(如:起飛,降落)放到一個trait就是一個合理的拆分。

Trait 有什麼優勢 ?

trait 有什麼優勢?來看一段代碼:

class User extends Model
{
    use Authenticate, SoftDeletes, Arrayable, Cacheable;

    ...
}

這個用戶模型類,我們引入了四個特性:註冊與授權、軟刪除、數組式操作、可緩存。

我們看到代碼的時候一眼便知道當前支持了哪些個特性。再看下面另外一種寫法:

abstract AdvansedUser {
  // ... 实现了 Authenticate, SoftDeletes, Arrayable, Cacheable 的所有方法
}
class User extends AdvansedUser
{
    ...
}

你不得不再去閱讀AdvansedUser的代碼才能理解。你想說沒有可讀性是因為我基類的名稱沒起好?可是,這種各種特性組合的一個基類是根本無法起一個見名知義的名稱的,不信你可以試一下。

就算你真的起了一個見名知義的名稱:AuthenticateCacheableAndArrayableSoftDeletesUser,可是當需求變更,要求在FooUser(同樣繼承了這個基類)中去除緩存特性,而User類保留這個特性,怎麼辦?再創建一個基類麼?

這就是我理解的Trait:

它不僅僅是可複用代碼段的集合,它應該是一組描述了某個特性的的屬性與方法的集合。它的優點在於隨意組合,耦合性低,可讀性高。

平常寫代碼的時候也許怎麼拆分才是大家的痛點,分享以下幾個技巧:

  • 從需求或功能描述拆分,而不是寫了兩段代碼發現代碼一樣就提到一起;
  • 拆分時某些屬性也一起帶走,比如上面第一個例子裡的價格,它是“可賣性”必備的屬性;
  • 拆分時如果給Trait 起名困難時,請認真思考你是否真的拆分對了,因為正確的拆分是很容易描述“它是一個具有什麼功能的特性” 的;

總之一定要記住:不要為了讓兩段相同的代碼提到一起這樣簡單粗暴的方式來拆分。

 

轉載自:

http://overtrue.me/articles/2016/04/about-php-trait.html

WordPress Hooks機制

稍有接觸過WordPress佈景或外掛客製修改的朋友,對WordPress的Hook機制應該不陌生,但通常剛接觸WordPress Hook的新手,對其運作原理可能會有點混亂或糢糊。本文針對WordPress Hook運作大致做個簡單的說明,而預設讀者是理解基本的PHP function語法及運作,但對WordPress Hook機制不是很明白。

Hook機制裡登場的角色

先從「登場角色」的個別說明開始:

WordPress核心

指的是WordPress內建的程式碼架構,提供WordPress主要的基本功能。

Hook

也許你早已聽說,Hook本身雖是鈎子的意思,但直譯又有點奇怪,所以一般通常都不直譯它,而是直接稱它Hook。WordPress的Hook也可以想像成「鈎子」,這些「鈎子」會埋在WordPress網站中特定幾處的程式碼中,埋進去時使用的語法,其「標示位置」的意義比較大,沒有實質運作的內容。當程式執行到有埋Hook的地方時,它會找出所有對應到自己的Hook Function (也就是所有「鈎到」該Hook的hook function),並一一執行。

因此若沒有針對此Hook去「加入」要鈎上去的Hook Function,執行到此什麼也不會做。因此,它等於是WordPress核心預留一個執行的機會給未來想要加入客製功能的開發者。

Hook Function

Hook Function裡會有實質運作的內容,即是實作了一些客製功能,可能是存取DB、增加HTML code、執行其他函式…等。我們在Hook Function裡寫好所需的功能後,就可以利用「加入至對應Hook」的語法,把Hook Function自已鈎到該Hook上,使得該Hook被執行到時,也會連帶執行自己。

Hook機制是如何運作的?

舉個例子,我們拿wp_head及wp_footer這兩個內建的hook來說明,wp_head這個hook就是用來埋在負責輸出標籤的程式碼中,而wp_footer就是用來埋在輸出頁尾的程式碼中 (定義於wp-includes/general-template.php,用wp_head()及wp_footer()包裝起來)。這兩個hook,主要都是在佈景檔案中使用的,常見會出現在header.php及footer.php中。

請看下面的情境示例圖,我們把wp_head及wp_footer看成是「鈎子」,而別的hook functions就能來鈎住它:

我們馬上來寫一個簡單的例子。我們要寫一個hook function,就叫它print_sth(),然後把它鈎上wp_head這個hook。因為wp_head()的內容實際上就只有do_action(‘wp_head’); 這一行內容,而wp_footer()的內容也只有 do_action(‘wp_footer’);,所以我們直接把do_action的語法換到圖上去,比較容易做說明,因此示意圖變成:

如此,只要執行到輸出header.php時,就會執行到wp_head(),就如同執行到do_action(‘wp_head’),此時WP核心會去找所有「鈎上」wp_head這個hook的hook function,於是就找到我們寫的print_sth(),然後就執行它,所以結果它做的事就會出現在網站上,也完成了「客製」的動作:

簡單的說,Hook機制就是:WP核心或其他plugin、theme提供想客製功能的人一個置入客製程式碼(Hook Function)到特定的執行時間點(Hook)的機會。

WordPress的Action Hook與Filter Hook

WordPress中的Hook有兩種,分別是「Action Hook」及「Filter Hook」,我們剛才舉例的wp_head及wp_footer都是屬於Action Hook。不過,一開始你可以先把這兩種Hook看成是一樣的東西,只是Filter多了一點點不同的特色,接著說明。

Action Hook

WP核心 (或佈景、外掛)在做它們該做的事時,如果執行到有埋action hook的程式碼 (即是do_action語法) 時,會去找尋對應到的hook functions,進而執行這些hook functions(即那些透過add_action()來加入的hook functions),藉此完成客製功能。WP核心並不期待Action Hook functions會有回傳值,所以這裡的hook function只被視為一個「獨立切出來運作的功能」。

WP核心做它該做的事,你做你想做的事,做完就各自結束。

Filter Hook

跟Action Hook一樣,WP核心 (或佈景、外掛)在做它們該做的事時,如果執行到有埋filter hook的程式碼 (即是apply_filters語法) 時,就會去找尋對應的hook functions,進而執行這些hook functions(即那些透過add_filter()來加入的hook functions),藉此完成客製功能。與Action Hook不同之處是,所有「鈎上」Filter Hook的hook functions通常都會接收到參數,而WP核心會期待你拿到它提供的參數,並做完你想做的事後,要回傳(return)一個值,讓WP核心再利用你回傳的值來接著完成它該做的事。

透過你的干涉,修改了WP核心丟給你的參數,WP核心再接著拿你改過的參數,繼續完成它該做的事,此動作就像「過濾」的動作,因而得名filter。

比較Action Hook與Filter Hook的實作語法

比較一下兩種Hook在埋進某處程式碼時所用的語法,假設我們在某處 (可能是在輸出頁首的程式碼處,或輸出文章標題、文章內容、側邊欄…等地方,要「出現客製效果」的地方)埋下這兩種hook:

/*--------------- Action Hook ---------------*/
// 埋下一個名叫'do_more'的action hook
do_action('do_more');
 
/*--------------- Filter Hook ---------------*/
// 埋下一個名叫'get_special'的filter hook,注意它會有回傳值
$c = apply_filters('get_special',$a, $b);

然後我們可以在某處 (可能是其他外掛、functions.php等處,要「實作客製功能」的地方) 實作對應的hook function:

/*--------------- Action Hook Function---------------*/
// 增加要鈎上'do_more'這個hook的hook function,
// 並為此hook function取名叫more_func。
// 第一個參數是hook名稱、第二個是hook function名稱
add_action('do_more', 'more_func');
// 實作more_func的內容,不需回傳值
function more_func()
{
    echo 'do more thing...';
}
 
/*--------------- Filter Hook Function ---------------*/
// 增加要鈎上'get_special' hook的hook function,
// 並為此hook function取名叫special_func。
// 參數1是hook名稱、參數2是hook function名稱
// 參數3是Priority(優先序)、參數4是hook function參數的數目
add_filter('get_special', 'special_func', 10, 2);
// 實作special_func的內容,需要給它回傳值
function special($a, $b)
{
    $c = $a.' & '.$b; //做一些事,例如把兩個參數連接起來
    return $c; //回傳值
}

所以其實兩種Hook的運作方式幾乎一樣,只差在增加Action Hook函式不需回傳值,而增加Filter Hook function時,你必須要回傳一個值。所以Filter Hook函式通常都有提供參數,讓想客製的人可以取得它,處理後再回傳。

但如果有一個Filter Hook,它沒有任何hook function有去鈎它,它該怎麼取得回傳值?答案是,直接拿第一個它給的參數,以上面的例子來說,它會直接拿$a丟進$c。另外,其實我們也可以把filter寫的跟action一樣,只要不回傳值就行,但action hook就沒辦法「模仿」filter hook,因為無法取得回傳值。

Hook Function的優先序(Priority)

如果有很多地方(plugin或者佈景functions.php)都add同一個hook,會怎麼決定出現順序?等案很顯然是可以透過Hook Function的Priority參數來作優先序的設定:

就像我們剛才說明的例子中,我們使用add_filter加入special_func時設定的優先序是10,這也是Priority參數的預設值。如果你希望它能優先被執行,就設定小於10的數字,反之,就設個100、500之類的,讓它延後被執行。

但其實這裡有個隱含的衝突問題。

以wp_head這個hook為例,如果我寫了一個外掛,希望透過wp_head來輸出「增加a.css檔案」的HTML語法,而a.css會重新設定body元素的樣式,所以我希望它可以最後才被匯入,不要被其他css檔干擾,於是我將Priority設為900,但我怎麼知道Priority 900夠不夠大?若某個WP網站,它除了安裝我的外掛,也安裝了其他外掛,而其他外掛剛好也重新設定body元素的樣式,然後把Priority設為950,此時我寫的外掛在處理body樣式時就出事了,於是就跟其他外掛衝突了。

所以此時我們需要了解的是:我的WP網站可能裝了很多外掛,我怎麼知道同一個Hook被加了多少Hook Function,而每個Hook Function的Priority被設定為多少?

答案是,我們可以透過$wp_filters這個global變數來取得所有hook的資訊,像是如下的function:

  // 列出所有的hook function及其priority
function list_hooked_functions($tag=false)
{
    global $wp_filter;
     
    if ($tag) 
    {
        $hook[$tag]=$wp_filter[$tag];
        if (!is_array($hook[$tag])) 
        {
            trigger_error("Nothing found for '$tag' hook", E_USER_WARNING);
            return;
        }
    }
    else
    {
        $hook=$wp_filter;
        ksort($hook);
    }
     
    echo '
<pre>';
    foreach($hook as $tag => $priority)
    {
        echo "
&gt;&gt;&gt;&gt;&gt;t<strong>$tag</strong>
";
        ksort($priority);
        foreach($priority as $priority => $function)
        {
            echo $priority;
            foreach($function as $name => $properties) echo "t$name
";
        }
    }
    echo '</pre>

';
    return;
}

當我們呼叫 list_hooked_functions(‘wp_head’); 時,就會列出wp_head這個Hook所鈎住的所有hook function,可以看到priority 10之後有好幾個都沒有數字,因為它們都沒有特別指定priority,所以都是10,包括我們剛才寫的print_sth也在其中:

>>>>> wp_head

1	wp_enqueue_scripts
2	feed_links
3	feed_links_extra
8	wp_print_styles
9	wp_print_head_scripts
10	rsd_link
wlwmanifest_link
index_rel_link
parent_post_rel_link
start_post_rel_link
adjacent_posts_rel_link_wp_head
locale_stylesheet
wp_generator
rel_canonical
wp_shortlink_wp_head
print_sth
wp_admin_bar_header
_admin_bar_bump_cb

所以,衝突很難提早避免,但發生衝突時,可以預先思考有沒有可能是因為priority的設定,導致結果跟預期不符合。

轉自http://www.mrmu.com.tw/2011/10/10/wordpress-hook/

Session的運作流程、實驗觀察心得(轉載自Ptt)

以下文章來自 Ptt實業坊

作者: megabio (LifeIsKuso) 看板: PHP
標題: [心得] php 的 session 運作流程,實驗觀察心得
時間: Sun Nov 15 13:02:45 2009

一切是從這段話開始的 :

“為什麼我修改了 gc_maxlifetime,還是沒有因為閒置而登出呢?”

隨便 google 一下就有很多討論區問過類似的問題
我自己本人對於 PHP 的 session 機制也是一知半解
所以打算以所學到的知識 + 動手實驗的方式
找出自己不清楚,疑惑的地方

作業環境是 Windows XP + Xampp 整合包 (http://tinyurl.com/lv287y)

★★★ 實驗開始 ★★★

首先新增一個 php 程式,以 session_start() 啟動 session

client browser 會寫入一個 cookie 叫做 “PHPSESSID” 內容是一長串亂碼
代表此 session 獨一無二的編號

server 端會在 “/tmp” (此數值設定在 php.ini 的 session.save_path) 這個目錄下寫
入 sess_* 的檔案

sess_* 後面的亂碼就是剛才 cookie 的編號,打開這個檔案,發現裡面沒有東西

再來我們在 php 程式中建立一個 session 變數,$_SESSION[‘user’] = ‘guest’
打開 sess_* 這個檔案,發現裡面多了以下內容 : user|s:5:”guest”;

現在我們了解,PHP 藉由 sess_* 來儲存我們建立的 $_SESSION 變數
並且跨網頁傳遞這些變數

★★★ 實驗二 : 觀察 session 垃圾回收機制 ★★★

為了方便觀察,修改 php.ini 的以下設定

session.gc_probability = 100
session.gc_divisor     = 100
session.gc_maxlifetime = 30

按照上面的設定。每次有人瀏覽網站時,PHP 就會檢查 “/tmp” 底下的 sess_* 檔案
如果有 “閒置” 超過 30 秒的 session,server 有 100% 的機率會將它刪除

有趣的部份在於 PHP 判斷 “閒置” 的條件是什麼呢? 我做了以下的實驗

第一步 : 寫兩個 php 程式

(1) login.php

 

(2) refresh.php

 

開啟 login.php 註冊一個 $_SESSION 變數,然後導向到 refresh.php
我們用 refresh.php 觀察 $_SESSION 變數是否逾期

第二步 :
先用一種 browser 開啟 login.php,此時會直接導向到 refresh.php
30 秒後我們按 F5 重整頁面,發現 session 依然還在

第三步 :
我們在同一台電腦上,開啟三種不同的 browser,並且都開啟 login.php
30 秒後,我們先重整第一個 browser 的頁面,session 一樣還在,和第二步的結果相同

但是當我們換到第二個和第三個 browser 的時候,按 F5 重整頁面發現 session 已經消
失了,”/tmp” 底下,這兩個 browser 的 sess_* 也不見了

從這裡我們可以得到結論,session.gc_maxlifetime 的確是用來清除閒置 session 的機
制,只不過和一般人所想的有些不同,它是用來清除 “別人” 的閒置 session

★★★ 我們可以想像一下 PHP 的處理過程 ★★★

(1) browser A,B,C 都連上 server 開啟 session
並且在 server 的 “/tmp” 當中留下 sess_* 檔案

(2) 30 過後,browser A 先重整頁面,對 server 提出連線要求

(3) server 檢查 “/tmp” 底下的 sess_* 發現有 browser A 的 session id
於是判定這個 sess_* 不是垃圾

(4) 至於其他的 sess_*,由於都已經超過 gc_maxlifetime 沒上來連線
所以這些 sess_* 有 100% 的機率被當成垃圾處理

* 那個 100% 的機率是我們在 php.ini 修改的設定 :

session.gc_probability = 100 (分子)
session.gc_divisor     = 100 (分母)

★★★ 結論 ★★★

PHP 踢除閒置 session 的機制就是 “先搶先贏”
超過閒置時間誰先上來連線,就判定 safe

其餘的,就看 PHP 的心情 (機率) 而定,心情不好時 (100%) 就通通踢除

經過實驗我清楚的了解到,PHP 預設的 session 垃圾回收機制其實是蠻合理的
24 分鐘後,有 1% 的機率刪除沒來連線的 sess_* 這的確符合大部分網站的流量負荷

★★★ 參考連結 ★★★

http://www.jollen.org/php/216_session_cookies/ (中文)
http://blog.wu-boy.com/2008/11/18/608/ (中文)
http://tinyurl.com/4fleql (中文)
http://chensh.loxa.edu.tw/php/X_17.php (中文)
http://www.captain.at/howto-php-sessions.php (英文)
http://tinyurl.com/yk53ndb (英文)

My Sketup

Image0303 - 12016 - 1 (1)2016 - 12016 - 1 (2)1 - 1IMG_20140308_204144IMG_20140118_19294315 - 115 - 1 (1)13 - 111 - 1IMG_02165 - 1IMG_20140104_200337IMG_20131229_125956IMG_20131229_125543

My Design

Print17966448_1482677115088710_6093646868481128014_o醉龍_直版Office Lens 20170707-00255019931913_1166903036789761_7076621256304361472_n雞蛋餅名信片Office Lens 20170707-002717Office Lens 20170707-003145Office Lens 20170707-003503Office Lens 20170707-002957Office Lens 20170707-003258Office Lens 20170707-003339Office Lens 20170707-002821Office Lens 20170707-003644

 

第一次用電腦畫素描,感覺線條有點生硬,手寫筆較細,難用.玻璃平面太滑
—–
工具:
Thinkpad X1 Yoga 2048 壓感
軟件:
Autodesk SketchBook

為INGRESS 設計的東西

EnlFinallflag6IMG_20170509_002656map - Copy-01back-01CARDBANNER850616840_28742_632294176477585823620170502_0828317815 - 1s215 - 22016 - 1

澳門醉龍海報

#澳門醉龍 畫的海報~~

科普:
舞醉龍,又稱醉龍舞或舞木龍,是流行於古香山縣(今中山、珠海、澳門)一帶的活動。一般於每年農曆四月初八,即佛誕節舉行。2006年被廣東省人民政府列為省級第一批非物質文化遺產代表作名錄。

澳門的舞醉龍是由魚商舉辦的,魚行在四月初八、初九休息。參加者首先在三街會館舉行開光典禮。舞者一邊飲用米酒,使自己醉倒,一邊手持木製的龍頭和龍尾舞動,在各區巡遊。

醉龍海報 – 橫版
醉龍海報 – 直版

《再見,地球。》

《再見,地球。》 — 因去年看了橫琴長隆的白鯨表演,雖然早有耳聞鯨類通曉人性,但親身觀看後才會更加深刻,因而對日本以科研理由重啟鯨類捕食行為更為憤概。 這幅畫的構思便是基於人類活動對鯨魚的死亡的慚悔和祈禱,愿它們的靈魂能獲得自由,並且能”飛”出地球去尋找更美好的歸屬地。  畫中的女子為地球化身,以安撫它們的靈魂和離開時不舍的心情。全畫以冷色調為主,襯托淡淡的憂傷。亦以平行線方式上色, 愿逝者靈魂得以安息的渴望。