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 neemzy
Comment simplifier son code PHP
Software design for dummies
- 3,130