無意間發現 Laravel 在 public/index.php 有這樣一段:
define('LARAVEL_START', microtime(true));
這樣要取得程式執行時間就方便多了,不用做一些 workaround。
若要取得程式執行時間,可以這樣寫:
$elapsed = mocrotime(true) - LARAVEL_START;
軟體開發和生活瑣事
無意間發現 Laravel 在 public/index.php 有這樣一段:
define('LARAVEL_START', microtime(true));
這樣要取得程式執行時間就方便多了,不用做一些 workaround。
若要取得程式執行時間,可以這樣寫:
$elapsed = mocrotime(true) - LARAVEL_START;
執行了 composer update 以後,發現會出現安全性警告:
Problem 1
- Root composer.json requires phpunit/phpunit ^11.5, found phpunit/phpunit[11.5.0, ..., 11.5.55] but these were not loaded, because they are affected by security advisories ("PKSA-5jz8-6tcw-pbk4", "PKSA-z3gr-8qht-p93v").....
看了 composer v2.9.0 release notes 才知道有這個功能。
感覺可以在 actions 裡面加個檢查。
ps. v2.9.2 中可以使用 -no-security-blocking 參數暫時關閉安全檢查
我接觸 PHP 蠻久了,大概從 5.1 還是 5.2 開始用到現在 8.4,避開了聽說很雷的 PHP 4,看著 PHP 引進新的功能,到現在社群開發出各種神奇的工具。
回顧過去一些事情,我覺得很重要的事情,不只可以了解 PHP 是什麼樣子程式語言,或許也可以協助判斷其他程式語言是否適合在你手上的專案使用。
就我所知,PHP 4 到 5 主要是支援物件導向,開始可以使用 class 來設計自己所需的功能,不用擔心 function name 和其他人的衝突。在這個時候,大家可以各自在網路上下載別人寫好的類別來使用,例如那個時候「藍色小舖」就有很多工程師分享自己實做的功能,例如 MySQL driver 之類的。
一些大型的專案仍保有這類的程式,像是 dolibarr ERP 的 /htdocs/core/class 目錄下,就有多個作者實做的 class。
在這個時候,功能比較複雜的專案可能會遇到類似的問題,我想要 require 新的 class 時,發現該 class 也有 require 其他的 class,如 dolibarr 的 fileupload.class.php:
<?php
/* Copyright (C) 2011-2022 Regis Houssin
* ....
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
/**
* This class is used to manage file upload using ajax
*/
class FileUpload
{
在 FileUpload 中需要使用到 files 和 images 這二個類別。
若現有的專案,剛好有這二個類別時,會遇到類別名稱衝突,導致 require 失敗,導致 FileUpload 沒有辦法使用。這個問題其實和更早期 PHP 的狀況相同,大家各自實做自己的 function、和別人分享 function,卻又怕遇到相同的 function 名稱導致衝突。
為了解決 dependency (相依性、依賴關係) 問題,PHP 5.3 開始支援 namespace。
透過 namespace 功能,大家為 class 和 function 設定各自的 namespace,如:
<?php
namesace Zeroplex;
class FileUpload {
}
雖然 class 名稱相同,但只要在不同的 namespace 就不會導致衝突。
再故事往下之前提一下 autoload 功能,當 require 寫到手軟時,就開始有人開始想各種偷懶的方法了。
PHP 的 autoload 功能,允許使用者在建議物件時,針對自訂的邏輯,決定要做什麼事。所以這讓帶來幾個好處:
當這個功能合併使用時,就獲得合併技能「要使用時在 require」,不僅可以讓程式碼可讀性增加,也可以減少 disk I/O、記憶體使用量。
例如我透過 spl_autoload_register() 先定義好 require 規則:
<?php
spl_autoload_register(function ($className) {
$classInfo = explode('/', $className);
$namespace = $classInfo[0];
$className = $classInfo[1];
echo "namespace: " . $namespace . "\n";
echo "class name: " . $className . "\n";
require __DIR__ . "/library/" . $namespace . "/" . $className . ".php";
}
上述設定會自動將 Zeroplex/FileUploader 拆解成 namespace 和檔案名稱並 require,執行程式時會在 new 階段透過 autoloader 來決定要去哪裡 require 檔案:
<?php $uploader = new Zeroplex\FileUploader(); namespace: Zeroplex class name: FileUploader
在百家爭鳴寫 library 時,還會遇到一個問題:A 和 B library 都需要用到 C library,但 A library 中任何版本的 C library 都可,而 B library 則需要 C library 的最新版。這樣開發程式時,就還需要考慮到底要使用 C library 的哪一個版本。
於是 composer 就出現了。composer 是 dependency management,會自動尋找專案中各 library 可正確運作的版本。
時間往後走一段時間, 來到 PHP 7。
PHP 7 做的最大改變,是大幅改寫直譯器,讓效能大幅提升,而且不只是快一點點而已,是快了 100%。以前為了讓 PHP 程式跑得更快,Facebook 開發了 hhvm,現在用不到了。
若想知道為什麼效能可以快這麼多,可以參考 Nikita Popov (PHP 核心工程師之一) 的簡報:PHP 7 – What changed internally?。或是聽他的演講:
除了效能以外,PHP 在此時也慢慢的提供一些資料型態的定義、檢查功能,像是在撰寫 function 時可以定義 argument 的資料型態 (type hint),除了 scalar type 也開始支援使用者自訂 class 的支援。
這些支援,讓原本是 dynamic typing 的 PHP 可以多一些檢查,程式的可讀性和穩定性上都有改善。
PHP 7 到 8 我打算很不負責任得什麼都不說直接跳過 XD
這邊列出一些有趣的專案:
在 Blade template engine 中使用判斷式:
@if (Auth::check())
<!-- 已登入 -->
@else
<!-- 未登入 -->
@endif
Laravel 允許自訂 directive,自訂常用的判斷式。
開啟 /app/Providers/AuthServiceProvider.php 並在 boot() 中加入以下設定:
// Illuminate\Support\Facades\Blade;
Blade::if('isLoggedIn', function() {
return Auth::check();
});
之後,就可以在 Blade 中使用自訂判斷:
@isLoggedIn
<!-- 已登入 -->
@else
<!-- 未登入 -->
@endisLoggedIn
PostgreSQL 中,要查詢 item 是否在 JSON list (array) 中的語法是:
where user_list::jsonb ? 'user name'
如果使用 DB:raw() 代入 Laravel Eloquent 會出現錯誤訊息:
-- where(DB::raw('user_list::jsonb ? \'user name\''))->get()
SQLSTATE[42601]: Syntax error: 7 ERROR: syntax error at or near "$1"
原因是 ? 會被視為 prepare/binding 的記號,上面語法沒有給定 bind 條件,所以發生錯誤。
若不使用 prepare/binding,則要 ? escape 為 ??:
-- where(DB::raw('user_list::jsonb ?? \'user name\''))->get()
Illuminate\Database\Eloquent\Collection {#6157
....
}