COMMENT SIMPLIFIER SON CODE PHP

Software design for dummies

SIMPLIFIER SON CODE

"Ah ouais, genre les conditions ternaires ?"
- Quelqu'un, la semaine dernière

COMPLEXITÉ DU CODE

COMPLEXITÉ DU CODE

// om_modules/accounting/observers/AccountingRefundRequestObserver.php

public function update(SplSubject $oSubject)
{
    // ...

    try {
        if ("RefundRequest" == get_class($oSubject)) {
            // ...

            if ($oPayment) {
                if ('DC' == $oPayment->getPlugin() && $oSubject->getAmountRefunded() > 0) {
                    // ...

                    foreach ($aoOrderLine as $oOrderLine) {
                        // ...

                        if ($oEcatProduct) {
                            // ...

                            if ($oEcatNomenclature) {
                                if (Application::ESMILE_BT == $oEcatProduct->getIdApp()) {
                                    $oAccountingLoyalty->setIdSector("B".$oEcatNomenclature->getIdElement());
                                } else {
                                    $oAccountingLoyalty->setIdSector($oEcatNomenclature->getIdElement());
                                }
                            } else {
                                $oAccountingLogger->error(/* ... */);
                            }

                            // ...
                        }
                    }
                }
            }
        }
    } catch (Exception $e){
        $oAccountingLogger->error(/* ... */);
    }
}

COMPLEXITÉ DU CODE

  • Éviter l'imbrication de structures logiques

  • Sortir dès que possible (return, throw, break, continue)

  • Utiliser des exceptions et les gérer au niveau le plus haut

COMPLEXITÉ DU CODE

public function update(SplSubject $oSubject)
{
    // ...

    try {
        if ("RefundRequest" != get_class($oSubject)) {
            return;
        }

        // ...

        if (null === $oPayment || 'DC' != $oPayment->getPlugin() || 0 >= $oSubject->getAmountRefunded()) {
            return;
        }

        // ...

        foreach ($aoOrderLine as $oOrderLine) {
            // ...

            if (null === $oEcatProduct) {
                continue;
            }

            // ...

            if (null === $oEcatNomenclature) {
                throw new Exception(/* ... */);
            }

            $idSector = $oEcatNomenclature->getIdElement();

            if (Application::ESMILE_BT == $oEcatProduct->getIdApp()) {
                $idSector = 'B'.$idSector;
            }

            $oAccountingLoyalty->setIdSector($idSector);

            // ...
        }
    } catch (Exception $e){
        $oAccountingLogger->error(/* ... */);
    }
}

COMPLEXITÉ DU CODE

  • Lisibilité
  • Maintenabilité
  • Performance
  • = Du temps
  • = De l'argent

PAS DE MÉTIER DANS UN CONTRÔLEUR

PAS DE MÉTIER DANS UN CONTRÔLEUR

  • Une logique métier doit se trouver une fois, au bon endroit
  • À quelle fréquence ce bon endroit sera-t-il un contrôleur ?

PAS DE MÉTIER DANS UN CONTRÔLEUR

PAS DE MÉTIER DANS UN CONTRÔLEUR

public function createRefundAction($aiIdOrderItem, $iIdReason)
{
    return PaymentRefundServices::createRefund($aiIdOrderItem, $iIdReason);
}

PAS DE MÉTIER DANS UN CONTRÔLEUR

  • Des classes et méthodes métier avec un nom explicite
  • = Un code proche du langage naturel
  • Localisation de la réponse à un problème métier donné

PAS DE MÉTIER DANS UN CONTRÔLEUR

Single responsibility principle

PAS DE MÉTIER DANS UN CONTRÔLEUR

  • Lisibilité
  • Maintenabilité
  • Réutilisabilité
  • = Du temps
  • = De l'argent

TYPE-HINTING

TYPE-HINTING

// busmodules/product/plugin/article/views/AjaxController.php

private function _saveUnitAssoc($iIdUnitCurrent, $sCurrentPrefix, $aArticlesInfos)
{
    // ...

    if ($aArticlesInfos == null) {
        $aArticlesInfos = array();
    }

    $tmp = is_array($aArticlesInfos) ? $aArticlesInfos : array($aArticlesInfos);

    // ...
}

TYPE-HINTING

  • Vous ne pouvez pas prévoir tous les cas possibles
  • C'est au code appelant d'utiliser une méthode correctement
  • Méthodes plus courtes (non Fabien)

TYPE-HINTING

private function _saveUnitAssoc($iIdUnitCurrent, $sCurrentPrefix, array $aArticlesInfos)
{
    // Si on passe autre chose qu'un tableau, on se mange une fatal, et c'est bien fait
}

TYPE-HINTING

  • array
  • callable
  • types scalaires (HHVM, PHP 7 *fingers crossed*)
  • classes et interfaces, évidemment (on y arrive)

TYPE-HINTING

"Et si je veux rendre le paramètre facultatif ?"
- Toi là-bas (je t'ai entendu)

TYPE-HINTING

private function _saveUnitAssoc($iIdUnit, $sCurrentPrefix, array $aArticlesInfos = null)
{
    // Pas mal
}
private function _saveUnitAssoc($iIdUnit, $sCurrentPrefix, array $aArticlesInfos = array())
{
    // Mieux
}

TYPE-HINTING

  • Lisibilité
  • Maintenabilité
  • Performance
  • = Du temps
  • = De l'argent

INJECTION DE DÉPENDANCE

INJECTION DE DÉPENDANCE

Dependency inversion principle

INJECTION DE DÉPENDANCE

// busmodules/order/OrderModule.php

public static function createLfcodebarreEntryByIdFacture($iIdFacture, $iIdSupplyingOrder)
{
    $oOrderModule = OrderModule::getInstance("order");
    $aoLfProduitLines = new LfproduitIterator('idfacture = '.$iIdFacture);

    //...

    $aProductShippingFeeBarcode = $oOrderModule->getProductsShippingBarcode();

    // ...
}

INJECTION DE DÉPENDANCE

  • Ma méthode a besoin d'un objet ?
  • Je ne l'instancie pas dedans, je le lui donne

INJECTION DE DÉPENDANCE

public function createLfcodebarreEntryByIdFacture(
    OrderModule $module,
    LfproduitIterator $iterator,
    $iIdSupplyingOrder
) {
    $aProductShippingFeeBarcode = $module->getProductsShippingBarcode();

    // ...
}

INJECTION DE DÉPENDANCE

  • Responsabilité du code appelant (again)
  • Code statique = procédural = pas testable
  • Dépendances mockables = testable !
  • Méthodes plus courtes (j'ai dit non Fabien)

INJECTION DE DÉPENDANCE

  • Lisibilité
  • Maintenabilité
  • = Du temps
  • = De l'argent

CONTENEUR

CONTENEUR

  • Injection de dépendance à son paroxysme
  • Notion de service

CONTENEUR

CONTENEUR

  • Réduction du boilerplate lié aux conceptions objet complexes
  • Natif dans Symfony (fichiers de configuration)

CONTENEUR

  • Lisibilité
  • Maintenabilité
  • Réutilisabilité
  • = Du temps
  • = De l'argent

COMPOSITION

COMPOSITION

  • Partage horizontal de code / de fonctionnalités
  • Par opposition à l'héritage (vertical)

COMPOSITION

class MasterDataProductTransformer implements TransformerInterface
{
    public function transform(Produit $product)
    {
        $masterDataProduct = new MasterDataProduct();
        $masterDataProduct->setId($product->getIdproduit());
        $masterDataProduct->setName($product->getNomproduit());

        return $masterDataProduct;
    }
}

COMPOSITION

Comment gérer la transformation des produits contenus dans un itérateur ?

COMPOSITION

class ProduitTransformableIterator extends ProduitIterator
{
    public function transformAll(TransformerInterface $transformer)
    {
        $products = array();

        foreach ($this as $product) {
            $products[] = $transformer->transform($product);
        }

        return $products;
    }
}

class ProduitSomethingSomethingIterator extends ProduitTransformableIterator
{
    // ...
}

COMPOSITION

  • Couplage fort
  • Héritage utilisé comme rustine : pas de sens sémantique
  • Impossible d'utiliser ce pattern deux fois

COMPOSITION

class IteratorTransformerService
{
    private $transformer;

    public function __construct(TransformerInterface $transformer)
    {
        $this->transformer = $transformer;
    }

    public function transformAll(Iterator $iterator)
    {
        $results = array();

        foreach ($iterator as $item) {
            $results[] = $transformer->transform($item);
        }

        return $results;
    }
}

COMPOSITION

  • Couplage faible
  • Injection de dépendance : sémantiquement correct
  • Pattern utilisable à l'infini

COMPOSITION

trait TransformableIterator
{
    public function transformAll(TransformerInterface $transformer)
    {
        $results = array();

        foreach ($this as $item) {
            $results[] = $transformer->transform($item);
        }

        return $results;
    }
}

class ProductSomethingSomethingIterator
{
    use TransformableIterator;
}

COMPOSITION

  • Couplage mi-fort
  • Économie de classes = simplicité
  • Pattern utilisable à l'infini

COMPOSITION

  • Maintenabilité
  • Réutilisabilité
  • = Du temps
  • = De l'argent

CODING STANDARDS

CODING STANDARDS

"Ideally, all of the code should look like it was written by one person in one sitting."
- Quelqu'un sur Stack Overflow qui a bien raison

CODING STANDARDS

  • Pas d'adaptation aux préférences de chacun
  • Facilite la mise en place de bonnes pratiques

CODING STANDARDS

  • Lisibilité
  • Maintenabilité
  • = Du temps
  • = De l'argent

EN CONCLUSION ?

SO MUCH CA$$$H

SO MUCH HAPPINESS

SO MUCH THANK YOU

Des questions ?

Comment simplifier son code PHP

By Tom Panier

Comment simplifier son code PHP

Software design for dummies

  • 3,023