前言
本文會介紹 PHP 程式的基本概念,並開始撰寫第一個 PHP 程式。
當前定位
PHP 最初的定位是「快速產生網站輸出的腳本」。這些輸出包括 HTML、XML、JSON、圖檔等網站常用的格式。
然而,這項優勢已逐漸被 靜態網站產生器(SSG, Static Site Generator) 吸收。SSG 同樣能產生網站輸出,並透過 CDN 部署。由於產出的是靜態網站,不需要在主機上保留 PHP Runtime,能有效降低主機漏洞風險。
若團隊仍需要傳統 內容管理系統(CMS, Content Management System),依然可以選擇 WordPress 等工具,但必須做好必要的主機維運與安全管理。
至於非內容導向的網站,PHP 已無顯著優勢,因此不建議使用。
PHP 的檔案名稱和副檔名
PHP 不會特別限制檔案名稱,只要簡單易懂即可。一般會以英文來命名檔名,打字會比較方便。不要在檔名中使用空格,以免造成不必要的麻煩。
PHP 腳本的副檔名為 .php 。在 Unix 上寫 PHP 命令列程式時,不需要副檔名,其餘情境則要加上副檔名。
終端機版本的 Hello World 程式
除了一開始練習語法外,必要時也可以用 PHP 來寫命令列程式。像是在 PHP 專案中需要寫個小工具程式,但不想用第二種語言,就可以用 PHP 來寫。
GNU/Linux
在 GNU/Linux 上的 Hello World 程式如下:
#!/usr/bin/env php
<?php
echo "Hello World" . "\n";
第一行不是 PHP 語法,而是 shebang。這項 Unix 特性用來指定腳本所用的直譯器 (interpreter),此處當然是 PHP。第二行開始的語法就是 PHP 的部分。
將上述腳本加上可執行的權限:
$ chmod +x hello
可注意到這個版本的 PHP 腳本沒有副檔名。
執行此腳本即可在終端機印出 Hello World 字串:
$ ./hello
Hello World
Windows
由於 Windows 沒有 shebang 行,所以直接寫 PHP 語法:
<?php
echo "Hello World" . "\n";
執行該腳本的方式是以 PHP 主程式直接執行:
> php.exe hello.php
Hello World
(選擇性) 包裝 PHP 命令列程式
用 PHP 寫命令列程式時,不一定要直接用 PHP 命令稿來寫。可以先用 Batch 腳本或 POSIX shell 腳本當外部指令,再從外部腳本間接呼叫實際運行的 PHP 命令稿。
由於 PHP 是跨平台的程式語言,藉由這種模式,可以很輕易地做出跨平台的命令列程式,而且可以繞過 Windows 無法使用 shebang 的議題。
Unix Wrapper
在 Unix 上建議使用 POSIX shell 或 Bash 做為 wrapper,因為這兩種 shell 較常使用。
以下是一個假想的 POSIX shell wrapper:
#!/bin/sh
#
# A wrapper to a pseudo PHP script.
# Check whether PHP is available on the system.
if ! command -v php 2>&1 >/dev/null;
then
echo "No PHP on the system" >&2;
exit 1;
fi
# Get the directory of the script itself.
cwd=$(dirname $0);
# Get the directory of the PHP script.
libexec="${cwd}/../libexec";
# Run the PHP script and pass command-line arguments to it.
php "${libexec}/program.php" "$@";
讀者可根據自身的需求,修改此 wrapper。
Windows Wrapper
在 Windows 沒有什麼命令列環境可選,當然是用 batch 腳本當成 wrapper:
@echo off
rem A wrapper to a pseudo PHP script.
rem Check PHP is available on the system.
php --version >nul 2>&1 || (
echo No PHP on the system >&2
exit 1
)
rem Get the directory of current batch script.
set cwd=%~dp0
rem Get the directory of the PHP script.
set libexec=%cwd%\..\libexec
rem Run the PHP script and pass command-line arguments to it.
php %libexec%\program.php %*
由於 batch 腳本本身的特性較弱,利用這種方式,可以把實際的工作交由 PHP 來執行。
網頁版本的 Hello World 程式
用 PHP 撰寫網頁時,輸出的不僅是文字,還有 HTML 標籤。以下是網頁版本的 Hello World 程式:
<!DOCTYPE html>
<html>
<head>
<title><?php echo "Hello World"; ?></title>
</head>
<body>
<h1><?php echo "Hello World"; ?></h1>
</body>
</html>
這裡大部分的代碼是 HTML,只有內嵌 (embedding) 少量的 PHP 程式碼。如果讀者覺得閱讀這樣的代碼有困難的話,建議回頭補強一下 HTML 的部分。
實際輸出的結果如下:
$ php hello.php
<!DOCTYPE html>
<html>
<head>
<title>Hello World</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
可以注意到原本 PHP 程式碼的部分已經不見了,替換成一般的字串。瀏覽器不認得 PHP 程式碼,所以 PHP 程式碼不會原封不動地輸出,而會在執行 PHP 腳本時動態轉換。
使用 PHP 內建網頁伺服器 (Built-in Web Server)
雖然直接閱讀輸出的 HTML 代碼也可以確認輸出結果是否正確,畢竟 HTML 代碼不易直接閱讀。可以用 PHP 內建的開發用網頁伺服器運行 PHP 腳本,以觀看上線後輸出的結果。
將工作目錄 (working directory) 移到 PHP 腳本所在的位置,在 GNU/Linux 下輸人以下指令:
$ php -S localhost:3000
在 Windows 下則改輸入以下指令:
> php.exe -S 127.0.0.1:3000
在瀏覽器上輸入 http://localhost:3000/hello.php 或 http://127.0.0.1:3000/hello.php 就可以看到頁面實際輸出的結果。
PHP 網頁程式實際上線時,不是使用內建的網頁伺服器,因為效能太差。還是要使用 Apache 或 Nginx 等真正在生產環境 (production environment) 所使用的網頁伺服器。由於篇幅限制,這裡不討論部署 (deployment) 的議題。
大小寫敏感性 (Case Sensitivity)
PHP 的變數 (variable) 會區分大小寫。$foo、$Foo 和 $FOO 視為相異的變數。
但 PHP 的保留字 (keyword) 不區分大小寫。大部分 PHP 程式設計者會沿用 C 的習慣,用小寫來寫保留字。而不會像在寫 SQL 般,用全大寫英文來寫指令。
PHP 的函式 (function) 也不區分大小寫。很多 PHP 內建函式使用 C 風格來命名,但按照 PHP 社群的慣例,會用仿 Java 的命名風格來命名函式和類別 (class),即為 CamelCase。
空白 (Space)、縮進 (Indentation)、換行 (End of Line)
PHP 對空白、縮進、換行等排版元素相對自由。除了要用空白區分識別字和語法外,可以自行將程式碼排列整齊,以利閱讀。
PHP 指令 (Instruction)
PHP 指令以敘述為單位。每行敘述後要用 ; 區隔開來。這項特性承襲自 C 和 Perl。按照 PHP 社群的慣例,每行敘述會寫在單獨一行。
PHP 標籤 (Tag)
由於 PHP 是內嵌在 HTML 頁面內的語言,要用一對 <?php 和 ?> 標籤將 PHP 指令的部分包起來。在 PHP 標籤以外的部分則會原封不動地輸出。
若 PHP 腳本不需輸出 HTML 頁面,按照社群慣例,只寫前半的 PHP 標籤即可。如下例:
<?php
function isEven($number)
{
return 0 == ($number % 2);
}
內嵌在 HTML 頁面內的 PHP 指令當然要寫出成對的 PHP 標籤。如下例:
<h1><?php echo "Hello World"; ?></h1>
對於簡短的 PHP 程式碼,可以用一對 <?= 和 ?> 來取代原本的 PHP 標籤:
<h1><?= echo "Hello World"; ?></h1>
實際上可用的 PHP 標籤不只這兩種。目前 PHP 官方文件只推薦這兩種 PHP 標籤 (出處),故不列出其他標籤。
以 PHP 撰寫網頁程式時,會將 PHP 腳本分為純 PHP 腳本和輸出 HTML 頁面 (註) 的腳本。函式、類別等會寫在純 PHP 腳本中,輸出頁面的腳本則專注在輸出頁面。
(註) 實際上不僅是 HTML 頁面、還包括 XML、JSON 等多種格式。
註解 (Comment)
PHP 承襲 C 和 Perl 的慣例,有三種風格的註解。
Perl 風格的註解是單行註解:
# A Perl-style comment.
C 風格的註解則是多行註解:
/* A C-style comment. */
雖然 C 風格的註解相對不易寫,但這種註解可跨越多行或內嵌在 PHP 程式碼內,仍然有其實用性。
C++ 風格的註解也是單行註解:
// A C++-style comment.
引入函式庫
PHP 引入函式庫的語法是 require_once。以下是一個虛擬例子:
require_once "/path/to/library/autoload.php";
這裡使用 require_once 而非 require 是因為前者沒有重覆引入的問題,適用在函式、類別等不能重覆宣告的項目。
主函式 (Main Function)
PHP 沒有主函式的概念,直接把指令寫在腳本頂層即可。函式、類別等則會用特定的區塊 (block) 來區隔。
離開狀態 (Exit Status)
在命令列程式中,使用 exit 函式回傳程式的離開狀態。慣例上,回傳 0 表示程式成功地結束,回傳 1 則是程式有錯。
但在網頁程式中,使用上述函式對網頁沒什麼實質意義。對網頁程式來說,比較有意義的回傳值是 HTTP 狀態碼 (HTTP status code)。回傳 HTTP 狀態碼的函式是 http_response_code。
網頁成功執行的 HTTP 狀態碼是 200。在網頁出錯時,根據不同的錯誤情境會有相異的回傳值。這裡有一份 HTTP 狀態碼的清單。
壓縮輸出的網頁
在撰寫 PHP 腳本時,為了維護方便,會將 PHP 腳本排列整齊。但這樣的腳本所輸出的 HTML 頁面會多出許多不必要的空白。這些空白對瀏覽器來說是沒有實際意義的,平白耗費網路頻寬而已。
所幸,Apache 和 Nginx 等網頁伺服器,都可以壓縮 HTML 頁面。因此,不需要故意將 PHP 程式碼擠在一起,這只是自找麻煩的動作。