【PHP5.3的新增、改进】匿名函数
也叫闭包(Closures), 经常被用来临时性地创建一个无名函数,用于回调函数等用途。
- $func = function ($arg) {
- print $arg;
- };
- $func("Hello World");
匿名函数还可以用 use 关键字来捕捉外部变量:
- function arrayPlus($array, $num)
- {
- array_walk($array, function (&$v) use ($num) {
- $v += $num;
- });
- }
上面的代码定义了一个 arrayPlus() 函数(这不是匿名函数), 它会将一个数组($array)中的每一项,加上一个指定的数字($num).在 arrayPlus() 的实现中,我们使用了 array_walk() 函数,它会为一个数组的每一项执行一个回调函数,即我们定义的匿名函数。在匿名函数的参数列表后,我们用 use 关键字将匿名函数外的 $num 捕捉到了函数内,以便知道到底应该加上多少。
后期静态绑定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;
- }
- public static function status()
- {
- static::getStatus();
- }
- protected static function getStatus()
- {
- echo "Person is alive";
- }
- }
- class Descendant extends ParentBase
- {
- static $property = 'Descendant Value';
- }
- //output: Descendant Value
- echo Descendant::render();
PHP的面向对象体系中,提供了若干“魔术方法”,用于实现类似其他语言中的“重载”,如在访问不存在的属性、方法时触发某个魔术方法。
__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();
- ?>
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;
- }
- $xxoo = new MyHelloWorld();
- // sayHello()函数是来自SayWorld构件的 $xxoo->var
- $xxoo->sayHello();
Traits还有很多神奇的功能,比如包含多个Traits, 解决冲突,修改访问权限,为函数设置别名等等。
新增在实例化时访问类成员的特征:
- (new MyClass)->xxoo();
新增支持对函数返回数组的成员访问解析
- print [1, 2, 3][0];
PHP5.5
yield的一个功能就是能有效的降低迭代的内存开销,yield关键字用于当函数需要返回一个迭代器的时候, 逐个返回值.也就是说, 每当产生一个数组元素, 就通过yield关键字返回成一个, 并且函数执行暂停, 当返回的迭代器的next方法被调用的时候, 会恢复刚才函数的执行, 从上一次被yield暂停的位置开始继续执行, 到下一次遇到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";
aaa