Estás desarrollando una aplicación PHP y te enfrentás a un problema que seguro alguien ya resolvió antes.
Para qué re-inventar la rueda, ¿cierto?
Abrís una nueva pestaña, entrás a Google y escribís «Cómo hacer X con php».
La lista es larga, pero hay un factor común en todos los ejemplos: composer
.
Ahí está otra vez.
Composer por aquí, composer por allá.
Por todos lados te muestran cosas como:
php composer require base/demo-base-code
Como quien dice «Buenos días».
Y, a pesar de no ser precisamente un newbie, te frustra tener que copiar y pegar… y rezar.
Necesitás entender qué es composer de una buena vez.
Así que… ¿de qué se trata el famoso php
composer
?
Qué es Composer
Composer es un gestor de dependencias para PHP (Similar a lo que npm es para JavaScript o pip para Python).
Es una aplicación PHP que ayuda a administrar las librerías desarrolladas por terceros que vas a incorporar a tu proyecto.
De algún modo podrías considerarlo como el heredero de PEAR.
Hoy día se considera el gestor de dependencias de-facto por unas cuantas buenas razones:
- Es muy simple de operar
- Cuenta con un repositorio super completo (Packagist)
- Disminuye significativamente los problemas de cambio de ambiente de ejecución (Mediante su funcionalidad de congelar dependencias)
Si nunca lo usaste, te recomiendo que lo pruebes.
Cómo se instala Composer
La instalación de composer es sumamente simple. Como dice el sitio getcomposer.org en su página de descargas:
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" php -r "if (hash_file('SHA384', 'composer-setup.php') === '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" php composer-setup.php php -r "unlink('composer-setup.php');"
A partir de ahí tendrás disponible un comando llamado composer
(Qué sorpresa ¿no?). Si lo ejecutas sin decir más nada te va a dar una lista de los sub-comandos disponibles:
about Shows the short information about Composer. archive Creates an archive of this composer package. browse Opens the package's repository URL or homepage in your browser. clear-cache Clears composer's internal package cache. clearcache Clears composer's internal package cache. config Sets config options. create-project Creates new project from a package into given directory. depends Shows which packages cause the given package to be installed. diagnose Diagnoses the system to identify common errors. dump-autoload Dumps the autoloader. dumpautoload Dumps the autoloader. exec Executes a vendored binary/script. global Allows running commands in the global composer dir ($COMPOSER_HOME). help Displays help for a command home Opens the package's repository URL or homepage in your browser. info Shows information about packages. init Creates a basic composer.json file in current directory. install Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json. licenses Shows information about licenses of dependencies. list Lists commands outdated Shows a list of installed packages that have updates available, including their latest version. prohibits Shows which packages prevent the given package from being installed. remove Removes a package from the require or require-dev. require Adds required packages to your composer.json and installs them. run-script Runs the scripts defined in composer.json. search Searches for packages. self-update Updates composer.phar to the latest version. selfupdate Updates composer.phar to the latest version. show Shows information about packages. status Shows a list of locally modified packages. suggests Shows package suggestions. update Updates your dependencies to the latest version according to composer.json, and updates the composer.lock file. validate Validates a composer.json and composer.lock. why Shows which packages cause the given package to be installed. why-not Shows which packages prevent the given package from being installed.
Te voy a mostrar algunas pocas cosas como para que veas el poder que tiene:
Cómo funciona Composer
Composer trabaja con dos archivos de configuración: composer.json
y composer.lock
.
El segundo es más bien de uso interno así que no hace falta conocer muchos detalles sobre él.
El primero en cambio es el que vas a usar bastante a menudo ya que en él se declaran todas las dependencias que tu sistema tendrá.
Es muy simple de leer (al igual que cualquier json) y permite una gran flexibilidad para definir las dependencias (por ejemplo se puede decir a partir de qué versión se acepta) tanto de librerías escritas en php como del intérprete y sus extensiones.
Típicamente un archivo composer.json se ve más o menos así:
{ "name": "leeway/scrapper", "description": "Un pequeño web scrapper", "minimum-stability": "stable", "license": "proprietary", "authors": [ { "name": "Mauro Chojrin", "email": "mauro.chojrin@leewayweb.com" } ], "require": { "fabpot/goutte": "v3.2.0" } }
Donde la parte más importante es lo que está dentro de require
(Las dependencias específicas del proyecto).
Se puede crear el archivo «manualmente» o, mejor, se puede usar el comando composer init
que lo hace por nosotros (y aparte permite definir algunas propiedades en forma interactiva).
Pero claramente, de poco sirve la declaración de las dependencias si no tenemos las librerías instaladas…
Cómo instalar librerías usando Composer
Para instalar librerías composer dispone del comando install
, el cual se encarga de bajarlas, armar el autoloading y demás… ¡magia!.
Lo único que te queda por hacer es require_once 'vendor/autoload.php';
donde vayas a usar las librerías instaladas y voilà.
Hasta acá todo muy lindo, se pueden instalar y usar dependencias con muy poco esfuerzo pero… ¿Te pasó alguna vez que desarrollaste un sistema, en tu computadora andaba 10 puntos pero al subirlo a producción daba algún error raro?
¿Te pasó estar horas dando vueltas sin entender nada hasta que te diste cuenta de que la versión 3.4.1 de la librería de envío de mails no era compatible con la 3.4.0 (Instalada en producción)?
Justamente para eso composer tiene el archivo composer.lock
. Si bien es un archivo de texto, ¡no lo toques!.
Te muestro el que corresponde al composer.json
que veías:
{ "_readme": [ "This file locks the dependencies of your project to a known state", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], "content-hash": "cc70bd9c59b4fdbb7e8e632402e15bcc", "packages": [ { "name": "fabpot/goutte", "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/Goutte.git", "reference": "8cc89de5e71daf84051859616891d3320d88a9e8" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/8cc89de5e71daf84051859616891d3320d88a9e8", "reference": "8cc89de5e71daf84051859616891d3320d88a9e8", "shasum": "" }, "require": { "guzzlehttp/guzzle": "^6.0", "php": ">=5.5.0", "symfony/browser-kit": "~2.1|~3.0", "symfony/css-selector": "~2.1|~3.0", "symfony/dom-crawler": "~2.1|~3.0" }, "type": "application", "extra": { "branch-alias": { "dev-master": "3.2-dev" } }, "autoload": { "psr-4": { "Goutte\\": "Goutte" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" } ], "description": "A simple PHP Web Scraper", "homepage": "https://github.com/FriendsOfPHP/Goutte", "keywords": [ "scraper" ], "time": "2016-11-15T16:27:29+00:00" }, { "name": "guzzlehttp/guzzle", "version": "6.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699", "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699", "shasum": "" }, "require": { "guzzlehttp/promises": "^1.0", "guzzlehttp/psr7": "^1.4", "php": ">=5.5" }, "require-dev": { "ext-curl": "*", "phpunit/phpunit": "^4.0 || ^5.0", "psr/log": "^1.0" }, "suggest": { "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { "dev-master": "6.2-dev" } }, "autoload": { "files": [ "src/functions_include.php" ], "psr-4": { "GuzzleHttp\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" } ], "description": "Guzzle is a PHP HTTP client library", "homepage": "http://guzzlephp.org/", "keywords": [ "client", "curl", "framework", "http", "http client", "rest", "web service" ], "time": "2017-06-22T18:50:49+00:00" }, { "name": "guzzlehttp/promises", "version": "v1.3.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", "shasum": "" }, "require": { "php": ">=5.5.0" }, "require-dev": { "phpunit/phpunit": "^4.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.4-dev" } }, "autoload": { "psr-4": { "GuzzleHttp\\Promise\\": "src/" }, "files": [ "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" } ], "description": "Guzzle promises library", "keywords": [ "promise" ], "time": "2016-12-20T10:07:11+00:00" }, { "name": "guzzlehttp/psr7", "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", "shasum": "" }, "require": { "php": ">=5.4.0", "psr/http-message": "~1.0" }, "provide": { "psr/http-message-implementation": "1.0" }, "require-dev": { "phpunit/phpunit": "~4.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.4-dev" } }, "autoload": { "psr-4": { "GuzzleHttp\\Psr7\\": "src/" }, "files": [ "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, { "name": "Tobias Schultze", "homepage": "https://github.com/Tobion" } ], "description": "PSR-7 message implementation that also provides common utility methods", "keywords": [ "http", "message", "request", "response", "stream", "uri", "url" ], "time": "2017-03-20T17:10:46+00:00" }, { "name": "psr/http-message", "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", "shasum": "" }, "require": { "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Common interface for HTTP messages", "homepage": "https://github.com/php-fig/http-message", "keywords": [ "http", "http-message", "psr", "psr-7", "request", "response" ], "time": "2016-08-06T14:39:51+00:00" }, { "name": "symfony/browser-kit", "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", "reference": "317d5bdf0127f06db7ea294186132b4f5b036839" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/browser-kit/zipball/317d5bdf0127f06db7ea294186132b4f5b036839", "reference": "317d5bdf0127f06db7ea294186132b4f5b036839", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8", "symfony/dom-crawler": "~2.8|~3.0" }, "require-dev": { "symfony/css-selector": "~2.8|~3.0", "symfony/process": "~2.8|~3.0" }, "suggest": { "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.3-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\BrowserKit\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", "time": "2017-10-02T06:42:24+00:00" }, { "name": "symfony/css-selector", "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", "reference": "07447650225ca9223bd5c97180fe7c8267f7d332" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/css-selector/zipball/07447650225ca9223bd5c97180fe7c8267f7d332", "reference": "07447650225ca9223bd5c97180fe7c8267f7d332", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.3-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\CssSelector\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Jean-François Simon", "email": "jeanfrancois.simon@sensiolabs.com" }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", "time": "2017-10-02T06:42:24+00:00" }, { "name": "symfony/dom-crawler", "version": "v3.3.10", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", "reference": "40dafd42d5dad7fe5ad4e958413d92a207522ac1" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/40dafd42d5dad7fe5ad4e958413d92a207522ac1", "reference": "40dafd42d5dad7fe5ad4e958413d92a207522ac1", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { "symfony/css-selector": "~2.8|~3.0" }, "suggest": { "symfony/css-selector": "" }, "type": "library", "extra": { "branch-alias": { "dev-master": "3.3-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\DomCrawler\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", "time": "2017-10-02T06:42:24+00:00" }, { "name": "symfony/polyfill-mbstring", "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", "shasum": "" }, "require": { "php": ">=5.3.3" }, "suggest": { "ext-mbstring": "For best performance" }, "type": "library", "extra": { "branch-alias": { "dev-master": "1.6-dev" } }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, "files": [ "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", "keywords": [ "compatibility", "mbstring", "polyfill", "portable", "shim" ], "time": "2017-10-11T12:05:26+00:00" } ], "packages-dev": [], "aliases": [], "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": [], "platform-dev": [] }
Es un json por dentro (Bastante más extenso y complejo que el original) pero su objetivo es el de congelar las dependencias que efectivamente se están usando en tu código.
Si mirás de nuevo el composer.json vas a notar que la versión de Goutte está expresada en forma explícita (v3.2.0) pero, si mirás un poco más profundamente (por ejemplo en el archivo vendor/fabpot/goutte/composer.json) verás que hay otras que no son tan estrictas:
{ "name": "fabpot/goutte", "type": "application", "description": "A simple PHP Web Scraper", "keywords": ["scraper"], "homepage": "https://github.com/FriendsOfPHP/Goutte", "license": "MIT", "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" } ], "require": { "php": ">=5.5.0", "symfony/browser-kit": "~2.1|~3.0", "symfony/css-selector": "~2.1|~3.0", "symfony/dom-crawler": "~2.1|~3.0", "guzzlehttp/guzzle": "^6.0" }, "autoload": { "psr-4": { "Goutte\\": "Goutte" } }, "extra": { "branch-alias": { "dev-master": "3.2-dev" } } }
Aquí dice que se necesita una versión de Guzzle al menos 6.0.
Si ves en composer.lock notarás que la instalación de Guzzle es clara:
... { "name": "guzzlehttp/guzzle", "version": "6.3.0", ... }
Y es que 6.3.0 es al menos 6.0 pero, al momento de distribuir el código necesitamos saber exactamente qué versión estamos usando.
De modo que el composer.lock
se genera:
- Recorriendo el árbol de dependencias completo
- Especificando las versiones concretas que están instaladas actualmente
De esta forma, si el código se lleva a un entorno nuevo (o hay que reinstalarlo por alguna razón), la información en composer.lock
asegura que se esté usando exactamente el mismo set de dependencias que se usó en testing (Si el testing no fue suficiente es otro tema…).
Actualización de dependencias
Por otro lado, si querés probar lo último disponible podés (¡en desarrollo!) ejecutar el comando composer update
y composer
se encargará nuevamente de descargar las nuevas versiones de tus dependencias (siempre respetando tus especificaciones) para que puedas probarlas y generar un nuevo composer.lock
(Que obviamente vas a tener que committear).
Remoción de dependencias
Esto puede ser algo más tricky (Siempre es más fácil meter cosas que no se usarán que sacarlas y arriesgarse pero… si sabés lo que hacés…). El comando que se usa para esto es composer remove
.
¡A usarlo con juicio!
Y si necesitas ayuda…
Siempre tenés la posibilidad de ejecutar el comando composer help <comando>
.
Conclusión
Ahora ya sabés qué es Composer y cómo te puede simplificar la vida.
La próxima vez que vayas a leer un ejemplo de código comprenderás los pasos de la instalación y podrás leer el archivo composer.json
para ver qué otras dependencias vienen incluidas.
¿Todavía no descargaste composer
?
¡Hacelo ahora mismo!
Acá te dejo el link otra vez, ya vas a tener tiempo de volver acá a agradecerme.
- Cómo usar PHPUnit - 03/12/2024
- ¿Cómo instalar extensiones PHP en Docker? - 26/11/2024
- Cómo agregar una página de error 500 en un proyecto PHP - 31/10/2024
10 comentarios