Administrando proyectos PHP con Composer
Tags: php, composer
Published: 2021-04-21 20:40:07

Leer o escribir planillas de Excel, incorporar alguna pasarela de pago, autenticación vía redes sociales, etc... son necesidades muy comunes en las aplicaciones web modernas.

Si bien en teoría podrías desarrollar tus propios códigos para solventar estas necesidades, es claro que perderías tiempo muy valioso haciéndolo, en lugar de concentrarte en lo que realmente importa: resolver los problemas del negocio.

Por ello, lo más común es valerse de código que otros hayan desarrollado para tí.

En el caso de PHP, existen diversas formas de incorporar código de terceros a tus proyectos.

## El manejo de librerías de terceros en proyectos PHP 

Históricamente esto se realizaba simplemente descargando algún archivo comprimido y depositándolo en algún directorio que pareciera cómodo.

Un tiempo más tarde apareció en escena [PEAR](https://pear.php.net/) y todo mejoró bastante, sin embargo, todavía existían problemas derivados de la incompatibilidad entre versiones de diferentes librerías y lograr una aplicación de cierta envergadura se tornaba algo bastante complicado.

Uno de los grandes problemas que presentaba el manejo de dependencias a través de PEAR era la dificultad de replicar los cambios realizados en forma local en otros ambientes (pruebas, producción, etc…).

Todo eso cambió a partir de la aparición de [composer](https://getcomposer.org/).

## Composer al rescate

Composer fue realmente un _game-changer_ para los que desarrollamos utilizando php.

Se trata de una herramienta de muy alto nivel que administra las dependencias de nuestros proyectos de un modo sumamente cómodo.

El principal cambio que introduce composer es la incorporación de las dependencias al propio código del proyecto.

Esto hace muy sencilla la igualación de ambientes ya que todo lo que se necesita para ejecutar una determinada aplicación está presente en un archivo (Que puede/debe ser subido al controlador de versiones junto con el resto del proyecto).

El formato elegido para la configuración de composer es json, un formato simple de escribir y leer para humanos.

Composer define una serie de claves que pueden utilizarse para declarar qué paquetes de terceros deben ser instalados para que nuestra aplicación pueda funcionar.

Este es un ejemplo de archivo `composer.json` de uno de mis proyectos:

```json
{
    "name": "leewayweb/followapp",
    "license": "proprietary",
    "type": "project",
    "minimum-stability": "stable",
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        },
        "psr-0": {
            "": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Tests\\": "tests/"
        }
    },
    "require": {
        "php": "^7.3",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "abdielcs/expanded-collection-bundle": "^0.1.0",
        "doctrine/doctrine-bundle": "^2.0",
        "doctrine/doctrine-cache-bundle": "^1.2",
        "doctrine/doctrine-migrations-bundle": "^2.0",
        "doctrine/orm": "^2.7",
        "fabpot/goutte": "^3.2",
        "knplabs/knp-menu-bundle": "^3.0",
        "nelexa/zip": "^3.3",
        "phpoffice/phpspreadsheet": "^1.10",
        "scheb/yahoo-finance-api": "^3.1",
        "sensio/framework-extra-bundle": "^5.0",
        "sunra/php-simple-html-dom-parser": "^1.5",
        "symfony/asset": "4.4.*",
        "symfony/console": "*",
        "symfony/dotenv": "*",
        "symfony/flex": "^1.5",
        "symfony/form": "4.4.*",
        "symfony/framework-bundle": "*",
        "symfony/http-client": "4.4.*",
        "symfony/monolog-bundle": "^3.5",
        "symfony/orm-pack": "^1.0",
        "symfony/polyfill-apcu": "^1.0",
        "symfony/security-bundle": "4.4.*",
        "symfony/swiftmailer-bundle": "^3.4",
        "symfony/translation": "4.4.*",
        "symfony/twig-pack": "^1.0",
        "symfony/validator": "4.4.*",
        "symfony/web-server-bundle": "4.4.*",
        "symfony/yaml": "*",
        "twbs/bootstrap": "^4.3"
    },
    "require-dev": {
        "dama/doctrine-test-bundle": "^6.3",
        "doctrine/doctrine-fixtures-bundle": "^3.3",
        "easycorp/easy-deploy-bundle": "^1.0",
        "fzaninotto/faker": "^1.9",
        "symfony/maker-bundle": "^1.14",
        "symfony/phpunit-bridge": "^4.0",
        "symfony/profiler-pack": "^1.0",
        "vimeo/psalm": "^3.7"
    },
    "config": {
        "preferred-install": {
            "*": "dist"
        },
        "sort-packages": true
    },
    "scripts": {
        "auto-scripts": {
            "cache:clear": "symfony-cmd",
            "assets:install %PUBLIC_DIR%": "symfony-cmd"
        },
        "post-install-cmd": [
            "@auto-scripts"
        ],
        "post-update-cmd": [
            "@auto-scripts"
        ]
    },
    "conflict": {
        "symfony/symfony": "*"
    },
    "extra": {
        "symfony": {
            "allow-contrib": false,
            "require": "4.4.*"
        }
    }
}
```

Puede parecer un poco complejo a primera vista, ¿cierto? Pues no te dejes intimidar :)

Este proyecto está basado en el framework [Symfony](https://symfony.com/), con lo cual muchas de sus dependencias y configuraciones le dan soporte a esta herramienta.

De todo lo que se ve, la clave más importante es `require`, en ella se declaran todas las librerías de terceros que serán necesarias para correr la aplicación sin problemas.

En este ejemplo vemos que la primera es `"php": "^7.3"`, esto significa que para que la aplicación pueda ejecutarse el servidor debe contar con un intérprete de php en versión, al menos, 7.3.

Posteriormente vemos `"ext-ctype": "*"` y `"ext-iconv": "*"`, lo que indica que, además de la versión de php, se requieren estas extensiones instaladas.

Hasta aquí vemos las dependencias duras, aquellas que, si no se satisfacen, harán que composer arroje un error al intentar instalar/configurar la aplicación.

Las siguientes dependencias siguen el patrón `vendor/package`, esto permite inferir que se trata de paquetes escritos en php.

Lo que se ve a la derecha de la definición de las dependencias es una restricción de versión, una de las partes más interesantes y complejas de composer, por ahora sólo digamos que a través de estas expresiones podemos evitar conflictos entre las diferentes dependencias.

El segundo archivo importante que trae consigo composer es `composer.lock`.

Este archivo también tiene formato json, sin embargo no está pensado para ser editado manualmente.

Su objetivo es congelar las versiones que se utilizarán efectivamente para evitar esos problemas tipo “en mi casa andaba”.

¿Por qué no alcanza con el archivo composer.json?

Precisamente por la existencia de expresiones de versiones.

Por ejemplo, en el archivo .json encontraremos cosas como `"symfony/console": "*"` (Usar cualquier versión del componente `symfony/console`) y en el archivo .lock veremos: 

```json
{
            "name": "symfony/console",
            "version": "v4.4.8",
            "source": {
                "type": "git",
                "url": "https://github.com/symfony/console.git",
                "reference": "10bb3ee3c97308869d53b3e3d03f6ac23ff985f7"
            },
}
```

Es decir una referencia resuelta: un número de versión específico que será utilizado en nuestro proyecto.

Ambos archivos deben ser incorporados al controlador de versiones que utilicemos.

Veamos algunos ejemplos prácticos

## Cómo instalar composer

Lo primero que debemos hacer para utilizar composer es instalar la utilidad de línea de comandos.

La mejor forma de hacerlo es seguir las instrucciones del sitio [getcomposer.org](https://getcomposer.org).

Al día de hoy sería:

```
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
```

Para verificar que todo ha salida correctamente puedes correr:

```php composer.phar --version```

Y deberías ver algo como:

```
Composer version 2.0.11 2021-02-24 14:57:23
```

## Cómo crear un nuevo proyecto usando composer

Si bien es posible escribir directamente en el archivo `composer.json`, lo más recomendable es utilizar la herramienta que acabas de descargar.

Para crear un nuevo proyecto puedes utilizar este comando:

```php composer.phar init```

El cual te irá realizando una serie de preguntas y, en base a tus respuestas, generará el archivo apropiado.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/afnoz2tqfhh5f0v1uipe.png)

Una pregunta muy importante es esta:

```
Would you like to define your dependencies (require) interactively [yes]?
```

En general te recomiendo responder que sí, de esta forma podrás incorporar las librerías que utilizarás de una forma muy cómoda.

No te preocupes si aún no las conoces todas, incorporarlas posteriormente será muy sencillo también.

Cuando te pida `Search for a package:` ingresa `leewayweb/ci_php` y luego dale enter hasta el final.

Muy importante contestar sí a la pregunta `Would you like to install dependencies now [yes]?`

Al hacerlo verás cómo composer descarga todo lo necesario para que puedas empezar a desarrollar tu proyecto.

Al cabo de un momentito deberías ver algo como:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ztfnyfqoyxynvo6mixix.png)

Y si pides el listado de archivos de tu directorio encontrarás:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w85vgp21fqmvcnvvfa0o.png)

Se han creado los archivos `composer.json`, `composer.lock` y, muy importante, el directorio `vendor`.

En ese directorio se encontrarán todas las librerías que vayas a utilizar junto con un archivo de configuración de la función de carga automática (autoload), el cual deberás incluir en tu proyecto.

## Cómo utilizar librerías administradas por composer

Lo primero que debes hacer al comenzar un nuevo archivo en el que vayas a requerir usar alguna librería instalada vía composer es:

```php
<?php

require_once 'vendor/autoload.php';
```

Y luego podrás usar las clases definidas en tus librerías.

En nuestro caso quedaría así:

```php
<?php

require_once 'vendor/autoload.php';

use Leewayweb\CiValidator\CiValidator;

$validator = new CiValidator();

if ($validator->validate_ci($argv[1])) {
        echo 'Valid CI!';
} else {
        echo 'Invalid CI!';
}

echo PHP_EOL;
```

Este es un ejemplo de un script que permite validar cédulas de identidad uruguayas pero del mismo modo puedes usar cualquier librería.

## Cómo incorporar nuevas librerías

Para incorporar nuevas librerías tienes dos opciones:

1. Agregar manualmente una entrada en la clave `require` del archivo `composer.json` y ejecutar el comando `composer install`
2. Ejecutar el comando `composer require` y seguir las instrucciones

Supongo que a esta altura imaginarás cuál te recomendaré :)

## Qué otras operaciones pueden hacerse con composer

A través de composer es sumamente simple:

1. Eliminar dependencias
2. Actualizar versiones de dependencias
3. Verificar qué librerías requieren actualizaciones 
4. Comprender por qué una determinada dependencia no puede ser instalada
5. Crear tus propios comandos para ser ejecutados luego de actualizar dependencias

Entre otras que puedes conocer leyendo la documentación oficial.

## De dónde salen los paquetes de Composer

Supongo que para este momento te estarás preguntando de dónde sale toda esta magia.

La respuesta es el sitio [packagist.org](https://packagist.org).

Se trata de un enorme repositorio de paquetes instalables vía composer disponibles en forma pública.

Incluso puedes publicar tus propios paquetes ahí y contribuir a la comunidad :)

## De aquí en adelante

Bueno, ahora que has conocido composer espero te haya gustado tanto como a mí y te ayude a ahorrar tiempo y dolores de cabeza.

Si te ha quedado alguna duda o comentario te invito a que lo dejes por aquí o me contactes a través de mis redes sociales.