PHP不權(quán)威總結(jié)
歡迎閱讀
本文目標(biāo)用戶是我自己抑月,系統(tǒng)地持續(xù)集成PHP方方面面的知識(shí),但不會(huì)事無巨細(xì)的一一列舉舆蝴,只會(huì)挑選我認(rèn)為易忘谦絮、易錯(cuò)、重要的內(nèi)容進(jìn)行集成洁仗,而且大多是點(diǎn)到為止层皱,需要更詳細(xì)文檔的話會(huì)額外鏈接。所以赠潦,如果你已經(jīng)靠PHP謀生了幾年的話這篇文檔應(yīng)該會(huì)對你有幫助叫胖,如果你是PHP新手,建議重點(diǎn)閱讀學(xué)習(xí)資料一節(jié)祭椰。
安裝配置 & 環(huán)境搭建
PHP的開發(fā)環(huán)境主要是LA/NMP臭家,即Linux、Apache/Nginx方淤、Mysql钉赁、PHP,關(guān)于這些工具在不同平臺(tái)的安裝有大量優(yōu)秀詳細(xì)的文檔携茂,這里不廢話你踩,重點(diǎn)還是PHP的安裝,不過強(qiáng)烈推薦Laravel的Homestead
解決方案讳苦,一套方便管理的带膜、跨平臺(tái)、統(tǒng)一的鸳谜、虛擬化開發(fā)環(huán)境膝藕。
編譯安裝
推薦使用Like Unix操作系統(tǒng)作為開發(fā)、線上環(huán)境咐扭,強(qiáng)大且簡單芭挽。我推薦的PHP安裝方式是編譯安裝滑废,對新手也是,不走彎路袜爪、不踩坑怎么成長蠕趁。源碼安裝你的選擇會(huì)更加自由,能第一時(shí)間嘗試各種Alpha版辛馆,而且在日常工作中俺陋,安裝擴(kuò)展、調(diào)試昙篙、優(yōu)化腊状,都需要對PHP的目錄、文件有一定了解瓢对。
- PHP官網(wǎng)下載你需要的版本源碼
- 編譯 & 安裝
# --prefix指定目錄寿酌,--with-config-file-path指定php-config目錄,其他為要一同安裝的擴(kuò)展或開啟的功能
./configure --prefix=/usr/local/php \
--with-config-file-path=/etc/php \
--enable-fpm \
--enable-pcntl \
--enable-mysqlnd \
--enable-opcache \
--enable-sockets \
--enable-sysvmsg \
--enable-sysvsem \
--enable-sysvshm \
--enable-shmop \
--enable-zip \
--enable-soap \
--enable-xml \
--enable-mbstring \
--disable-rpath \
--disable-debug \
--disable-fileinfo \
--with-mysql=mysqlnd \
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--with-pcre-regex \
--with-iconv \
--with-zlib \
--with-mcrypt \
--with-gd \
--with-openssl \
--with-mhash \
--with-xmlrpc \
--with-curl \
--with-imap-ssl
sudo make
sudo make install
- 添加配置硕蛹。PHP的源碼包中附帶了一個(gè)開發(fā)環(huán)境使用的配置(開啟了方便調(diào)試醇疼、減少性能消耗的配置),只需要放到默認(rèn)位置就行
sudo mkdir /etc/php
sudo cp php.ini-development /etc/php/php.ini
- 擴(kuò)展安裝法焰。如果需要在已安裝的PHP中添加擴(kuò)展的話秧荆,只需下載好擴(kuò)展源碼后進(jìn)入擴(kuò)展的源碼目錄
phpize
./configure --with-php-config=/usr/local/php-config
make
make install
Homestead
團(tuán)隊(duì)作戰(zhàn)中,由于依賴眾多(git埃仪、redis乙濒、memcache、nodejs卵蛉、npm)颁股,每個(gè)人習(xí)慣不同,開發(fā)環(huán)境都會(huì)有所差別傻丝,會(huì)導(dǎo)致許多難以察覺的問題甘有,并大大提高團(tuán)隊(duì)協(xié)調(diào)的難度,我曾遇到過PHP5.2葡缰、5.4兩個(gè)版本的正則執(zhí)行結(jié)果不同而導(dǎo)致上線折騰到凌晨2點(diǎn)的坑事亏掀。所以Laravel團(tuán)隊(duì)提供了一套基于虛擬機(jī)的跨平臺(tái)開發(fā)環(huán)境搭建方案 —— Homestead(譯為家園)。
我來簡單梳理一下泛释。Homestead的做法是滤愕,首先選擇免費(fèi)穩(wěn)定的Virtual Box作為虛擬機(jī),用Vagrant來管理虛擬機(jī)和開發(fā)環(huán)境怜校,然后又配置了一套統(tǒng)一的git间影、redis等常用工具,直接安裝即可茄茁。這里有詳細(xì)的說明文檔宇智。
Mac安裝
直接使用Homebrew安裝蔓搞,類似Linux的yum、apt-get方案随橘。
Windows安裝
推薦使用XAMPP、EasyPHP锦庸、WAMP等一類的套件机蔗,簡單易用,包括了LN/AMP最基本的開發(fā)環(huán)境甘萧,提供了GUI管理界面萝嘁。需要提醒的是千萬不要用這些套件部署線上環(huán)境
。
基本特性
日期時(shí)間
日期處理非常討厭的地方是每月天數(shù)不同和閏年扬卷、時(shí)區(qū)的差異牙言,以及日期之間的運(yùn)算。早期的phper主要使用date怪得、strtotime等幾個(gè)函數(shù)進(jìn)行轉(zhuǎn)換咱枉,費(fèi)時(shí)費(fèi)力易出錯(cuò)。5.2之后其實(shí)提供了以下幾個(gè)類徒恋,來簡化日期處理蚕断。
// 基本的日期處理對象
DateTime: __construct ([ string $time = "now" [, DateTimeZone $timezone = NULL ]] );
// 時(shí)區(qū)對象
DateTimeZone: __construct ($timezone);
// 時(shí)間段對象
DateInterval: __construct ($interval_spec);
// 時(shí)間迭代器
DatePeriod: __construct (DateTimeInterface $start, DateInterval $interval, DateTimeInterface $end, $options=0);
列舉幾個(gè)使用場景
$datetime = new DateTime();
echo $datetime->format('Y-m-d H:i:s'); // out當(dāng)前時(shí)間
$datetime->setTimezone(new DateTimeZone('Asia/ShangHai'));
echo $datetime->format('Y-m-d H:i:s'); // out上海時(shí)間
$yesterday = new DateTime('-1 day');
echo $datetime->diff($yesterday)->format('%a'); // out日期天數(shù)差,1
// 生成一個(gè)時(shí)間段
$interval = new DateInterval('P2DT2H'); // P開頭, 日期和時(shí)間T隔開 Y M D W H M S
$datetime->add($interval);
$datetime->sub($interval);
// 兩個(gè)時(shí)間點(diǎn)之間進(jìn)行迭代
$start = DateTime('2019-11-01');
$end = DateTime('2019-11-21');
$interval = new DateInterval('P2D');
$period = new DatePeriod($start, $interval, $end, DatePeriod::EXCLUDE_START_DATE);
foreach ($period as $datetime) {
echo $datetime->format("Y-m-d H:i:s") , PHP_EOL; // out 2019-11-03 2019-11-05...
}
延伸
數(shù)據(jù)庫
常用mysqli_* mysql_*函數(shù)簇由于比較底層且繁瑣入挣,加之經(jīng)常使用框架封裝好的方法亿乳,經(jīng)常忘記原生的數(shù)據(jù)庫操作,出于便捷性径筏、通用性與安全性的考慮葛假,數(shù)據(jù)庫連接推薦使用PDO
// 建立數(shù)據(jù)庫連接,pdo支持mysql oracle sqlite等多種常用數(shù)據(jù)庫
$pdo = new PDO('mysql:host=...;dbname=...;prot=...;charset=...');
$stmt = $pdo->query('select * from user');
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC); // fetchAll一次讀取尚未讀取的所有記錄
// 逐條讀取滋恬,減少內(nèi)存消耗聊训;注意查詢結(jié)果以及緩存在本地,只是逐條加載近內(nèi)存
$stmt = $pdo->query('select * from user');
while (false !== ($row = $stmt->fetch()) {
print_r($row);
}
// 一般為了防止sql注入夷恍,和提高性能魔眨,常使用prepare
$sql = "insert into foo (name, age) values (:name, :age)";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':name', 'name');
$stmt->bindValue(':age', 18);
$stmt->execute();
define VS const
關(guān)于常量的處理有define和const兩種實(shí)現(xiàn)方法,其中const是在編譯階段執(zhí)行性能酿雪、可讀性都要略好一些遏暴,但由于其不能使用函數(shù)、不能if條件控制指黎,靈活性要比define差朋凉。
// define
define('NAME_1', 1);
define('NAME_2', [1, 2, 3]);
define('NAME_3', time());
if ($flag) {
define('NAME_4', true);
}
class Foo {
define('class_const', 1); // compile error
}
// const
const NAME_5 = 1;
const NAME_6 = time(); // compile error
if ($flag) {
const NAME_7 = 1; // compile error
}
function foo () {
const A = 1; // compile error
}
OOP特性
錯(cuò)誤與異常
php程序運(yùn)行時(shí)可能會(huì)出現(xiàn)各種錯(cuò)誤,錯(cuò)誤分為notice醋安、warning杂彭、error墓毒、compile_error等,正確處理這些錯(cuò)誤信息能夠幫助我們快速解決問題亲怠。
// 錯(cuò)誤報(bào)告控制所计,也可以在php.ini中修改error_reporting
error_reporting(E_ALL & ~E_NOTICE); // 關(guān)閉notice錯(cuò)誤
error_reporting(E_WARNING | E_ERROR); // 只開啟warning和error錯(cuò)誤
// 錯(cuò)誤顯示
ini_set('display_errors', 1);
# php中錯(cuò)誤相關(guān)配置
# 錯(cuò)誤日志是否打開
log_errors = On (Off)
# 錯(cuò)誤日志記錄的位置
error_log = php_errors.log
# 是否打開錯(cuò)誤顯示
display_errors = Off
# 定義錯(cuò)誤顯示的級(jí)別
error_reporting = E_ALL
關(guān)于錯(cuò)誤的控制,建議為:
- 任何環(huán)境都要記錄錯(cuò)誤
- 開發(fā)環(huán)境盡可能多顯示錯(cuò)誤
- 生產(chǎn)環(huán)境要關(guān)閉錯(cuò)誤顯示
異常OOP的錯(cuò)誤處理機(jī)制团秽,強(qiáng)大且優(yōu)雅主胧。但php在這方面比較混亂,程序出錯(cuò)時(shí)既可能拋異常习勤,也可能只是個(gè)warning或直接中斷運(yùn)行踪栋,甚至繼續(xù)執(zhí)行下去。php7中嘗試對此進(jìn)行一定的統(tǒng)一图毕,部分運(yùn)行時(shí)fatal error不再直接中斷程序夷都,而是改為拋出Error異常,并與Exception統(tǒng)一實(shí)現(xiàn)了Throwable接口
try {
require_once 'b.php'; // 依然fatal error
$b = 1 % 0; // Error, DivisionByZeroError
foo(); // Error
echo $a; // warning而已
$b = null;
$b->test(); // Error
} catch (Exception $e) {
} catch (Error $e) {
} catch (Throwable $e) {
}
如果想嚴(yán)格執(zhí)行異常機(jī)制予颤,可以注冊一個(gè)error回調(diào)函數(shù)將所有錯(cuò)誤轉(zhuǎn)換為異常拋出囤官。注意,E_ERROR E_PARSE E_CORE_ERROR E_CORE_WARNING E_COMPILE_ERROR E_COMPILE_WARNING
會(huì)直接導(dǎo)致程序退出而無法轉(zhuǎn)換為異常
set_error_handler(function($errno, $errstr, $errfile, $errline) {
// 屏蔽不報(bào)告的錯(cuò)誤
if (!(error_reporting() & $errno)) {
return;
} else {
throw new \ErrorException($errstr, $errno);
}
});
延伸
延遲靜態(tài)綁定
延遲靜態(tài)綁定是指類靜態(tài)方法調(diào)用時(shí)執(zhí)行調(diào)用者的邏輯荣瑟,而非方法定義時(shí)的邏輯治拿,看個(gè)例子
class People {
public static function out() {
echo 'people' . PHP_EOL;
}
public function callOut() {
self::out();
static::out();
}
}
class User extends People {
public static function out() {
echo 'user' . PHP_EOL;
}
}
$user = new User();
$user->callOut();
// static指向運(yùn)行時(shí)的調(diào)用者也就是User類,而self為定義者也就是People類笆焰,所以輸出為
// people
// user
反射
反射是一種面向?qū)ο蟮臋C(jī)制劫谅,提供了一套API允許程序在運(yùn)行時(shí)對類進(jìn)行檢查,類定義嚷掠、類方法捏检、成員變量、可訪問性不皆、源碼贯城,甚至訪問私有成員與方法。反射機(jī)制經(jīng)常使用在測試霹娄、構(gòu)建與框架底層代碼中能犯,能夠產(chǎn)生很多“神奇”的效果。
延伸
- 《深入PHP 面向?qū)ο笕堋⒛J脚c實(shí)踐》反射一節(jié)
- 反射在PHP中應(yīng)用
trait
trait踩晶,翻譯為性狀,主要用于在類之間抽象一些特性。比如超人和小鳥,他們都需要實(shí)現(xiàn)一個(gè)fly方法夭拌,一般的做法是抽象一個(gè)更高級(jí)的類實(shí)現(xiàn)fly方法他們共同繼承突倍,但是這樣強(qiáng)行耦合了兩個(gè)并不強(qiáng)相關(guān)的類;另一種做法是定義一個(gè)Fly的接口實(shí)現(xiàn)fly方法唆垃,這樣雖然避免耦合肛真,但同樣的fly方法需要實(shí)現(xiàn)兩遍袁铐,違背了DYR原則淘衙;比較好的辦法是has-a,即定義一個(gè)包含fly方法的公共類幔翰,并在超人類和小鳥類中分別實(shí)例化西壮。其實(shí)trai就類似第三種做法,而且使用起來更方便一些
trait Fly {
public function fly() {
echo 'I can fly';
}
}
class Superman {
use Fly;
}
class Bird {
use Fly;
}
$clark = new Superman();
$clark->fly();
就是這么簡單霍狰,但trait使用時(shí)有幾個(gè)需要注意的地方
- php的方法調(diào)用優(yōu)先級(jí)為class自身方法 > trait > 繼承
- 多個(gè)trait用逗號(hào)分隔
- trait也可以使用trait
- trait也可以定義屬性蔗坯,但不能與class中屬性沖突康震,fatal error
高級(jí)特性
命名空間
命名空間主要用來解決命名沖突。php的命名空間其實(shí)很簡單宾濒,只是由于其只是個(gè)虛擬空間腿短,并不要求與代碼實(shí)際文件相對應(yīng),雖然會(huì)靈活許多绘梦,但導(dǎo)致使用起來不好理解橘忱,這也是我詬病php哲學(xué)的一點(diǎn),過分追求靈活簡單卸奉,反而導(dǎo)致方案太多而困惑钝诚。
先看幾個(gè)命名空間的常見場景
namespace App\A\Route;
class Http {
}
namespace App\B\Route;
class Http {
}
// 直接new,默認(rèn)當(dāng)前命名空間
$routeB = new Http();
// 使用全限定命名空間榄棵,\開頭
$routeA = new \App\A\Route();
// use導(dǎo)入凝颇,同時(shí)重命名
use App\A\Route\Http as HttpA;
$routeA = new HttpA();
// use導(dǎo)入半限定
use App\A;
$routeA = new A\Route\Http();
php的命名空間只是個(gè)虛擬概念,use并不能自動(dòng)加載文件疹鳄,還需要依靠其他辦法加載文件
拧略,php標(biāo)準(zhǔn)psr要求每個(gè)php文件只能定義一個(gè)namespace,且與其文件路徑保持一致尚辑,并利用spl_autoload自動(dòng)加載辑鲤,這樣就大大優(yōu)化了命名空間
- namespace必須在文件開頭
- function const同樣可以使用命名空間,導(dǎo)入時(shí)關(guān)鍵字為use function, use const
延伸
yield
yield杠茬,生成器月褥,主要用于“逐步”的處理大文件弛随、復(fù)雜計(jì)算,減少內(nèi)存消耗宁赤,和一定程度的降低代碼復(fù)雜度舀透,類似游標(biāo)。生成器是基于協(xié)程實(shí)現(xiàn)的决左,其基本特點(diǎn)是函數(shù)不再是調(diào)用-返回
的模式愕够,而是調(diào)用-運(yùn)行-暫停……-返回
佛猛,即函數(shù)可以讓出惑芭,和重入。
生成器的基本使用继找,定義遂跟、生成、執(zhí)行
function foo () {
yield 'first record';
yield 'second record';
}
$gen = foo();
$gen->current(); // out: first record
$gen->current(); // out: second record
// or
foreach($gen as $v) {
echo $v;
}
// or
while ($gen->valid()) {
$gen->current();
}
生成器婴渡,就是一個(gè)包含yield關(guān)鍵字的函數(shù)幻锁,比較特殊,調(diào)用這個(gè)函數(shù)時(shí)边臼,會(huì)返回一個(gè)迭代器(即實(shí)現(xiàn)了Iterator哄尔,next valid current),這也是生成器名字的由來
生成器的返回值柠并,和交互
function foo () {
yield; // null
yield 'string'; // string
yield 'key' => 10; // key-val foreach($gen as $k => $v)
return 'retval'; // php7以后支持岭接,$foo->getReturn();
}
function foo () {
while ('end' != ($msg = yield)) {
echo $msg;
sleep(1);
}
}
$gen->send('go');
$gen->send('go ahead');
$gen->send('end');
需要注意的是直接send可能導(dǎo)致數(shù)據(jù)丟失
function foo() {
yield 1;
yield 2;
}
$gen = foo();
$gen->send('msg');
echo $gen->current(); // 2
// 正確做法
$gen->current(); // 1
$gen->send('msg');
$gen->current(); // 2
延伸
session
session意為會(huì)話,作用是解決無狀態(tài)的Http協(xié)議實(shí)現(xiàn)用戶登錄的問題堂鲤。用戶第一次訪問應(yīng)用時(shí)生成一個(gè)seesion_id并將會(huì)話信息持久化亿傅,用戶后續(xù)訪問時(shí)均帶上該session_id,應(yīng)用程序籍此區(qū)分用戶并共享會(huì)話信息瘟栖,用戶在退出時(shí)注銷該session_id葵擎。目前直接使用原生的php session機(jī)制并不多,大多數(shù)是使用一個(gè)單點(diǎn)的統(tǒng)一用戶會(huì)話管理服務(wù)半哟,不過php本身的session機(jī)制在一些場景依然簡單有效酬滤。
session_start(); // 開啟session,無session_id時(shí)創(chuàng)建寓涨,并生成文件
$_SESSION; // 超全局?jǐn)?shù)組盯串,保存著session信息
session_unset(); // 清空session信息,并非unset($_SESSION)
session_commit(); // 保存session信息戒良,并結(jié)束session
session_destroy(); // 清空session体捏,刪除session文件
php的session在使用時(shí)有這么幾個(gè)缺陷和注意點(diǎn):
- session_id默認(rèn)是使用cookie傳遞,一旦cookie被禁用,需要其他辦法兼容几缭,比如session_id應(yīng)用自己維護(hù)傳輸河泳,或者自動(dòng)加在url中
- session信息默認(rèn)是保存在單機(jī)文件中的,這就導(dǎo)致分布式集群無法正常使用年栓,需要覆蓋實(shí)現(xiàn)php的session回調(diào)拆挥,借助redis、mysql等集中式存儲(chǔ)實(shí)現(xiàn)分布式的session
- php的session過期有些費(fèi)解
- 由于判斷session過期是要頻繁的文件檢查某抓,性能原因考慮纸兔,配置了一個(gè)檢查概率,默認(rèn)1/100
- 坑爹的以文件最后修改時(shí)間為過期依舊否副,而每次更新會(huì)話變量汉矿,最后修改時(shí)間都會(huì)刷新
- 由于默認(rèn)目錄是/tmp,假如服務(wù)器部署多個(gè)應(yīng)用而沒有調(diào)整時(shí)备禀,超時(shí)時(shí)間短的應(yīng)用會(huì)導(dǎo)致長的被清理
延伸
取值范圍
php中整型不區(qū)分int负甸、short、long痹届,統(tǒng)一全是long,且始終為有符號(hào)打月,位數(shù)與平臺(tái)有關(guān)队腐,32位系統(tǒng)取值范圍為-2147483648到2147483647(正負(fù)21億,共10位奏篙,-232~232-1)柴淘,64位系統(tǒng)上為-9223372036854775808到9223372036854775807(正負(fù)19位)
浮點(diǎn)型不區(qū)分float和double,統(tǒng)一全是double秘通,且始終有符號(hào)为严,位數(shù)與平臺(tái)有關(guān),具體表示數(shù)值的范圍需要根據(jù)精度而定肺稀,php默認(rèn)配置的精度是14位有效數(shù)字第股。32位系統(tǒng)中1位符號(hào)8位精度23位尾數(shù),64位系統(tǒng)中1位符號(hào)11位精度52位尾數(shù)
調(diào)試
xdebug
xhprof
xdebug雖然強(qiáng)大话原,但由于采集的信息比較多夕吻,對性能影響較大無法在實(shí)際生產(chǎn)環(huán)境使用,導(dǎo)致一些問題難以發(fā)現(xiàn)繁仁。xhprof是Facebook發(fā)布的一款輕量級(jí)性能分析器涉馅,性能影響小,能夠用于生產(chǎn)環(huán)境黄虱,同時(shí)收集的信息也能滿足大部分分析需求稚矿,主要是function級(jí)別的,包括運(yùn)行時(shí)間、運(yùn)行次數(shù)晤揣、cpu占用桥爽、內(nèi)存占用等。需要注意由于Facebook不再維護(hù)碉渡,官方版并不支持php7聚谁,github另外一個(gè)分支維護(hù)了php7版本
xhprof需要額外安裝擴(kuò)展,需要在應(yīng)用程序中顯示開啟滞诺、終止分析形导,會(huì)生成性能分析文件,借助可視化工具习霹,可清晰的看到性能瓶頸
單元測試
單測自己的使用經(jīng)驗(yàn)有限朵耕,但也嘗到了甜頭。PHP的單元測試主要借助PHPunit淋叶,可以通過composer安裝阎曹。簡單記錄幾個(gè)使用經(jīng)驗(yàn)
- 最好在調(diào)試階段就開始使用phpunit,
既可以第一時(shí)間實(shí)際使用類/方法煞檩,發(fā)現(xiàn)錯(cuò)誤與難用之處处嫌;也可以持續(xù)積累case,方便后續(xù)回歸
- TDD斟湃,所謂測試驅(qū)動(dòng)熏迹,是先通過測試用例確認(rèn)軟件行為,然后再實(shí)現(xiàn)軟件來通過測試用例凝赛,達(dá)到明確的需求確認(rèn)和保證軟件質(zhì)量注暗。但由于測試用例比較耗費(fèi)精力,所以我一般用在小模塊開發(fā)中
- phpunit的--filter選項(xiàng)比較有用墓猎,可以方便的測試指定的用例
- 將case放在一個(gè)回滾的事務(wù)中捆昏,可以在自動(dòng)測試完后恢復(fù)現(xiàn)場
- phpunit中的data準(zhǔn)備回調(diào)非常好用,可以自動(dòng)準(zhǔn)備毙沾、恢復(fù)測試數(shù)據(jù)骗卜,保證每次測試的現(xiàn)場一致可復(fù)現(xiàn)
延伸
PHPUnit手冊
如何有效的書寫項(xiàng)目單元測試
gdb
延伸
安全
加密
加密的一個(gè)原則是絕對不要明文存儲(chǔ)、傳輸密碼左胞,一般使用一種單向算法對密碼加密膨俐,以前常用md5+salt的方式,但由于彩虹表的出現(xiàn)罩句,md5不再安全焚刺,目前PHP中比較安全的是使用bcrypt算法加密。這個(gè)算法故意被設(shè)計(jì)的比較耗時(shí)(單次大約毫秒級(jí))门烂,從而增加破解成本乳愉。
// 計(jì)算hash兄淫,可以指定算法、計(jì)算因子蔓姚,會(huì)自動(dòng)補(bǔ)充salt
$pwd = 'password';
$hash = password_hash($pwd, PASSWORD_DEFAULT, ['cost' => 10]);
// 調(diào)試捕虽,可以看到hash值中同時(shí)保存了salt,和配置信息(算法坡脐、因子等)
$pwdInfo = password_get_info($hash);
// 驗(yàn)證泄私,就這么簡單
$bolEqual = password_verify($pwd, $hash);
// 維護(hù),可以自動(dòng)判斷hash是否滿足當(dāng)前配置备闲,籍此可判斷是否需要重新生成hash
$bolNeedRehash = password_needs_rehash($hash, PASSWORD_DEFAULT, ['cost' => 15]);
延伸
password_hash晌端、password_verify、password_needs_rehash手冊
組件 & 擴(kuò)展 & 工具
自動(dòng)加載
PHP中所謂的自動(dòng)加載是class的命名空間與類名去解析class文件真實(shí)path并require恬砂,映射規(guī)則一般使用官方推薦的PSR-4咧纠,大概為一個(gè)文件只包含一個(gè)class、trait泻骤、interface漆羔,同時(shí)namespace要與文件路徑一致。這樣利用PHP內(nèi)置的類加載器注冊機(jī)制便可以自動(dòng)require了狱掂。一個(gè)典型的autoloader.php如下:
<?php
/**
* 使用SPL組冊這個(gè)自動(dòng)加載函數(shù)后演痒,遇到下述代碼時(shí)這個(gè)函數(shù)會(huì)嘗試 從/path/to/project/src/Baz/Qux.php文件中加載\Foo\Bar\Baz\Qux類:
* new \Foo\Bar\Baz\Qux;
* @param string $class 完全限定的類名。
* @return void
**/
spl_autoload_register(function ($class) {
// 項(xiàng)目的命名空間前綴
$prefix = 'Foo\\Bar\\';
// 目錄前綴對應(yīng)的根目錄
$base_dir = __DIR__ . '/src/';
// 判斷傳入的類是否使用了這個(gè)命名空間前綴
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
// 沒有使用趋惨,交給注冊的下一個(gè)自動(dòng)加載器處理
return;
}
// 獲取去掉前綴后的類名
$relative_class = substr($class, $len);
// 把命名空間前綴替換成根目錄嫡霞,
// 在去掉前綴的類名中,把命名空間分隔符替換成目錄分隔符希柿,
// 然后在后面加上.php
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
// 如果該文件存在,就將其導(dǎo)入
if (file_exists($file)) {
require $file;
}
});
composer
類似apt-get yum npm等养筒,composer是PHP的類管理器曾撤,可以自動(dòng)下載更新class、庫晕粪、項(xiàng)目挤悉,管理項(xiàng)目依賴,結(jié)合目前最大的PHP包分享社區(qū)packagist可以非常方便的完成項(xiàng)目環(huán)境搭建
延伸
PHP最流行的包分享packagist
Composer使用手冊
底層原理
生命周期
SPAI
Server Application Programming Interface巫湘,服務(wù)應(yīng)用編程接口装悲,php一切的開始,簡單說作用是負(fù)責(zé)隔離php程序的執(zhí)行與運(yùn)行環(huán)境尚氛,使得無論是cli诀诊、fpm、webserver module阅嘶,php代碼都可以正常運(yùn)行
php的生命周期如圖所示属瓣,fpm中主要是啟動(dòng)請求周期中循環(huán)载迄,cli中則僅僅是一次完整的流程。簡要說明一下各個(gè)環(huán)節(jié)的主要作用:
- MINIT抡蛙,模塊初始化階段护昧,只執(zhí)行一次
- 激活SAPI
- 初始化垃圾回收器
- 啟動(dòng)zend引擎
- 注冊PHP內(nèi)置常量
- 解析php.ini
- 注冊$_GET、$_POST等超全局變量及處理回調(diào)
- 動(dòng)態(tài)加載 .so
- 回調(diào)擴(kuò)展minit方法
- RINIT粗截,請求初始化階段惋耙,每次請求到達(dá)時(shí)都會(huì)執(zhí)行
- 重置zend引擎,重置編譯器熊昌、符號(hào)表等
- 重置垃圾回收器
- 回調(diào)擴(kuò)展rinit
- EXECUTE绽榛,腳本執(zhí)行階段
- 加載php腳本
- 預(yù)編譯、詞法解析浴捆、語法解析蒜田,生成抽象語法樹(AST)
- 編譯生成中間碼,op_code
- ZendVM執(zhí)行op_code
- RSHUTDOWN选泻,請求關(guān)閉階段
- flush輸出內(nèi)容
- 發(fā)送http response header
- 清理全局變量冲粤、關(guān)閉編譯器
- 關(guān)閉內(nèi)存管理器
- 回調(diào)擴(kuò)展rshutdown
- MSHUTDOWN,模塊關(guān)閉階段
- 關(guān)閉zend引擎
- 回調(diào)擴(kuò)展mshutdown
- 清理資源
延伸
- TIPI深入理解PHP內(nèi)核-生命周期
- 《PHP7內(nèi)核剖析》第1章基礎(chǔ)架構(gòu)
zval zend_value zend_reference 和 CoW
PHP7中一個(gè)重要改動(dòng)是變量數(shù)據(jù)結(jié)構(gòu)的優(yōu)化页眯,將原來的變量的數(shù)據(jù)結(jié)構(gòu)從一個(gè)zval拆為zval梯捕、zend_value,并將原zval中引用計(jì)數(shù)移入zend_value中窝撵,可以簡單理解為zval是變量名傀顾,zend_value是實(shí)際的變量值,減少了內(nèi)存占用碌奉,結(jié)構(gòu)更清晰短曾。另一個(gè)變化是 PHP7中整形,浮點(diǎn)型赐劣,布爾型嫉拐,NULL是直接保存在zval中,無zend_value魁兼,所以無引用計(jì)數(shù)婉徘;另外字符串雖然有zend_value,但由于是程序結(jié)束后統(tǒng)一回收咐汞,所以也沒引用計(jì)數(shù)
zend_reference則是原來的是否為引用變量標(biāo)記位盖呼,在PHP7中擴(kuò)展為一種數(shù)據(jù)類型。生成引用類型的唯一方法就是使用&$val化撕,直接復(fù)制一個(gè)引用類型的變量是無法復(fù)制引用的几晤,而對象和資源復(fù)制則默認(rèn)為引用復(fù)制
struct _zval_struct {
zend_value value; // 變量實(shí)際值
……
};
struct _zend_value {
zend_refcounted *counted; // gc頭部
zend_reference *ref; // 引用類型
……
}
實(shí)際測試一下
<?php
/* 引用計(jì)數(shù) */
$a = 1; // a[zval]
xdebug_debug_zval('a'); // a: (refcount=0, is_ref=0)=1
$b = 'string'; // b[zval] -> 'string'[zend_value]
xdebug_debug_zval('b'); // b: (refcount=1, is_ref=0)='string'
$c = []; // c[zval] -> Array[zend_value]
xdebug_debug_zval('c'); // c: (refcount=2, is_ref=0)=array ()
$b_1 = $b; // b_1[zval] b[zval] -> 'string'[zend_value]
xdebug_debug_zval('b'); // b: (refcount=1, is_ref=0)='string'
xdebug_debug_zval('b_1'); // b_1: (refcount=1, is_ref=0)='string'
$c_1 = $c; // c_1[zval] c[zval] -> Array[zend_value]
xdebug_debug_zval('c'); // c: (refcount=3, is_ref=0)=array ()
xdebug_debug_zval('c_1'); // c_1: (refcount=3, is_ref=0)='array ()
/* 引用類型 */
$r_c = &$c; // r_c[zval] c[zval] -> [zend_reference] -> Array[zend_value]
xdebug_debug_zval('c'); // c: (refcount=2, is_ref=1)=array ()
xdebug_debug_zval('r_c'); // r_c: (refcount=2, is_ref=1)=array ()
$d = $r_c; // c[zval] -> Array[zend_value]
xdebug_debug_zval('d'); // d: (refcount=4, is_ref=0)=array (),d并不是引用變量植阴,但他們?nèi)匀恢赶蛲粋€(gè)zend_value
/* CoW */
$d = [];
xdebug_debug_zval('d'); // d: (refcount=2, is_ref=0)=array ()锌仅,寫時(shí)復(fù)制章钾,d指向了新的zend_value
$a = [];
xdebug_debug_zval('a'); // a: (refcount=2, is_ref=0)=array ()
$b = $a; // 此時(shí)公用zend_value
xdebug_debug_zval('a'); // a: (refcount=3, is_ref=0)=array ()
xdebug_debug_zval('b'); // b: (refcount=3, is_ref=0)=array ()
$c = &$a; // a、c為引用變量热芹,b單獨(dú)贱傀,但他們依舊指向同一zend_value
xdebug_debug_zval('a'); // a: (refcount=2, is_ref=1)=array ()
xdebug_debug_zval('b'); // b: (refcount=3, is_ref=0)=array ()
xdebug_debug_zval('c'); // c: (refcount=2, is_ref=1)=array ()
$c = 'new'; // 發(fā)生變量分離
xdebug_debug_zval('a'); // a: (refcount=2, is_ref=1)='new'
xdebug_debug_zval('b'); // b: (refcount=2, is_ref=0)=array ()
xdebug_debug_zval('c'); // c: (refcount=2, is_ref=1)='new'
在前面的代碼中一并演示了Cow,寫時(shí)復(fù)制伊脓,變量復(fù)制時(shí)府寒,PHP解釋器并不會(huì)真的復(fù)制內(nèi)存,而是當(dāng)變量的值實(shí)際發(fā)生變化時(shí)才真正復(fù)制內(nèi)存报腔,從而減少內(nèi)存消耗株搔,提高性能
但引用類型的加入導(dǎo)致CoW有一點(diǎn)疑惑,在PHP5中由于只有zval纯蛾,所以即便變量沒有修改值纤房,但由于&操作變?yōu)橐妙愋停瑒t會(huì)導(dǎo)致一次隱性的變量分離翻诉,潛在內(nèi)存溢出風(fēng)險(xiǎn)炮姨,不易排查。不過PHP7中由于zval分離碰煌,zend_value沒有變化時(shí)舒岸,便不會(huì)發(fā)生CoW,算是修復(fù)了這個(gè)缺陷
最后看一個(gè)引用類型導(dǎo)致的詭異現(xiàn)象
<?php
$foo['love'] = 1;
$bar = &$foo['love'];
$tipi = $foo;
$tipi['love'] = '2';
echo $foo['love']; // 1 or 2?
延伸
Hash Table
HashTable芦圾,散列表蛾派,PHP的殺手锏array便使用的該數(shù)據(jù)結(jié)構(gòu),既可以O(shè)(1)讀个少,也可以按序遍歷數(shù)據(jù)洪乍,其大致結(jié)構(gòu)如下:
插入一個(gè)新key,先經(jīng)過散列函數(shù)映射到中間映射表里夜焦,其中記錄著元素?cái)?shù)組的位置壳澳,中間映射表與元素?cái)?shù)組為內(nèi)存中連續(xù)空間。當(dāng)直接訪問key值時(shí)與插入步驟一樣糊探,當(dāng)遍歷數(shù)組時(shí)則直接依次讀元素?cái)?shù)組
gc回收
PHP提供了自動(dòng)內(nèi)存管理功能,zend_value中記錄了變量值的引用次數(shù)河闰,當(dāng)我們使用unset時(shí)便會(huì)減少引用次數(shù)科平,當(dāng)引用次數(shù)減少為0時(shí)便會(huì)觸發(fā)內(nèi)存回收
但 循環(huán)引用 無法使用該方法回收,比如某個(gè)數(shù)組中元素又引用了數(shù)組本身姜性。針對這種情況瞪慧,PHP會(huì)在引用計(jì)數(shù)減少時(shí)收集可能的垃圾變量,然后定期對收集的變量進(jìn)行深度遍歷部念,在遍歷中引用次數(shù)依次減一弃酌,若存在循環(huán)引用則最終引用計(jì)數(shù)將變?yōu)?氨菇,變量回收
JIT
JIT,just in time妓湘,即時(shí)編譯查蓉,是解釋型語言的一種性能優(yōu)化手段。雖然編譯型語言與解釋型語言都有編譯階段榜贴,但不同的是編譯型語言是直接將源碼編譯為機(jī)器碼豌研,而解釋型語言則是編譯為字節(jié)碼供虛擬機(jī)執(zhí)行,導(dǎo)致運(yùn)行性能折損唬党。而JIT技術(shù)鹃共,則是實(shí)時(shí)在程序運(yùn)行期間,將熱點(diǎn)代碼直接編譯為機(jī)器碼驶拱,從而提高性能霜浴。PHP已經(jīng)確定將在PHP8中引入JIT技術(shù),將PHP的運(yùn)行性能提高到一個(gè)新的臺(tái)階
那為什么不直接編譯為機(jī)器碼運(yùn)行呢蓝纲? 直接編譯運(yùn)行稱為事前編譯AOT(ahead of time)阴孟,php也是支持這種做法的,但這樣做有幾個(gè)缺點(diǎn)驻龟,首先背離php堅(jiān)持的一貫簡單原則温眉,項(xiàng)目的發(fā)布上線需要提前編譯,對研發(fā)者也不夠友好翁狐,而且JIT由于可以收集大量運(yùn)行時(shí)信息类溢,編譯的優(yōu)化效果更好,綜合來說露懒,JIT更適合一些
JIT也并非沒有缺點(diǎn) 首先是JIT是需要額外消耗性能的闯冷,如果程序中無明顯熱點(diǎn)代碼,或者運(yùn)行周期比較短懈词,則JIT反而會(huì)導(dǎo)致性能下降
php引入JIT后的性能變化
社區(qū)
FIG & PSR
PHP令人詬病的問題之一是框架太多蛇耀,ThinkPHP、CI坎弯、Yii纺涤、Zend、Laravel……抠忘,雖然有各自的適合場景撩炊,但由于風(fēng)格設(shè)計(jì)不統(tǒng)一,一些公共組件如日志崎脉、緩存拧咳、http請求無法公用,反復(fù)造輪子囚灼,浪費(fèi)精力骆膝。PHP-FIG PHP Framework Interop Group 目標(biāo)便是解決這個(gè)問題祭衩,它從代碼規(guī)范、接口標(biāo)準(zhǔn)阅签、自動(dòng)加載幾個(gè)角度持續(xù)推薦了一批規(guī)范 這些推薦標(biāo)準(zhǔn)便是 PSR PHP Standards Recommendation掐暮,并不強(qiáng)制,各框架開發(fā)者自行決定是否支持愉择,任何人都可以向FIG反饋意見
目前已經(jīng)初見成效劫乱,目前最流行的Laravel框架便大量使用了公共組件,提高開發(fā)效率
延伸
架構(gòu)
實(shí)踐
PHP7比較常用的變化
PHP7發(fā)布后除了性能上的巨大變化外锥涕,在語言的規(guī)范衷戈、語法糖和許多細(xì)節(jié)地方都做了許多改進(jìn),這里只記錄一些我認(rèn)為經(jīng)常涉及的改動(dòng)
// 新增??三元操作符层坠,類似?:
$foo = $a ?? 'nothing';
$foo = isset($a) ? $a : 'nothing';
// 常量數(shù)組
define('OPTION', [1, 2, 3,]);
// json_encode支持unicode不轉(zhuǎn)碼
json_encode(['一', '二',], JSON_UNESCAPED_UNICODE);
// 數(shù)組解包
$a = [1, 2, 3];
$b = [1, 2, ...$a, 3,]; // $b為[1, 2, 1, 2, 3, 3,]
// 箭頭函數(shù)殖妇,一個(gè)語法糖
$factor = 10;
$nums = array_map(fn($n)=>$n * $factor,[1,2,3]); // [10,20,30]
// 之前的寫法
$nums = array_map(function($num)use($factor){
return $num * $factor;
},[1,2,3]);
// group use
use app\model\service\{User, Task, Log};
正則
正則能夠替代大部分字符串相關(guān)工作,而且非常高效方便破花,只是性能問題不適合密集計(jì)算場景谦趣。PHP7中廢棄了ereg_,保留preg_座每。此處記錄使用正則過程中的經(jīng)驗(yàn)
// 常用的三個(gè)為
preg_replace($pattern, $replace, $subject);
preg_match($pattern, $subject, &$match); // 只匹配第一個(gè)
preg_match_all($pattern, $subject, &$match); // 可匹配多個(gè)
常見的正則網(wǎng)上一堆前鹅,這里只積累一些易錯(cuò)的地方:
- [家|(春秋)] 與 [家|春秋]有區(qū)別嗎?有的峭梳,中括號(hào)中'|'就是匹配豎線舰绘,并非‘或’的意思,所以第一個(gè)意思為:匹配包含家或豎線或春秋葱椭;第二個(gè)意思為:匹配包含家或豎線或春或秋
- 處理Unicode時(shí)別忘了加u捂寿,/pattern/u
延伸
輸入/輸出過濾、轉(zhuǎn)義
一般場景使用urlencode(url中特殊字符轉(zhuǎn)義孵运,如漢字秦陋、空格),http_build_query(urlencode的封裝)治笨,htmlspecialchars和html_entity_decode是對html實(shí)體轉(zhuǎn)義(如< >)
如果應(yīng)對復(fù)雜安全性要求更高的場景驳概,建議學(xué)習(xí)并使用htmlpurifier
文件操作
常用的文件讀寫為feof fread fwrite fgets fget file_get_contents file_put_contents fgetcsv fputcsv
延伸
- 建議學(xué)習(xí)并使用Symfony中Finder類庫
執(zhí)行外部命令
PHP執(zhí)行外部命令并不友好,提供了多種方式旷赖,令人迷惑顺又,自己目前并不清楚底層差別,只是從函數(shù)行為角度加以區(qū)分
// exec最常用
$last_line = exec(string $cmd, array &$out, int &$retval);
// system會(huì)在方法中輸出命令內(nèi)容
$last_line = system(string $cmd, int &$retval);
// 返回值為cmd執(zhí)行結(jié)果的字符串
$out = shell_exec(string $cmd, int &$retval);
$out = `$cmd`;
// 命令與system的最大區(qū)別是杠愧,直接將執(zhí)行結(jié)果輸出到瀏覽器待榔,支持二進(jìn)制
passthru($cmd, int &$retval);
延伸
- PHP執(zhí)行外部命令
- 建議學(xué)習(xí)并使用Symfony中Process類庫
字符串操作
PHP提供了很多方便的字符串函數(shù)逞壁,常用的有:
-
strstr ( string $haystack , mixed $needle [, bool $before_needle = false ] )
流济。返回 haystack 字符串從 needle 第一次出現(xiàn)的位置開始到 haystack 結(jié)尾的字符串锐锣。若為before_needle為 TRUE,strstr() 將返回 needle 在 haystack 中的位置之前的部分绳瘟。 -
substr( string $string , int $start [, int $length ] )
雕憔。返回字符串 string 由 start 和 length 參數(shù)指定的子字符串。 -
substr_replace ( mixed $string , mixed $replacement , mixed $start [, mixed $length ] )
糖声。substr_replace() 在字符串 string 的副本中將由 start 和可選的 length 參數(shù)限定的子字符串使用 replacement 進(jìn)行替換斤彼。 -
strrev ( string $string )
。返回 string 反轉(zhuǎn)后的字符串蘸泻。 -
str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )
琉苇。該函數(shù)返回一個(gè)字符串或者數(shù)組。該字符串或數(shù)組是將 subject 中全部的 search 都被 replace 替換之后的結(jié)果悦施。subject為執(zhí)行替換的數(shù)組或者字符串并扇。也就是 haystack。如果 subject 是一個(gè)數(shù)組抡诞,替換操作將遍歷整個(gè) subject穷蛹,返回值也將是一個(gè)數(shù)組。如果count被指定昼汗,它的值將被設(shè)置為替換發(fā)生的次數(shù)肴熏。 -
strpos ( string $haystack , mixed $needle [, int $offset = 0 ] )
。返回 needle 在 haystack 中首次出現(xiàn)的數(shù)字位置顷窒;如果提供了offset參數(shù)蛙吏,搜索會(huì)從字符串該字符數(shù)的起始位置開始統(tǒng)計(jì)。 如果是負(fù)數(shù)蹋肮,搜索會(huì)從字符串結(jié)尾指定字符數(shù)開始出刷。 -
ltrim()
、rtrim()
坯辩、trim()
馁龟。這仨都是刪除字符串中的空白符。ltrim()
刪除字符串開頭的空白字符;rtrim()
刪除字符串末端的空白字符漆魔;trim()
去除字符串首尾處的空白字符坷檩。
數(shù)組操作
這篇文章總結(jié)的非常好,不做贅述改抡。PHP數(shù)組使用之道
非阻塞與并行操作
在遇到慢速操作導(dǎo)致fpm進(jìn)程積壓時(shí)矢炼,可以使用fastcgi_finish_request()
方法觸發(fā)請求結(jié)束,但fpm進(jìn)程繼續(xù)執(zhí)行慢速操作
另一個(gè)有用的場景是批量網(wǎng)絡(luò)請求或io阿纤,若不使用非阻塞方法句灌,則需要依次進(jìn)行,效率極其低下,可以使用curl_muti_*和非阻塞socket胰锌,詳見PHP的非阻塞或并行請求實(shí)現(xiàn)方式
除此之外其他常用的辦法可以直接借助系統(tǒng)命令nohup
,或者使用多進(jìn)程編程
多進(jìn)程編程
PHP多進(jìn)程編程中幾點(diǎn)心得
fork子進(jìn)程和父進(jìn)程的分道揚(yáng)鑣
以前總是好奇那種if pid>0寫分支的方式是否足矣支持復(fù)雜編程资昧,“沒有什么計(jì)算機(jī)問題是一層抽象解決不了酬土,如果不能解決,就再抽象一層”格带,確實(shí)通過簡單抽象撤缴,再結(jié)合exit()便能很清晰的控制邏輯邊界
子進(jìn)程會(huì)繼承父進(jìn)程打開的資源(文件描述符)
以前對這句話理解不深,這次數(shù)據(jù)庫連接的子進(jìn)程關(guān)閉算是結(jié)實(shí)的上了一課叽唱,也學(xué)到了php運(yùn)行模式在多進(jìn)程方面的一個(gè)缺陷(進(jìn)程結(jié)束后釋放所有資源)屈呕。為了避免混亂,資源的申請若不需要共享棺亭,一定要控制好申請的時(shí)機(jī)凉袱。
另外一個(gè)關(guān)于該點(diǎn)的坑是,標(biāo)準(zhǔn)輸入侦铜、輸出专甩、錯(cuò)誤,也是父進(jìn)程打開的資源钉稍,同樣會(huì)被繼承(導(dǎo)致exec不能退出)
進(jìn)程守護(hù)化
實(shí)現(xiàn)進(jìn)程的守護(hù)化需要進(jìn)行以下幾個(gè)步驟:
- fork一次涤躲,父進(jìn)程退出,子進(jìn)程認(rèn)1作父贡未,自成一個(gè)進(jìn)程組組長
- setsid种樱,重新創(chuàng)建一個(gè)會(huì)話,成為一個(gè)會(huì)話組(包含多個(gè)進(jìn)程組)的組長
- 改變工作目錄為/俊卤,與當(dāng)前啟動(dòng)目錄解耦
- 關(guān)閉標(biāo)準(zhǔn)輸入嫩挤、輸出,重新定向到文件或者/dev/null消恍,避免資源泄露
- umask(0)岂昭,避免父進(jìn)程繼承權(quán)限掩碼
實(shí)用代碼片段
php管理
# 查看配置文件位置
php --ini
# 指定加載php.ini的絕對路徑
php -c another.ini
# 查看phpinfo
php -i
# 查看擴(kuò)展目錄
php-config --extension-dir
# 查看擴(kuò)展模塊,注意擴(kuò)展模塊是可以通過修改ini不啟用的狠怨,內(nèi)置的不行
php -m
# 查看摸個(gè)擴(kuò)展的信息
php --ri swoole
# 查看某個(gè)擴(kuò)展提供了哪些類和函數(shù)
php --re swoole
# 啟動(dòng)一個(gè)內(nèi)置的Web服務(wù)器约啊,用于開發(fā)環(huán)境內(nèi)進(jìn)行程序的調(diào)試
php -S 0.0.0.0:9000 [-t /data/webroot/]
# 檢測一個(gè)php代碼文件是否有語法錯(cuò)誤
php -l file
# 執(zhí)行一段php代碼
php -r "echo 'hello world';"
php-fpm管理
# php在5.3.3之前fpm是需要自己打補(bǔ)丁的
# 然后在管理時(shí)
php-fpm [start|stop|reload]
# 5.3.3之后則已加入源碼中,只需要編譯中開啟即可
# 關(guān)于php-fpm的編譯參數(shù)有
–enable-fpm –with-fpm-user=www –with-fpm-group=www –with-libevent-dir=libevent_path
# 還有個(gè)變化是佣赖,必須通過信號(hào)管理fpm
# SIGINT, SIGTERM 立刻終止
# SIGQUIT 平滑終止
# SIGUSR1 重新打開日志文件
# SIGUSR2 平滑重載所有worker進(jìn)程并重新載入配置和二進(jìn)制模塊
kill -SIGINT `cat fpm.pid`
配置修改
ini_set('memory_limit', '200M');
數(shù)組去重
# 正常方法
$array = array_unique($array);
# 快速方法恰矩,key與val連續(xù)翻轉(zhuǎn)
$array = array_flip($array);
$array = array_flip($array);
# 但這樣還有個(gè)問題就是,若是索引數(shù)組憎蛤,則索引亂序外傅,可以直接使用array_keys
$array = array_flip($array);
$array = array_keys($array);
輸出所有已定義的常量
print_r(get_defined_constants());
curl
# 以前我們通過 PHP 的 cURL 上傳文件是,是使用“@+文件全路徑”的來實(shí)現(xiàn)的:
curl_setopt(ch, CURLOPT_POSTFIELDS, array(
'file' => '@'.realpath('image.png'),
));
# PHP 從 5.5 開始引入了新的 CURLFile 類用來指向文件,CURLFile 類也可以詳細(xì)定義 MIME 類型萎胰、文件名等可能出現(xiàn)在multipart/form-data 數(shù)據(jù)中的附加信息彬碱,PHP 推薦使用 CURLFile 替代舊的@語法
# 而PHP 5.6 直接只支持 CURLFile 方法
curl_setopt(ch, CURLOPT_POSTFIELDS, [
'file' => new CURLFile(realpath('image.png')),
]);
時(shí)間處理
// 獲取上個(gè)月第一天及最后一天,下個(gè)月同理
date('Y-m-01', strtotime('-1 month'));
date('Y-m-t', strtotime('-1 month'));
// 獲取當(dāng)月第一天及最后一天.
date('Y-m-01', time());
date('Y-m-t', time());
// 當(dāng)前年份
date('Y');
// 當(dāng)前月份
date('m');
// 當(dāng)前幾號(hào)
date('d');
// 本月天數(shù)奥洼,因?yàn)閠為最后一天的號(hào)
date("t");
文件操作
// 遍歷文件夾,加載文件
foreach ($arrRequireDir as $requireDir) {
$objDir = dir($requireDir);
while ($file = $objDir->read()) {
$filePath = $requireDir . $file;
if (is_file($filePath) && ($filePath != __FILE__)) {
var_dump($filePath);
include_once($filePath);
}
}
}
編碼轉(zhuǎn)換
// utf8轉(zhuǎn)big5晚胡,ignore跳過無法編碼
$T = iconv("utf8","big5//ignore", $T);
$T = mb_convert_encoding($T, "big5", "utf8");
令人迷惑
PHP由于初期的野蠻生長灵奖,即便進(jìn)入PHP7時(shí)代,語言中依然保留了大量令人迷惑的地方:
- php://output和php://stdout的區(qū)別
- 升PHP7后isset不太對了
- isset和is_null區(qū)別
- 0.58 * 100 == 57?
- utf8中文截?cái)嘣硪约皃hp的實(shí)現(xiàn)
面試常見
有些知識(shí)在實(shí)際開發(fā)中很少遇到估盘,或者遇到就要把始作俑者叉出去瓷患,但在面試中卻喜聞樂見: