현재(2014.2) 현재 PHP의 최신 안정 버전은 PHP5.5이지만 사용자의 거의 절반은 더 이상 유지 관리되지 않는 PHP5.2를 여전히 사용하고 있으며[참고] 나머지 절반의 사용자는 PHP5.3 사용 [참고]].
"수백 가지 학파의 강점을 모으는" PHP의 고통스러운 구문과 열악한 커뮤니티 분위기로 인해 많은 사람들이 새 버전과 새로운 기능에 관심이 없습니다.
이 기사에서는 PHP5.6부터 PHP5.2에 추가된 새로운 기능을 소개합니다.
참고: 2011년 1월에 지원이 중단되었습니다. http://www.php.net/eol.php
참고: http://w3techs.com/technologies/details/pl-php/5/all
(2006년 이전)
그런데 이미 PHP5.2에 등장했지만 소개할만한 가치가 있는 기능을 소개하겠습니다.
__autoload() 함수는 누구나 알고 있을 것입니다. 이 함수를 정의하면 코드에서 정의되지 않은 클래스가 사용되면 이 함수에서 해당 클래스를 로드할 수 있습니다. :
function __autoload($classname) { require_once("{$classname}.php") }
그러나 이 함수는 더 이상 권장되지 않습니다. 왜냐하면 PHP는 중복된 이름을 가진 함수를 허용하지 않기 때문에 프로젝트에 이러한 __autoload() 함수가 하나만 있을 수 있기 때문입니다. 그러나 일부 클래스 라이브러리를 사용하면 필연적으로 여러 자동 로드 기능이 필요하므로 spl_autoload_register()가 이를 대체합니다.
spl_autoload_register(function($classname) { require_once("{$classname}.php") });
spl_autoload_register()는 정의되지 않은 클래스가 나타나면 자동 로드 함수 목록에 함수를 등록합니다. SPL[참고]은 등록된 자동 로드 함수를 등록의 역순으로 하나씩 호출하므로 spl_autoload_register()를 사용할 수 있습니다. 다양한 자동로드 기능을 등록하세요.
참고: SPL: 표준 PHP 라이브러리인 표준 PHP 라이브러리는 일부 고전적인 문제(예: 데이터 구조)를 해결하도록 설계되었습니다.
즉, PHP 데이터 개체, PHP의 새로운 데이터베이스 액세스 인터페이스인 PHP 데이터 개체입니다.
기존 스타일에 따르면 MySQL 데이터베이스에 액세스하는 방법은 다음과 같습니다.
// 连接到服务器,选择数据库 $conn = mysql_connect("localhost", "user", "password"); mysql_select_db("database"); // 执行 SQL 查询 $type = $_POST['type']; $sql = "SELECT * FROM `table` WHERE `type` = {$type}"; $result = mysql_query($sql); // 打印结果 while($row = mysql_fetch_array($result, MYSQL_ASSOC)) { foreach($row as $k => $v) print "{$k}: {$v}\n"; } // 释放结果集,关闭连接 mysql_free_result($result); mysql_close($conn);
코드 데이터베이스를 독립적으로 만들기 위해, 즉 코드 조각이 동시에 여러 데이터베이스에 적용 가능하도록 하기 위해(예를 들어 위 코드는 MySQL에만 적용 가능함) PHP는 공식적으로 PDO를 설계했습니다.
또한 PDO는 다음과 같은 더 많은 기능을 제공합니다.
PDO를 사용하여 구현한 위 코드는 다음과 같습니다.
// 连接到数据库 $conn = new PDO("mysql:host=localhost;dbname=database", "user", "password"); // 预编译SQL, 绑定参数 $query = $conn->prepare("SELECT * FROM `table` WHERE `type` = :type"); $query->bindParam("type", $_POST['type']); // 执行查询并打印结果 foreach($query->execute() as $row) { foreach($row as $k => $v) print "{$k}: {$v}\n"; }
PDO는 좀 더 일반적인 데이터베이스 접근 방법으로 공식적으로 권장됩니다. 특별한 요구 사항이 없다면 PDO를 배워서 사용하는 것이 좋습니다.
그러나 MySQL 고유의 고급 기능을 사용해야 한다면 MySQLi를 사용해 보는 것이 좋습니다. 왜냐하면 PDO에는 여러 데이터베이스에서 동시에 사용하기 위한 MySQL 고유 기능이 포함되어 있지 않기 때문입니다.
MySQLi는 MySQL을 위한 향상된 인터페이스이며, 현재 권장되는 MySQL 드라이버이기도 합니다. 이전 C 스타일 MySQL 인터페이스는 앞으로는 기본적으로 종료됩니다.
위의 두 코드와 비교하면 MySQLi의 사용법에는 새로운 개념이 많지 않습니다. 여기서는 PHP 공식 웹사이트 문서를 참조하세요.
참고: http://www.php.net/manual/en/mysqli.quickstart.php
유형 제약을 통해 매개변수 유형을 제한할 수 있지만 이 메커니즘은 현재 클래스, 호출 가능(실행 가능 유형) 및 배열(배열)에만 적용되며 문자열 및 int에는 적용되지 않습니다.
// 限制第一个参数为 MyClass, 第二个参数为可执行类型,第三个参数为数组 function MyFunction(MyClass $a, callable $b, array $c) { // ... }
(2006~2011)
json_encode(), json_decode() 및 기타 기능을 포함합니다. JSON은 웹 분야에서 매우 일반적으로 사용되는 데이터 교환 형식이며 JS에서 직접 지원할 수 있습니다. JSON은 실제로 JS 구문의 일부입니다.
JSON 시리즈 함수는 PHP의 배열 구조를 JSON 문자열로 변환할 수 있습니다.
$array = ["key" => "value", "array" => [1, 2, 3, 4]]; $json = json_encode($array); echo "{$json}\n"; $object = json_decode($json); print_r($object);
출력:
{"key":"value","array":[1,2,3,4]} stdClass Object ( [key] => value [array] => Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 ) )
json_decode()가 기본적으로 배열 대신 객체를 반환한다는 점은 주목할 가치가 있습니다. 배열을 반환해야 하는 경우 두 번째 매개변수를 true로 설정해야 합니다.
(2009-2012)
PHP5.3은 많은 새로운 기능을 추가하고 이전 버전과 호환되지 않는 일부 수정 사항을 적용한 매우 대규모 업데이트입니다.
다음 기능은 더 이상 사용되지 않습니다. 구성 파일에서 활성화되면 PHP는 런타임 시 경고를 표시합니다.
这是 php.ini 中的一个选项(register_globals), 开启后会将所有表单变量($_GET和$_POST)注册为全局变量.
看下面的例子:
if(isAuth()) $authorized = true; if($authorized) include("page.php");
这段代码在通过验证时,将 $authorized 设置为 true. 然后根据 $authorized 的值来决定是否显示页面.
但由于并没有事先把 $authorized 初始化为 false, 当 register_globals 打开时,可能访问 /auth.php?authorized=1 来定义该变量值,绕过身份验证。
该特征属于历史遗留问题,在 PHP4.2 中被默认关闭,在 PHP5.4 中被移除。
对应 php.ini 中的选项 magic_quotes_gpc, 这个特征同样属于历史遗留问题,已经在 PHP5.4 中移除。
该特征会将所有用户输入进行转义,这看上去不错,在第一章我们提到过要对用户输入进行转义。
但是 PHP 并不知道哪些输入会进入 SQL , 哪些输入会进入 Shell, 哪些输入会被显示为 HTML, 所以很多时候这种转义会引起混乱。
很多虚拟主机提供商使用 Safe Mode 来隔离多个用户,但 Safe Mode 存在诸多问题,例如某些扩展并不按照 Safe Mode 来进行权限控制。
PHP官方推荐使用操作系统的机制来进行权限隔离,让Web服务器以不同的用户权限来运行PHP解释器,请参见第一章中的最小权限原则.
也叫闭包(Closures), 经常被用来临时性地创建一个无名函数,用于回调函数等用途。
$func = function($arg) { print $arg; }; $func("Hello World");
以上代码定义了一个匿名函数,并赋值给了 $func.
可以看到定义匿名函数依旧使用 function 关键字,只不过省略了函数名,直接是参数列表。
然后我们又调用了 $func 所储存的匿名函数。
匿名函数还可以用 use 关键字来捕捉外部变量:
function arrayPlus($array, $num) { array_walk($array, function(&$v) use($num){ $v += $num; }); }
上面的代码定义了一个 arrayPlus() 函数(这不是匿名函数), 它会将一个数组($array)中的每一项,加上一个指定的数字($num).
在 arrayPlus() 的实现中,我们使用了 array_walk() 函数,它会为一个数组的每一项执行一个回调函数,即我们定义的匿名函数。
在匿名函数的参数列表后,我们用 use 关键字将匿名函数外的 $num 捕捉到了函数内,以便知道到底应该加上多少。
PHP 的面向对象体系中,提供了若干“魔术方法”,用于实现类似其他语言中的“重载”,如在访问不存在的属性、方法时触发某个魔术方法。
随着匿名函数的加入,PHP 引入了一个新的魔术方法 __invoke().
该魔术方法会在将一个对象作为函数调用时被调用:
class A { public function __invoke($str) { print "A::__invoke(): {$str}"; } } $a = new A; $a("Hello World");
输出毫无疑问是:
A::__invoke(): Hello World
__callStatic() 则会在调用一个不存在的静态方法时被调用。
PHP的命名空间有着前无古人后无来者的无比蛋疼的语法:
<?php // 命名空间的分隔符是反斜杠,该声明语句必须在文件第一行。 // 命名空间中可以包含任意代码,但只有 **类, 函数, 常量** 受命名空间影响。 namespace XXOO\Test; // 该类的完整限定名是 \XXOO\Test\A , 其中第一个反斜杠表示全局命名空间。 class A{} // 你还可以在已经文件中定义第二个命名空间,接下来的代码将都位于 \Other\Test2 . namespace Other\Test2; // 实例化来自其他命名空间的对象: $a = new \XXOO\Test\A; class B{} // 你还可以用花括号定义第三个命名空间 namespace Other { // 实例化来自子命名空间的对象: $b = new Test2\B; // 导入来自其他命名空间的名称,并重命名, // 注意只能导入类,不能用于函数和常量。 use \XXOO\Test\A as ClassA }
更多有关命名空间的语法介绍请参见官网 [注].
命名空间时常和 autoload 一同使用,用于自动加载类实现文件:
spl_autoload_register( function ($class) { spl_autoload(str_replace("\\", "/", $class)); } );
当你实例化一个类 \XXOO\Test\A 的时候,这个类的完整限定名会被传递给 autoload 函数,autoload 函数将类名中的命名空间分隔符(反斜杠)替换为斜杠,并包含对应文件。
这样可以实现类定义文件分级储存,按需自动加载。
注:http://www.php.net/manual/zh/language.namespaces.php
PHP 的 OPP 机制,具有继承和类似虚函数的功能,例如如下的代码:
class A { public function callFuncXXOO() { print $this->funcXXOO(); } public function funcXXOO() { return "A::funcXXOO()"; } } class B extends A { public function funcXXOO() { return "B::funcXXOO"; } } $b = new B; $b->callFuncXXOO();
输出是:
B::funcXXOO
可以看到,当在 A 中使用 $this->funcXXOO() 时,体现了“虚函数”的机制,实际调用的是 <code>B::funcXXOO().<br />
然而如果将所有函数都改为静态函数:
class A { static public function callFuncXXOO() { print self::funcXXOO(); } static public function funcXXOO() { return "A::funcXXOO()"; } } class B extends A { static public function funcXXOO() { return "B::funcXXOO"; } } $b = new B; $b->callFuncXXOO();
情况就没这么乐观了,输出是:
A::funcXXOO()
这是因为 self 的语义本来就是“当前类”,所以 PHP5.3 给 static 关键字赋予了一个新功能:后期静态绑定:
class A { static public function callFuncXXOO() { print static::funcXXOO(); } // ... } // ...
这样就会像预期一样输出了:
B::funcXXOO
PHP5.3 对 Heredoc 以及 Nowdoc 进行了一些改进,它们都用于在 PHP 代码中嵌入大段字符串。
Heredoc 的行为类似于一个双引号字符串:
$name = "MyName"; echo <<< TEXT My name is "{$name}". TEXT;
Heredoc 以三个左尖括号开始,后面跟一个标识符(TEXT), 直到一个同样的顶格的标识符(不能缩进)结束。
就像双引号字符串一样,其中可以嵌入变量。
Heredoc 还可以用于函数参数,以及类成员初始化:
var_dump(<<<EOD Hello World EOD ); class A { const xx = <<< EOD Hello World EOD; public $oo = <<< EOD Hello World EOD; }
Nowdoc 的行为像一个单引号字符串,不能在其中嵌入变量,和 Heredoc 唯一的区别就是,三个左尖括号后的标识符要以单引号括起来:
$name = "MyName"; echo <<< 'TEXT' My name is "{$name}". TEXT;
输出:
My name is "{$name}".
PHP5.3 起同时支持在全局命名空间和类中使用 const 定义常量。
旧式风格:
define("XOOO", "Value");
新式风格:
const XXOO = "Value";
const 形式仅适用于常量,不适用于运行时才能求值的表达式:
// 正确 const XXOO = 1234; // 错误 const XXOO = 2 * 617;
旧式风格:
echo $a ? $a : "No Value";
可简写成:
echo $a ?: "No Value";
即如果省略三元运算符的第二个部分,会默认用第一个部分代替。
Phar即PHP Archive, 起初只是Pear中的一个库而已,后来在PHP5.3被重新编写成C扩展并内置到 PHP 中。
Phar用来将多个 .php 脚本打包(也可以打包其他文件)成一个 .phar 的压缩文件(通常是ZIP格式)。
目的在于模仿 Java 的 .jar, 不对,目的是为了让发布PHP应用程序更加方便。同时还提供了数字签名验证等功能。
.phar 文件可以像 .php 文件一样,被PHP引擎解释执行,同时你还可以写出这样的代码来包含(require) .phar 中的代码:
require("xxoo.phar"); require("phar://xxoo.phar/xo/ox.php");
更多信息请参见官网 [注].
注:http://www.php.net/manual/zh/phar.using.intro.php
(2012-2013)
Short Open Tag 自 PHP5.4 起总是可用。
在这里集中讲一下有关 PHP 起止标签的问题。即:
<?php // Code... ?>
通常就是上面的形式,除此之外还有一种简写形式:
<? /* Code... */ ?>
还可以把
<?php echo $xxoo;?>
简写成:
<?= $xxoo;?>
这种简写形式被称为 Short Open Tag, 在 PHP5.3 起被默认开启,在 PHP5.4 起总是可用。
使用这种简写形式在 HTML 中嵌入 PHP 变量将会非常方便。
对于纯 PHP 文件(如类实现文件), PHP 官方建议顶格写起始标记,同时 省略 结束标记。
这样可以确保整个 PHP 文件都是 PHP 代码,没有任何输出,否则当你包含该文件后,设置 Header 和 Cookie 时会遇到一些麻烦 [注].
注:Header 和 Cookie 必须在输出任何内容之前被发送。
这是非常方便的一项特征!
// 原来的数组写法 $arr = array("key" => "value", "key2" => "value2"); // 简写形式 $arr = ["key" => "value", "key2" => "value2"];
所谓Traits就是“构件”,是用来替代继承的一种机制。PHP中无法进行多重继承,但一个类可以包含多个Traits.
// Traits不能被单独实例化,只能被类所包含 trait SayWorld { public function sayHello() { echo 'World!'; } } class MyHelloWorld { // 将SayWorld中的成员包含进来 use SayWorld; } $xxoo = new MyHelloWorld(); // sayHello() 函数是来自 SayWorld 构件的 $xxoo->sayHello();
Traits还有很多神奇的功能,比如包含多个Traits, 解决冲突,修改访问权限,为函数设置别名等等。
Traits中也同样可以包含Traits. 篇幅有限不能逐个举例,详情参见官网 [注].
注:http://www.php.net/manual/zh/language.oop5.traits.php
PHP从5.4开始内置一个轻量级的Web服务器,不支持并发,定位是用于开发和调试环境。
在开发环境使用它的确非常方便。
php -S localhost:8000
这样就在当前目录建立起了一个Web服务器,你可以通过 http://localhost:8000/ 来访问。
其中localhost是监听的ip,8000是监听的端口,可以自行修改。
很多应用中,都会进行URL重写,所以PHP提供了一个设置路由脚本的功能:
php -S localhost:8000 index.php
这样一来,所有的请求都会由index.php来处理。
你还可以使用 XDebug 来进行断点调试。
PHP5.4 新增了动态访问静态方法的方式:
$func = "funcXXOO"; A::{$func}();
新增在实例化时访问类成员的特征:
(new MyClass)->xxoo();
新增支持对函数返回数组的成员访问解析(这种写法在之前版本是会报错的):
print func()[0];
(2013起)
yield关键字用于当函数需要返回一个迭代器的时候, 逐个返回值。
function number10() { for($i = 1; $i <= 10; $i += 1) yield $i; }
该函数的返回值是一个数组:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
可以用 list() 在 foreach 中解析嵌套的数组:
$array = [ [1, 2, 3], [4, 5, 6], ]; foreach ($array as list($a, $b, $c)) echo "{$a} {$b} {$c}\n";
结果:
1 2 3 4 5 6
不推荐使用 mysql 函数,推荐使用 PDO 或 MySQLi, 参见前文。
不再支持Windows XP.
可用 MyClass::class 取到一个类的完整限定名(包括命名空间)。
empty() 支持表达式作为参数。
try-catch 结构新增 finally 块。
定义常量时允许使用之前定义的常量进行计算:
const A = 2; const B = A + 1; class C { const STR = "hello"; const STR2 = self::STR + ", world"; }
允许常量作为函数参数默认值:
function func($arg = C::STR2)
用于代替 func_get_args()
function add(...$args) { $result = 0; foreach($args as $arg) $result += $arg; return $result; }
同时可以在调用函数时,把数组展开为函数参数:
$arr = [2, 3]; add(1, ...$arr); // 结果为 6
命名空间支持常量和函数:
namespace Name\Space { const FOO = 42; function f() { echo __FUNCTION__."\n"; } } namespace { use const Name\Space\FOO; use function Name\Space\f; echo FOO."\n"; f(); }