Ignorons une seconde que la méthode en question est __construct
et appelons-la frobnicate
. Supposons maintenant que vous ayez un objet api
implémenté IHttpApi
et un objet config
implémenté IHttpConfig
. De toute évidence, ce code correspond à l'interface:
$api->frobnicate($config)
Mais supposons que nous diffusions api
vers IApi
, par exemple en le passant à function frobnicateTwice(IApi $api)
. Maintenant, dans cette fonction, frobnicate
est appelé, et puisqu'il ne traite que IApi
, il peut effectuer un appel tel que $api->frobnicate(new SpecificConfig(...))
where SpecificConfig
implements IConfig
mais pas IHttpConfig
. À aucun moment, personne n'a fait quoi que ce soit de désagréable avec les types, mais a IHttpApi::frobnicate
obtenu un SpecificConfig
endroit où il s'attendait à un IHttpConfig
.
Ce n'est pas bien. Nous ne voulons pas interdire l'upcasting, nous voulons le sous-typage, et nous voulons clairement que plusieurs classes implémentent une interface. Ainsi, la seule option raisonnable est d'interdire une méthode de sous-type nécessitant des types plus spécifiques pour les paramètres. (Un problème similaire se produit lorsque vous souhaitez renvoyer un type plus général .)
Formellement, vous êtes entré dans un piège classique entourant le polymorphisme, la variance . Toutes les occurrences d'un type T
ne peuvent pas être remplacées par un sous-type U
. Inversement, toutes les occurrences d'un type T
ne peuvent pas être remplacées par un supertype S
. Un examen attentif (ou mieux encore, une application stricte de la théorie des types) est nécessaire.
Pour en revenir à __construct
: depuis AFAIK, vous ne pouvez pas instancier exactement une interface, seulement un implémenteur concret, cela peut sembler une restriction inutile (il ne sera jamais appelé via une interface). Mais dans ce cas, pourquoi inclure __construct
dans l'interface pour commencer? Quoi qu'il en soit, il serait de peu d'utilité dans ce cas particulier __construct
.