XF 2.2 Trapped in a dependency hell with composer

Cylon

Member
Hi!
I'm developing an addon for my forum, and the version that I currently have from XF is the 2.2.8 Patch 1. As far as I understand, this version of XF uses the symfony/process dependency v 3.4.0.

My addon uses the php library php-ffmpeg/php-ffmpeg installed with composer in my addon folder, because I want to extract a thumbnail from an uploaded video. The problem is that this dependency, needs a more actual version of symfony/process. I think it needs at least the version 5.4.0.

Is it possible to configure my addon to use newer versions of the composer dependencies, or am I limited by the versions of dependencies of the main XF installation? Because in other areas, I'm having problems with the guzzle http library, and I suppose I will have more problems with other libraries too.

I don't know if there is a better practice for this problem, or if this is possible at all.
Thanks in advance.
 
So this is possible. I have added a number of composer packages that are maintained within my core plugin. One thing to note, I just don't have the time to put this into a plugin, so I will try to support this answer the best I can here.

I created a new code event listener for Pub Setup (app_pub_setup). Here is the file that it points to:
PHP:
<?php

namespace HE\Core\Listener\App;

use HE\Core\Composer;

class PubSetup
{
    public static function extend(\XF\Pub\App $app)
    {
        Composer::autoloadNamespaces($app);
        Composer::autoloadPsr4($app);
        Composer::autoloadClassmap($app);
        Composer::autoloadFiles($app);
    }
}

Here is the Composer.php file that it is referencing:
PHP:
<?php

namespace HE\Core;

class Composer
{
    public static function autoloadNamespaces($app, $prepend = false)
    {
        $namespaces = \XF::getRootDirectory() .
            DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .
            'vendor' . DIRECTORY_SEPARATOR .
            'composer' . DIRECTORY_SEPARATOR .
            'autoload_namespaces.php';

        if (!file_exists($namespaces))
        {
            $app->error()->logError('Missing vendor autoload files at %s', $namespaces);
        }
        else
        {
            $map = require $namespaces;

            foreach ($map as $namespace => $path) {
                \XF::$autoLoader->add($namespace, $path, $prepend);
            }
        }
    }

    public static function autoloadPsr4($app, $prepend = false)
    {
        $psr4 = \XF::getRootDirectory() .
            DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .
            'vendor' . DIRECTORY_SEPARATOR .
            'composer' . DIRECTORY_SEPARATOR .
            'autoload_psr4.php';

        if (!file_exists($psr4))
        {
            $app->error()->logError('Missing vendor autoload files at %s', $psr4);
        }
        else
        {
            $map = require $psr4;

            foreach ($map as $namespace => $path) {
                \XF::$autoLoader->addPsr4($namespace, $path, $prepend);
            }
        }
    }

    public static function autoloadClassmap($app)
    {
        $classmap = \XF::getRootDirectory() .
            DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .
            'vendor' . DIRECTORY_SEPARATOR .
            'composer' . DIRECTORY_SEPARATOR .
            'autoload_classmap.php';

        if (!file_exists($classmap))
        {
            $app->error()->logError('Missing vendor autoload files at %s', $classmap);
        }
        else
        {
            $map = require $classmap;

            if ($map)
            {
                \XF::$autoLoader->addClassMap($map);
            }
        }
    }

    public static function autoloadFiles($app)
    {
        $files = \XF::getRootDirectory() .
            DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .
            'vendor' . DIRECTORY_SEPARATOR .
            'composer' . DIRECTORY_SEPARATOR .
            'autoload_files.php';

        if (!file_exists($files))
        {
            $app->error()->logError('Missing vendor autoload files at %s', $files);
        }
        else
        {
            $includeFiles = require $files;

            foreach ($includeFiles as $fileIdentifier => $file)
            {
                if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
                    require $file;

                    $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
                }
            }
        }
    }
}

You can modify the composer file to change the location of the new composer package files, but I have mine at the root of my project. So if you are trying to make a plugin and package it up, you may need to change the folders to point to that location. One thing to note, is that is only for access the new composer packages in the public facing website. You can create more code event listeners if you need to access these composer files in more locations:
  • app_cli_setup
  • app_admin_setup
  • app_api_setup
Hopefully, this helps!
 
Hi Robdog,
First of all, thank you very much for your answer. Since I posted the original question I was thinking about alternative solutions for the problem, and after reading and testing your answer, I decided to go into another direction.

It is not that your solution does not works, it is that I do not want to convert the development of the add on in fight against a dragon. The truth is that the problem that I am having with the ffpmeg dependency is only one problem. There are other dependencies that are causing problems too, and although writing integration tests for the add on send getting them to work is possible, but it is laborious.

Because of that I decided to remove this functionality from the add on, and develop them in an isolated Microservice. I am using the slim framework for it, which is a very thin framework but works very well for my needs. The tests work out of the box and it is easy to use. With XF I have always the impression that I am fighting against a big animal that do not wants to cooperate.

I decided to move at least two features from the add on I am developing to microservices. Another advantage is the easy of update. I do not have to release a new add on version if something is fixed in a ms. Of course, there are disadvantages too, like for example the latency that accessing a microservice adds. But in this case I think it will benefit me despite of the disadvantages.

In any case, thank you for your answer and your time.

Regards.
 
Last edited:
Top Bottom