33 votes

Comment fonctionne une extension de classe ou une interface?

Avons rencontré ce de nombreuses fois et ne sais pas pourquoi ça m'a fait curieux. Certaines classes de travail avant qu'ils ne soient déclarés et d'autres n'ont pas;

Exemple 1

$test = new TestClass(); // top of class
class TestClass {
    function __construct() {
        var_dump(__METHOD__);
    }
}

Sortie

 string 'TestClass::__construct' (length=22)

Exemple 2

Lorsqu'une classe étend une autre classe ou implémente une interface

$test = new TestClass(); // top of class
class TestClass implements JsonSerializable {

    function __construct() {
        var_dump(__METHOD__);
    }

    public function jsonSerialize() {
        return json_encode(rand(1, 10));
    }
}

Sortie

Fatal error: Class 'TestClass' not found 

Exemple 3

Essayons la même classe au-dessus, mais changer la position

class TestClass implements JsonSerializable {

    function __construct() {
        var_dump(__METHOD__);
    }

    public function jsonSerialize() {
        return json_encode(rand(1, 10));
    }
}

$test = new TestClass(); // move this from top to bottom 

Sortie

 string 'TestClass::__construct' (length=22)

Exemple 4 ( j'ai testé aussi avec class_exists )

var_dump(class_exists("TestClass")); //true
class TestClass {

    function __construct() {
        var_dump(__METHOD__);
    }

    public function jsonSerialize() {
        return null;
    }
}

var_dump(class_exists("TestClass")); //true

dès qu'il implémente JsonSerializable ( Ou autre)

var_dump(class_exists("TestClass")); //false
class TestClass implements JsonSerializable {

    function __construct() {
        var_dump(__METHOD__);
    }

    public function jsonSerialize() {
        return null;
    }
}

var_dump(class_exists("TestClass")); //true

Aussi Vérifié Les Opcodes without JsonSerializable

line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   3     0  >   SEND_VAL                                                 'TestClass'
         1      DO_FCALL                                      1  $0      'class_exists'
         2      SEND_VAR_NO_REF                               6          $0
         3      DO_FCALL                                      1          'var_dump'
   4     4      NOP                                                      
  14     5    > RETURN                                                   1

Aussi Vérifié Les Opcodes with JsonSerializable

line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   3     0  >   SEND_VAL                                                 'TestClass'
         1      DO_FCALL                                      1  $0      'class_exists'
         2      SEND_VAR_NO_REF                               6          $0
         3      DO_FCALL                                      1          'var_dump'
   4     4      ZEND_DECLARE_CLASS                               $2      '%00testclass%2Fin%2FaDRGC0x7f563932f041', 'testclass'
         5      ZEND_ADD_INTERFACE                                       $2, 'JsonSerializable'
  13     6      ZEND_VERIFY_ABSTRACT_CLASS                               $2
  14     7    > RETURN                                                   1

Question

  • Je connais Example 3 a fonctionné parce que la classe a été déclaré avant son lancé, mais pourquoi serait - Example 1 de travail en premier lieu ?
  • Comment est-ce tout le processus de l'extension ou de l'interface de travail dans le PHP pour faire une bonne et l'autre non valide?
  • Ce qui se passe Exactement dans l'Exemple 4?
  • Opcodes était censé rendre les choses claires, mais juste le fait qu'il est plus complexe, car class_exists a été appelé avant TestClass , mais l'inverse est le cas.

18voto

Bubba Points 2826

Je ne peux pas trouver une écriture sur PHP définitions de classe; cependant, j'imagine que c'est exactement le même que les fonctions définies par l'Utilisateur qui vos expériences indiquent.

Les fonctions n'ont pas besoin d'être défini avant qu'ils sont référencés, sauf lorsqu'une fonction est conditionnelle définies comme indiqué dans les deux exemples ci-dessous. Lorsqu'une fonction est définie dans une manière conditionnelle; sa définition doit être traitée avant d'être appelé.

<?php

$makefoo = true;

/* We can't call foo() from here 
   since it doesn't exist yet,
   but we can call bar() */

bar();

if ($makefoo) {
  function foo()
  {
    echo "I don't exist until program execution reaches me.\n";
  }
}

/* Now we can safely call foo()
   since $makefoo evaluated to true */

if ($makefoo) foo();

function bar() 
{
  echo "I exist immediately upon program start.\n";
}

?>

Cela est vrai pour les classes:

  • Exemple 1 fonctionne parce que la classe n'est pas conditionnelle à autre chose.
  • Exemple 2 échoue parce que la classe est conditionnelle à l' JsonSerializable.
  • Exemple 3 fonctionne parce que la classe est correctement défini, avant d'être appelé.
  • Exemple 4 devient faux la première fois parce que la classe est conditionnelle, mais réussit plus tard parce que la classe a été chargé.

La classe est conditionnée par la mise en œuvre d'une interface ou d'étendre une autre classe à partir d'un autre fichier (require). Je suis l'appelant conditionnel, car la définition repose désormais sur une autre définition.

Imaginez le PHP interprète prend un premier coup d'oeil au code dans ce fichier. Il voit un non-conditionnelle de la classe et/ou de la fonction, de sorte qu'il va de l'avant et les charge en mémoire. Il voit un peu de conditionnel et saute au-dessus d'eux.

Ensuite, l'Interprète commence à analyser la page pour l'exécution. Dans l'exemple 4, il obtient à l' class_exists("TestClass") instruction, vérifie la mémoire, et de dit non, je n'ai pas que. Si ce n'est pas parce qu'il était conditionnelle. Il continue à exécuter les instructions, voir la condition de la classe et exécute les instructions pour charger la classe en mémoire.

Puis il descend jusqu'à la dernière class_exists("TestClass") et voit que la classe existe bel et bien dans la mémoire.

À la lecture de vos opérateurs, l' TestClass n'est pas appelée avant class_exist. Ce que vous voyez est le SEND_VAL qui est de l'envoi de la valeur TestClass de sorte qu'il est dans la mémoire de la ligne suivante, qui fait des appels DO_FCALL sur class_exists

Vous pouvez ensuite voir comment c'est de la manipulation de la définition de la classe elle-même:

  1. ZEND_DECLARE_CLASS - c'est le chargement de votre définition de classe
  2. ZEND_ADD_INTERFACE - ce extrait JsonSerializable et l'ajoute à votre définition de la classe
  3. ZEND_VERIFY_ABSTRACT_CLASS - ce vérifie tout ce qui est sain d'esprit.

C'est à ce deuxième morceau ZEND_ADD_INTERFACE semble empêcher le Moteur PHP de se contenter de chargement de la classe sur le pic initial à elle.

Si vous désirez une discussion plus détaillée de la façon dont l'Interprète PHP Compile et Exécute le code dans ces scénarios, je suggère de prendre un regarde @StasM répondre à cette question, il donne un excellent aperçu de cela plus en profondeur que cette réponse va.

Je pense que nous avons répondu à toutes vos questions.

Meilleure Pratique: Placez chacune de vos classes dans son propre fichier, puis autoload au besoin, comme @StasM unis, dans sa réponse, l'utilisation judicieuse de nom de fichier et le chargement automatique de la stratégie - par exemple, le PSR-0 ou quelque chose de similaire. Lorsque vous faites cela, vous n'avez plus à être concernés par la commande du Moteur de chargement eux, c'est juste des poignées pour vous automatiquement.

5voto

StasM Points 6232

Le principe de base est que, pour la classe pour être utilisée, elle doit être définie, c'est à dire connu pour le moteur. Cela ne peut jamais être changé - si vous avez besoin d'un objet d'une classe, le moteur PHP a besoin de savoir ce que la classe est.

Cependant, le moment où le moteur de gains, par exemple, les connaissances peuvent être différentes. Tout d'abord, longue de code PHP par le moteur se compose de deux processus distincts: - la compilation et l'exécution. Sur la phase de compilation, le moteur convertit le code PHP comme vous le savez à l'ensemble des opérateurs (que vous connaissez déjà), sur la deuxième étape, le moteur passe à travers les opcodes que le processeur serait aller à travers les instructions dans la mémoire, et les exécute.

L'un des opcodes est l'opcode qui définit une nouvelle classe, qui est habituellement inséré dans le même lieu où la définition de la classe est dans la source.

Toutefois, lorsque le compilateur rencontre la définition de la classe, il peut être en mesure d'entrer dans la classe dans la liste des classes que l'on connaît pour le moteur avant d'exécuter n'importe quel code. Cette opération est appelée "liaison anticipée". Cela peut se produire si le compilateur décide qu'il a déjà toutes les informations nécessaires à la création d'une définition de classe, et il n'y a aucune raison de reporter la création de la classe jusqu'à ce que le réel de l'exécution. Actuellement, le moteur ne présente que si la classe:

  1. n'a pas d'interfaces ou des traits attaché à elle
  2. n'est pas abstrait
  3. soit ne s'étend pas à toute les classes ou ne s'étend qu'à la classe qui est déjà connu dans le moteur
  4. est déclaré en tant que haut instruction (c'est à dire pas à l'intérieur d'état, fonction, etc.)

Ce comportement peut également être modifié par les options du compilateur, mais ceux-ci sont disponibles uniquement pour les extensions, comme APC ne devrait donc pas être un sujet de préoccupation important pour vous, sauf si vous allez à développer l'APC ou de l'extension similaire.

Cela signifie aussi ce serait OK:

 class B extends A {}
 class A { }

mais ce ne serait pas:

 class C extends B {}
 class B extends A {}
 class A { }

Depuis Un serait lié au plus tôt, et donc disponibles pour B de la définition de, mais B ne se définit pas uniquement à la ligne 2 et donc indisponible pour la ligne 1 de la définition de C.

Dans votre cas, lorsque votre classe en œuvre de l'interface, il n'était pas lié au plus tôt, et est ainsi devenu connu pour le moteur au point lors de la "classe" déclaration a été atteint. Quand il était simple de la classe sans interfaces, il a été lié au plus tôt, et qui est devenu connu pour le moteur dès que le fichier de compilation a été terminé (vous pouvez le voir, cette question avant la première instruction dans le fichier).

Afin de ne pas s'embêter avec tous ces curieux détails du moteur, je serais en faveur de la recommandation de la réponse précédente - si votre script est petit, il suffit de déclarer des classes avant l'utilisation. Si vous avez plus d'application, de définir les classes dans des fichiers individuels, et ont judicieux de nom de fichier et le chargement automatique de la stratégie - par exemple, le PSR-0 ou quelque chose de semblable, comme approprié dans votre cas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X