PHP5.3
匿名函数
也叫闭包(Closures), 经常被用来临时性地创建一个无名函数,用于回调函数等用途。
$func = function ($arg) {
print $arg;
};
$func("Hello World");
<?php
$f = function () {
return 100;
};
function testClosure(Closure $callback) {
return $callback();
}
$a = testClosure($f);
print_r($a); //100
exit;
如果要调用一个类里面的匿名函数呢
<?php
class C
{
public static function testC() {
return function ($i) {
return $i + 100;
};
}
}
function testClosure(Closure $callback) {
return $callback(13);
}
$a = testClosure(C::testC());
print_r($a);exit;
其中的C::testC()返回的是一个funciton,我们就需要将“一个匿名函数绑定到一个类中”。
<?php
class A{
public $base = 100;
}
$f = function () {
return $this->base + 3;
};
$a = Closure::bind($f, new A);
print_r($a());
这个匿名函数中莫名奇妙的有个this,这个this关键词就是说明这个匿名函数是需要绑定在类中的。绑定之后,就好像A中有这么个函数一样,但是这个函数是public还是private,bind的最后一个参数就说明了这个函数的可调用范围
function arrayPlus($array, $num)
{
array_walk($array, function (&$v) use ($num) {
$v += $num;
});
}
上面的代码定义了一个 arrayPlus() 函数(这不是匿名函数), 它会将一个数组($array)中的每一项,加上一个指定的数字($num).在 arrayPlus() 的实现中,我们使用了 array_walk() 函数,它会为一个数组的每一项执行一个回调函数,即我们定义的匿名函数。在匿名函数的参数列表后,我们用 use 关键字将匿名函数外的 $num 捕捉到了函数内,以便知道到底应该加上多少。
public function addConnection($name, ConnectionInterface $connection)
{
$this->connections[$name] = $connection;
}
后期静态绑定
PHP的继承模型中有一个存在已久的问题,那就是在父类中引用扩展类的最终状态比较困难。
<?php
class ParentBase
{
static $property = 'Parent Value';
public static function render()
{
return self::$property;
}
}
class Descendant extends ParentBase
{
static $property = 'Descendant Value';
}
//output: Parent Value
echo Descendant::render();
在这个例子中,render()方法中使用了self关键字,这是指ParentBase类而不是指Descendant类。在 ParentBase::render()方法中没法访问$property的最终值。为了解决这个问题,需要在子类中重写render()方法。通过引入延迟静态绑定功能,可以使用static作用域关键字访问类的属性或者方法的最终值
<?php
class ParentBase
{
static $property = 'Parent Value';
public static function render()
{
return static::$property;
}
}
class Descendant extends ParentBase
{
static $property = 'Descendant Value';
}
//output: Descendant Value
echo Descendant::render();
新增两个魔术方法__callStatic()和__invoke()
__call($funcname, $arguments)
__callStatic($funcname, $arguments)
参数说明:
$funcname String 调用的不存在的方法名称。
$arguments Array 调用方法时所带的参数。
__invoke魔术方法会在将一个对象作为函数调用时被调用:
class A
{
public function __invoke($args)
{
print "A::__invoke(): {$args}";
}
}
$a = new A;
//output: A::__invoke(): Hello World
$a("Hello World");
__callStatic则会在调用一个不存在的静态方法时被调用,有了__callStatic,可以省不少代码了。而且这个方法支持在子类中调用,配合上get_called_class,子类也一起魔术了
<?php
class ActiveRecordBase
{
/** As of PHP 5.3.0 */
public static function __callStatic($func, $arguments)
{
if ($func == 'getById') {
$id = $arguments[0];
return get_called_class() . '(' . $id . ')';
}
throw new Exception('Invalid method : ' . $name);
}
}
class Person extends ActiveRecordBase
{
}
// output: Person(123)
echo Person::getById(123);
__call 当要调用的方法不存在或权限不足时,会自动调用__call 方法。
<?php
class Db
{
private $sql = array(
"field" => "",
"where" => "",
"order" => "",
"limit" => "",
"group" => "",
"having" => "",
);
// 连贯操作调用field() where() order() limit() group() having()方法,组合sql语句
function __call($methodName, $args)
{
// 将第一个参数(代表不存在方法的方法名称),全部转成小写方式,获取方法名称
$methodName = strtolower($methodName);
// 如果调用的方法名和成员属性数组$sql下标对应上,则将第二个参数给数组中下标对应的元素
if (array_key_exists($methodName, $this->sql)) {
$this->sql[$methodName] = $args[0];
} else {
echo '调用类' . get_class($this) . '中的方法' . $methodName . '()不存在';
}
// 返回自己对象,则可以继续调用本对象中的方法,形成连贯操作
return $this;
}
// 输出连贯操作后组合的一个sql语句,是连贯操作最后的一个方法
function select()
{
echo "SELECT {$this->sql['field']} FROM user {$this->sql['where']} {$this->sql['order']} {$this->sql['limit']} {$this->sql['group']}
{$this->sql['having']}";
}
}
$db = new Db();
// 连贯操作
$db->field('sex, count(sex)')
->where('where sex in ("男","女")')
->group('group by sex')
->having('having avg(age) > 25')
->select();
?>
命名空间
一个最明确的目的就是解决重名问题,PHP中不允许两个函数或者类出现相同的名字,否则会产生一个致命的错误。这种情况下只要避免命名重复就可以解决,最常见的一种做法是约定一个前缀。
<?php
//创建空间Blog
namespace Blog;
class Comment
{
}
//非限定名称,表示当前Blog空间
//这个调用将被解析成 Blog\Comment();
$blog_comment = new Comment();
//限定名称,表示相对于Blog空间
//这个调用将被解析成 Blog\Article\Comment();
$article_comment = new Article\Comment(); //类前面没有反斜杆\
//完全限定名称,表示绝对于Blog空间
//这个调用将被解析成 Blog\Comment();
$article_comment = new \Blog\Comment(); //类前面有反斜杆\
//完全限定名称,表示绝对于Blog空间
//这个调用将被解析成 Blog\Article\Comment();
$article_comment = new \Blog\Article\Comment(); //类前面有反斜杆\
//创建Blog的子空间Article
namespace Blog\Article;
class Comment
{
}
?>
别名和导入
可以看作是调用命名空间元素的一种快捷方式。PHP并不支持导入函数或常量。 它们都是通过使用use操作符来实现:
<?php
namespace Blog\Article;
class Comment
{
}
//创建一个BBS空间(我有打算开个论坛)
namespace BBS;
//导入一个命名空间
use Blog\Article;
//导入命名空间后可使用限定名称调用元素
$article_comment = new Article\Comment();
//为命名空间使用别名
use Blog\Article as Arte;
//使用别名代替空间名
$article_comment = new Arte\Comment();
//导入一个类
use Blog\Article\Comment;
//导入类后可使用非限定名称调用元素
$article_comment = new Comment();
//为类使用别名
use Blog\Article\Comment as Comt;
//使用别名代替空间名
$article_comment = new Comt();
?>
php内置的类,不隶属于任何命名空间如果你需要在命名空间中使用须有 \ 声明
new \DateTime();
支持动态调用静态方法
class Test{
public static function testgo()
{
echo "gogo!";
}
}
$class = 'Test';
$action = 'testgo';
$class::$action(); //输出 "gogo!"
在类外也可使用const来定义常量
//并且新增了一种常量定义方式
const CONSTANT = 'Hello World';
新增Nowdoc语法
支持通过Heredoc来初始化静态变量、类成员和类常量。
<?php
// 静态变量
function foo()
{
static $bar = <<<LABEL
Nothing in here...
LABEL;
}
// 类成员、常量
class foo
{
const BAR = <<<FOOBAR
Constant example
FOOBAR;
public $baz = <<<FOOBAR
Property example
FOOBAR;
}
PHP5.4
数组简写形式
<?php
//原来的数组写法
$arr = array("key" => "value", "key2" => "value2");
// 简写形式
$arr = ["key" => "value", "key2" => "value2"];
?>
Traits
所谓Traits就是“构件”,是用来替代继承的一种机制。Trait和类相似,但不能被实例化PHP中无法进行多重继承,但一个类可以包含多个Traits.
<?php
trait SayWorld
{
public $var = 'test';
public function sayHello()
{
echo 'World!';
}
}
class MyHelloWorld
{
// 将SayWorld中的成员包含进来
use SayWorld;
}
$xx = new MyHelloWorld();
// sayHello()函数是来自SayWorld构件的 $xx->var
$xx->sayHello();
Traits还有很多神奇的功能,比如包含多个Traits, 解决冲突,修改访问权限,为函数设置别名等等。
<?php
class A
{
public function callFuncTest() {
print $this->funcTest();
}
public function funcTest() {
return "this is A::funcTest().<br/>";
}
}
$func = "funcTest";
echo A::{$func}();
echo (new A)->funcTest();
新增在实例化时访问类成员的特征:
(new MyClass)->xx();
新增支持对函数返回数组的成员访问解析
print [1, 2, 3][0];
让json更懂中文
<?php
$arr = array(
'name' => '哈哈'
);
echo json_encode($arr, JSON_UNESCAPED_UNICODE);
新增array_column()函数
<?php
$arr = array(
array('name' => 'test1', 'age' => 22),
array('name' => 'test2', 'age' => 23),
array('name' => 'test3', 'age' => 24),
);
//取出name列
print_r(array_column($arr, 'name'));
//取出name列,并用age作键
print_r(array_column($arr, 'name', 'age'));
PHP5.5
yield
<?php
function generators()
{
for ($i = 1; $i <= 10; $i += 1) {
yield $i;
}
}
foreach (generators() as $v) {
echo $v;
}
可以用 list() 在 foreach 中解析嵌套的数组:
$array = [
[1, 2, 3],
[4, 5, 6],
];
foreach ($array as list($a, $b, $c))
echo "{$a} {$b} {$c}\n";
类名通过::class可以获取
<?php
namespace Name\Space;
class ClassName {}
echo ClassName::class;
echo "\n";
?>
finally关键字
这个和java中的finally一样,经典的try ... catch ... finally 三段式异常处理
empty() 支持自定义函数了
<?php
function foo(){
return false;
}
if(empty(foo())){
echo 11;
} else {
echo 12;
}
非变量array和string也能支持下标获取了
<?php
echo [1, 2, 3][0];
echo "foobar"[2];
PHP5.6
可以使用表达式定义常量
<?php
const ONE = 1 * 1;
class A {
const TWO = 1 * 2;
const THREE = self::TWO + 1;
public function test($i = self::THREE + ONE) {
echo $i;
}
}
(new A())->test();
使用...定义变长函数参数
<?php
function total(...$nums) {
$total = 0;
foreach($nums as $num) {
$total += $num;
}
return $total;
}
echo total(1, 2, 3, 4, 5);
$arr = [3, 4, 5, 6];
echo total(...$arr);
使用**进行幂运算
<?php
echo 2 ** 4;
$a = 2;
$a **= 4;
echo $a;
use function和use const
<?php
namespace A {
const PI = 3.14;
function test() {
echo 'test';
}
}
namespace B {
use function \A\test;
use const \A\PI;
echo PI;
test();
}
PHP7
三元运算符
(expr1) ? :(expr3) 这个是php5.3开始才有的功能在 expr1 求值为 TRUE 时返回 expr1,否则返回 expr3。
(expr1) ?? (expr3) 这是PHP7开始才有的功能会判断变量(isset(expr1))是否存在来赋值,TRUE 时返回 expr1,否则返回 expr3。
函数返回值类型声明
<?php
declare(strict_types=1);
function add(int $num1, int $num2) : int {
return $num1 + $num2;
}
echo add(2, 3);
//在严格模式下,下面这行会报错
echo add(2.0, 3.0);
通过define()定义常量数组
<?php
define('ARR', ['a', 'b', 'c']);
echo ARR[2];
匿名类
<?php
(new class {
public function test() {
echo 'test';
}
})->test();
新增操作符“<=>”
语法:$c = $a <=> $b
如果$a > $b, $c 的值为1
如果$a == $b, $c 的值为0
如果$a < $b, $c 的值为-1
use 组合声明
use PHPGoodTaste\Utils\{
Util,
Form,
Form\Validation,
Form\Binding
};
PHP7.1
list支持键名
$data = [
["id" => 1, "name" => 'Tom'],
["id" => 2, "name" => 'Fred'],
];
// list() style
list("id" => $id1, "name" => $name1) = $data[0];
var_dump($id1);//1
一次捕捉多种类型的异常 / 错误
try {
throw new LengthException("LengthException");
// throw new DivisionByZeroError("DivisionByZeroError");
// throw new Exception("Exception");
} catch (\DivisionByZeroError | \LengthException $e) {
echo "出错消息 --- ", $e->getMessage(), PHP_EOL;
} catch (\Exception $e) {
echo "出错消息 --- ", $e->getMessage(), PHP_EOL;
} finally {
// ...
}
可见性修饰符的变化
class YourClass
{
const THE_OLD_STYLE_CONST = "One";
public const THE_PUBLIC_CONST = "Two";
private const THE_PRIVATE_CONST = "Three";
protected const THE_PROTECTED_CONST = "Four";
}
可空类型
$fn = function (?int $in) : ?int {
return $in ?? "NULL";
};
$fn(null);
$fn(5);
多种返回类型
class Number {
private int|float $number;
public function setNumber(int|float $number): void {
$this->number = $number;
}
public function getNumber(): int|float {
return $this->number;
}
}
Void 返回类型
function first(): void {
// ...
}
PHP7.2
增加新的类型object
function test(object $obj) : object
{
return new SplQueue();
}
test(new StdClass());
允许重写抽象方法
<?php
abstract class A
{
abstract function test(string $s);
}
abstract class B extends A
{
// overridden - still maintaining contravariance for parameters and covariance for return
abstract function test($s) : int;
}
\