PHP教程系列4-按需載入的程式碼才有靈魂
有一天產品經理給大家提了個需求,指著淘寶說,做個一樣的,這個功能很簡單,怎麼實現我不管……
說時遲那時快,小明拿起鍵盤就是幹。小明快速的封裝了兩個類,分別如下:
-
TestA.php
<?php class TestA{ public function getDemo(){ echo '淘寶首頁'; } }
-
TestB.php
<?php class TestB{ public function getDemo(){ echo '淘寶商品頁'; } } /**** 小明拿到需求後幹勁十足,於是在程式碼中添加了一億行註釋 什麼渣渣需求,大爺不幹了…… 什麼渣渣需求,大爺不幹了…… 什麼渣渣需求,大爺不幹了…… *****/
包含註釋資訊的TestB.php檔案有1G,請自行貼上複製將TestB.php檔案弄成1G左右,下面會有驚喜。
然後建立一個index.php檔案來呼叫這兩個類。
<?php include("TestA.php"); include("TestB.php"); if(isset($_GET['type'])){ if($_GET['type'] == 1){ $a = new TestA(); }else{ $a = new TestB(); } $a->getDemo(); }else{ echo "頁面不存在,請指訪問 http://127.0.0.1:3000/index.php?type=1"; }
在當前目錄下開啟命令列,然後執行php -S 127.0.0.1:3000
(啟動PHP服務),再訪問http://127.0.0.1:3000/index.php?type=1
等你的頁面載入完,你會打心裡說一句“這麼卡,PHP這語言真**”。
於是我們優化一下index.php的程式碼,讓首頁不那麼卡,優化後的程式碼如下:
<?php if(isset($_GET['type'])){ if($_GET['type'] == 1){ include("TestA.php"); $a = new TestA(); }else{ include("TestB.php"); $a = new TestB(); } $a->getDemo(); }else{ echo "頁面不存在,請指訪問 http://127.0.0.1:3000/index.php?type=1"; }
這次再訪問http://127.0.0.1:3000/index.php?type=1
發現速度快了很多。
從上面這個簡單的例子可以得出下面一些結論:
- PHP是解釋型語言,每次執行時都需要載入檔案、編譯檔案中的程式碼、最後執行。雖然註釋不是程式碼,但也會被載入導致速度變慢。
- 硬碟的讀取速度很慢,45M/秒左右。
- 按需載入能優化程式,因為不同請求要載入的類和檔案是不一樣的。
一. 解釋型語言的優缺點
程式語言分為解釋型語言和編譯型語言,像c,golang就是編譯型語言,需要build成可執行檔案才能執行。像PHP,Python,JavaScript就是解釋型語言,可以理解為需要實時編譯(解釋)再執行。
作為解釋型語言,PHP最大的優點有如下幾個:
-
不關注變數型別,直譯器會自己推算出變數型別。定義變數很輕鬆,很大程度的提升了程式設計速度。比如定義了
$a = 1000;
之後執行$a = 12.11;
和$a = "abc";
程式都能正常執行,開發人員不用關心變數是int、float、char。 - 熱更新,程式碼修改了之後是實時生效的,不用重啟伺服器。
- 自帶很多實用的擴充套件,常用的時間處理,字串處理,檔案處理,資料庫讀寫等等,真正的開箱即用。
- 容易上手,也導致很多人只注重功能實現不注重程式碼質量。
作為解釋型語言,PHP最大的缺點如下:
- 有很多歷史包袱,年紀太大的鍋。
- 函式命名不規範,怎麼開心怎麼來的節奏,今天小駝峰明天匈牙利。
- 缺少好用的包管理器和依賴管理方案,安裝擴充套件很麻煩。
- 只能用於Web開發,其他領域不實用。也算是優點吧(專一)
二. 硬碟讀取速度的侷限
任何程式語言,在專案程式碼多了之後都會遇到硬碟讀取速度的瓶頸,很多程式語言設計之初就引入了包管理的概念,比如C++ 的using namespace std;
比如 golang的import "fmt"
Python的import re
。通過合理的對包做管理,就能解決由於專案檔案太大導致使用者請求變慢的問題,很少有哪個請求或者方法需要將所有包程式碼都載入到記憶體中。
如果一個資原始檔的大小是100K,400個使用者同時發起請求就是40M。這個時候硬碟的瓶頸就出來了,通常我們會將檔案放入到記憶體中,記憶體的讀取速度在10G/秒左右,100K的檔案支援10萬用戶同時獲取。理論上是這樣,但網絡卡不一定承受的住,現在大部分網絡卡還是千M網絡卡,扛不住10G/秒的請求。有興趣的朋友可以看看PHP教程系列3-寫PHP程式前必須知道的5點資訊 。
三. 按需載入要解決哪些問題
還是之前的例子,小明封裝的檔案越來越多,新加一個檔案就寫一條 include 語句,淘寶網有幾萬個頁面,難道要寫幾萬個include?我們需要再優化一下程式碼,讓程式變的智慧一點,別寫太多include。優化後的程式碼如下:
<?php function autoload($className) { $fileName = $className. '.php'; include $fileName; } spl_autoload_register('autoload'); if(isset($_GET['type'])){ if($_GET['type'] == 1){ $a = new TestA(); }else{ $a = new TestB(); } $a->getDemo(); }else{ echo "頁面不存在,請指訪問 http://127.0.0.1:3000/index.php?type=1"; }
我們去掉了之前的include語句,通過spl_autoload_register()
函式來實現按需載入,這個函式的含義是如果當前找不到類名,就去指定的目錄查詢。通過spl_autoload_register()
方法我們把 include 去掉了,這時候再新建TestC.php ,TestD.php 都不需要再通過 include 引入。大大的節省了程式設計時間,簡單的實現了按需載入。
不過上面的方法有個問題,如果我需要引入小芳的程式碼庫,小芳程式碼庫的檔名並不等於類名,例如檔名是TestC.class.php類名是TestC。我們就沒法通過類名載入到指定的檔案了。如果小芳也有一個TestA.php檔案,和小明的衝突了怎麼辦?
為了解決上面這兩個問題,PHP5.3引入namespace
名稱空間的概念,PHP5.3之前的版本是不支援名稱空間的。名稱空間的引入,主要就是為了優化包管理,按需載入等問題。官方指定了PSR-0和PSR-4兩個自動載入規範,其中PSR-0已經被棄用。現在PSR-4應用最廣泛,通過Composer一句命令就實現。PHP官方制定了很多編碼規範,主要是防止一些PHP程式設計師瞎寫程式碼。具體規範地址看這裡https://www.php-fig.org/psr/
-
PSR-0 早期版本的按需載入規範
<?php function autoload($className) { $className = ltrim($className, '\\'); $fileName= ''; $namespace = ''; if ($lastNsPos = strrpos($className, '\\')) { $namespace = substr($className, 0, $lastNsPos); $className = substr($className, $lastNsPos + 1); $fileName= str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; } $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; require $fileName; } spl_autoload_register('autoload');
-
PSR-4 最新版本的按需載入規範
PSR-4可通過composer實現(理論上所有PSR標準都能通過composer命令來自動實現)。composer.json資訊如下:
{ "name": "lesliexiong/php-server", "description": "server", "authors": [ { "name": "layne", "email": "[email protected]" } ], "require": { "php": ">=5.4.0" }, "autoload": { "psr-4":{ "Layne\\Taobao\\": "src/" } } }
然後將 TestA.php 和 TestB.php 放到src目錄下,最後執行
composer install
,所有按需載入的程式碼會自動生成。之後任何人都可以訪問封裝好的TestA.php 和 TestB.php。像Yii2,Laravel5這種上萬個原始碼檔案的專案就是使用Composer做管理並共享元件。下一章我將詳細說說composer的使用。
四. 寫在最後
為了解決按需載入、命名衝突、包管理這些問題,我們引出了名稱空間和PSR標準,這些標準都來之不易,每一位PHPer都應該掌握。程式碼千萬行,註釋第一行,編碼無標準,家人兩行淚。