Mettre à jour un plugin tout en conservant la rétrocompatibilité: général
-
-
Depuis que vous avezmentionné un article sur l'instanciation sansglobal,voici untel article avec des liens versplusieurs autres articles connexes: http://hardcorewp.com/2013/using-singleton-classes-for-wordpress-plugins/Since you mentioned a post about instantiating without a global here's such a post about with links to several other related posts: http://hardcorewp.com/2013/using-singleton-classes-for-wordpress-plugins/
- 0
- 2013-04-11
- MikeSchinkel
-
1 réponses
- votes
-
- 2013-03-02
Lesnoms de classe sontinsensibles à la casseen PHP (avec uneexception ),donc celane devraitpas être unproblème.
L'accès à votreinstanceprincipale depluginpour une classeestpossible avec uneméthode statique
get_instance()
(exemple ). Vousn’avezpasbesoin de construire un Singletonpour cela. Et celane devraitpasinterrompre la rétrocompatibilité.Si vous aviez desméthodespubliques dans votre ancien codeet que cesméthodes ont été utiliséespar du codetiers,vous devez conserver la signatureexacte. Donc,soitgardez laméthodeet passez l'appel à l'instance de lanouvelle classe,soit - s'ilexiste denombreusesméthodes de cetype - utilisez
__call()
pour capturertoutes cesméthodesen une.(Update) Comptetenu de votreexemple de
remove_action()
… c'est délicat. Si vous ajoutez ce rappel depuis une autre classemaintenant,iln'existe aucunmoyen sûr de rester compatible avec les versions antérieures,car vousne pouvezpas «regarder» les appelsremove_action()
.
Vouspouvezenregistrer l'ancien rappelet implémenter un Observateur pour être remarqué s'il a été supprimé .Uneidée avec laquellej'aijouépour defutursprojets: séparer l'APIpublique de la logiqueinterne.
Exemple: créez une classe
Plugin_API
et transmetteztous les objets de votre code detravail dans cette classe lors de leur configuration. Lorsqu'uneméthode de cette APIest appelée,transmettez cet appel à l'objet responsable en interne ,sansexposer la structure de votreplugin aupublic.Exemple de code
add_action( 'wp_loaded', array ( Plugin_Controller::get_instance(), 'plugin_setup' ) ); class Plugin_Controller { protected static $instance = NULL; public static function get_instance() { NULL === self::$instance and self::$instance = new self; return self::$instance; } public function plugin_setup() { $api = Plugin_API::get_instance(); $api->add_object( 'post_type', new Plugin_Custom_Post_Type ); $api->add_object( 'taxonomy', new Plugin_Custom_Taxonomy ); $api->add_object( 'options_page', new Plugin_Options_Page ); add_action( 'wp_head', array ( $api, 'wp_head' ) ); } } class Plugin_Custom_Post_Type {} class Plugin_Custom_Taxonomy {} class Plugin_Options_Page {} class Plugin_API { protected static $instance = NULL; protected $objects; public static function get_instance() { NULL === self::$instance and self::$instance = new self; return self::$instance; } public function add_object( $name, $object ) { $this->objects[ $name ] = $object; } public function wp_head() { $this->objects['post_type']->wp_head_callback(); $this->objects['taxonomy']->wp_head_callback(); } }
Maintenant,un autrepluginpeut supprimer cette action avec…
remove_action( 'wp_head', array ( Plugin_API::get_instance(), 'wp_head' ) );
…et vous êtestoujours libre demodifier la structure de votreplugin quand vous le souhaitez.
Cen'est qu'uneidée,je ne l'aipas utilisée. Mais celapourrait valoir lapeine d'essayer unplugin complexe avecbeaucoup de classes. Jene saispas comment celafonctionnera avec lesfiltres,mais ceux-ci sont detoutefaçonplusfaciles à déplacer.
Lamise àjour d'unnombreinconnu de champsméta depublicationpeut être coûteuse. Jen'ytoucheraispas.
Class names are case insensitive in PHP (with one exception), so that should not be a problem.
Accessing your main plugin instance for a class is possible with a static method
get_instance()
(example). You don’t have to build a Singleton for that. And it should not break backwards-compatibility.If you had public methods in your old code and these methods have been used by third party code, you have to keep the exact signature. So either keep the method and pass the call through to the instance of the new class, or – if there are many such methods – use
__call()
to catch all these methods in one.(Update) Given your example of
remove_action()
… this is tricky. If you add this callback from another class now, there is no safe way to stay backwards compatible, because you cannot “watch”remove_action()
calls.
You could register the old callback and implement an Observer to be noticed if it has been removed.One idea I have been playing with for future projects: separate the public API from the inner logic.
Example: Create a class
Plugin_API
and pass all objects from your working code into that class when they are set up. When a method from that API is called, pass that call to the responsible object internally, without exposing your plugin structure to the public.Sample code
add_action( 'wp_loaded', array ( Plugin_Controller::get_instance(), 'plugin_setup' ) ); class Plugin_Controller { protected static $instance = NULL; public static function get_instance() { NULL === self::$instance and self::$instance = new self; return self::$instance; } public function plugin_setup() { $api = Plugin_API::get_instance(); $api->add_object( 'post_type', new Plugin_Custom_Post_Type ); $api->add_object( 'taxonomy', new Plugin_Custom_Taxonomy ); $api->add_object( 'options_page', new Plugin_Options_Page ); add_action( 'wp_head', array ( $api, 'wp_head' ) ); } } class Plugin_Custom_Post_Type {} class Plugin_Custom_Taxonomy {} class Plugin_Options_Page {} class Plugin_API { protected static $instance = NULL; protected $objects; public static function get_instance() { NULL === self::$instance and self::$instance = new self; return self::$instance; } public function add_object( $name, $object ) { $this->objects[ $name ] = $object; } public function wp_head() { $this->objects['post_type']->wp_head_callback(); $this->objects['taxonomy']->wp_head_callback(); } }
Now another plugin can remove that action with …
remove_action( 'wp_head', array ( Plugin_API::get_instance(), 'wp_head' ) );
… and you are still free to change your plugin structure whenever you want.
This is just an idea, I haven’t used it. But it could be worth a try for a complex plugin with lots of classes. Not sure how it will work with filters, but these are easier to move anyway.
Updating an unknown number of post meta fields can be expensive. I would not touch that.
-
J'espérais que vouspourriez répondre à cela!Merci!Puis-je vous demander dejeter un œil à l'exemple de codetiers quej'ai ajouté?Pourrais-jetoujoursimplémenter votre structure de classe de démonstration,et/ou déplacer `some_templates ()` vers une classe séparée sans casser cela?Was hoping you might respond to this! Thanks! Could I trouble you to take a look at the example third-party code I've added? Could I still implement your demo class structure, and/or move `some_templates()` to a separate class without breaking that?
- 0
- 2013-03-03
- helgatheviking
-
@helgatheviking Voirmamise àjour.@helgatheviking See my update.
- 0
- 2013-03-03
- fuxia
-
Mercibeaucoup!Jene saispas sije pourrai l'implémenter avec cepluginparticulier à cause detoute la rétro-compatibilité,mais ce sera superpratiquepour leprochain!Thanks so much! I don't know if I'll be able to implement it with this particular plugin because of all the back-compatibility, but this will be super handy for the next one!
- 0
- 2013-03-03
- helgatheviking
Commentpuis-jemettre àjour un demespluginspublics sans casser lepluginpourtous les utilisateursexistants.
Premièrement ,j'aimerais changer la classe/l'espace denoms dupluginprincipal,carj'aimerais qu'il soit similaire aux autres classes duplugin qu'il étend.
J'aimerais donc changer
à
Deuxièmement ,la classeentièreestinstanciéeet enregistrée dans une variableglobale.
Quelquepartici,j'ai vu unbonexemple de lafaçon d'instancier une classe sansglobal (jene letrouvepasmaintenant,#doh). Leglobalpeut être utilisépar les utilisateurspour ajouter/supprimer quelquesmodèles. Existe-t-il unmoyen de se débarrasser duglobal sans casser complètement lesthèmes despersonnes quipourraient l'utiliserpourmanipuler lesmodèles?
Troisièmement ,lié à ci-dessus,monfichier depluginprincipalest devenu vraimentgroset j'aimerais le diviseren morceauxpourmapropre raison. Cependant,si laméthode
some_templates()
est danssome_class()
,est-ilpossible de la déplacer versfront_end_class()
sans casser les chosespour utilisateurs?Parexemple
Pour décrocher une action dansmonplugin
La structure de classeet la configuration des variablesglobalespeuvent-elles être ajustées sans casser cela? Commentpuis-je avertir lesgens que cela a changé?
_doing_it_wrong()
?Enfin ,j'aienregistré uneméta depublication sous laforme "oui"par rapport à "non" au lieu d'unbooléen. Est-iljudicieux de changer cela?