发布于 5年前

深入学习7个PHP预定义接口

我们倾向于在日常工作中编写业务模块,但很少实现这样的接口,但接口在框架中使用了很多。

1. Traversable接口

该接口不能由类直接实现。如果你编写一个普通的类Traversable直接实现接口,它将直接报告致命错误,提示使用IteratorIteratorAggregate实现。稍后将介绍这两个接口,通常用于确定是否可以通过使用遍历类foreach;

class Test implements Traversable
{
}

以上是一个错误的例子,将提示这样的错误:

致命错误:类测试必须实现接口Traversable作为其中一部分IteratorIteratorAggregate在第0行的未知中

正确的方法:

如果我们想确定一个类是否可以foreach用来遍历,我们只需要确定它是否是可遍历的实例。

class Test
{
}
$test = new Test;
var_dump($test instanceOf Traversable);

2. Iterator接口

Iterator接口的实际实现类似于指针的移动。当我们正在写一类,我们通过实施相应的五个方法来实现数据的反复运动:key()current()next()rewind()valid()。这是代码。

<?php
    class Test implements Iterator
    {
        private $key;
        private $val = [
            'one',
            'two',
            'three',
        ];

        public function key()
        {
            return $this->key;
        }

        public function current()
        {
            return $this->val[$this->key];
        }

        public function next()
        {
            ++$this->key;
        }

        public function rewind()
        {
            $this->key = 0;
        }

        public function valid()
        {
            return isset($this->val[$this->key]);
        }
    }

    $test = new Test;

    $test->rewind();

    while($test->valid()) {
        echo $test->key . ':' . $test->current() . PHP_EOL;
        $test->next();
    }

输出结果:

0: one
1: two
2: three

事实上,迭代的移动方法是:rewind()- > valid()- > key()- > current()- > next()- > valid()- > key()....-> valid();

现在让我们看一下Iterator接口,发现它实现了Traversable接口。让我们证明这一点:

var_dump($test instanceOf Traversable);

如果返回的结果为true,则类的对象是可遍历的。

foreach ($test as $key => $value){
   echo $test->key . ':' . $test->current() . PHP_EOL;
}

结果与while循环实现的模式相同。

3. IteratorAggregate接口

为原则IteratorAggregate,并Iterator是一样的。您只需要实现一个getIterator()方法来实现迭代,因为IteratorAggregate它实现了迭代器原则。这是代码。

<?php
    class Test implements IteratorAggregate
    {
        public $one = 1;
        public $two = 2;
        public $three = 3;

        public function __construct()
        {
            $this->four = 4;
        }

        public function getIterator()
        {
            return new AraayIterator($this);
        }
    }

    $test = (new Test())->getIterator();
    $test->rewind();
    while($test->valid()) {
        echo $test->key() . ' : '  .  $test->current() . PHP_EOL;
        $test->next();
    }

从上面的代码中,我们可以看到,我们传递的对象类的迭代器。通过while循环,我们必须通过调用来获取迭代器对象的方法,然后进行迭代,并直接输出而不去执行相关的方法,如。Test``getIterator()``key()

然后让我们打印结果:

$test = new Test;
var_dump($test instanceOf Traversable);

它输出bool true。接下来我们将使用foreach它直接实现它。

$test = new Test;
foreach($test as $key => $value) {
   echo $key . ' : ' . $value  .  PHP_EOL;
}

我们可以迭代对象,迭代数组怎么样?

 class Test implements IteratorAggregate
 {
    public $data;

    public function __construct()
    {
        $this->data = ['one' =>  1 , 'two' => 2];
    }

    public function getIterator()
    {
        return new AraayIterator($this->data);
    }
 }

实现它的方法是一样的。

4. ArrayAccess接口

这个['name']的用法很常见。既然$this是一个对象,那么如何使用数组进行访问?答案是ArrayAccess接口。具体代码如下:

<?php
    class Test implements ArrayAccess
    {
        public $container;

        public function __construct()
        {
            $this->container = [
                'one' => 1,
                'two' => 2,
                'three'  => 3,
            ];
        }

        public function offsetExists($offset)
        {
            return isset($this->container[$offset]);
        }

        public function offsetGet($offset)
        {
            return isset($this->container[$offset]) ? $this->container[$offset] : null;
        }

        public function offsetSet($offset, $value)
        {
            if (is_null($offset)) {
                $this->container[] = $value;
            } else {
                $this->container[$offset] = $value;
            }
        }

        public function offsetUnset($offset)
        {
            unset($this->container[$offset]);
        }
    }
   $test = new Test;
   var_dump(isset($test['one']));
   var_dump($test['two']);
   unset($test['two']);
   var_dump(isset($test['two']));
   $test['two'] = 22;
   var_dump($test['two']);
   $test[] = 4;
   var_dump($test);
   var_dump($test[0]);

当然,有一种经典的方法是将对象的属性作为数组访问。

class Test implements ArrayAccess
{
    public $name;

    public function __construct()
    {
        $this->name = 'gabe';
    }

    public function offsetExists($offset)
    {
        return isset($this->$offset);
    }

    public function offsetGet($offset)
    {
        return isset($this->$offset) ? $this->$offset : null;
    }

    public function offsetSet($offset, $value)
    {
        $this->$offset = $value;
    }

    public function offsetUnset($offset)
    {
        unset($this->$offset);
    }
}

$test = new Test;
var_dump(isset($test['name']));
var_dump($test['name']);
var_dump($test['age']);
$test[1] = '22';
var_dump($test);
unset($test['name']);
var_dump(isset($test['name']));
var_dump($test);
$test[] = 'hello world';
var_dump($test);

5. Serializable接口

通常情况下,如果有魔术方法sleep()wakeup()在类中定义的,它会调用的魔术方法sleep()执行时第一serialize()。我们通过返回一个数组来定义对象的哪些属性被序列化。同样,它也会wakeup()在调用方法时首先调用magic unserialize()方法。我们可以初始化魔术方法,例如为对象的属性赋值; 但如果类实现了Serialization接口,我们必须实现serialize()unserialize(),同时,无论是sleep()wakeup()将得到支持。具体代码如下:

<?php
    class Test
    {
        public $name;
        public $age;

        public function __construct()
        {
            $this->name = 'gabe';
            $this->age = 25;
        }

        public function __wakeup()
        {
            var_dump(__METHOD__);
            $this->age++;
        }

        public function __sleep()
        {
            var_dump(__METHOD__);
            return ['name'];
        }
    }

    $test = new Test;
    $a = serialize($test);
    var_dump($a);
    var_dump(unserialize($a));

   //You'll find that the magic methods have failed after implementing the Serialization interface.

   class Test implements Serializable
   {
    public $name;
    public $age;

    public function __construct()
    {
        $this->name = 'gabe';
        $this->age = 25;
    }

    public function __wakeup()
    {
        var_dump(__METHOD__);
        $this->age++;
    }

    public function __sleep()
    {
        var_dump(__METHOD__);
        return ['name'];
    }

    public function serialize()
    {
        return serialize($this->name);
    }

    public function unserialize($serialized)
    {
        $this->name = unserialize($serialized);
        $this->age = 1;
    }
}
$test = new Test;
$a = serialize($test);
var_dump($a);
var_dump(unserialize($a));

6. Closure

任何匿名函数实际上都返回一个Closure类的实例,并且类中主要有两个表示匿名函数的方法,bindTo()bind()。如果检查源代码,您可以发现这两种方法实现了相同的目标,尽管bind()是静态方法而bindTo()不是。具体代码如下:

<?php
    $closure = function () {
        return 'hello world';
    }

    var_dump($closure);
    var_dump($closure());

从上面的例子中,您可以看到第一个打印的是实例Closure,第二个打印的是匿名函数返回的"hello world"字符串。接下来让我们使用这两种方法,两种方法的目的是将匿名函数绑定到类。

bindTo()

<?php
namespace demo1;
    class Test {
        private $name = 'hello woeld';
    }

    $closure = function () {
        return $this->name;
    }

    // This is the wrong approach
    $func = $closure->bindTo(new Test);
    $func();

    // This is the right approach
    $func = $closure->bindTo(new Test, Test::class);
    $func();

namespace demo2;
    class Test
    {
        private statis $name = 'hello world';
    }

    $closure = function () {
        return self::$name;
    }
    $func = $closure->bindTo(null, Test::class);
    $func();

bind()

<?php
namespace demo1;
class Test
{
    private  $name = 'hello world';
}

$func = \Closure::bind(function() {
    return $this->name;
}, new Test, Test::class);

$func();

namespace demo2;
class Test
{
    private static  $name = 'hello world';
}

$func = \Closure::bind(function() {
    return self::$name;
}, null, Test::class);

$func()

7.Generator

Generator实现了Iterator,并且还生成了实例,但它不能被继承。由于Iterator实现了,如上所述,它具有与以下内容相同的功能Iteratorrewind- > valid- > current- > key- > next....语法Generator主要来自yield记录活动轨迹并返回实例的关键字Generator

优点Generator是当我们需要遍历大数据或读写大文件时,如果我们的内存不够,那么我们可以大大减少内存消耗。传统的遍历将返回所有数据并将它们存储到内存中,而yield只返回当前值。但是当我们使用yield时会有一个处理内存的过程,所以实际上这是一种交换空间的方法。

<?php
$start_time = microtime(true);
function xrange(int $num){
    for($i = 0; $i < $num; $i++) {
        yield $i;
    }
}
$generator = xrange(100000);
foreach ($generator as $key => $value) {
    echo $key . ': ' . $value . PHP_EOL;
}
echo 'memory: ' . memory_get_usage() . ' time: '. (microtime(true) - $start_time);
Output: memory: 388904 time: 0.12135100364685
<?php
$start_time = microtime(true);
function xrange(int $num){
    $arr = [];
    for($i = 0; $i < $num; $i++) {
        array_push($arr, $i);
    }
    return $arr;
}
$arr = xrange(100000);
foreach ($arr as $key => $value) {
    echo $key . ': ' . $value . PHP_EOL;
}
echo 'memory: ' . memory_get_usage() . ' time: '. (microtime(true) - $start_time);
Output: memory: 6680312 time: 0.10804104804993

译文地址: https://www.tutorialdocs.com/article/7-php-predefined-interfaces.html

相关文章

©2020 edoou.com   京ICP备16001874号-3