PHP7速度是 PHP5.6 的兩倍
php7 最顯著的變化就是效能的極大提升,已接近Facebook開發的PHP執行引擎HHVM。在WordPress基準效能測試中,速度比5.6版本要快2~3倍,大大減少了記憶體佔用。PHP7在語言上也有一些變化,比如新增返回型別宣告、增加了一些新的保留關鍵字等。在安全方面,去除了PHP安全模式,新增魔術引號等。不僅如此,新版還支援64位,而且包含最新版Zend引擎。
測試一下
很簡單的一個例子,生成一個 60 萬元素的陣列,通過查詢key 的方式,來確定key是否存在。
<?php
$a = [];
for($i=0;$i<600000;$i++){
$a[$i] = $i;
}
foreach($a as $item) {
array_key_exists($item, $a);
}
我們分別在php5.6.11和php7.0.4來測試下效能。
php5.6.11
➜ time php 1.php
0.67s user 0.06s system 67% cpu 1.078 total
➜ time php 1.php
0.68s user 0.06s system 98% cpu 0.748 total
➜ time php 1.php
0.65s user 0.06s system 67% cpu 1.052 total
三次平均下來,大致是 user使用 0.65秒,system使用0.06秒,67%的cpu率。總共1秒左右。
再看php7的情況
➜ time /usr/local/opt/php70/bin/php 1.php
0.52s user 0.02s system 98% cpu 0.544 total
➜ time /usr/local/opt/php70/bin/php 1.php
0.49s user 0.02s system 99% cpu 0.513 total
➜ time /usr/local/opt/php70/bin/php 1.php
0.51s user 0.02s system 98% cpu 0.534 total
對比下來,user使用時間下降20%左右,system使用時間下降70%,cpu使用率更高高達98%。總體時間減少為。0.5秒。
這個例子看下來,效率提供了2倍。確實不錯。
再看一個例子。同樣也是生成一個 60 萬元素的陣列,查詢 value是否存在。
<?php
$a = [];
for($i=0;$i<600000;$i++){
$a[$i] = $i;
}
foreach($a as $i) {
array_search($i, $a);
}
?>
先看php5.6.11
➜ testPHP time php 2.php
0.68s user 0.03s system 66% cpu 1.077 total
➜ testPHP time php 2.php
0.68s user 0.02s system 98% cpu 0.710 total
➜ testPHP time php 2.php
0.68s user 0.02s system 98% cpu 0.713 total
➜ testPHP time php 2.php
0.69s user 0.02s system 98% cpu 0.721 total
再接著看php7.0.4
➜ testPHP time /usr/local/opt/php70/bin/php 2.php
0.12s user 0.02s system 69% cpu 0.201 total
➜ testPHP time /usr/local/opt/php70/bin/php 2.php
0.11s user 0.01s system 97% cpu 0.131 total
➜ testPHP time /usr/local/opt/php70/bin/php 2.php
0.11s user 0.01s system 96% cpu 0.130 total
明顯看出,快了6倍多。
1. 更多的標量型別宣告
現在php的標量有兩種模式: 強制 (預設) 和嚴格模式。 現在可以使用下列型別引數(無論用強制模式還是嚴格模式): 字串(string), 整數 (int), 浮點數 (float), 以及布林值 (bool)。它們擴充了PHP5中引入的其他型別:類名,介面,陣列和 回撥型別。在舊版中,函式的引數申明只能是(Array $arr)、(CLassName $obj)等,基本型別比如Int,String等是不能夠被申明的。
怎麼理解呢?php7之前的版本,我們要想限定一個函式的引數的型別,只有array或者class2種。
php7之前:
class MyInfo
{
public $a = 123;
public function getInfo(array $a, $b)
{
var_dump($a, $b);
}
}
function getClass(MyInfo $a) {
var_dump($a->a);
}
- 我們想限定 getInfo的第一個引數,必須是陣列,所以,我們可以在引數$a前加一個array。來申明。
- 同樣,我們想getClass的引數,必須是一個類,所以我們就用這個類的className字首來申明,起到強制使用的目的。
php7之前,只有這2種標量可以使用。
我們來使用一下:
$info = new MyInfo();
$info->getInfo([1,2,3,4], 4);
我們按照規定的來,第一個引數,傳陣列,結果當然是正常列印:
➜ testPHP php 3.php
array(3) {
[0] =>
int(1)
[1] =>
int(2)
[2] =>
int(3)
}
int(4)
要是我們不按照規定來,就會報知名錯誤:
$info = new MyInfo();
$info->getInfo(122, 0);
報錯:
PHP Catchable fatal error: Argument 1 passed to MyInfo::getInfo() must be of the type array, integer given, called in /Users/yangyi/www/testPHP/3.php on line 25 and defined in /Users/yangyi/www/testPHP/3.php on line 8
PHP Stack trace:
PHP 1. {main}() /Users/yangyi/www/testPHP/3.php:0
PHP 2. MyInfo->getInfo() /Users/yangyi/www/testPHP/3.php:25
使用類也一樣:
$info = new MyInfo();
getClass($info);
輸出結果:
➜ testPHP php 3.php
int(123)
同樣,我們傳入別的引數,就會報錯:
getClass(123);
➜ testPHP php 3.php
PHP Catchable fatal error: Argument 1 passed to getClass() must be an instance of MyInfo, integer given, called in /Users/yangyi/www/testPHP/3.php on line 27 and defined in /Users/yangyi/www/testPHP/3.php on line 17
PHP Stack trace:
PHP 1. {main}() /Users/yangyi/www/testPHP/3.php:0
PHP 2. getClass() /Users/yangyi/www/testPHP/3.php:27
我們回到這次php7的升級,它擴充了標量的型別,增加了bool、int、string、float。
php7有2種兩種模式: 強制 和 嚴格模式。
強制模式
強制模式是預設模式,強制模式下,它會幫我們把數字型別的string型別,int整型,bool,強制型別。其他型別不能轉換,就會報錯。
還是剛才的例子:
class MyInfo
{
public $a = 123;
public function get1(bool $b)
{
var_dump($b);
}
public function get2(int $b)
{
var_dump($b);
}
public function get3(string $b)
{
var_dump($b);
}
public function get4(float $b)
{
var_dump($b);
}
public function get5(array $b)
{
var_dump($b);
}
}
我們先全部傳入int 1
$info = new MyInfo();
$info->get1(1);
$info->get2(1);
$info->get3(1);
$info->get4(1);
看列印結果,它已經幫我們強制轉換了。
➜ testPHP /usr/local/opt/php70/bin/php 3.php
/Users/yangyi/www/testPHP/3.php:11:
bool(true)
/Users/yangyi/www/testPHP/3.php:19:
int(1)
/Users/yangyi/www/testPHP/3.php:26:
string(1) "1"
/Users/yangyi/www/testPHP/3.php:33:
double(1)
我們繼續,傳入 string 1.23 :
$info = new MyInfo();
$info->get1('1.23');
$info->get2('1.23');
$info->get3('1.23');
$info->get4('1.23');
看下,列印結果。也已經幫我們強制轉換了。
➜ testPHP /usr/local/opt/php70/bin/php 3.php
/Users/yangyi/www/testPHP/3.php:11:
bool(true)
/Users/yangyi/www/testPHP/3.php:19:
int(1)
/Users/yangyi/www/testPHP/3.php:26:
string(4) "1.23"
/Users/yangyi/www/testPHP/3.php:33:
double(1.23)
但是我們如果引數是array就沒法強制轉換,就會報錯了:
$info->get5('1.23');
testPHP /usr/local/opt/php70/bin/php 3.php
PHP Fatal error: Uncaught TypeError: Argument 1 passed to MyInfo::get5() must be of the type array, string given, called in /Users/yangyi/www/testPHP/3.php on line 54 and defined in /Users/yangyi/www/testPHP/3.php:37
我們在PHP5.6.11執行這些程式碼會報錯嗎?試一試:
$info = new MyInfo();
$info->get1('1.23');
$info->get2('1.23');
$info->get3('1.23');
$info->get4('1.23');
➜ testPHP php 3.php
PHP Catchable fatal error: Argument 1 passed to MyInfo::get1() must be an instance of bool, string given, called in /Users/yangyi/www/testPHP/3.php on line 48 and defined in /Users/yangyi/www/testPHP/3.php on line 8
好吧。直接報錯了,雖然錯誤提示也是說型別錯誤,但是,其他是不支援這些型別的申明。
嚴格模式
前面說了,強制模式下,它會幫我們強制轉換,那麼嚴格模式下呢?
首先,如何開啟嚴格模式呢?
<?php
declare(strict_types=1);
加上就可以了,這樣,就進入嚴格模式,引數必須符合規定,不然報錯:
我們加上這句話,再執行下:
<?php
declare(strict_types=1);
...
...
$info = new MyInfo();
$info->get1('1.23');
$info->get2('1.23');
$info->get3('1.23');
$info->get4('1.23');
執行,看下結果,果然直接報錯了。
PHP Fatal error: Uncaught TypeError: Argument 1 passed to MyInfo::get1() must be of the type boolean, string given, called in /Users/yangyi/www/testPHP/3.php on line 49 and defined in /Users/yangyi/www/testPHP/3.php:9
2. 返回值型別宣告
- 我們知道php的函式是沒有返回值型別的,return啥型別,就是啥型別。php7中增加了返回值型別,我們可以定義一個函式的返回值型別。
- 和php7升級的標量型別宣告一樣,return的型別可以是以下這些:bool、int、string、float、array、class。
舉個例子來說,我們希望一個函式的返回值是一個陣列,我們可以這樣子書寫:
:array {} // 冒號+返回型別
function returnInfo ($a) : array {
return $a;
}
var_dump(returnInfo([1,2,3]));
是不是覺得很奇怪,又無可思議!!!
檢視列印結果:
➜ testPHP /usr/local/opt/php70/bin/php 3.php
/Users/yangyi/www/testPHP/3.php:64:
array(3) {
[0] =>
int(1)
[1] =>
int(2)
[2] =>
int(3)
}
同樣,我們想返回是int整型:
function returnInfo ($a) : int {
return $a;
}
var_dump(returnInfo('1.233'));
檢視結果,他已經幫我們強制轉換成整型了。
➜ testPHP /usr/local/opt/php70/bin/php 3.php
/Users/yangyi/www/testPHP/3.php:64:
int(1)
同樣,我們可以返回一個class型別的:
public function getLogger(): Logger {
return $this->logger;
}
預設,也是強制模式,會幫我們轉換,如果,我們想使用嚴格模式,同樣是一樣的,在檔案頭部加上:
<?php
declare(strict_types=1);
就可以了,這樣,我們規定返回值是什麼型別,就必須得是這樣,不然就報致命報錯。
3. null合併運算子 (??)
由於日常使用中存在大量同時使用三元表示式和 isset()的情況, php7增加了一個新的語法糖 : null合併運算子 (??)
如果變數存在且值不為NULL, 它就會返回自身的值,否則返回它的第二個運算元。
//php version = 7
$username = $user ?? 'nobody';
//php version < 7 得這樣使用:
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';
確實方便了很多。
我記得php5.3的更新中,加入了 三元運算子簡寫形式:
$a ?: $b
千萬別和??搞混淆了!!!
- $a ?: $b的意思是 $a為true時,直接返回$a, 否則返回$b
- $a ?? $b的意思是 $a isset($a)為true, 且不為NULL, 就返回$a, 否則返回$b。
看例子:
$user = 0;
$username = $user ?? 'nobody';
echo $username; //輸出 0,因為 0 存在 且 不為NULL。
$username = $user ?: 'nobody';
echo $username; //輸出 'nobody',因為 0 為 false
4. 太空船操作符(組合比較符)
- php7 中,新加入了一個比較符號:<=> ,因為長相像太空船,所以,也叫太空船操作符。
- <=>用於比較兩個表示式。當$a小於、等於或大於$b時它分別返回-1、0或1。
看例子:
<?php
// Integers
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1
// Floats
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1
// Strings
echo "a" <=> "a"; // 0
echo "a" <=> "b"; // -1
echo "b" <=> "a"; // 1
?>
其實,蠻多地方可以派上用場的。
5. 通過define()定義常量陣列
Array型別的常量現在可以通過 define()來定義。在 PHP5.6 中僅能通過const定義。
在php5.3中,增加了可以使用const來申明常量,替代define()函式,但是隻能申明一些簡單的變數。舊式風格
define("XOOO", "Value");
新式風格
const XXOO = "Value";
const 形式僅適用於常量,不適用於執行時才能求值的表示式:
正確
const XXOO = 1234;
錯誤
const XXOO = 2 * 617;
在php5.6中,又對const進行來升級,可以支援上面的運算了。
const A = 2;
const B = A + 1;
但是,一隻都是在優化const,可是確把define()給搞忘記了,php 5.6申明一個陣列常量,只能用const。所以,在 php7 中把 define()申明一個陣列也給加上去了。
//php 7
define ('AWS' , [12,33,44,55]);
// php < 7
const QWE = [12,33,44,55];
echo AWS[1]; //12
echo QWE[2]; //33
至此,到php7版本,define()的功能和const就一摸一樣了,所以,你隨便用哪一個都可以,但是因為在class類中,什麼常量是const。所以,我們就統一用const申明常量好了。
6. 匿名類
現在已經支援通過new class 來例項化一個匿名類,這可以用來替代一些用後即焚的完整類定義。
看下這個官方文件上的一個栗子:
<?php
interface Logger {
public function log(string $msg);
}
class Application {
private $logger;
public function getLogger(): Logger {
return $this->logger;
}
public function setLogger(Logger $logger) {
$this->logger = $logger;
}
}
$app = new Application;
$app->setLogger(new class implements Logger {
public function log(string $msg) {
echo $msg;
}
});
var_dump($app->getLogger());
?>
我們先輸出的列印的結果,顯示為匿名類:
class class@anonymous#2 (0) {
}
我們來分解下,還原被偷懶的少寫的程式碼:
class logClass implements Logger {
public function log(string $msg) {
echo $msg;
}
}
$app = new Application;
$log2 = new logClass;
$app->setLogger($log2);
輸出結果為:
class logClass#2 (0) {
}
雖然程式碼簡潔了很多,但是還是有點不適應,多用用就好了。
還記得php中的匿名函式嘛?在php5.3中新增的匿名函式,結合新的,順便複習下:
function arraysSum(array ...$arrays): array {
return array_map(function(array $array): int {
return array_sum($array);
}, $arrays);
}
print_r(arraysSum([1,2,3], [4,5,6], [7,8,9]));
輸出結果為:
Array
(
[0] => 6
[1] => 15
[2] => 24
)
7. Unicode codepoint 轉譯語法
ps : 由於用的少,我就直接抄官網的說明了。
這接受一個以16進位制形式的 Unicode codepoint,並列印出一個雙引號或heredoc包圍的 UTF-8 編碼格式的字串。 可以接受任何有效的 codepoint,並且開頭的 0 是可以省略的。
echo "\u{0000aa}";
echo "\u{aa}"; //省略了開頭的0
echo "\u{9999}";
看下輸出:
我們在php5.6環境下執行下呢?會怎樣:
\u{aa} \u{0000aa} \u{9999}
好吧,直接原樣輸出了。
8. Closure::call() 閉包
ps : 由於用的少,我就直接抄官網的說明了。
Closure::call() 現在有著更好的效能,簡短幹練的暫時繫結一個方法到物件上閉包並呼叫它。
<?php class A {private $x = 1;} // php 7之前: $getXCB = function() {return $this->x;}; $getX = $getXCB->bindTo(new A, 'A'); // intermediate closure echo $getX(); // PHP 7: $getX = function() {return $this->x;}; echo $getX->call(new A);
會輸出:
1 1
9. 為unserialize()提供過濾
unserialize 這個函式應該不陌生,它是php中用解開用serialize序列化的變數。
看個栗子:
<?php
$a = [1,2,3,4,5,6];
$b = serialize($a);
$c = unserialize($b);
var_dump($a, $b, $c);
列印結果為:
array(6) {
[0] =>
int(1)
[1] =>
int(2)
[2] =>
int(3)
[3] =>
int(4)
[4] =>
int(5)
[5] =>
int(6)
}
string(54) "a:6:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;}"
array(6) {
[0] =>
int(1)
[1] =>
int(2)
[2] =>
int(3)
[3] =>
int(4)
[4] =>
int(5)
[5] =>
int(6)
}
現在php7中unserialize會變得更佳好用,它多了一個引數,用來反序列化包涵class的過濾不需要的類,變的更加安全。
- unserialize($one, ["allowed_classes" => true]);
- unserialize($one, ["allowed_classes" => false]);
- unserialize($one, ["allowed_classes" => [class1,class2,class3]]);
舉個例子,先序列化一個類。class MyInfo { public function getMyName() { return 'phper'; } } $phper = new MyInfo(); $one = serialize($phper);
//引數allowed_classes 設定為 true,表示允許解析class $two = unserialize($one, ["allowed_classes" => true]); //引數allowed_classes 設定為 false,表示不允許解析class $three = unserialize($one, ["allowed_classes" => false]); //不加引數。正常解析。 $four = unserialize($one); //只允許解析 類 MyInfo1。 $five = unserialize($one, ["allowed_classes" => ["MyInfo1"]]); //分別輸出下 getMyName方法; var_dump($one); var_dump($two->getMyName()); var_dump($three->getMyName()); var_dump($four->getMyName()); var_dump($five->getMyName());
發現3和5直接報致命錯誤了:
PHP Fatal error: main(): The script tried to execute a method or access a property of an incomplete object. Please ensure that the class definition "MyInfo" of the object you are trying to operate on was loaded _before_ unserialize() gets called or provide a __autoload() function to load the class definition in /Users/yangyi/www/php7/5.php on line 22 大致意思就是,沒許可權解析。
所以,我們改一下:
$three = unserialize($one, ["allowed_classes" => true]);
$five = unserialize($one, ["allowed_classes" => ["MyInfo"]]);
再輸出,就正常了。
/Users/yangyi/www/php7/5.php:22:
string(17) "O:6:"MyInfo":0:{}"
/Users/yangyi/www/php7/5.php:23:
string(5) "phper"
/Users/yangyi/www/php7/5.php:24:
string(5) "phper"
/Users/yangyi/www/php7/5.php:25:
string(5) "phper"
/Users/yangyi/www/php7/5.php:26:
string(5) "phper"
發現我目前為止並沒用到,並沒有什麼亂用,好吧,繼續下一個。
10. IntlChar
ps : 由於用的少,我就直接抄官網的說明了。
新增加的 IntlChar(http://php.net/manual/zh/class.intlchar.ph...) 類旨在暴露出更多的 ICU 功能。這個類自身定義了許多靜態方法用於操作多字符集的 unicode 字元。
<?php printf('%x', IntlChar::CODEPOINT_MAX); echo IntlChar::charName('@'); var_dump(IntlChar::ispunct('!'));
以上例程會輸出:
10ffff COMMERCIAL AT bool(true)
若要使用此類,請先安裝Intl擴充套件。
1.廢棄擴充套件
Ereg 正規表示式
- mssql
- mysql
- sybase_ct
2.廢棄的特性
- 不能使用同名的建構函式
- 例項方法不能用靜態方法的方式呼叫
3.廢棄的函式
方法呼叫
- call_user_method()
- call_user_method_array()
應該採用
- call_user_func()
- call_user_func_array()
加密相關函式
- mcrypt_generic_end()
- mcrypt_ecb()
- mcrypt_cbc()
- mcrypt_cfb()
- mcrypt_ofb()
注意: PHP7.1 以後mcrypt_*序列函式都將被移除。推薦使用:openssl 序列函式
雜項
- set_magic_quotes_runtime
- set_socket_blocking
- Split
- imagepsbbox()
- imagepsencodefont()
- imagepsextendfont()
- imagepsfreefont()
- imagepsloadfont()
- imagepsslantfont()
- imagepstext()
4.廢棄的用法
- $HTTP_RAW_POST_DATA 變數被移除, 使用
php://input
來代 - ini檔案裡面不再支援#開頭的註釋, 使用
”;”
- 移除了ASP格式的支援和指令碼語法的支援:
<%
和< script language=php >
1.字串處理機制修改
含有十六進位制字元的字串不再視為數字, 也不再區別對待.
var_dump("0x123" == "291"); // false
var_dump(is_numeric("0x123")); // false
var_dump("0xe" + "0x1"); // 0
var_dump(substr("f00", "0x1")) // foo
2.整型處理機制修改
Int64支援, 統一不同平臺下的整型長度, 字串和檔案上傳都支援大於2GB. 64位PHP7字串長度可以超過2^31次方位元組.
// 無效的八進位制數字(包含大於7的數字)會報編譯錯誤
$i = 0681; // 老版本php會把無效數字忽略。
// 位移負的位置會產生異常
var_dump(1 >> -1);
// 左位移超出位數則返回0
var_dump(1 << 64);// 0
// 右位移超出會返回0或者-1
var_dump(100 >> 32);// 0
var_dump(-100 >> 32);// -1
3.引數處理機制修改
不支援重複引數命名
function func(a,a,b, c,c,c) {} ;//hui報錯
func_get_arg()
和func_get_args()
這兩個方法返回引數當前的值, 而不是傳入時的值, 當前的值有可能會被修改
所以需要注意,在函式第一行最好就給記錄下來,否則後續有修改的話,再讀取就不是傳進來的初始值了。
function foo($x) {
$x++;
echo func_get_arg(0);
}
foo(1); //返回2
## 4.foreach修改
foreach()迴圈對陣列內部指標不再起作用
$arr = [1,2,3];
foreach ($arr as &$val) {
echo current($arr);// php7 全返回0
}
按照值進行迴圈的時候, foreach是對該陣列的拷貝操作
$arr = [1,2,3];
foreach ($arr as $val) {
unset($arr[1]);
}
var_dump($arr);
最新的php7依舊會列印出[1,2,3]。(ps:7.0.0不行)
老的會列印出[1,3]
按照引用進行迴圈的時候, 對陣列的修改會影響迴圈
$arr = [1];
foreach ($arr as $val) {
var_dump($val);
$arr[1]=2;
}
最新的php7依舊會追加新增元素的迴圈。(ps:7.0.0不行)
## 5. list修改
不再按照相反的順序賦值
//$arr將會是[1,2,3]而不是之前的[3,2,1]
list($arr[], $arr[], $arr[]) = [1,2,3];
不再支援字串拆分功能
// $x = null 並且 $y = null
$str = 'xy';
list($x, $y) = $str;
空的list()賦值不再允許
list() = [123];r;
list()現在也適用於陣列物件
list($a, $b) = (object)new ArrayObject([0, 1]);
## 6.變數處理機制修改
> 對變數、屬性和方法的間接呼叫現在將嚴格遵循從左到右的順序來解析,而不是之前的混雜著幾個特殊案例的情況。
引用賦值時自動建立的陣列元素或者物件屬性順序和以前不同了
$arr = [];
$arr['a'] = &$arr['b'];
$arr['b'] = 1;
// php7: ['a' => 1, 'b' => 1]
// php5: ['b' => 1, 'a' => 1]
## 7.雜項
* debug_zval_dump() 現在列印 “int” 替代 “long”, 列印 “float” 替代 “double”
* dirname() 增加了可選的第二個引數, depth, 獲取當前目錄向上 depth 級父目錄的名稱。
* getrusage() 現在支援 Windows.mktime() and gmmktime() 函式不再接受 is_dst 引數。
* preg_replace() 函式不再支援 “\e” (PREG_REPLACE_EVAL). 應當使用 preg_replace_callback() 替代。
* setlocale() 函式不再接受 category 傳入字串。 應當使用 LC_* 常量。
* exec(), system() and passthru() 函式對 NULL 增加了保護.
* shmop_open() 現在返回一個資源而非一個int, 這個資源可以傳給shmop_size(), shmop_write(), shmop_read(), shmop_close() 和 shmop_delete().
* 為了避免記憶體洩露,xml_set_object() 現在在執行結束時需要手動清除 $parse。
* curl_setopt 設定項CURLOPT_SAFE_UPLOAD變更
* TRUE 禁用 @ 字首在 CURLOPT_POSTFIELDS 中傳送檔案。 意味著 @ 可以在欄位中安全得使用了。 可使用 CURLFile作為上傳的代替。
* PHP 5.5.0 中新增,預設值 FALSE。 PHP 5.6.0 改預設值為 TRUE。. PHP 7 刪除了此選項, 必須使用 CURLFile interface 來上傳檔案。
本作品採用《CC 協議》,轉載必須註明作者和本文連結