Tous les plugins doivent-ils être encapsulés dans une classe?

28

Lors du développement d'un plugin, les fonctions doivent-elles être regroupées dans une classe pour éviter les conflits d'espace de noms?

L'utilisation de classes crée-t-elle des frais généraux de performance pour PHP?

S'il y a un impact sur les performances, les noms de fonction devraient-ils simplement être préfixés à la place?

Jamie
la source
8
Probablement plus de question PHP qu'une question WordPress, voyez si cette question Stackoverflow répond adéquatement à votre question.
t31os

Réponses:

24

Lors du développement d'un plugin, les fonctions doivent-elles être regroupées dans une classe pour éviter les conflits d'espace de noms?

Oui, mais ce n'est qu'un des arguments mineurs. En fait, ce n'est pas la "vraie" nature d'une classe dans OOAD .

L'utilisation de classes crée-t-elle des frais généraux de performance pour PHP?

Non, pas notamment. Une mauvaise conception et / ou un mauvais code écrit ou une optimisation prématurée créent beaucoup plus de problèmes de performances que les fonctionnalités de langage réelles.

S'il y a un impact sur les performances, les noms de fonction devraient-ils simplement être préfixés à la place?

Comme écrit, il n'y a aucun impact sur les performances. Un mauvais code écrit sera plus un problème de performances qu'un bon code écrit qui a plus de lignes de code mais ne vous oblige pas à faire de mauvaises choses.


Conclusion:

Vous pouvez utiliser différemment les classes pour les plugins. Vous pouvez simplement les utiliser pour avoir une sorte d'espace de noms et les utiliser "juste" pour les fonctions globales. La forme la plus directe en est les fonctions de classe statiques, l'exemple de code suivant montre les deux, d'abord les fonctions globales, puis les fonctions de classe statique globales:

/* global function */
function myplug_hook()
{
}

add_filter('the_hook', 'myplug_hook');


/* global static function */
class myplug
{
    public static function hook()
    {
    }
}

add_filter('the_hook', 'myplug::hook');

Ceci est juste un petit exemple montrant que vous devez taper plus pour le crochet unique. De plus, il montre comment fonctionne l'espace de noms: vous pouvez plus facilement remplacer le nom d'une seule classe pour renommer toutes les fonctions statiques, puis rechercher et remplacer myplug::ce qui pourrait être plus difficile à myplug_cause des faux positifs. Mais au final, il n'y a pas beaucoup de différence.

Le point clé est: les fonctions de classe statiques Docs ne sont pas vraiment beaucoup plus que les fonctions globales Docs .

Et cet exemple montre aussi: l'espace de noms est bien, mais avec worpdress, l'espace de noms s'arrête avec l'utilisation de crochets: la fonction de rappel est codée en dur, d'où l'avantage de l'espace de noms en utilisant la classe (un endroit pour le nom de base, le nom de classe) ne le fait pas aide lorsque vous intervenez votre code avec wordpress pour les noms de hook.

Le véritable avantage commence par l'utilisation d'instances de classe réelles et de fonctions non statiques. Cela a l'avantage que vous pouvez commencer à utiliser les principes OO et vous pouvez rationaliser votre code. Les fonctions de classe statiques sont plus un problème qu'une solution en fait.

Ensuite, c'est plus que du sucre syntaxique.

Le point clé est: faites quelque chose qui vous aide à écrire le code que vous pouvez facilement gérer et maintenir. Ne surévaluez pas les performances, c'est une erreur courante. Plus important encore, vous écrivez du code facile à lire et à comprendre, qui fait exactement ce dont vous avez besoin. Peut - être cette question et la réponse est utile pour une image plus grande dans ce contexte: Multiple personnalisé Metabox Aide .

Une approche courante que j'ai même avec des plugins plus petits utilise une fonction d'assistance statique pour instancier le plugin et le reste réside alors dans l'instance du plugin. Cela aide à encapsuler la logique principale du plugin et il bénéficie de l'espace de noms avec les hooks ainsi que les membres privés peuvent être réutilisés entre les hooks, ce qui n'est pas possible avec les fonctions globales standard. L'exemple de code suivant montre le modèle:

<?php
/** Plugin Headers ... */

return MyPlugin::bootstrap(); 

class MyPlugin
{
    /** @var MyPlugin */
    static $instance;
    static public function bootstrap() {
        if (NULL === self::$instance) {
            self::$instance = new __CLASS__;
        }
        return self::$instance;
    }
    # ...
}

C'est un modèle commun que j'utilise pour le fichier de plugin de base. La classe de plugin représente d'une part le plugin pour wordpress et d'autre part elle permet de commencer à utiliser des paradigmes orientés objet pour le propre code qui peuvent même être complètement orientés objet (mais pas nécessairement). C'est une sorte de contrôleur, s'interfaçant avec l'ensemble de l'API wordpress en tant que demande (s).

Comme le montre l'exemple, une instance du plugin sera créée. Cela vous permet d'utiliser des communs connus comme un constructeur Docs ( __construct) pour initialiser le plugin réel:

# ...
class MyPlugin
{
    # ...
    public function __construct()
    {
        add_filter('the_hook', array($this, 'hook'));
    }

    public function hook()
    {
    }
    # ...
}

Au moment où le hook est enregistré, cet objet de plugin bénéficie déjà de sa conception: vous avez cessé de coder en dur la fonction de hook réelle par rapport au nom de classe du plugin concret . Cela est possible en raison de la liaison de la classe à l'instance d'objet pour le rappel. Cela semble compliqué, juste dire: $this c'est le plugin. Peut être utilisé dans les rappels de hook, comparez les méthodes Registering Class comme des rappels de hook .

Ce modèle permet de faciliter l'interface avec wordpress: l'injection est réduite aux noms des hooks et aux données qu'ils fournissent. Vous pouvez ensuite commencer à implémenter directement dans cette classe de plug-in ou à refactoriser votre implémentation par rapport à celle-ci, afin de ne mettre que du code dans la classe de plug-in qui est le strict minimum pour définir votre interface de plug-ins contre wordpress, mais gardez la logique générale en dehors de worpdress. C'est là que le plaisir commence et probablement ce que chaque auteur de plugin veut réaliser à long terme.

Alors ne programmez pas avec inquiétude mais contre elle. Comme worpdress est assez flexible, il n'y a pas d'interface commune ou facile à décrire. Une classe de plugin de base peut assumer ce rôle, vous offrant plus de flexibilité pour votre propre code, ce qui entraînera un code plus facile et de meilleures performances.

Il y a donc plus qu'un simple avantage pour l'espacement des noms. La meilleure suggestion que je puisse donner est: Essayez-vous. Il n'y a pas grand chose à perdre, seulement de nouvelles choses à découvrir.

Vous remarquerez très probablement des différences après avoir passé quelques mises à jour plus importantes de wordpress tout en gardant votre plugin compatible.

Attention : si votre plugin s'intègre directement à wordpress pour faire le travail, l'utilisation d'une ou deux fonctions publiques pourrait vous convenir mieux. Prenez le bon outil pour le travail.

hakre
la source
1
Si les fonctions de classe statiques ne sont vraiment pas différentes des fonctions globales et que votre objectif est de prévenir les conflits d'espaces de noms, je n'ai vraiment pas encore compris la nécessité de passer à l'écriture de plugins en tant que classes. En outre, je suis confus par votre fonction d'amorçage d'aide. Pourquoi ne pas simplement déclarer le nouvel objet comme $ new_object = new MyClass () ;?
AlxVallejo
@AlxVallejo: Pour l'espace de noms seul, il n'y a pas de véritable nécessité (comme je l'ai écrit dans la réponse, les méthodes de classe statique sont à peu près les mêmes que les fonctions globales). Vous pouvez donc faire votre propre espace de noms (le type d'espaces de noms pré PHP 5.3). Vous l'avez donc parfaitement remarqué. Similaire à la fonction d'amorçage statique: techniquement, ce n'est pas nécessaire, un simple le return $myPlugin = new MyPlugin(); fait aussi. Cependant, pour une vue d'ensemble, une nouvelle simple peut ne pas suffire, comparez le plugin WordPress: comment éviter les «couplages serrés»? .
hakre
9

Classes VS ensemble de fonctions


Performance

Général: Afaik, il n'y a pas de différence de "performances" entre les classes et les ensembles de fonctions.

Détail:

  • Il y a une grande différence si vous vous interrogez par function_exists()rapport class_exists()à normalement, vous avez beaucoup de fonctions (~ 1.800 (?) Dans le noyau wp) par rapport aux classes (~ 100 (?) Dans le noyau wp). Donc, rendre les choses «enfichables» et donc remettre en question l'existence est une différence de temps d'exécution.
  • Les classes offrent un gros avantage sur les ensembles de fonctions: vous pouvez beaucoup plus facilement éviter de l'appeler sur une demande où vous n'en avez pas besoin, puis avec des fonctions. Vous n'avez qu'à effectuer des vérifications conditionnelles pour la classe et non pour chaque fonction. Donc, si vous n'en avez pas besoin à chaque chargement de page et que vous pouvez éviter d'appeler beaucoup d'instructions if / else, une fonction "fonctionne mieux".

Architecture - Comment ça marche:

ensemble de fonctions: En général, les fonctions sont exécutées dans la ligne que vous appelez. Donc, chaque fois que vous appelez des trucs, vous devez les réécrire, si vous devez les appeler plusieurs fois.

Classe: Il existe différentes approches pour les classes. La classe, qui se rapproche le plus d'un ensemble de fonctions est la classe "usine" ( wikipedia / google ). Imo c'est presque la même chose qu'un ensemble de fonctions, mais encapsulé dans une classe. Mais il existe également d'autres "types" de classes. Vous pouvez par exemple écrire un résumé ou une classe de classe parent, que vous étendez avec une classe enfant. Dans un exemple réel: supposons que vous ayez une classe qui crée des champs de texte statiques. Dans votre __construct()fonction, vous avez un ensemble de scénarios comme "left_column", "right_column" & "footer_field". Ensuite, vous appelez quelque chose comme $text_field = new TextFieldClass();pour instancier la classe. Et plus tard, vous appelez simplement $text_field->add( $case => 'left_column', 'case' => 'foo text' );et$text_field->add( $case => 'footer_field', 'case' => 'bar text' );. Ensuite, toutes vos conditions et autres ont déjà été effectuées lorsque vous avez instancié la classe et seules les deux fonctions de classe auraient été appelées lorsque vous avez créé les champs de texte. Dans ce szenario, vous auriez pu économiser quelques ms de temps d'exécution.


Opinion personnelle

Si vous écrivez vos cours avec sagesse, vous aurez un avantage mineur en termes de performances. Mais vous aurez une structure bien organisée sur laquelle travailler. Jusqu'à présent, rien de spectaculaire. Mais si vous considérez les cas d'utilisation "fractionnés" suivants pour les classes et les fonctions dans un plugin, vous obtiendrez mon dernier point: la classe est interne, les fonctions sont des API . Tant que vous proposez l'API uniquement via des fonctions utilisables publiquement (qui appellent ensuite des classes ou des fonctions de classe), vous serez du côté de la sauvegarde en développant davantage votre plugin. Vous avez obtenu la liberté de modifier la structure interne ou même les possibilités de votre plugin sans affecter les utilisateurs à tout moment et partout.

Exemple:

// construction of object
if ( ! class_exists( 'WPSE_HelloWorld' ) )
{

class WPSE_HelloWorld
{
    function __construct( $args = array( 'text', 'html', 'echo' ) )
    {
        // call your object building procedures here
        $this->hello_world( 'text', 'html', 'echo' );
    }

    function hello_world( 'text', 'html', 'echo' )
    {
        $start_el = '<{$html}>';
        $end_el = '</{$html}>';
        if ( $echo )
        {
            return print "{$start_el}{$some}{$end_el}";
        }

        return "{$start_el}{$some}{$end_el}";
    }
} // END Class 

}

// API: public functions
function the_hello_world( $args( 'echo' => true ) )
{
    $new = new WPSE_HelloWorld();
    return $new->hello_world( $args );
}

function get_hello_world( array( $args( 'echo' => false) ) )
{
    $new = new WPSE_HelloWorld();
    return $new->hello_world( $args );
}

// then you can call it like get_the_title() or the_title(), which you know from the WP API:
// 'echo' is set to false per default:
$some_var = get_hello_world( array( 'text' => 'hello reader', 'html' => 'strong' ) );
# *returns* "<strong>hello reader</strong>"

// 'echo' is set to true per default:
the_hello_world( array( 'text' => 'hello reader', 'html' => 'strong' ) );
# *prints/echos* "<strong>hello reader</strong>"

Remarque: veuillez également lire le lien @ t310s publié dans le commentaire au Q.

kaiser
la source
juste curieux, pourquoi vous attendez-vous à ce que votre fichier de plugin soit inclus plus d'une fois avec wordpress?
hakre
@hakre Où exactement ai-je dit cela? sry, assez fatiguée à la maman.
kaiser
1
@kaiser, je suppose que @hakre fait référence à la if( ! class_exists )ligne que vous avez au début?
jjeaton
1
@hakre Je suppose que @kaiser effectue la class_existsvérification non pas parce qu'il pourrait être inclus plus d'une fois mais pour éviter un conflit avec une autre classe?
Michal Mau
Oui, je me posais des questions sur les class_exists.
hakre
4

C'est un choix purement stylistique de la part de l'auteur du plugin. Il n'y a pas de réelle différence de vitesse.

Otto
la source
1

Les cours n'offrent généralement aucun avantage en termes de performances, mais ils ont très rarement des effets négatifs non plus. Leur véritable avantage est de rendre le code plus clair et d'éviter les conflits d'espace de noms.

Bainternet
la source
Pourtant, comme @hakre l'a mentionné, les conflits d'espace de noms ne sont vraiment pas différents lors de l'utilisation de préfixes sur des fonctions globales. Le code "plus propre" et la prévention des conflits d'espace de noms sont également synonymes dans ce cas, non?
AlxVallejo
@AlxVallejo je suppose que oui :)
Bainternet
0

La plupart du temps, si vous utilisez des fonctions, vous mettrez le nom du plugin dans chaque nom de fonction, donc efficacement, vous dupliquerez ce nom une douzaine de fois si le plugin a une douzaine de fonctions ce qui est un peu compliqué .

Avec les classes, vous n'aurez probablement qu'une seule fois le nom du plugin dans le nom de la classe.

De plus, vous pouvez utiliser l'héritage ou d'autres constructions oo pour implémenter les comportements de manière très propre. Voici un ex:

class animalplugin{
  //plugin functions...
  function talk(){print "animalnoise";}
}
class animalplugin_with_cat_mods extends abcplugin{
  //cat functions overrides
  function talk(){print "meow";}
}
if (iscat()){
  new animalplugin_with_cat_mods();
} else {
  new animalplugin();
}
zeedre
la source