Le static, c’est pas automatique ?

Sujet intéressant je trouve, voici donc mon partage :)

🔗 Le static, par définition

Le fait de déclarer des propriétés ou des méthodes comme statiques vous permet d’y accéder sans avoir besoin d’instancier la classe.

Source

🔗 Procédural et objet

Il peut arriver que l’on pense faire la programmation orientée objet (POO) dès qu’on utilise des classes ou que l’on écrit :

$objet->prima_method($args)

ce qui est faux mais malheureusement pas toujours évident sad

C’est probablement parce que le mot « procédural » fait penser à ça instinctivement :

$func = my_function($args);

et, qu’à l’inverse, dès que l’on va voir la fameuse flèche et l’utilisation de méthodes provenant d’une classe instanciée, on va penser qu’il y a de la POO derrière. Ce qui est encore faux, du moins pas nécessairement vrai.

Le fait de prendre par exemple my_function() et de la mettre dans une classe ne suffira pas à faire de la POO. Le code pourra très bien rester procédural. La POO suit des patterns, des modèles de code.

La clé pour distinguer le vrai du faux AMHA c’est de poser le sujet en ces termes :

Le procédural et la POO sont des approches pas des modes d’écriture ou des figures de style.

Remarque : vous aurez noté l’utilisation des « et » dans mon titre et pas des « vs » comme on peut le lire parfois « procédural vs POO ».

🔗 Et le static dans tout ça ?

Une méthode statique dans une classe, c’est un peu comme la fonction dont je parle plus haut, imaginons qu’on l’ait posée sans autre action supplémentaire dans une classe,  on aurait :

My_Class {
    public static function my_function($args) {}
}

et pour l’appeler on ferait :

$static_call = My_Class::my_function($args);

La différence avec un :

$call = my_function($args);

Ici on pourrait même dire que je n’ai fait que « namespacer » ma fonction smile

Passer par du statique permet de ne pas se soucier du contexte. Le résultat de la fonction sera toujours le même, sans influence extérieure, sans effet secondaire, si on passe les mêmes $args.

Dans le jargon on parle de fonctions stateless ce qui est équivalent à sans contexte. En cela, on se rapproche beaucoup plus du procédural que de l’objet, rien avoir même ici et pourtant on a écrit une classe.

D’ailleurs, dans une méthode statique, il est par définition impossible d’utiliser $this. La méthode s’utilise sans instancier la classe, sans donc créer un objet.

🔗 C’est quoi le souci avec le statique alors ?

Le statique est souvent décrié et malheureusement souvent au motif que tout devrait être objet ce qui est peut-être génial dans l’idée mais très peu convaincant au final. Toutes les opérations n’ont pas besoin d’objet, loin s’en faut.

Un argument plus important est celui des unit tests car il n’est pas trivial de remplacer la référence d’une méthode statique par autre chose, c’est compliqué même. Le grand nombre de cas de mauvaise utilisation n’apporte pas d’eau au moulin des partisans du statique. Et enfin un 3ème argument qui découle des deux derniers :

LA COMPLEXITÉ. Une application truffée de méthodes statiques rajouterait de la complexité inutilement puisque contrairement à la POO, on aura pas accès à l’ensemble des actions possibles (méthodes) depuis un objet mais bien souvent à des méthodes éparpillées à droite à gauche. Le risque d’erreur serait donc accru et la maintenance plus complexe si on suit ce raisonnement.

🔗 Donc quand utiliser du statique et quand éviter ?

Commençons par le mauvais, on finira sur une bonne note je l’espère. L’argument est le suivant :

une méthode statique n’a, par définition pas le même état que la classe qui la contient, elle est là sans être vraiment là. Elle ne dépend pas vraiment de la classe. On peut l’appeler de n’importe où, comme une globale.

J’ai dit un gros mot ? lol

Je taquine mais pas vraiment en fait, on aura bien créé une procédure globale. Deuxième grot mot ?

Prenons un exemple plus parlant. Je décide de créer une méthode statique qui va incrémenter un compteur et une autre qui me servira à afficher le résultat. On comprend déjà dans cette phrase le souci. Comme ma méthode est statique, elle accessible partout, si un autre développeur l’utilise, il va me pourrir mon résultat et là on sera loin mais alors très très loin du côté pas d’effet secondaire.

Mise en image :

My_Class::update_counter( 19 );
$counter = My_Class::get_count();

Typiquement ici on aurait plutôt besoin du côté stateful autrement dit d’un contexte ! Logiquement on se tournerait vers l’objet :

$obj = new My_Class();
$obj->update_counter( 19 );
$counter = $obj->get_count();

J’ai contextualisé mon code, il n’y aura plus de problème à l’utiliser dans d’autres parties du code sans effet de bord.

🔗 Côté variable

On a pas encore parlé des variables ! J’ai commencé par les méthodes mais les variables statiques ont aussi leur utilité. En réalité, la différence entre :

global $my_global_var;

et :

public static $my_global_var;

que l’on peut voir dans certaines classes, est minime. Les deux seront accessibles de partout, à niveau global.

🔗 Reparlons de compteurs

Contrairement aux variables locales classiques qu’on définit dans les fonctions, une variable static ne sera pas détruite au moment de l’exit. Et si on utilise à nouveau la fonction, ailleurs, elle conservera sa valeur. L’exemple style Hello World que l’on donne souvent est de créer un compteur qui incrémente chaque fois qu’une fonction est utilisée quelque part dans le code :

function _do_something() {
 static $count = 0;
 var_dump($count);
 $count++;
}

_do_something();
_do_something();
_do_something();

$count va bien s’incrémenter pour valoir 3 au final dans cet exemple.

🔗 WordPress, le static et les singletons

Pour rappel, dans WordPress pour ajouter un filtre, donc modifier des données à la volée avant affichage par exemple, on fait :

function example_callback( $example ) {
    return $example;
}
add_filter( 'example_filter', 'example_callback' );

Source

On va pouvoir retrouver l’approche suivante dans le constructeur :

public function __construct(){
    add_action('wp_enqueue_scripts', array($this, 'enqueue_script'));
}

et ensuite un new de la classe pour terminer. Cela peut poser problème parce que même en déclarant une variable et en « mettant le new dedans » :

$my_var = new My_Class();

l’objet est perdu. Il faudrait carrément une variable globale pour y accéder. Et là ça devient moche.

🔗 Le singleton pour s’adapter

Les approches évoluent, la méthodologie aussi, rien n’est figé par définition, mais le singleton peut constituer une bonne approche pour WordPress.

Constat : faire de l’objet dans un plugin WordPress c’est pas du gâteau car tel est notre ghetto.

Deux réactions possibles :

  • casser WordPress et tout refaire en objet à la Extreme Makeover, bim
  • s’adapter le temps que WordPress s’adapte lui aussi (et c’est en cours de modernisation à l’heure où j’écris youpi !)

Le singleton permet de conserver une seule et unique instance de la classe à dispo, pour votre confort de développeur WP.  A tout moment, il suffira d’un simple get_instance() comme on va le voir pour récupérer votre trousse à outils :

class My_Class
{
	/**
	 * @var self
	 */
	protected static $instance;

	/**
	 * @return self
	 */
	public static function get_instance() {
		if ( is_null( self::$instance ) ) {
			self::$instance = new static;
		}

		return self::$instance;
	}
	/**
	 * prevent the instance from being cloned
	 *
	 * @return void
	 */
	private function __clone() {
	}

	/**
	 * prevent from being unserialized
	 *
	 * @return void
	 */
	private function __wakeup() {
	}

        public function hook_something() {
            echo 'OK';
        }
}

add_action( 'wp_footer', [ My_Class::get_instance(), 'hook_something' ] );

Le choix du singleton est fait pour des raisons pratiques. Les méthodes sont toujours accessibles grâce à une instance unique et c’est relativement simple à implémenter.

🔗 Importante précision

À aucun moment je ne dis qu’utiliser le singleton est LA voie à suivre pour les développeurs WP. C’est une solution qui a l’avantage de bien s’adapter à WP en tant qu’environnement tout en permettant d’industrialiser un minimum, en attendant (ou en allant soi-même contribuer, patcher, fixer, etc), qui sait, une mutation technique de WP, qui, pour le coup, a déjà commencé.

🔗 Conclusion

Le statique n’est pas le MAL. Cela a beau être mal compris et/ou méconnu, l’utiliser pour ajouter des filtres et des actions n’a rien d’erroné en soi, ce n’est, en revanche, pas vraiment adapté à la modernité, c’est incontestable, sans compter les difficultés qui se posent en matière de units tests et autres.

Ne pas oublier pourquoi on utilise du WordPress et surtout tout ce qu’on peut faire avec ! Nous sommes les premiers à critiquer notre outil, et croyez-moi ça râle ^^.

Il y a une vraie cohérence dans tout ça, la seule chose à faire c’est rester constructif, positif et éviter la politique, donc contribuer, patcher chacun à sa mesure, et faire avancer les choses.

🔗 Sélection de liens