POO : les interfaces en PHP

19 Fév 2013 5 No tags

En programmation orientée objet (POO) et en PHP 5 en particulier, les interfaces définissent le comportement publique d’une classe. Les interfaces regroupent donc la signature des méthodes qui pourront être utilisées sur l’instance d’une classe. En implémentant une interface, une classe s’oblige à définir l’ensemble des méthodes de l’interface. Voici un exemple d’interface à une seule méthode :

[php] interface AuthenticationInterface {
public function authenticate($user);
}
[/php]

Et une classe l’implémentant :

[php] class CustomAuthentication implements AuthenticationInterface {
public function authenticate($user) {
if ($user === ‘test’) {
return true;
}
}
}
[/php]

Rien de plus simple puisqu’il s’agit uniquement de définir les méthodes spécifiées dans l’interface. A noter qu’une interface ne définit que des méthodes publiques. Logique puisqu’on définit le comportement publique…

Ce qui est intéressant c’est que les interfaces peuvent être utilisées comme type dans les méthodes. Ceci est tout à fait autorisé :

[php] public function uneMethode(AuthenticationInterface $authentication) { … }
[/php]

Du coup ça ouvre des perspectives intéressantes : on n’est pas obligé de connaitre la classe d’un objet pour l’utiliser, on peut se contenter de connaitre l’interface qu’il implémente.

A quoi ça sert ?

Prenons l’exemple de l’authentification d’un utilisateur via plusieurs biais : formulaire de login / mot de passe, Twitter, Facebook ou autre. Pour factoriser son code d’authentification et s’abstraire du type d’authentification, on définit les classes suivantes :

Interface :

[php] interface AuthenticationInterface {
public function authenticate($user);
}
[/php]

Implémentations :

[php] class FormAuthentication implements AuthenticationInterface {
public function authenticate($user) {
// Traitement du formulaire
}
}

class FacebookAuthentication implements AuthenticationInterface {
public function authenticate($user) {
// Authentification via Facebook (OAuth)
}
}

class TwitterAuthentication implements AuthenticationInterface {
public function authenticate($user) {
// Authentification via Twitter (OAuth)
}
}
[/php]

Manager :

[php] class AuthenticationManager {
public function authenticate(AuthenticationInterface $authentication, $user) {
return $authentication -> authenticate($user);
}
}
[/php]

C’est un peu raccourci, mais l’idée est là. Dans nos services ayant besoin d’un mécanisme d’authentification, on utilise le manager AuthenticationManager qui s’abstrait du type d’authentification puisque qu’il prend en paramètre une classe implémentant l’interface AuthenticationInterface. Cela lui garantit que la méthode authenticate() est disponible.

Dans ce cas, il est possible d’améliorer le code en ajoutant une factory (fabrique d’objets). Ce design pattern est intéressant car il permet de récupérer des objets sans avoir à instancier directement la classe. Voici un petit exemple :

[php] class AuthenticationFactory {
public static get($authName) {
switch ($authName) {
case "form" :
return new FormAuthentication();
break;
case "facebook" :
return new FacebookAuthentication();
break;
case "twitter" :
return new TwitterAuthentication();
break;
}
}
}
[/php]

Et ainsi dans notre manager, on peut également s’abstraire de la création des classes correspondant aux types d’authentification.
Nouveau manager :

[php] class AuthenticationManager {
public function authenticate($authType, $user) {
return AuthenticationFactory :: get($authType) -> authenticate($user);
}
}
[/php]

Utilisation dans un service :

[php] $isAuthenticated = $this -> get(‘authentication_manager’) -> authenticate(‘twitter’, ‘Nicolas Hachet’);
[/php]

Au final, le code métier utilisant le manager AuthenticationManager a une vision de haut niveau : il ne sait pas quelles sont les classes utilisées ni comment est faite l’implémentation.  Son seul besoin est d’authentifier un utilisateur en fonction d’un type donné.