# CLI PHP 的優點
多年以來,我一直都在應用不確定的工程師職責定義。我個人認為,工程師就是使用工具來實現與該工具最初開發目的無關的功能。雖然這種想法并不總是正確,但當您認真考慮它時,將發現大多數創新和發明確實來自于通過以前從未考慮過的方法使用工具。
想象一下我是多么驚訝,然后,我忽然想到一個主意:使用我的老朋友 PHP,作為命令行工具,它對于 Web 頁面來說一直都非常可靠。我絕對不是這樣做的第一人,但是這對我來說的確是全新的想法。
當然,只是可以 在命令行中使用 PHP 當然不是這樣做的最佳理由。但是,您在第一次開始以這種方式用 PHP 進行試驗時可能很快就會發現幾個令人興奮的驚喜。首先,調試現有腳本將變得前所未有地簡單。由主要輸出和極少邏輯組成的 PHP 程序都將變得令人難以置信地簡單。
除此之外,您還可以使用所有 PHP 知識來完成以前從未想過使用 PHP 完成的任務。事實上,確實沒有任何事能阻止您使用 PHP 作為幾乎所有給定編程項目的全能王。
產生興趣了?很好,讓我們開始著手并看看您可以在命令行中使用 PHP 來完成哪些任務。
## 安裝
安裝十分簡單扼要,它甚至可能都不需要特別安裝任何內容。開始時先在命令行中嘗試一個簡單的 PHP 腳本。您可以使用現有 PHP 腳本,也可以嘗試使用以下代碼。本例是基于 Linux? 的,但是類似的原理也適用于其他系統。
首先,確認 PHP 可執行文件的位置 —— 對于大多數 Linux 系統,幾乎肯定是 /usr/bin/php。如果不確定其位置,請在命令行中鍵入 which php并查看響應內容。
其次,鍵入以下代碼,確保將 /usr/bin/php 替換為 PHP 可執行文件的實際路徑。
#!/usr/bin/php -q
Hello world
保存文件并確保用可執行權限做了標記。在大多數系統中,您都可以用 chmod +x hello-world 或類似代碼完成此操作。然后,執行 PHP 文件(通過運行 ./hello-world 或者,如果有必要,運行 php hello-world),并查看它是否運行。如果它運行,則安裝的 PHP 在默認情況下會包括命令行功能。
如果代碼未能正確運行,事情可能會變得有點兒麻煩。關于原因有很多種不同的可能性。如果您得到一個與 PHP 相關的錯誤(除了 “未找到程序” 之類的結果),則問題是代碼中的輸入錯誤。如果未找到 PHP 可執行文件,請確保您使用了正確的路徑。如果沒有名為 PHP 的可執行文件,則必須獲得一個。
獲得 PHP 可執行文件可能根據所在系統的不同采取不同的步驟,但是獲得所需支持不應當太難。您可以從查閱針對特定操作系統或發行版的文檔開始。
很明顯,如果這時系統中未安裝 PHP,則先安裝 PHP,然后再次嘗試執行以上代碼。對于許多系統,安裝 PHP 是您需要做的全部操作。如果需要執行更多操作,有時只需使用您最喜歡的包管理工具(例如 apt-get 或 yum)獲得 PHP CLI 包(名稱可能略有不同)就可以解決此問題。
如果在 PHP 包管理工具中未能找到命令行,最糟糕的情況是您可以用 --enable-cli 標記重新編譯 PHP。事實上,這樣做的優點是提供了稍微優化了的系統并且不管怎么說都不是個壞主意。無論采用哪種方式,開始使用它都不應當太難。
到現在,您應當已經運行了 HelloWorld 腳本,并且輸出就是可能已經猜測到的結果。我不會詳細說明該腳本的工作原理,但是使用過 shell 腳本和 PHP 的人應當十分熟悉腳本的大部分內容。由于這第一個腳本現在的運行沒有出現任何問題(我們希望如此!),因此我們將略微偏離真正的命令行接口 (CLI) 應用程序,看一看為什么在命令行中使用 PHP 對于所有 PHP 程序員來說都是最佳選擇的好理由:調試。
PHP 調試
可能只有我有這種體驗,但是我經常發現調試 CLI 程序會演變成一場噩夢,尤其是處理嵌入了 HTML 的腳本,例如 Microsoft? Active Server Pages (ASP) 或 PHP。通常很難判斷特定錯誤消息所表達的精確含義,用戶輸入可能很難再次生成,并且整件事通常會使您頭痛的希望拔光頭發。
不幸的是,雖然足夠聰明的程序員可以找到 CLI 能夠有所幫助的方法(例如,通過從文件讀取輸入或通過一個通道的另一個程序),但是大部分問題都不會由于使用命令行而顯著減弱。不過,CLI 確實使一件事變得更加簡單:找到錯誤消息,以便可以在第一位置讀取這些錯誤消息。
為了查看使用命令行調試的值,讓我們從下面所示的非常非常糟糕的 PHP 文件開始。
#!/usr/bin/php -q
Don<'t>code<?php while drunk(); ?<
雖然第一眼看到代碼時,就發現這個腳本中的一些錯誤十分明顯,但是代碼在 Web 瀏覽器中提供了無用調試信息的極好示例。尤其是,不會發生這種情況:在 Apache 中運行此頁面只是得到了根本沒有輸出的結果。如果錯誤不明顯,那么您怎樣找到錯誤原因?
解決這個問題的傳統方法是查看錯誤日志。例如,您可能先運行:
tail -f /var/log/httpd/error_log
然后再將頁面裝入運行 Apache 2 的 Linux 系統,這將得到諸如下面的輸出:
[client 127.0.0.1] PHP Parse error: parse error, unexpected T_STRING, expecting
'(' in /var/www/html/dont-code-drunk.php on line 2
不幸的是,您可能同意我的觀點,考慮這種不太理想的解決方案。一方面,文件日志位置可能因系統的不同而有所不同。另一方面,您必須瀏覽不相關事件的日志以查找所需內容,這對于活動的服務器來說很可能是一場噩夢。
此時,您可能和我曾經想的一樣:一定會有更好的方法。
使用 CLI 定位并修正錯誤
通過直接在 CLI 上運行腳本,可以輕松地分離出代碼中存在的問題。運行:
php dont-code-drunk.php
結果,可能會得到以下輸出:
PHP Parse error: parse error, unexpected T_STRING, expecting
'(' in /var/www/html/dont-code-drunk.php on line 2
Content-type: text/html
X-Powered-By: PHP/4.3.11
更棒了!錯誤消息已與其他數據分離,并且在做出更改后可以輕松地重新查看頁面。在這種情況下,很清楚它在行中某個位置需要 '('。while語句看似適合用在這里,因此添加一組新括號,得到以下源代碼。
#!/usr/bin/php -q
Don<'t>code<?php while (drunk()); ?<
現在,再次運行代碼將得到以下輸出:
清單 1. 再次運行結果
Content-type: text/html
X-Powered-By: PHP/4.3.11
<'t>codePHP Fatal error: Call to undefined function:
drunk() in /var/www/html/dont-code-drunk.php on line 2
腳本仍有很多問題,但是您已經取得了一些進展。您已經相對輕松地提取到了所需的錯誤消息,使您可以快速修正程序中的問題并繼續處理下一個問題。
全然使用 PHP 的任何人都可以以這種方式充分利用 CLI PHP 進行調試。但是為什么不進一步伸展您自己并開始用 PHP 真正實現 shell 腳本?向前邁進,并且您將會看到將 PHP 用于簡單或者不那么簡單的 shell 腳本的一些可能性。
PHP I/O 通道
為第一個 PHP 腳本制定一個簡單的初始目標:創建一個能讀入文件并打亂該文件中各行的腳本。如果要打亂 m3u 文件或類似內容,則使用此功能可能很便利。這樣做意味著您必須能夠從文件或標準輸入讀取數據并將數據寫回終端。
這將出現 PHP 中的第一大挑戰。PHP 最初不是設計用于與用戶直接的鍵盤輸入或文本輸出結合使用。了解這一設計是至關重要的,因為如果需要在命令行中執行任何操作,都必須能夠與用戶來回通信。在諸如 C 之類的傳統編程語言中,您將使用 STDIN、STDOUT 和 STDERR 完成此操作。您可以將 PHP 中的相同通道分別用于輸入、標準輸出和輸出到錯誤通道。
STDOUT:echo、print、STDOUT 和 php://stdout
即使 PHP 設計用于輸出到瀏覽器而不是輸出到 CLI,從 PHP 創建輸出也是非常簡單的,它幾乎不需要花太多時間思考。記住,PHP 標記外的任何內容都將被直接輸出到 CLI,這就是上面的 HelloWorld 程序如此簡單的原因。這也是為什么先輸出上面的 Don<'t>code 后再輸出錯誤消息的原因。您是否用 HTML 標記圈起文字都沒有關系;因為不管怎樣都會顯示這些文字。事實上,您通常需要避免使用 HTML 標記,因為它們都將直接打印給用戶。
您還可以使用基本函數進行輸出。例如,echo 和 print 命令打印到標準輸出。
#!/usr/bin/php -q
Output #1.
<?php echo "Output #2.";
print "Output #3."?>
這將得到:
Output #1.
Output #2.Output #3.
注:PHP 標記外的新行已被輸出,但是 echo 命令或 print 命令中沒有暗含的新行。事實上,命令提示符重新出現在 Output #2.Output #3.所在的行中。PHP 擁有的任何其他打印函數將會像此函數一樣運行正常,任何寫回文件的函數也是一樣的。
#!/usr/bin/php -q
<?php
$STDOUT = fopen("php://stdout", "w");
fwrite($STDOUT, "Output #1.");
fclose($STDOUT);
?>
以上代碼將把 php://stdout 作為輸出通道顯式打開,并且 php://output 通常以與 php://stdout 相同的方法運行。最新版本的 PHP 可以使用 STDOUT 作為常量而不是定義上面使用的變量 $STDOUT。
STDERR:STDERR 和 php://stderr
STDERR 與 STDOUT 十分接近。用于寫入此通道的所有技術將鏡像 STDOUT 的那些技術,惟一的差別是您將打開 php://stderr 而不是php://stdout 或 php://error。
STDIN:STDIN 和 php://stdin
STDIN 是從 Web 編程而來的最有趣更改,因為它向您展示了真正的用戶輸入而不是使用表單或其他基于瀏覽器的方法。嘗試以下命令:
#!/usr/bin/php -q
<?php
$file = file_get_contents("php://stdin", "r");
echo $file;
?>
這段代碼的工作原理應當很像 cat,回轉提供給它的所有輸入。但是,這時它還不能接受參數。
第一個 PHP shell 腳本
很好 —— 從這里開始事情會變得非常有趣。運用到目前為止學到的簡單知識,您可以創建簡單而有用的 shell 腳本。在文本編輯器中鍵入以下代碼。
清單 2. randomize-lines
#!/usr/bin/php -q
<?php
$lines = split("\n", file_get_contents("php://stdin", "r"));
shuffle($lines);
foreach ($lines as $line) {
if ($line !== "") {
echo "$line\n";
}
}
?>
現在,只需一些快速檢查即可運行這個腳本:
確保 hashbang(第一行,以 #! 開頭)被設為先前描述的 PHP 可執行文件的位置
保存文件
使用 chmod 添加可執行權限
運行程序
注:randomize-lines 將完全執行所期望的操作:它打亂鍵入的輸入內容行并將它們以不同的順序返回回來。這項功能可以頗具價值地填補 shell 腳本庫中的空白。
作為此腳本的應用程序示例,您可以使用它為音樂或視頻播放器動態生成隨機播放列表。例如,要打亂 XMMS 播放列表,請嘗試:
./randomize-lines < .xmms/xmms.m3u > temp
mv temp .xmms/xmms.m3u
現在,再升一級。
命令行參數
實際命令行程序使用參數。同樣,就像 C 語言和其他類似語言一樣,您可以為此目的而使用 argv 和 argc。特別是,argv 是程序的參數數組,第一個參數是程序本身。使用這個函數,構建根據給定參數從文件或用戶輸入讀取數據的程序就不難了。例如,請查看以下代碼。
清單 3. randomize-lines-w-args
#!/usr/bin/php -q
<?php
array_shift($argv);
if (count($argv) == 0) {
$argv[0] = "php://stdin";
}
foreach ($argv as $file) {
$lines = split("\n", file_get_contents($file, "r"));
shuffle($lines);
foreach ($lines as $line) {
if ($line !== "") {
echo "$line\n";
}
}
}
?>
現在您擁有這樣一個程序:完全運行的 CLI PHP 程序,它可以接受用戶輸入,也可以接受文件列表并隨機排列每個文件的相關內容。
結束語
工欲善其事,必先利其器;但是記住:最佳工具通常都不是您期望使用的那一個工具。請給 PHP 一個機會,在命令行接口中使用它,您就會發現它已經成為您的 shell 腳本工具新寵。最糟糕的情況是:它可以省去一些 Web 服務器麻煩。
- PHP技術文章
- PHP中session和cookie的區別
- php設計模式(一):簡介及創建型模式
- php設計模式結構型模式
- Php設計模式(三):行為型模式
- 十款最出色的 PHP 安全開發庫中文詳細介紹
- 12個提問頻率最高的PHP面試題
- PHP 語言需要避免的 10 大誤區
- PHP 死鎖問題分析
- 致PHP路上的“年輕人”
- PHP網站常見安全漏洞,及相應防范措施總結
- 各開源框架使用與設計總結(一)
- 數據庫的本質、概念及其應用實踐(二)
- PHP導出MySQL數據到Excel文件(fputcsv)
- PHP中14種排序算法評測
- 深入理解PHP原理之--echo的實現
- PHP性能分析相關的函數
- PHP 性能分析10則
- 10 位頂級 PHP 大師的開發原則
- 30條爆笑的程序員梗 PHP是最好的語言
- PHP底層的運行機制與原理
- PHP 性能分析與實驗——性能的宏觀分析
- PHP7 性能翻倍關鍵大揭露
- 鳥哥:寫在PHP7發布之際一些話
- PHP與MySQL通訊那點事
- Php session內部執行流程的再次剖析
- 關于 PHP 中的 Class 的幾點個人看法
- PHP Socket 編程過程詳解
- PHP過往及現在及變革
- PHP吉祥物大象的由來
- PHP生成靜態頁面的方法
- 吊炸天的 PHP 7 ,你值得擁有!
- PHP開發中文件操作疑難問答
- MongoDB PHP Driver的連接處理解析
- PHP 雜談《重構-改善既有代碼的設計》之二 對象
- 在php中判斷一個請求是ajax請求還是普通請求的方法
- 使用HAProxy、PHP、Redis和MySQL支撐10億請求每周架構細節
- HTML、HTML5、XHTML、CSS、SQL、JavaScript、PHP、Web Services 是什么?
- 重構-改善既有代碼的設計
- PHP場景中getshell防御思路分享
- 移動互聯時代,你看看除了PHP你還會些什么
- 安卓系統上搭建本地php服務器環境
- PHP中常見的緩存技術!
- PHP里10個鮮為人知但卻非常有用的函數
- 成為一名PHP專家其實并不難
- PHP 命令行?是的,您可以!
- PHP開發提高效率技巧
- PHP八大安全函數解析
- PHP實現四種基本排序算法
- PHP開發中的中文編碼問題
- php.get.post
- php發送get、post請求的6種方法簡明總結
- 中高級PHP開發者應該掌握哪些技術?
- 前端開發
- web前端知識體系大全
- 前端工程與性能優化(下)
- 前端工程與性能優化(上)
- 2016 年技術發展方向
- Web應用檢查清單
- 如何成為一名優秀的web前端工程師
- 前端組件化開發實踐
- 移動端H5頁面高清多屏適配方案
- 2015前端框架何去何從
- 從前端看“百度遷徙”的技術實現(一)
- 從前端看“百度遷徙”的技術實現(二)
- 前端路上的旅行
- 大公司里怎樣開發和部署前端代碼?
- 5個經典的前端面試問題
- 前端工程師新手必讀
- 手機淘寶前端的圖片相關工作流程梳理
- 一個自動化的前端項目實現(附源碼)
- 前端代碼異常日志收集與監控
- 15年雙11手淘前端技術總結 - H5性能最佳實踐
- 深入理解javascript原型和閉包系列
- 一切都是對象
- 函數和對象的關系
- prototype原型
- 隱式原型
- instanceof
- 繼承
- 原型的靈活性
- 簡述【執行上下文】上
- 簡述【執行上下文】下
- this
- 執行上下文棧
- 簡介【作用域】
- 【作用域】和【上下文環境】
- 從【自由變量】到【作用域鏈】
- 閉包
- 完結
- 補充:上下文環境和作用域的關系
- Linux私房菜