Cómo utilizar symplify/vendor-patches en proyectos php

Es posible que alguna vez hayan encontrado un bug en una dependencia de un proyecto de php que utiliza composer, y el arreglo es simple y lo único que se requiere es cambiar 1 o 2 líneas de código en el paquete referenciado es decir en /vendor/usuario/paquete-con-error.

Como buen ciudadano del mundo open source, lo primero que se debe hacer es abrir un issue y crear un pull request con los cambios en el proyecto dependencia para que el dueño lo pueda aceptar y se arregle para todos los usuarios de ese paquete, pero no vamos a negar que este proceso toma bastante tiempo, y si necesitamos arreglar lo más pronto hay otras opciones.

Puedes crear un fork del proyecto en tu github y usar ese url de github en la version de composer.json, pero estarías tomando una responsabilidad manual de actualizar y aplicar el cambio cada vez que haya actualizaciones. Se puede copiar el paquete localmente que es más rápido pero con esto ya no hay actualizaciones.

O la mejor opción se puede usar “composer patches”.

Hace un tiempo estaba trabajando en un proyecto laravel con el paquete de administración nova, y un addon para manejo de archivos infinety-es/nova-filemanager el paquete funcionaba muy bien con archivos públicos, pero para mi proyecto necesitaba guardar los archivos en un bucket privado de S3 y para descargarlos debía generar una url temporal con un tiempo de expiración máximo.

Analizando el código del paquete encontré 2 lugares donde se generaba la url del archivo para descarga en el archivo fuente src/Http/Services/NormalizeFile.php en la función toArray y en la función getImage lo único que había que hacer es realizar el siguiente cambio:

--- /dev/null
+++ ../src/Http/Services/NormalizeFile.php
@@ -50,7 +50,7 @@
             'mime' => $this->getCorrectMimeFileType(),
             'path' => $this->storagePath,
             'size' => $this->getFileSize(),
-            'url'  => $this->cleanSlashes($this->storage->url($this->storagePath)),
+            'url'  => $this->cleanSlashes($this->storage->temporaryUrl($this->storagePath, '+5 minutes')),
             'date' => $this->modificationDate(),
             'ext'  => $this->file->getExtension(),
         ]);
@@ -145,7 +145,7 @@
     private function getImage($mime, $extension = false)
     {
         if (Str::contains($mime, 'image') || $extension == 'svg') {
-            return $this->storage->url($this->storagePath);
+            return $this->storage->temporaryUrl($this->storagePath, '+5 minutes');
         }

         $fileType = new FileTypesImages();

Cómo se cambia esas 2 líneas en /vendor?

Se hace en 4 simples pasos:

  1. Instalar el paquete que controla los parches
composer require cweagans/composer-patches symplify/vendor-patches --dev
  1. Se crea una copia del archivo a cambiar en /vendor por la extensión .old

Por ejemplo en mi caso cambie vendor/infinety-es/nova-filemanager/src/Http/Services/NormalizeFile.php a vendor/infinety-es/nova-filemanager/src/Http/Services/NormalizeFile.php.old

  1. Abrir el archivo original vendor/infinety-es/nova-filemanager/src/Http/Services/NormalizeFile.php y realizar los cambios necesarios. Se verifica que los cambios funcionen ya que solo el archivo *.php funciona no el *.php.old.

  2. Generar el parche usando el comando:

vendor/bin/vendor-patches generate

La herramienta de symplify genera todos los parches en el directorio /patches/:

ls -l patches
.rw-r--r-- 883 marcelo 30 Apr  7:16 infinety-es-nova-filemanager-src-http-services-normalizefile-php.patch

Y también genera la configuración de parches al composer.json:

    ...
    "extra": {
        "laravel": [],
        "patches": {
            "infinety-es/nova-filemanager": [
                "patches/infinety-es-nova-filemanager-src-http-services-normalizefile-php.patch"
            ]
        }
    },
    ...

Y eso es todo, la siguiente vez que realicen composer install, el parche se aplica automáticamente.

Referencias: