calcinai / xero-php

A php library for the Xero API, with a cleaner OAuth interface and ORM-like abstraction.
MIT License
359 stars 262 forks source link

getPackageVersion in Helpers causes OutOfBoundsException errors on systems with multiple composer installs #833

Open N1ghteyes opened 2 years ago

N1ghteyes commented 2 years ago

Run into a weird problem with a wordpress build, with the following error:

[22-Oct-2021 08:55:16 UTC] PHP Fatal error:  Uncaught OutOfBoundsException: Package "calcinai/xero-php" is not installed in public_html/wp-content/plugins/woocommerce-eu-vat-assistant/src/vendor/composer/InstalledVersions.php:188

That pathing looked odd to me, so i Tracked the issue to here:

public static function getPackageVersion()
    {
        if (!is_callable('\\Composer\\InstalledVersions::getPrettyVersion')) {
            return self::DEFAULT_VERSION;
        }

        return \Composer\InstalledVersions::getPrettyVersion(self::PACKAGE_NAME);
    }

The reference to \Composer is loading the composer namespace instantiated by the woocommerce-eu-vat-assistant plugin, not the custom plugin that is calling it.

For clarity, several plugins have vendor directories included, which means several versions of the \Composer namespace which means that \Composer\InstalledVersions is calling the wrong composer.json for checks, thus is failing (so far as i can tell).

Not sure what the fix is here - i'll provide one if i can work it out. Anyone else run into the same thing?

peteralewis commented 2 years ago

Hit the same issue.

v2.2.4 didn't have this issue, although the file you reference was still labelled v2.2.1 in the header comments, I reverted back to this and no longer hit the error...

const PACKAGE_VERSION_FILE = '/VERSION';
public static function getPackageVersion()
{
    if (!file_exists(self::PACKAGE_VERSION_FILE)) {
        return self::DEFAULT_VERSION;
    }

    return file_get_contents(self::PACKAGE_VERSION_FILE);
}
calcinai commented 2 years ago

Oh man. @N1ghteyes did you find a solution to this?

FYI, the reason for this was for Xero to see which versions of the library were bring used during support requests.

N1ghteyes commented 2 years ago

@calcinai I hacked it in the end to return a set value (rather than checking against composer), so no not really a fix I'm afraid.

I suspect the actual fix here is to maintain a version constant that can be referenced - it'll just mean a manual update to it every time you push a minor version.

ValCanBuild commented 2 years ago

For reference, this is what I ended up doing (based on @N1ghteyes comment above). I created a custom \XeroPHP\Application subclass and overrode the constructor and just passed it a hardcoded version for the User-Agent string instead of having it use Helpers::getPackageVersion():

class CustomXeroApplication extends \XeroPHP\Application
{
    /** @noinspection MagicMethodsValidityInspection
     * @noinspection PhpMissingParentConstructorInspection
     */
    public function __construct($token, $tenantId)
    {
        $this->config = static::$_config_defaults;

        $transport = new Client([
            'headers' => [
                'User-Agent' => sprintf(static::USER_AGENT_STRING, 'v2.4.1'),
                'Authorization' => sprintf('Bearer %s', $token),
                'Xero-tenant-id' => $tenantId,
            ],
        ]);

        $reflectionProperty = new \ReflectionProperty(\XeroPHP\Application::class, 'transport');
        $reflectionProperty->setAccessible(true);
        $reflectionProperty->setValue($this, $transport);
    }
}
calcinai commented 2 years ago

@ValCanBuild I think the best way to do this is just to add a file as part of the build pipeline (and the tag). Just not completely sure how this would work with composer releases in terms of keeping the git tag consistent with the version in the code.