Categories


Archives


Recent Posts


Categories


Magento, Composer, and Autoload Patterns

astorm

Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Updated for Magento 2! No Frills Magento Layout is the only Magento front end book you'll ever need. Get your copy today!

This entry is part 4 of 4 in the series Magento 2 and Composer. Earlier posts include Magento 2: Composer, Marketplace, and Satis, Magento 2: Composer Plugins, and Magento 2: Composer and Components. This is the most recent post in the series.

A few weeks ago I set out to put Magento composer package support into pestle, and then I got sidetracked by the whole how to deterministically know where a package’s root and “code root” folders are thing. I’ve been staring at a dataset of around 3400 Magento composer.json files trying to come up with a cogent story about Magento 2, composer, and autoloading, but I’m starting to think there isn’t one. So this post is just me doing an information dump and declaring bankruptcy so I can get back to work.

The basic problem I set out to solve: What’s a deterministic way to determine a Magento composer package’s base code folder without bootstrapping the entire Magento 2 framework. In most PHP frameworks (that have opinions about code organization), this would be straight forward. This is less straight forward in Magento 2, as it’s a framework that came from a tradition of having opinions about code organization, but went through a period where “the architects” didn’t want this responsibility, and we’ve ended up with a bit of a miasmic mess.

Here’s some of what I found in the dataset, (a dataset which I’ve put up on GitHub)

PSR-4 Standard

The good news is that a large majority of the files handle things in the defacto-standard way. We get a single psr-4 entry with a PHP Namespace that doubles as the traditional module name, and a single files entry that points at a registration.php file.

"autoload": {
    "files": [
        "registration.php"
    ],
    "psr-4": {
        "Easygento\\Core\\": ""
    }
}

You’ll see some variation here — sometimes it’s ./registration.php. Sometimes (like the example below) both the files and psr-4 section will point to a sub-folder.

"autoload": {
    "files": [
        "src/BlogSampleData/registration.php"
    ],
    "psr-4": {
        "Mirasvit\\BlogSampleData\\": "src/BlogSampleData"
    }
}

I even found one poor developer who had files pointed to registartion.php. Sympathies for the person who got frustrated enough to not see their typo and abandoned the module that never worked.

Of the 3405 files I managed to pull together, 3204 were these standard-ish psr-4/Package\Name namespace with a registration.php file. That’s 94% — a number where it’s easy enough to sit in a meeting and say this is all we need to worry about.

Or, you could look at it and say there were 201 people out there who didn’t do it this way, and if your tools don’t accommodate them you’re contributing to the “software is awful and inconsistent” ecosystem.

Or, extrapolating a bit, when you’re working on Magento projects with a large number of modules, six out of 100 of those modules will have a non-standard autoloader.

PSR-4 Many

A common variation on the “psr-4 standard” files were files that had multiple modules definitions. Of these, I saw two patterns. Here’s the first

"autoload": {
    "files": [
        "src/module-etm-api/registration.php",
        "src/module-etm-core/registration.php",
        "src/module-etm-admin-ui/registration.php"
    ],
    "psr-4": {
        "Ainnomix\\EtmApi\\": "src/module-etm-api",
        "Ainnomix\\EtmCore\\": "src/module-etm-core",
        "Ainnomix\\EtmAdminUi\\": "src/module-etm-admin-ui"
    }
}

Here we have multiple psr-4 module namespaces, and each has a corresponding registration.php file.

The second pattern was unexpected —

"autoload": {
    "psr-4": {"SM\\": ""},
    /*...*/
}

Here we have a single psr-4 entry with a one segment PHP namespace, and then this

"autoload": {
    "files": [
        "Category/registration.php",
        /* ... */
        "XRetail/registration.php"
    ],
}

A large files section that points to the registration.php files.

Unorthodox — but it’s something that works, and without strong guidance from the framework authors — another perfectly legal usage for tooling authors to deal with.

PSR-0 Standard

Another thing I saw was psr-0 autoloaders.

"autoload": {
    "files": [
        "src/Tkotosz/CommandScheduler/registration.php"
    ],
    "psr-0": {
        "Tkotosz\\CommandScheduler\\": "src"
    }
}

Understanding this one requires a bit of history. The psr-0 standard was an attempt to take the super flexible PHP class autoloader and, by shouting really loud, get everyone to do autoloading in a way that was compatible with everyone else.

The Composer project adopted this standard early, but had a few extra problems to solve that weren’t directly addressed by the spec, leading to some confusion. Composer and psr-0 were huge boons to each other’s popularity (one probably more than the other), and this eventually led to psr-4, which codified a lot of composer’s behaviors.

Even though you can deprecate a specification (PSR-0 was deprecated around five years ago) that doesn’t mean people will stop using it, and the composer folks are smart-and-or-kind enough not to yank out support for psr-0.

So — another legitimate use case for tooling authors to be aware of.

Both PSR-4 and PSR-0?

There’s also an even smaller handful of packages that have both psr-4 and psr-0 autoloaders. Take this one for example.

"autoload": {
    "psr-4": {
        "DTuX\\Sentry\\Sentry\\": "Sentry"
    },
    "psr-0": {
        "DTuX\\Sentry\\Sentry\\": "Sentry"
    },
    "files": [ "registration.php" ]
}

It’s not entirely clear what’s going on here — maybe it’s someone just hedging their bets on composer’s then new psr-4 functionality. Also notice that three segment namespace vs. the two segment Magento module name.

Here’s another example

"autoload": {
    "files": [
        "registration.php"
    ],
    "psr-0": {
        "Braintree": "braintree_php/lib/"
    },
    "psr-4": {
        "Magento\\Braintree\\": "",
        "Braintree\\": "braintree_php/lib/Braintree"
    }
},

Here we have a standard psr-4 autoloader — but we also have both a psr-0 and psr-4 autoloader pointing at an external library embedded in the module. Again this is a sign of the times the module was developed in: How to handle the psr-0 to psr-4 transition and deal with folks (like Braintree) who had non-composer based PHP libraries that still needed to be embedded in a project. Lots of non-standard looking things, but all things a working Magento developer or tooling author might need to know.

PSR-4 Standard with extra->map

The last weird thing we’ll mention is composer files that have the defacto-standard psr-4 and files autoloaders — but also the pre-release extra->map settings.

"autoload": {
    "files": ["registration.php"],
    "psr-4": {
        "Techbandhu\\Fbpage\\": ""
    }
},
"extra": {
    "map": [
        [
            "*",
            "Techbandhu/Fbpage"
        ]
    ]
}

Probably another bit of hedging as Magento 2 approached it’s release date, cemented in place because either “hey, it works” or “hey, we don’t use this anymore”.

Wrap Up

That’s not the end to the weirdness — composer’s classmap and exclude-from-classmap makes some appearances, and there’s other strange combinations or missing autoloaders to consider. I put the published corpus of files up on GitHub if you’re curious.

At this point, my research has turned into a bit of a rat-hole, and all I’ve really discovered is there’s no safe assumptions to be made about what you’ll find in an open-source Magento extension.

The only real way to determine an extension’s base code folder? Parsing the registration.php file and writing (or borrowing) algorithms to figure out what folder a class will be loaded from based on the composer configuration, or bootstrapping Magento.

It’s unfortunate that Magento’s product strategy focused hard on enforcing standards via the Magento Marketplace submission process and left the open-source side to fend for itself — but open-source and billion dollar companies can make for strange decisions.

Series Navigation<< Magento 2: Composer and Components

Copyright © Alan Storm 1975 – 2019 All Rights Reserved

Originally Posted: 17th October 2019