Remplacer une classe de premier niveau

Billet

Dans cet article nous allons voir de façon très succincte comment remplacer une des classes du cœur de Dotclear en utilisant un conteneur de services.

Autant le dire tout de suite, ça ne va pas servir à tout le monde, et uniquement pour des cas bien précis, par des utilisateurs connaissant cette façon de coder.

Définition

- Mais c'est quoi un conteneur, un service, une usine ?
- C'est un patron de conception aka design pattern.
- Mais encore ?

Profitant du passage aux espaces de nom PHP, le cœur de Dotclear, appelé core ou dcCore, a disparu au profit d'un conteneur qui s'occupe uniquement de fournir les classes maitresses de Dotclear, ces classes sont définies et leur objets générés par le conteneur, appelé Core. (Original comme nom !) Tout ce petit monde suivant des règles bien précises écrites dans des interfaces.
Pour faire simple:

  • Un service est un ensemble de méthodes dédiées à une tâche, par exemple la gestion des behaviors, regroupées dans une classe
  • Le conteneur Container fournit à l'utilisateur les objets prêt à l'emploi, ici App:: avec App::unService()
  • L'usine Factory fournit ces classes, sans que l'utilisateur sache lesquelles sont réellement chargées.
  • Les interfaces xxxInterface définissent les règles que doivent suivre les classes de l'usine. exemple: BehaviorInterface

Cette technique va permettre de remplacer toutes les classes accessibles depuis App::xxx()

Déclaration

Il faut commencer par déclarer notre service, une classe dédiée \Dotclear\Helper\Container\Factories est là pour ça, on va lui dire d'ajouter notre service. Le plus simple est de lui dire depuis le fichier inc/config.php de l'installation, dans l'exemple qui suit, on veut simplement remplacer une seule méthode de la classe Error :

<?php
//... contenu du fichier inc/config.php

// Notre service n'utilise pas d'espace de nom, on le récupère directement
require_once __DIR__ . '/../MyError.php';

// On dit à la maison mère que notre service va être utilisé dans le core de Dotclear pour le service ErrorInterface::class
\Dotclear\Helper\Container\Factories::addService('core', ErrorInterface::class, MyError::class);

Voilà notre service remplace celui de Dotclear.

Service

Passons à notre service. Il doit obligatoirement suivre des règles strictes pour pouvoir être utilisé à la place d'un autre, voici le contenu de notre fichier MyError.php :

<?php
// On va reprendre la classe d'erreur et ses méthodes
use Dotclear\Core\Error;
// Pas besoin d'implémenter l'interface qui l'est déjà par la classe Error
//use Dotclear\Interface\Core\ErrorInterface;

// on crée notre classe d'erreur qui va étendre celle par défaut.
class MyError extends Error
{
    // on remplace uniquement la méthode qui nous intéresse
    public function add(string $msg): void
    {
        // notre classe va arrêter net le script sur une erreur au lieu de renvoyer un bandeau par exemple
        exit($msg);
    }
}

Cet exemple est simple, il permet de montrer la mécanique d'utilisation de Factories, si vous souhaitez remplacer la gestion de connexion à la base de données cela va être bien plus compliqué !

Liste des classes maîtresses

Voici la liste des appels depuis l'application et des interfaces associées :

  • App::auth(): \Dotclear\Interface\Core\AuthInterface,
  • App::behavior(): \Dotclear\Interface\Core\BehaviorInterface,
  • App::blog(): \Dotclear\Interface\Core\BlogInterface,
  • App::blogSettings(): \Dotclear\Interface\Core\BlogSettingsInterface,
  • App::blogs(): \Dotclear\Interface\Core\BlogsInterface,
  • App::blogWorkspace(): \Dotclear\Interface\Core\BlogWorkspaceInterface,
  • App::categories(): \Dotclear\Interface\Core\CategoriesInterface,
  • App::cache(): \Dotclear\Interface\Core\CacheInterface,
  • App::con(): \Dotclear\Interface\Core\ConnectionInterface,
  • App::error(): \Dotclear\Interface\Core\ErrorInterface,
  • App::filter(): \Dotclear\Interface\Core\FilterInterface,
  • App::formater(): \Dotclear\Interface\Core\FormaterInterface,
  • App::lang(): \Dotclear\Interface\Core\LangInterface,
  • App::log(): \Dotclear\Interface\Core\LogInterface,
  • App::media(): \Dotclear\Interface\Core\MediaInterface,
  • App::meta(): \Dotclear\Interface\Core\MetaInterface,
  • App::nonce(): \Dotclear\Interface\Core\NonceInterface,
  • App::notice(): \Dotclear\Interface\Core\NoticeInterface,
  • App::plugins(): \Dotclear\Interface\Module\ModulesInterface,
  • App::postMedia(): \Dotclear\Interface\Core\PostMediaInterface,
  • App::postTypes(): \Dotclear\Interface\Core\PostTypesInterface,
  • App::rest(): \Dotclear\Interface\Core\RestInterface,
  • App::session(): \Dotclear\Interface\Core\SessionInterface,
  • App::themes(): \Dotclear\Interface\Module\ModulesInterface,
  • App::trackback(): \Dotclear\Interface\Core\TrackbackInterface,
  • App::url(): \Dotclear\Interface\Core\UrlInterface,
  • App::users(): \Dotclear\Interface\Core\UsersInterface,
  • App::userPreferences(): \Dotclear\Interface\Core\UserPreferencesInterface,
  • App::userWorkspace(): \Dotclear\Interface\Core\UserWorkspaceInterface,
  • App::version(): \Dotclear\Interface\Core\VersionInterface,

Le nom de l'interface sera le nom du service dans l'usine.

Le contenu de ce document a été écrit suivant le code de la version 2.28 de Dotclear.

La discussion continue ailleurs

URL de rétrolien : https://dotclear.watch/trackback/435