如何寫出安全又可靠的PHP腳本


前言

咔咔目前所做的項目是一個saas系統,在開發新功能之后,需要為用戶角色添加相應的權限,這時整個系統的所有用戶都需要添加相應的權限。

因為以前系統的缺陷現在只能用腳本來處理這些工作,所以接下來咔咔將向你介紹如何編寫安全可靠的PHP腳本,以及如何事先設計好這個功能,踩過一個坑直接把它埋起來

一、如何寫一份安全又可靠的PHP腳本

1-1 設置合理的內存

PHP中使用memory_limit為每個進程設置內存,跑腳本的內存給256M或512M。

通過設置內存來防止腳本執行死循環占用大量的內存,導致系統崩潰。

在文件開頭寫入ini_set('memory_limit', '512M');即可。

1-2 可接收命令行傳參

在PHP中接命令行的參數為$argv,下標0是文件名,下標1為你傳入的參數。

簡單來說就是先創建一個 index.php文件,然后打印出$argv

執行index.php文件php index.php kaka,打印出來的數據與上面介紹的數組形式完全一樣。第一個值是執行的文件名,第二個參數就是攜帶給腳本的參數。

之前寫了一篇laravel中給命令行攜帶參數不了解一下嗎?可以看看,當時是rabbitmq路由模式遇到問題才知道的這個參數,如今在寫腳本就可以直接用了。

1-3 使用while死循環執行

$id = !empty($argv[1]) ? $argv[1] : 0;
while(true){
    $sql = "select * from user id > $id  order by id asc limit 10000";
    $res = 執行$sql語句;
    if(empty($res)){
        break;
    }
    foreach ($res as $k => $v) {
     // 這個id保存每次執行的值
        $id = $v['id'];
        $checkDataSql = "檢測數據是否已存在";
    // 不存在時在進行添加數據
        if(!$checkSql){
            $sql = "insert into user ...";
            $res = "執行添加操作";
            // 返回最后執行的主鍵ID
            echo $res."\n";
        }
    }
    // 刪除此次的變量,防止內存溢出
    unset($res);
}
echo 'ok';

簡單的解釋一下這份代碼

  • 接受命令參數,此參數用於防止腳本執行一半掛掉,可以輸入參數繼續執行。
  • 使用while執行死循環邏輯
  • 獲取需要添加權限的數據,每次查詢10000行。
  • 死循環退出條件是查詢不到需要添加權限的數據。
  • 循環處理每次查詢的10000行數據。
  • 將查詢出來的數據ID循環一次就賦值給while外層的ID,防止腳本掛掉知道從哪條數據繼續執行。
  • 做一步檢測操作,判斷此條數據是否已經存在將要給添加的權限。不存在時再進行添加操作。
  • 最后一步也是最重要的一步將添加完成的主鍵ID返回到終端,腳本掛掉直接用這個ID作為參數繼續執行。
  • 刪除或者10000行數據的變量,防止內存溢出。
  • 最后一步也是最重要的數據處理完成后需要返回一個標識,知道此次腳本已經執行完成了。

這里舉的例子的是添加,若你的數據量大的情況下可以進行批量添加、修改。

這份腳本還不是很完善,如你有更好的想法那咱們評論區見。

二、如何提前設計好類似此種情況

1-1 使用腳本刷數據的缺點

  • 新員工對業務不熟悉,數據刷錯后恢復數據工作量更大
  • 前期用戶基數少沒感覺,用戶量起來后每次執行腳本會花費很長時間
  • 用戶量大之后MySQL深度分頁查詢非常慢,會人為造成慢查詢影響正常用戶使用
  • 在你刷數據的同時會有新用戶注冊進來,非常容易造成數據庫死鎖。

為什么會產生死鎖?

例如你目前的數據數據是這樣的,id、authority_info、name

insert into t values(1,5,5),(5,5,5),(10,10,10),(15,15,15),(20,20,20),(25,25,25);

腳本執行語句為update role set authority_info=authority_info+1 where id = 7

此條語句為等值查詢會退化為間隙鎖,加鎖范圍為(5,10),若此時來了一個用戶執行語句為insert into role (8,8,8)很遺憾是執行不成功的。

update將(5,10)的數據加了間隙鎖,在沒有釋放鎖的情況下,這個范圍的所有數據是無法添加的。

當然這種情況僅在無主鍵索引的情況下會產生,因為主鍵索引的等值查詢會退化為行鎖。

總之使用腳本刷數據是百害而無一利的,接下來看看如何處理這種情況。

1-2 應用場景

每一家公司注冊后,默認有幾個角色,如超管、子管、財務、行政、人事等,所有權限都綁定在相應的角色下,使用單條記錄方式。

既然需要添加一個CUI功能模塊,就需要添加此模塊對應的權限,默認情況下為所有租戶相應的所有角色添加這個權限。

每一個租戶對應角色的權限都存儲在角色表中,所有權限信息是在一個json串中存儲,並切維護了一個更新時間。

那么我們就以獲取權限為切入點,主系統單獨維護一個權限表,來看看這個過程是怎樣的。

1-2 方案落地

那么如何在這個設計上進行優化,確保后期不再使用腳本進行刷數據,而是讓用戶自主觸發。

role表結構數據如下

系統表

系統添加新功能需要設置權限時,將權限名以json存儲至系統表的auth_info中,每次更新權限都添加一條數據。

用戶登錄后獲取role表的更新時間,去系統表中做判斷將大於role時間的數據拿出來,將查詢出來的權限合並到role表中,並更新這條記錄的更新時間。

並將新增的權限維護至應用初始化中,新注冊的用戶需將所有的權限給添加上,這點根據自身系統看是否需要維護。

1-4 方案弊端

此方案雖然解決了每次添加新權限需要執行腳本,但不可避免用戶每次登錄獲取權限時都需要使用自身的權限時間跟系統權限時間做比對。

無形中會多進行一次查詢,這也是咔咔目前能想到的方案,你要是有更好的方案可以探討一下。

三、總結

本文圍繞實際業務進行討論了腳本如何寫、設計缺陷如何后期彌補,在腳本中一定要使用正序(防止新用戶注冊進來)這點相信大家都清楚。

方案在我看來還不是最優的,你要是有更好的思路,可以在評論區擴展一下。

堅持學習、堅持寫作、堅持分享是咔咔從業以來所秉持的信念。願文章在偌大的互聯網上能給你帶來一點幫助,我是咔咔,下期見。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM