Compréhension approfondie des sept interfaces prédéfinies en PHP

藏色散人
Libérer: 2023-04-08 11:20:01
avant
1846 Les gens l'ont consulté

Compréhension approfondie des sept interfaces prédéfinies en PHP

Compréhension approfondie des interfaces prédéfinies

Scénario : Dans le travail normal, j'écris des modules métier, rarement Pour implémenter une telle interface, elle est beaucoup utilisée dans le framework.

1. Interface traversable

Cette interface ne peut pas être directement implémentée par une classe si vous écrivez directement une classe normale pour implémenter la traversée. L'interface signalera directement une erreur fatale, incitant à implémenter Iterator (interface itérateur) ou IteratorAggregate (interface itérateur d'agrégation). Ces deux interfaces seront présentées plus tard, dans des circonstances normales, nous ne les utilisons que pour juger la classe. utiliser foreach pour le parcours ?

class Test implements Traversable
    {
    }
    上面这个是错误示范,该代码会提示这样的错误:
    Fatal error: Class Test must implement interface Traversable as part of either Iterator or 
    IteratorAggregate in Unknown on line 0
Copier après la connexion

La signification générale de ce qui précède est que si vous souhaitez implémenter cette interface, vous devez l'implémenter avec Iterator ou IteratorAggregate

L'approche correcte :

Lorsque nous voulons déterminer si une classe peut être parcourue à l'aide de foreach, il nous suffit de déterminer s'il s'agit d'une instance de traversable

     class Test
     {
     }
     $test = new Test;
     var_dump($test instanceOf Traversable);
Copier après la connexion

2. 🎜>Itération Le principe d'implémentation actuel de l'interface du périphérique est similaire au mouvement des pointeurs. Lorsque nous écrivons une classe, nous pouvons implémenter les cinq méthodes correspondantes : key(), current(), next(), rewind() et. valid(). Pour réaliser le mouvement itératif des données, regardez le code suivant

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

        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 . &#39;:&#39; . $test->current() . PHP_EOL;
        $test->next();
    }
Copier après la connexion

## Ce résultat de sortie :

        0: one
        1: two
        2: three
Copier après la connexion

Nous savons après avoir vu ce principe valid()-> key() -> current() -> next() -> valid()-> valid();

D'accord, après avoir compris ce qui précède , nous ouvrons l'interface Iterator et constatons qu'elle implémente l'interface Traversable. Ensuite, prouvons-le :

var_dump($test instanceOf Traversable);
Copier après la connexion

Le résultat renvoie vrai, prouvant que l'objet de cette classe est Il peut être parcouru.

 foreach ($test as $key => $value){
         echo $test->key . &#39;:&#39; . $test->current() . PHP_EOL;
  }
Copier après la connexion

Le résultat est le même que le modèle implémenté par la boucle while.

3. Interface IteratorAggregate (itérateur d'agrégation)

Les principes des itérateurs d'agrégation et des itérateurs sont les mêmes, mais l'itérateur agrégé a déjà implémenté le principe de l'itérateur. Il vous suffit d'implémenter une méthode getIterator() pour implémenter l'itération. Pour plus de détails, voir le code suivant

<?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() . &#39; : &#39;  .  $test->current() . PHP_EOL;
        $test->next();
    }

    从上面的代码,我们可以看到我们将Test类的对象传进去当做迭代器,通过while循环的话,我们必须通过调用getIterator()方法获取到迭代器对象,然后直接进行迭代输出,而不需要去实现相关的key()等方法。
    当然这个时候,我们肯定想知道是否可以直接从foreach进行迭代循环出去呢?那么我们来打印一下结果

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

    结果是输出bool true,所以我们接下来是直接用foreach来实现一下。

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

 接下来,我们看到是对对象进行迭代,这个时候我们是否可以数组进行迭代呢?

 class Test implements IteratorAggregate
 {
    public $data;

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

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

 同理实现的方式跟对对象进行迭代是一样的。
Copier après la connexion

De nombreux PHPers sont avancés lorsqu'ils sont là. Il y a toujours des problèmes et des goulots d'étranglement. J'écris trop de code métier et je n'ai aucun sens de l'orientation. J'ai compilé quelques informations à ce sujet, y compris, mais sans s'y limiter : l'architecture distribuée, la haute évolutivité. , et hautes performances, haute concurrence, réglage des performances du serveur, TP6, laravel, YII2, Redis, Swoole, Swoft, Kafka, optimisation Mysql, scripts shell, Docker, microservices, Nginx et autres points de connaissances. vous pouvez le partager gratuitement. Tout le monde, si vous avez besoin de rejoindre le groupe (cliquez →) 677079770

4. Interface ArrayAccess (accès au tableau)

Normalement, nous verra ceci ['name '] Une telle utilisation, mais nous savons que $this est un objet, comment y accéder en utilisant la méthode array ? La réponse est d'implémenter l'interface d'accès au groupe de données ArrayAccess. Le code spécifique est le suivant

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

        public function __construct()
        {
            $this->container = [
                &#39;one&#39; => 1,
                &#39;two&#39; => 2,
                &#39;three&#39;  => 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[&#39;one&#39;]));
   var_dump($test[&#39;two&#39;]);
   unset($test[&#39;two&#39;]);
   var_dump(isset($test[&#39;two&#39;]));
   $test[&#39;two&#39;] = 22;
   var_dump($test[&#39;two&#39;]);
   $test[] = 4;
   var_dump($test);
   var_dump($test[0]);

   当然我们也有经典的一个做法就是把对象的属性当做数组来访问

   class Test implements ArrayAccess
   {
        public $name;

        public function __construct()
        {
            $this->name = &#39;gabe&#39;;  
        }

        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[&#39;name&#39;]));
  var_dump($test[&#39;name&#39;]);
  var_dump($test[&#39;age&#39;]);
  $test[1] = &#39;22&#39;;
  var_dump($test);
  unset($test[&#39;name&#39;]);
  var_dump(isset($test[&#39;name&#39;]));
  var_dump($test);
  $test[] = &#39;hello world&#39;;
  var_dump($test);
Copier après la connexion

Interface sérialisable (sérialisation)

. Normalement, si les méthodes magiques, sleep() et wakeup() sont définies dans notre classe, lorsque nous sérialisons (), nous appellerons d'abord la méthode magique de sleep(). Nous renvoyons un tableau pour définir quelles sont les propriétés de l'objet. sérialisé. De même, lorsque nous appelons la méthode unserialize (), nous appellerons également d'abord la méthode magique wakeup (). Nous pouvons initialiser, comme attribuer des valeurs aux propriétés d'un objet, mais si cette classe implémente l'interface de sérialisation ; , nous devons donc implémenter la méthode serialize() et la méthode unserialize() en même temps, les deux méthodes magiques sleep() et wakeup() ne seront plus prises en charge en même temps. Le code spécifique est le suivant : 🎜>

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

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

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

        public function __sleep()
        {        
            var_dump(__METHOD__);
            return [&#39;name&#39;];    
        }
    }

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

    //实现序列化接口,发现魔术方法失效了

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

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

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

    public function __sleep()
    {
        var_dump(__METHOD__);
        return [&#39;name&#39;];
    }

    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));
Copier après la connexion

6. La classe de fermeture

est une classe utilisée pour représenter des fonctions anonymes. Toutes les fonctions anonymes renvoient en fait une instance de la classe de fermeture Closure. Cette classe Il existe deux méthodes principales, bindTo() et bind() En regardant le code source, vous pouvez constater que les deux méthodes ont le même objectif, mais bind() est une méthode statique. L'utilisation spécifique est la suivante. ;

<?php
    $closure = function () {
        return &#39;hello world&#39;;
    }

    var_dump($closure);
    var_dump($closure());
Copier après la connexion

Grâce à l'exemple ci-dessus, vous pouvez voir que le premier imprimé est une instance de Closure, et le second imprime la chaîne hello world renvoyée par la fonction anonyme ; l'étape suivante consiste à utiliser la méthode de cette classe anonyme, le but de ces deux méthodes est de lier des fonctions anonymes à une classe

bindTo()

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

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

    $func = $closure->bindTo(new Test);
    $func();
    // 这个是可以访问不到私有属性的,会报出无法访问私有属性
    // 下面这个是正确的做法
    $func = $closure->bindTo(new Test, Test::class);
    $func();

namespace demo2;
    class Test
    {
        private statis $name = &#39;hello world&#39;;
    }

    $closure = function () {
        return self::$name;
    }
    $func = $closure->bindTo(null, Test::class);
    $func();
Copier après la connexion

bind ; ()

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

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

$func();

namespace demo2;
class Test 
{
    private static  $name = &#39;hello world&#39;;
}

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

$func()
Copier après la connexion

7. Générateur

Generator 实现了 Iterator,但是他无法被继承,同时也生成实例。既然实现了 Iterator,所以正如上文所介绍,他也就有了和 Iterator 相同的功能:rewind->valid->current->key->next...,Generator 的语法主要来自于关键字 yield。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 . &#39;: &#39; . $value . PHP_EOL;
}
echo &#39;memory: &#39; . memory_get_usage() . &#39; time: &#39;. (microtime(true) - $start_time);
Copier après la connexion

输出: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 . &#39;: &#39; . $value . PHP_EOL;
}
echo &#39;memory: &#39; . memory_get_usage() . &#39; time: &#39;. (microtime(true) - $start_time);
Copier après la connexion

输出:

memory: 6680312 time: 0.10804104804993
Copier après la connexion

更多相关php知识,请访问php教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
php
source:cnblogs.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal