Etiqueta: Composer

  • Por qué PHP 8 no satisface el requisito ^7.3 de composer

    Por qué PHP 8 no satisface el requisito ^7.3 de composer

    He ejecutado el comando:

    composer install

    En un ordenador de desarrollo recién instalado. Me ha arrojado un error:

    Root composer.json requires php ^7.3 but your php version (8.1.0) does not satisfy that requirement

    ¿No se supone que si tengo la versión 8 de PHP ese requisito debería no dar un error?

    Me crucé con esta consulta recientemente y me pareció interesante responderla ya que, probablemente, quien la realizó no sea el único con este problema u otro similar.

    Claramente la respuesta a la pregunta ¿No se supone que si tengo la versión 8 de PHP ese requisito debería no dar un error? es un No rotundo. Vamos que si no fuese así la pregunta ni existiría pero bueno… tomémosla como una pregunta retórica y un buen disparador para este post.

    Es más, cambiaré la pregunta por la que puse como título: ¿Por qué PHP 8 no satisface el requisito ^7.3 de composer?

    Para responder a esta pregunta es necesario comprender qué significa el ^7.3 que está en el archivo composer.jon.

    Antes de meterme en los detalles es importante saber que, en este contexto, esa expresión corresponde a un requisito de versión (Version constraint).

    Cómo funcionan los version constraints de composer

    Una de las características más potentes (¡y más confusas!) de composer es esta: la especificación de las versiones aceptables de cada dependencia.

    Precisamente, el motivo de la creación de composer fue el poder evitar tener la versión X.Y.Z de una librería en desarrollo y luego, en producción encontrarte con la versión X+1.Y.Z y que todo explote misteriosamente.

    La premisa en la que se basa composer es que las dependencias definen sus versiones utilizando versionado semántico. Si este supuesto no se cumple… difícilmente composer pueda hacer bien su trabajo.

    Ahora, el modo de expresar estos requerimientos requiere prestarle bastante atención a los detalles.

    Para comenzar, existen diferentes tipos de restricción.

    La más simple de ellas es la exacta. Este tipo de restricción debe usarse cuando sabés exactamente qué versión de una dependencia querés usar. En este ejemplo sería algo como 7.3.33.

    En un caso como este no hay dudas, el trabajo de composer es bien sencillo: descargar los archivos correspondientes al tag 7.3.33 del repo de php.

    Un caso de uso más realista es aquel en que no sabés el número exacto de la versión si no un aproximado.

    Sería algo como decir quiero usar una versión de php 7.3, el patch no me interesa demasiado.

    Esto sería similar a decir quiero una versión que se encuentre entre la 7.3.0 y la última antes de la 7.4.0.

    Para expresar dicha restricción podés usar la combinación de dos restricciones:

    • >=7.3
    • <7.4

    En el caso de composer, estas se combinan usando un simple espacio en blanco, que se interpreta como un &:

    >=7.3 <7.4

    Un detalle importante: composer siempre responderá con la versión más reciente que pueda, es decir, aquella que satisfaga todas las restricciones solicitadas.

    Qué significan los símbolos de las versiones en composer

    Ahora sí, vamos a ir un poco más abajo y veamos cada uno de los símbolos que se pueden usar para especificar versiones.

    >= y < se explican por si mismos creo.

    Pero también existen ~, ^ y *… la cosa se complica, ¿no?

    Intentaré ir del más simple al más complejo.

    El * es un viejo conocido de quienes estamos en software. ¿Tal vez te suene de las experesiones regulares? Pues sí, es el comodín.

    Esto significa que una restricción de tipo 7.3.* se interpreta como cualquier patch de la rama 7.3. Es decir, composer mapeará este valor al tag más reciente que encuentre en dicha rama.

    El ~ es una forma abreviada de escribir una restricción tipo >= <.

    El ^ se comporta casi como ~ pero es algo más estricto.

    La idea tanto de ~ como de ^ es evitar problemas de incompatibilidad hacia atrás.

    Esta es la clave para comprender el problema que está experimentando el protagonista de esta historia:

    La restricción ^7.3 marca, ante todo, que el major de la versión de php a utilizar debe ser 7, el minor podría cambiar (y, por supuesto, también el patch).

    Esto se debe a que, en cualquier salto de versión major es posible encontrar cambios que rompan compatibilidad hacia atrás.

    Es por eso que composer no permitirá que inadevertidamente introduzcamos un cambio de infraestructura que ponga nuestro sistema en riesgo.

    El sistema de restricciones de versiones es bastante complejo, si quieres conocer más sobre él te recomiendo ir directo a la fuente.

    De hecho, si te encuentras en una situación como la que dio origen a este post, te recomiendo abrir ese documento en una ventana y tu composer.json en otra.

  • ¿Es mala práctica commitear el composer.lock?

    ¿Es mala práctica commitear el composer.lock?

    Acabas de montar tu proyecto.

    Es hora de instalar alguna que otra librería. Sin dudarlo un segundo arranca la seguidilla de composer require.

    Escribes algo de código, realizas tus pruebas, todo listo para hacer un commit.

    git add .

    git commit -m "Initial commit"

    Y de pronto… algo llama tu atención.

    ¿Por qué se están agregando dos archivos de composer?

    Más específicamente, ¿por qué el composer.json y el composer.lock?

    ¿Acaso el .lock no se genera automáticamente al ejecutar composer install?

    Es más, dando una rápida mirada se ve que el .lock pesa mucho más (¡muchísimo más!) que el .json:

    ¿Es realmente necesario engordar el repo con este archivo?

    Respuesta corta: sí.

    ¿Querés saber por qué?

    Te lo explico a continuación.

    composer.json vs. composer.lock

    Ambos archivos tienen formato json, así que, por este lado no pasa el tema.

    ¿Qué tiene composer.lock que no tenga composer.json?

    Si abrís ambos archivos notarás que son parecidos, pero no iguales.

    En principio, en el composer.json se almacenan los patrones de dependencias, mientras que en el composer.lock se almacenan las dependencias con sus versiones exactas (Más las dependencias de esas dependencias y otro montón de información).

    Un ejemplo:

    Donde composer.json dice:

    "doctrine/common": "^3.4"

    En composer.lock encontrarás:

    {
                "name": "doctrine/common",
                "version": "3.4.3",
                "source": {
                    "type": "git",
                    "url": "https://github.com/doctrine/common.git",
                    "reference": "8b5e5650391f851ed58910b3e3d48a71062eeced"
                },
                "dist": {
                    "type": "zip",
                    "url": "https://api.github.com/repos/doctrine/common/zipball/8b5e5650391f851ed58910b3e3d48a71062eeced",
                    "reference": "8b5e5650391f851ed58910b3e3d48a71062eeced",
                    "shasum": ""
                },
                "require": {
                    "doctrine/persistence": "^2.0 || ^3.0",
                    "php": "^7.1 || ^8.0"
                },
                "require-dev": {
                    "doctrine/coding-standard": "^9.0 || ^10.0",
                    "doctrine/collections": "^1",
                    "phpstan/phpstan": "^1.4.1",
                    "phpstan/phpstan-phpunit": "^1",
                    "phpunit/phpunit": "^7.5.20 || ^8.5 || ^9.0",
                    "squizlabs/php_codesniffer": "^3.0",
                    "symfony/phpunit-bridge": "^6.1",
                    "vimeo/psalm": "^4.4"
                },
                "type": "library",
                "autoload": {
                    "psr-4": {
                        "Doctrine\\Common\\": "src"
                    }
                },
                "notification-url": "https://packagist.org/downloads/",
                "license": [
                    "MIT"
                ],
                "authors": [
                    {
                        "name": "Guilherme Blanco",
                        "email": "guilhermeblanco@gmail.com"
                    },
                    {
                        "name": "Roman Borschel",
                        "email": "roman@code-factory.org"
                    },
                    {
                        "name": "Benjamin Eberlei",
                        "email": "kontakt@beberlei.de"
                    },
                    {
                        "name": "Jonathan Wage",
                        "email": "jonwage@gmail.com"
                    },
                    {
                        "name": "Johannes Schmitt",
                        "email": "schmittjoh@gmail.com"
                    },
                    {
                        "name": "Marco Pivetta",
                        "email": "ocramius@gmail.com"
                    }
                ],
                "description": "PHP Doctrine Common project is a library that provides additional functionality that other Doctrine projects depend on such as better reflection support, proxies and much more.",
                "homepage": "https://www.doctrine-project.org/projects/common.html",
                "keywords": [
                    "common",
                    "doctrine",
                    "php"
                ],
                "support": {
                    "issues": "https://github.com/doctrine/common/issues",
                    "source": "https://github.com/doctrine/common/tree/3.4.3"
                },
                "funding": [
                    {
                        "url": "https://www.doctrine-project.org/sponsorship.html",
                        "type": "custom"
                    },
                    {
                        "url": "https://www.patreon.com/phpdoctrine",
                        "type": "patreon"
                    },
                    {
                        "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcommon",
                        "type": "tidelift"
                    }
                ],
                "time": "2022-10-09T11:47:59+00:00"
            },

    En otras palabras, en el archivo composer.json se almacena la mínima información necesaria para generar el composer.lock

    Ya sé, ya sé, todavía no respondí a la pregunta: si puedo generar el composer.lock a partir del composer.json… ¿para qué quiero comitear el primero?

    La respuesta radica en el hecho de que un patrón de dependencias puede resolverse con muchas opciones diferentes.

    Por ejemplo,

    "doctrine/common": "^3.4"

    Podría satisfacerse con doctrine/common en sus versiones :

    • 3.4.0
    • 3.4.1
    • 3.4.2
    • 3.4.3
    • 3.4.4
    • 3.4.5

    Aunque no sería compatible con 3.3.* ni con 3.5.*.

    El sistema de restricciones de versiones tiene sus vueltas pero si querés aprender más podés consultarlo directamente acá.

    Y todo esto es importante porque…?

    Porque, si hiciste tus pruebas con la versión x.y.z de una librería y luego, al momento de instalar la aplicación, sin que te des cuenta instalás la versión x.y.z+1 es posible que te encuentres con ese tipo de sorpresitas que a nadie le gustan.

    De hecho, una de las razones más importantes que dieron origen a Composer es precisamente esta, asegurarte de que las versiones de las dependencias estén sincronizadas entre entornos.

    Por eso el archivo se llama .lock, porque las dependencias están cerradas.

    Así que, si pensabas ahorrarte algunos bytes en el repo… me temo que no estás de suerte en esta ocasión.

  • ¿Qué es PHP Composer? Todo lo que Necesitas Saber

    ¿Qué es PHP Composer? Todo lo que Necesitas Saber

    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:

    1. Recorriendo el árbol de dependencias completo
    2. 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.