Using Composer Packages in XenForo 2.1+ Addons Tutorial

Using Composer Packages in XenForo 2.1+ Addons Tutorial 2.1.0

No permission to download

Sim

Well-known member
Sim submitted a new resource:

Using Composer Packages in XenForo 2.1+ Addons Tutorial - How to use Composer packages in XenForo 2.1+ addons

Composer is a tool for dependency management in PHP. It allows you to declare the libraries your project depends on and it will manage (install/update) them for you.

XenForo v2 uses Composer behind the scenes to include certain packages used by the core software. As addon developers, we can include Composer packages in our own addons which will be autoloaded alongside those provided by the core.

In XenForo 2.0, we had to use extension points to do our...

Read more about this resource...
 

Xon

Well-known member
@Sim I recommend adding hashes.json and _metadata.json to the ignore list. Committing either of these into git is a nightmare if you need to work with another developer or rebase/merge at all.

I would also recommend a .gitattributes file with the contents;
Code:
*.php text eol=lf
*.js text eol=lf
(This forces consistent line-endings on commit, regardless of git client configuration. Just makes things easier)
 

Jeremy P

XenForo developer
Staff member
If you run composer upgrade it will upgrade packages based on the version constraints you have specified in your package.json file.
 

Jaxel

Well-known member
Getting this error:

The "https://repo.packagist.org/packages.json" file could not be downloaded: failed to open stream: Connection timed out
 

Jeremy P

XenForo developer
Staff member
Can you open it in a browser? It loads fine for me, so I imagine it's some sort of networking/firewall issue between you and them.
 

Jaxel

Well-known member
Okay, I got it working... but it's not upgrading.

Currently I have google/cloud-firestore 1.4... and I know for a fact that 1.10 is available:

But its not upgrading:
Code:
composer upgrade google/cloud-firestore
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
 

Jaxel

Well-known member
Okay, doing that led to a whole bunch of stuff that I have no idea what it means:

Code:
composer upgrade google/cloud-firestore
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Installation request for google/cloud-firestore ^1.10.1 -> satisfiable by google/cloud-firestore[v1.10.1].
    - Conclusion: remove google/auth v1.4.0
    - Conclusion: don't install google/auth v1.4.0
    - google/cloud-firestore v1.10.1 requires google/cloud-core ^1.31 -> satisfiable by google/cloud-core[v1.31.0, v1.32.0, v1.33.0, v1.33.1, v1.34.0].
    - google/cloud-core v1.31.0 requires google/auth ^1.5.1 -> satisfiable by google/auth[v1.5.1, v1.5.2, v1.6.0, v1.6.1].
    - google/cloud-core v1.32.0 requires google/auth ^1.5.1 -> satisfiable by google/auth[v1.5.1, v1.5.2, v1.6.0, v1.6.1].
    - google/cloud-core v1.33.0 requires google/auth ^1.5.1 -> satisfiable by google/auth[v1.5.1, v1.5.2, v1.6.0, v1.6.1].
    - google/cloud-core v1.33.1 requires google/auth ^1.6 -> satisfiable by google/auth[v1.6.0, v1.6.1].
    - google/cloud-core v1.34.0 requires google/auth ^1.6 -> satisfiable by google/auth[v1.6.0, v1.6.1].
    - Can only install one of: google/auth[v1.5.1, v1.4.0].
    - Can only install one of: google/auth[v1.5.2, v1.4.0].
    - Can only install one of: google/auth[v1.6.0, v1.4.0].
    - Can only install one of: google/auth[v1.6.1, v1.4.0].
    - Installation request for google/auth (locked at v1.4.0) -> satisfiable by google/auth[v1.4.0].
 

Jeremy P

XenForo developer
Staff member
You have conflicting dependencies, meaning that two of your dependencies each require different versions of the same package. You'll likely want to upgrade all of your dependencies to the latest versions to resolve this.
 

Sim

Well-known member
Perhaps paste your require block from composer.json here so we can see what packages you are requiring?
 

smozgur

Active member
Great article, @Sim. Thank you!

I run into a problem when I was building my add-on. I'd like to share here, and ask your opinion if there is a preferred and/or better solution than I came up with.

My add-on requires google/apiclient package. And guzzlehttp/guzzle is one of its dependencies. However, it conflicts with the Xenforo package. Google API client requires a different version of guzzlehttp/guzzle from XenForo's installed version, and this causes failure during the link proxy.

So, this is how I set up my composer.json to avoid installing duplicate dependencies since I can't control the Google API client dependencies.

JSON:
{
    "require": {
        "google/apiclient": "^2.8"
    },
    "replace": {
        "guzzlehttp/guzzle": "*",
        "guzzlehttp/psr7": "*",
        "psr/http-message": "*",
        "psr/log": "*"
    }
}

Google API client's dependencies also require psr packages. Although the same version is installed, the only missing package is psr/cache in the XenForo package. So, I also remove the duplicate psr packages to avoid any conflict during the future add-on updates.

I'd really like to hear your thoughts on this matter.
 

Sim

Well-known member
My add-on requires google/apiclient package. And guzzlehttp/guzzle is one of its dependencies. However, it conflicts with the Xenforo package. Google API client requires a different version of guzzlehttp/guzzle from XenForo's installed version, and this causes failure during the link proxy.

I would strongly recommend against arbitrarily replacing any core package unless you have thoroughly tested all functionality (including addons) to ensure compatibility.

In any case, I don't think using the "replace" keyword will achieve what you want here - since we have no control over the core composer dependency versions and the autoloader will load those first, regardless of which versions we try to load in our addons. (This behaviour can be changed, but I strongly recommend against doing so).

So the core will load it's required version of Guzzle (6.3.x I believe is the current version) and then your addon will only have that version available - regardless of what version is included in your addon.

In this particular case, you don't need to replace Guzzle anyway, the google/apiclient package is compatible with Guzzle 6.x: "guzzlehttp/guzzle": "~5.3.1||~6.0||~7.0"

So if you just require the specific Guzzle version used by XenForo, it will sort itself out:

JSON:
{
    "require": {
        "google/apiclient": "^2.8",
        "guzzlehttp/guzzle": "^6.3",
        "guzzlehttp/psr7": "^1.6"
    }
}

Note that you don't even have to do this - since the core provides its own Guzzle client which is compatible, so there's no need to package up the additional Guzzle dependencies in your addon.

I would simply use the following:

JSON:
{
    "require": {
        "google/apiclient": "^2.8"
    },
    "require-dev": {
        "guzzlehttp/guzzle": "^6.0",
        "guzzlehttp/psr7": "^1.6"
    }
}

... this way you get to validate that the dependencies are compatible when you run composer update on your dev machine. You can also add whatever other core package versions you want to validate into the require-dev section so that your addon dependencies are resolved on your dev machine without being loaded into the built package.

Then just ensure that your build.json uses the --no-dev composer flag to remove all dev dependencies at build time (as per the tutorial instructions).

I don't know that I would get too concerned about the PSR packages - you could spend hours tracking the dependencies and sub-dependencies for all packages in use, but these PSR packages don't change very frequently (by design - they are "standards" based and so are intended for maximum compatibility).
 

smozgur

Active member
Thank you for your reply, @Sim.

I would strongly recommend against arbitrarily replacing any core package unless you have thoroughly tested all functionality (including addons) to ensure compatibility.
I certainly don't touch anything in XenForo core.

In any case, I don't think using the "replace" keyword will achieve what you want here - since we have no control over the core composer dependency versions and the autoloader will load those first, regardless of which versions we try to load in our addons. (This behaviour can be changed, but I strongly recommend against doing so).
The "replace" keyword in composer.json with providing the unwanted package by using no version, guzzlehttp/guzzle": "*", avoids installing that dependency. This way, it won't be installed, and the add-on will use the core package already installed with XenForo.

Google API client is installing the Guzzle 7.2.0 - 2020-10-10 version while XenForo has 6.3.3 - 2018-04-22. And I think Guzzle 7.2.0 doesn't work with XenForo link proxy well. That's why I tried to avoid installing that package by using "replace".

However, the following setting you suggested solved the problem by installed the version 6.5.5 - 2020-06-16 (or with require-dev).

JSON:
{
    "require": {
        "google/apiclient": "^2.8",
        "guzzlehttp/guzzle": "^6.3",
        "guzzlehttp/psr7": "^1.6"
    }
}

... this way you get to validate that the dependencies are compatible when you run composer update on your dev machine. You can also add whatever other core package versions you want to validate into the require-dev section so that your addon dependencies are resolved on your dev machine without being loaded into the built package.

Then just ensure that your build.json uses the --no-dev composer flag to remove all dev dependencies at build time (as per the tutorial instructions).

Yes, this solved my problem exactly.

Thank you so much once again!

By the way, there was only 5 stars rating that I could give, but this article really deserves much more than that. (y)
 
Top