Après avoir activé les avertissements stricts dans PHP 5.2), j’ai vu une série d’avertissements de normes strictes provenant d’un projet initialement écrit sans avertissements stricts:
Normes strictes : Fonction statique Programme :: getSelectSQL () ne doit pas être abstrait dans Program.class.inc
La fonction en question appartient à un programme abstrait de classe parent et est déclarée abstraite car elle doit être implémentée dans ses classes enfants, telles que TVProgram.
J'ai trouvé des références à ce changement ici :
Supprimé des fonctions de classe statiques abstraites. En raison d'un oubli, PHP 5.0.x et 5.1.x ont autorisé des fonctions statiques abstraites dans les classes. À partir de PHP 5.2.x, seules les interfaces peuvent en disposer .
Ma question est la suivante: quelqu'un peut-il expliquer clairement pourquoi il ne devrait pas y avoir de fonction statique abstraite en PHP?
les méthodes statiques appartiennent à la classe qui les a déclarées. Lors de l'extension de la classe, vous pouvez créer une méthode statique du même nom, mais vous n'implémentez pas de méthode abstraite statique.
Il en va de même pour l'extension de n'importe quelle classe avec des méthodes statiques. Si vous étendez cette classe et créez une méthode statique de la même signature, vous ne remplacez pas réellement la méthode statique de la superclasse.
[~ # ~] éditer [~ # ~] (16 septembre 2009)
Mise à jour à ce sujet. En cours d’exécution PHP 5.3, je vois que l’abstrait statique est de retour, pour le meilleur ou pour le pire. (Voir http://php.net/lsb pour plus d’informations)
[~ # ~] correction [~ # ~] (par philfreo)abstract static
n'est toujours pas autorisé dans PHP 5.3, LSB est lié mais différent.
C'est une longue et triste histoire.
Lorsque PHP 5.2 a introduit cet avertissement pour la première fois, liaisons statiques tardives n'étaient pas encore dans le langage. Si vous n'êtes pas familiarisé avec les dernières liaisons statiques, notez que le code comme cela ne fonctionne pas comme on pourrait s'y attendre:
<?php
abstract class ParentClass {
static function foo() {
echo "I'm gonna do bar()";
self::bar();
}
abstract static function bar();
}
class ChildClass extends ParentClass {
static function bar() {
echo "Hello, World!";
}
}
ChildClass::foo();
Laissant de côté l’avertissement de mode strict, le code ci-dessus ne fonctionne pas. L’appel self::bar()
dans foo()
fait explicitement référence à la méthode bar()
de ParentClass
, même lorsque foo()
est appelée en tant que méthode de ChildClass
. Si vous essayez d'exécuter ce code avec le mode strict désactivé, vous verrez " Erreur PHP irrécupérable: Impossible d'appeler la méthode abstraite ParentClass :: bar () " .
Compte tenu de cela, les méthodes statiques abstraites dans PHP 5.2 étaient inutiles. Le point entier d'utilisation de l'utilisation d'une méthode abstraite est que peut écrire du code qui appelle la méthode sans savoir quelle implémentation il appellera - et ensuite fournir différentes implémentations sur différentes classes enfants. Mais depuis PHP 5.2 n'offre aucun moyen propre d'écrire une méthode d'un classe parent qui appelle une méthode statique de la classe enfant sur laquelle elle est appelée, cette utilisation de méthodes statiques abstraites est impossible. Par conséquent, aucune utilisation de abstract static
dans PHP 5.2 C'est un mauvais code, probablement inspiré par un malentendu sur le fonctionnement du mot clé self
. Il était parfaitement raisonnable de lancer un avertissement à ce sujet.
Mais alors PHP 5.3 a été ajouté pour pouvoir faire référence à la classe sur laquelle une méthode a été appelée via le mot clé static
(contrairement au mot clé self
, qui fait toujours référence à la classe dans laquelle la méthode a été définie ). Si vous remplacez self::bar()
par static::bar()
dans mon exemple ci-dessus, cela fonctionne très bien dans PHP 5.3 et plus. Vous pouvez en savoir plus sur self
vs static
à New self vs. new static .
Avec le mot-clé static ajouté, l'argument clair pour que abstract static
Jette un avertissement avait disparu. L'objectif principal des liaisons statiques tardives était de permettre aux méthodes définies dans une classe parent d'appeler des méthodes statiques qui seraient définies dans des classes enfants; autoriser des méthodes statiques abstraites semble raisonnable et cohérent compte tenu de l'existence de liaisons statiques tardives.
Vous pouvez toujours, je suppose, plaider en faveur du maintien de l'avertissement. Par exemple, vous pourriez faire valoir que depuis PHP vous permet d'appeler des méthodes statiques de classes abstraites, dans mon exemple ci-dessus (même après l'avoir corrigé en remplaçant self
par static
) vous exposez une méthode publique ParentClass::foo()
qui est cassée et que vous ne voulez pas vraiment exposer. -static, c'est-à-dire que toutes les méthodes d'instance et les enfants de ParentClass
sont tous singletons ou quelque chose du genre - résoudrait ce problème, puisque ParentClass
, étant abstrait, ne peut être instanciée et ses méthodes d'instance ne peuvent donc pas être appelées. Je pense que cet argument est faible (car je pense que l'exposition de ParentClass::foo()
n'est pas grave et que l'utilisation de singletons à la place de classes statiques est souvent inutilement verbeuse et laide), mais vous pouvez raisonnablement être en désaccord - c'est un appel quelque peu subjectif.
Donc, basé sur cet argument, les PHP devs ont gardé l’avertissement dans la langue, non?
Euh, pas exactement .
Le rapport de bogue PHP 53081, lié ci-dessus, demandait que l'avertissement soit supprimé, car l'ajout de la construction static::foo()
avait rendu les méthodes abstraites statiques raisonnables et utiles. Rasmus Lerdorf (créateur de PHP) commence par qualifier la demande de fictive et passe par une longue chaîne de mauvais raisonnements pour tenter de justifier l'avertissement. Puis, enfin, cet échange a lieu:
Giorgio
je sais, mais:
abstract class cA { //static function A(){self::B();} error, undefined method static function A(){static::B();} // good abstract static function B(); } class cB extends cA { static function B(){echo "ok";} } cB::A();
Rasmus
Bon, c'est exactement comme ça que ça devrait fonctionner.
Giorgio
mais ce n'est pas permis :(
Rasmus
Qu'est-ce qui n'est pas permis?
abstract class cA { static function A(){static::B();} abstract static function B(); } class cB extends cA { static function B(){echo "ok";} } cB::A();
Cela fonctionne bien. Vous ne pouvez évidemment pas appeler self :: B (), mais static :: B () va bien.
L'affirmation de Rasmus que le code dans son exemple "fonctionne bien" est fausse; comme vous le savez, il lance un avertissement de mode strict. Je suppose qu'il testait sans que le mode strict ne soit activé. Quoi qu'il en soit, un Rasmus confus a laissé la demande fermée à tort comme étant "fausse".
Et c'est pourquoi l'avertissement est toujours dans la langue. Cette explication n’est peut-être pas tout à fait satisfaisante: vous êtes probablement venu ici dans l’espoir que l’avertissement était raisonnablement justifié. Malheureusement, dans le monde réel, les choix naissent parfois d'erreurs banales et d'un mauvais raisonnement plutôt que d'une prise de décision rationnelle. C'est tout simplement l'un de ces moments.
Heureusement, l’estimable Nikita Popov a supprimé l’avertissement du langage dans PHP 7 dans le cadre de PHP RFC: Reclassifier les avis E_STRICT) . PHP 7 est disponible, nous pouvons tous utiliser abstract static
Avec joie sans recevoir cet avertissement stupide.
Il existe un moyen très simple de contourner ce problème, ce qui est logique du point de vue de la conception. Comme Jonathan a écrit:
Il en va de même pour l'extension de n'importe quelle classe avec des méthodes statiques. Si vous étendez cette classe et créez une méthode statique de la même signature, vous ne remplacez pas réellement la méthode statique de la superclasse.
Donc, comme un travail autour de vous pourrait faire ceci:
<?php
abstract class MyFoo implements iMyFoo {
public static final function factory($type, $someData) {
// don't forget checking and do whatever else you would
// like to do inside a factory method
$class = get_called_class()."_".$type;
$inst = $class::getInstance($someData);
return $inst;
}
}
interface iMyFoo {
static function factory($type, $someData);
static function getInstance();
function getSomeData();
}
?>
Et maintenant, vous indiquez que toute classe qui sous-classe MyFoo implémente une méthode statique getInstance et une méthode getSomeData publique. Et si vous ne sous-classez pas MyFoo, vous pouvez toujours implémenter iMyFoo pour créer une classe avec des fonctionnalités similaires.
Je sais que c'est vieux mais ....
Pourquoi ne pas simplement lancer une exception à la méthode statique de cette classe parente, de cette façon si vous ne la remplacez pas, l'exception est générée.
Je dirais qu'une interface/classe abstraite pourrait être vue comme un contrat entre programmeurs. Il traite plus de la façon dont les choses devraient ressembler/se comporter et ne pas implémenter les fonctionnalités réelles. Comme vu dans php5.0 et 5.1.x, ce n'est pas une loi naturelle qui empêche les développeurs php de le faire, mais l'envie d'aller avec d'autres OO modèles de conception dans d'autres langues. En gros, ces les idées tentent d’empêcher les comportements inattendus, s’il est déjà familiarisé avec d’autres langues.
Je ne vois aucune raison d'interdire les fonctions abstraites statiques. Le meilleur argument selon lequel il n'y a aucune raison de les interdire est qu'ils sont autorisés en Java. Les questions sont les suivantes: - Les solutions techniques sont-elles réalisables? - Oui, puisque les existaient dans PHP 5.2 et qu'ils existent en Java. Alors, on peut le faire. DEVRAIT-on le faire? - Ont-ils un sens? Oui. Il est logique d'implémenter une partie d'une classe et laisser une autre partie d'une classe à l'utilisateur.Ceci est logique dans les fonctions non statiques, pourquoi ne devrait-il pas en avoir pour les fonctions statiques? Une utilisation des fonctions statiques est une classe où il ne doit pas y avoir plus d'une instance (singletons). Par exemple, un moteur de chiffrement. Il n’est pas nécessaire qu’il existe dans plusieurs cas et il ya des raisons de l’empêcher - par exemple, vous ne devez protéger qu’une partie de la mémoire contre les intrus. Il est donc parfaitement logique de le mettre en œuvre. une partie du moteur et laissez l’algorithme de chiffrement à l’utilisateur. Ce n’est qu’un exemple. Si vous êtes habitué à utiliser des fonctions statiques, vous en trouverez beaucoup plus.
En php 5.4+ utiliser le trait:
trait StaticExample {
public static function instance () {
return new self;
}
}
et dans votre classe au début:
use StaticExample;