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

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 (英文)