Muchas veces se nos llena la boca hablando de Clean code, de hacer código limpio, de seguir los principios SOLID, pero escribir ¿cómo podemos escribir código difícil de mantener en PHP? ¿cómo hacer que todo a tu alrededor dependa de nosotros? Eso no parece sencillo, pero siguiendo los siguientes consejos podemos conseguir el famoso vendor lock-in y así volvernos una pieza indispensable de nuestra compañía.
Este post está hecho en tono irónico/sátira. No nos tomemos al pie de la letra el contenido

No uses estándares como PSR-4
Para poder hacer «autoload» de las clases necesitamos añadir una pequeña configuración a nuestro fichero composer.json
{
"autoload": {
"psr-4": {
"App\\": "src"
}
}
}
Con estas lines, cada fichero que esté dentro del directorio src
se corresponderá con una clase, pero esto podemos hackearlo para que añadir un nuevo fichero, ya sea una clase, interfaz, test,… sea toda una odisea:
Añadiendo más de una clase al mismo fichero
Si añadimos más de una clase al mismo fichero ahorraremos en número de ficheros y quizás ganemos en dolores de cabeza 🙂
<?php declare(strict_types=1);
class FatalException extends Exception
{
}
class ApplicationException extends Exception
{
}
class RequestException extends Exception
{
}
Usar minúsculas en los nombres de los directorios
Usar minúsculas en los nombres de los directorios no es una buena idea, o quizá si porque el autoload se volverá loco y pasaremos un rato debugando que siempre es divertido.
/app
/controller
ProductController.php
Por cierto, ya que hablamos de esto de las mayúsculas y minúsculas si usas Macs todo se vuelve más divertido. Parece que el sistema operativo no distingue muy bien entre directorios en mayúscula y directorios en minúscula…
╰─$ cd /Users
╭─jesuslopez@MacBook-Pro-de-Admin /Users
╰─$ ls
Shared admin jesuslopez
╭─jesuslopez@MacBook-Pro-de-Admin /Users
╰─$ cd /users
╭─jesuslopez@MacBook-Pro-de-Admin /users
╰─$ ls
Shared admin jesuslopez
Usar tu propio Autoload
Hay bastantes librerías que usan su propio autoload, es más quizás podemos pensar que el autoload nos hace perder microsegundos y que es mucho mejor «crear nuestro propio autoload» utilizando spl_autoload_register
o usar includes
. Si hemos llegado a la conclusión de que nuestro cuello de botella está en que el autoload es lento y no hay nada más que mejorar… avísame que me gustaría trabajar en un sitio como ese 🙂
Queremos ser libres
A los programadores nos gusta de poder cambiar el código, tener flexibilidad, poco acoplamiento,… aquí veremos como conseguir esa sensación de «libertad»
No utilizar final
Si no utilizamos Final podremos Mockear cualquier clase en nuestros test y no necesitaremos interfaces, eso es un ahorro en número de ficheros.
<?php declare(strict_types=1);
namespace App;
class TaxCalculator
{
}
Otra opción que mejorará mucho nuestro código es la herencia, si usamos final
estamos haciendo más difícil la herencia. Por ejemplo cuando estemos construyendo nuestro propio ORM…
class Db { /* ... */ }
class Core extends Db { /* ... */ }
class User extends Core { /* ... */ }
class Admin extends User { /* ... */ }
class Bot extends Admin { /* ... */ }
class BotThatDoesSpecialThings extends Bot { /* ... */ }
class PatchedBot extends BotThatDoesSpecialThings { /* ... */ }
Si queremos aprender un poco más sobre porque sí o porque no debemos utilizar final
este post de Ocramius es interesante: https://ocramius.github.io/blog/when-to-declare-classes-final/
Usar siempre protected
Si de verdad queremos clases abiertas ¿por qué usamos métodos private
? Es mucho mejor que utilicemos protected
así podremos sobreescribir cualquier método de manera mucho más sencilla.
?php declare(strict_types=1);
class TaxCalculator
{
protected function calculatePrice(Product $product)
{
// ...
}
}
class BetterPriceCalculator extends TaxCalculator
{
protected function calculatePrice(Product $product)
{
return 1000;
}
}
Por aquí hay un post antiguo de Fabien Potencier comentando algo como «private» is for purists and «protected» is for pragmatic developers. http://fabien.potencier.org/pragmatism-over-theory-protected-vs-private.html
Usemos lo que usemos lo más importante: no usemos public
porque sino quedaría obvio que no estamos siguiendo los principios SOLID.
métodos dinámicos
Esto es un truco para que parezcamos programadores senior y qué hará nuestro código versátil.
<?php
class Calculator
{
public function calculate(Product $product, string $type)
{
$methodName = 'calculate' . $type; // aquí está la magia
return $this->$methodName($product);
}
}
methodName
es un string que puede ser cualquier cosa, eso nos proporciona libertad y dinamísmo.
Un ejemplo práctico donde podemos utilizar este enfoque es en los controladores, puede venir una variable que nos diga qué método debemos utilizar.
call_user_func es una función muy útil
<?php
class ProductController
{
public function orderAction(Request $request)
{
$form = new Form;
$form->afterSubmit = [$this, 'processForm']; // great job!
$form->handle($request);
}
public function processForm(Request $request)
{
// ...
}
}
class Form
{
public $afterSubmit;
public function handle(Request $request)
{
// ...
call_user_func($this->afterSubmit, $request);
}
}
Imaginemos que estamos en una base de código legado, con pocos test y que encontramos que un método se utiliza sólo en 20 sitios. Si intentamos renombrar processForm
a algo distinto como processOrder
seguramente nuestro IDE no detecte los usos de call_user_func
y nos toque hacerlo a mano buscando la cadena processForm
y analizando cada uno de los casos que nos encontremos… con esto conseguiremos entender mucho mejor todo el código porque seguro que tenemos que leerlo casi todo 🙂
Usar anotaciones para definir métodos mágicos
Siguiendo con el caso anterior podemos llevar nuestro código al siguiente nivel, usando anotaciones, eso hace nuestro código mucho más versátil. ¡Qué recuerdos con la API de guzzle!
**
* @method ResponseInterface get(string|UriInterface $uri, array $options = [])
* @method ResponseInterface head(string|UriInterface $uri, array $options = [])
* @method ResponseInterface put(string|UriInterface $uri, array $options = [])
* @method ResponseInterface post(string|UriInterface $uri, array $options = [])
* @method ResponseInterface patch(string|UriInterface $uri, array $options = [])
* @method ResponseInterface delete(string|UriInterface $uri, array $options = [])
* @method Promise\PromiseInterface getAsync(string|UriInterface $uri, array $options = [])
* @method Promise\PromiseInterface headAsync(string|UriInterface $uri, array $options = [])
* @method Promise\PromiseInterface putAsync(string|UriInterface $uri, array $options = [])
* @method Promise\PromiseInterface postAsync(string|UriInterface $uri, array $options = [])
* @method Promise\PromiseInterface patchAsync(string|UriInterface $uri, array $options = [])
* @method Promise\PromiseInterface deleteAsync(string|UriInterface $uri, array $options = [])
*/
class Client implements ClientInterface....
Aunque es cierto que hay librerías como Doctrine que utilizan annotations
y son super útiles normalmente debugeando para saber que es lo que está pasando con esta o aquella anotación.
Usar Traits con annotations para definir métodos mágicos
Esto sí que es otro nivel. Los traits nos pueden ayudar bastante en los test para crear algunos objetos o para tener funciones de utilidades, pero si de verdad queremos mejorar y que nuestro código sea de 0 lo mejor es utilizar algo como esto:
<?php declare(strict_types=1);
class ApiCaller
{
use GetMethodTrait;
}
/**
* @method getPackagesByOrganization($organization)
* @method getPackagesByCategory($category)
*/
trait GetMethodTrait
{
}
El análisis estático no volverá a molestarnos con esto ni tampoco el typehinting.
Ocultar dependencias
Este es uno de mi favoritos. Pasar dependencias a través del constructor hace a las clases más difíciles de instanciar. Es mucho mejor el new en mitad del método.
<?php
class PackagistApi
{
public function getPackagesByOrganization(string $name): array
{
$guzzle = new Guzzle\Client();
$response = $guzzle->get('https://packagist.org/packages/list.json?vendor=' . $name);
// ...
return $packages;
}
}
$symfonyPackages = (new PackagistApi)->getPackagesByOrganization('symfony');
Aunque cuidado, si enviamos esto a una code review quizás nos la rechacen con un comentario diciendo: las dependencias se pasan por el constructor así que siempre podemos enviar las dependencias al constructor así:
<?php
class PackagistApi
{
private $guzzle;
public function __construct()
{
$this->guzzle = new Guzzle\Client();
}
public function getPackagesByOrganization(string $name): array
{
$response = $this->guzzle->get('https://packagist.org/packages/list.json?vendor=' . $name);
// ...
return $packages;
}
}
Si nos encontramos en un código que hace cosas como esta y queremos mejorarlo quizás nos interese echar un ojo a la kata tripservice de Sandro Mancuso. Hace un tiempo la resolvimos en este post: https://jesuslc.com/2015/12/09/haciendo-testing-unitario-al-100/
Interface, Trait, Class todos pueden tener el mismo nombre
Todo el mundo sabe que es un «bad smell» tener una interfaz que sea ProductInterface
porque el nombre Interface indica que no hemos sabido diseñar del todo bien nuestro código, es mucho mejor tener nombres concretos, así pasamos de tener:ProductInterface
,Product
,ProductTrait
a que todo sea Product
.
Conseguiremos que nuestro código sea mucho más entendible de cerca, pero cuando naveguemos por lso directorios o cuando intentemos buscar, solo encontraremos Product

Fluent interfaces
Las fluent interfaces son geniales, nos ahorran un montón de tiempos y el tener que escribir el nombre de la variable una y otra vez
<?php declare(strict_types=1);
class Definition
{
private $class;
private $arguments;
public function setClass(array $class)
{
$this->class = $class;
return $this;
}
public function setArguments(array $arguments)
{
$this->arguments = $arguments;
return $this;
}
}
$definition = (new Definition)->setClass('ProductController')
->setArguments(['@Request']);
Viendo esto puede parecer que todo está bien, pero si queremos conocer un poco más acerca de por qué las fluent interfaces solo tienen sentido a veces podemos leer este post de ocramius https://ocramius.github.io/blog/fluent-interfaces-are-evil/
Git nuestro gran aliado
Somos «buenos» profesionales y por eso tenemos que usar git. Así que aquí una serie de trucos para que todo vaya mejor.
composer.lock siempre nos da muchos conflictos
composer.lock siempre nos da muchos conflictos cuando hacemos un merge, así que es mucho mejor ignorarlo. Ganaremos velocidad y los merges serán mucho más limpios.
el directorio /vendor mejor en el repositorio
Es mucho mejor tener nuestras dependencias controladas y que todo el directorio vendor
esté dentro de nuestro git. Además si alguna de las librerías que utilizamos tiene un «bug» siempre podremos arreglarlo y commitear ese cambio en nuestro vendor y «todo arreglado».
ignorar no está bien visto
Añadir directorios o ficheros al gitignore no siempre funciona bien, es más a veces es complicado. Es mucho mejor dejar esos ficheros «untracked» y no preocuparse. Tan solo tenemos que avisar por slack a los compañeros de que los ficheros del directorio app/web/css
no hay que comitearlos porque se generan en nuestro ordenador.
Conclusiones
Este post es una «sátira» sobre cómo no deberíamos escribir software. Si de verdad nos importa lo que hacemos, si nos importa mejorar cómo programador intentemos no utilizar los puntos que damos en este post.
Otro punto a tener en cuenta es la «deuda técnica visual», es decir, no usar las opciones que nos proporciona PHP para hacer nuestro código más robusto (typehinting, final, visibilidad de las contantes,…)
Este post está basado en https://tomasvotruba.com/blog/2018/11/26/14-tips-to-write-php-code-that-is-hard-to-maintain-and-upgrade/