Skip to content

Instantly share code, notes, and snippets.

@philsturgeon
Created May 7, 2013 13:56
Show Gist options
  • Save philsturgeon/5532765 to your computer and use it in GitHub Desktop.
Save philsturgeon/5532765 to your computer and use it in GitHub Desktop.
PSR-0 != Composer

A native implementation of PSR-0 using only the autoloader from the PSR-0 example in the spec would expect a folder structure like this:

League\Oauth2\Client\Foo = myapp/somefolder/League/Oauth2/Client/Foo.php
League\Oauth2\Server\Bar = myapp/somefolder/League/Oauth2/Server/Bar.php

If I was making these as packages, I could make them into two packages, which would be installed in different locations, because thats how Composer rolls:

League\Oauth2\Client\Foo = myapp/vendor/league/oauth2/src/League/Oauth2/Client/Foo.php
League\Oauth2\Server\Bar = myapp/vendor/league/oauth2-server/src/League/Oauth2/Server/Bar.php
@ircmaxell
Copy link

If it fails in the reference implementation, but works in another implementation, that implies that either there's a problem with the spec, or that the other (composer) implementation is not compliant.

And considering it's been proven over and over again that there are several logical (and functional) inconsistencies in the spec, why can't you admit to it and move on?

This is why we can't have nice things. Because those who are maintaining and evangelizing for adoption of a specification can't even understand the bad parts of it and what needs to be fixed. That's a failure right there.

@philsturgeon
Copy link
Author

If I wanted to work with a bunch of League packages without ever using Composer then it would look like this:

$loader = new SplClassLoader('\League', 'myapp/vendor/');
$loader->register();

$bar = new \League\Oauth2\Server\Bar;

That would be the folder structure, because if I was installing this with Composer I wouldnt be trying to randomly do this with the SplClassLoader. If I was doing this with the SplClassLoader I wouldnt be using Composer, and I would instead be copying and pasting random chunks of code around.

This is how something like the Zend Framework always used to work in ZF1. I could make ZF2 work exactly the same by grabbing all of the code out of a ZIP file, merging them together into one folder and using the SplClassLoader. But why the fuck would I do that if I could use Composer to install them?

If you have multiple PSR-0 autoload areas (multiple Composer packages for example) then you have to mount them at multiple areas, just like your second example, which will work perfectly.

@philsturgeon
Copy link
Author

I'm not blindly saying PSR-0 is good, you're just too busy blindly saying PSR-0 sucks all the time to notice that.

I know exactly what is wrong:

https://github.com/philsturgeon/psr0-naming-oddity

This is one of a few issues, which are all being addressed in the autoloader. The problem you've suggested just doesn't exist.

@ircmaxell
Copy link

Who the hell said that PSR-0 sucks? I general I think it is very much in the right direction. I have said this MANY times.

What I'm trying to get through is that there are problems that need fixing. And the one you're ignoring here is very much a problem. If I can get it to fatal error on valid code, it's a problem. End of story. The problem exists, because the fatal error exists. Done. End of story.

Ignoring the problem, or trying to justify it by saying "it's not a problem with Composer" ignores the fact of how specifications work. And how they are supposed to work.

@tassoevan
Copy link

Why the fuck SplClassLoader must throw exceptions or errors if a class is not found?

@philsturgeon
Copy link
Author

Yes, the fact that it fatal errors instead of throwing an exception is bad. I'm not blindly ignoring this, you just lead into the conversation on Twitter talking about one thing and now you're talking about another.

So Point B is "It should throw an exception. To point B I say "Absofuckinglutely it should".

Back to Point A, as I'm assuming I have not explained this well enough:

My examples are perfectly PSR-0. On twitter, you said they were not.

I am splitting them into two Composer packages.

That doesn't stop them being PSR-0.

Composer will have the same effect as merging them into one folder.

So it's still PSR-0.

I'm not trying to blame the spec implementation on Composer, im saying, MY EXAMPLES ARE PSR-0.

That is all.

@ircmaxell
Copy link

Phil,

Under no circumstances should an autoloader EVER throw an exception or an error. That's the failure of SplClassLoader, and one of the failures of PSR-0 (because it does not specify that). So no, Point B should not be "it should throw an exception". It should be "It should never under any circumstance directly throw an exception or error". Any other behavior is in bad form.

As far as your examples being "perfectly PSR-0", not based on the reference implementation. Since that's put in the spec, it cannot be divorced from the spec. Therefore, if it works in Composer, but not in the reference implementation, that implies that Composer isn't implementing the specification properly. That's it.

@philsturgeon
Copy link
Author

This is so far a messed up conversation in several different ways. Let's begin to work out why:

  • I thought you were talking about the example autoloader until you specifically mentioned SplClassLoader.

  • Your examples are not the folder structure I would be using, and that is not how any vaugely right minded person would ever do it. There are two ways to use PSR-0 code: Install with Composer, or install manually (taking files out of zips and merging them into one folder like "a moron" as I said on Twitter). Either way is going to give you a different folder structure, and this does not break PSR-0 and cannot be considered a fault in the spec.

  • Installing with Composer mounts multiple areas to the Class Loader, exactly like you have done in your example:

    $loader = new SplClassLoader('\League\Oauth2\Client', 'myapp/vendor/league/oauth2/src/');
    $loader->register();
    $loader2 = new SplClassLoader('\League\Oauth2\Server', 'myapp/vendor/league/oauth2-server/src/');
    $loader2->register();

That would work perfectly fine, if for some reason I chose to ignore the Composer autoloader and use the SplClassLoader. I wouldn't ever do that, but it would definitely work.

  • I was only ever talking about the fact that I could split my sub-namespaces into two different folders. One namespace goes to one, another namespace goes to another. Your example here is trying to make one namespace go to two different locations.
  • You're right, after seeing your example (linked above) an exception doesn't sound very fun at all, but it definitely shouldnt be a fatal error. We agree here, it would be nicer if it tracked it as a fallback, so if you registered the same autoloader twice it would try all of the locations. I totally agree.

@philsturgeon
Copy link
Author

tl;dr: You misread my examples and the PSR-0 SplClassLoader could do with some improvements.

@ircmaxell
Copy link

Phil:

Even the "example" implementation in the spec body itself suffers the same exact problem.

And you missed my point entirely. My original tweet was that "The reference implementation doesn't allow it ;-)". Which indicated that while Composer does allow multiple paths within a namespace, the reference and example implementations do not. Which indicates that the spec does not allow it. Which indicates that the code that you're claiming to be PSR-0 (which is individually), is not actually PSR-0 when used together. Which also indicates that Composer's autoloader itself is not PSR-0 compliant (even though it claims to be)...

And as far as "and that is not how any vaugely right minded person would ever do it.", be very careful what you say or insinuate. Do you claim to know the requirements and usages for all time going forward? I have run into issues like that before. And I can assure you that if you work at it long enough, you will as well.

That attitude of "If you do it this way, it all works" is the fundamental failure here. I'm not advocating that there shouldn't be standards. I'm not even advocating for generic ones. I'm advocating for logically consistent standards (which PSR-0 is not, based on the number of ways we've proven it fails).

And what I'm advocating for is that people like you who are preaching it, understand what and why you are preaching it. I remember back a few years someone very prominent in the PSR/FIG group saying very publicly "PSR-0 is the only autoloading solution we will ever need". And now we have the proposal for the "package loader" because other requirements and workflows are needed. Sure, hind sight is 20-20, but foresight is usually blind. And how arrogant it is of you to make a statement like that is not how any vaugely right minded person would ever do it. Just because there are other ways of thinking and building doesn't imply that they are "not in the right mind".

The fact remains. You claimed PSR-0 allows mapping multiple directories to a single namespace tree quite fine. I pointed out that there are logical inconsistencies that can prevent it from happening, so in general PSR-0 actually does not allow it. Your case (common prefix, different suffixes) is actually a special case, and is the edge-case here. The general case is that it will fatal error. Which is the problem.

@philsturgeon
Copy link
Author

Which indicated that while Composer does allow multiple paths within a namespace

No it doesn't. It maps different locations with different namespace prefixes.

the reference and example implementations do not. Which indicates that the spec does not allow it.

SplClassLoader will work in the same way, but you have to do it yourself.

Which indicates that the code that you're claiming to be PSR-0 (which is individually), is not actually PSR-0 when used together.

Sure it is, you can use the example you posted when League\Oauth2\Client is pointed to one Composer package, and League\Oauth2\Server is pointed to another. Thats fine.

That example works if its the SplClassLoader, if you use the actual example in the spec itself then you'd have to merge the folders. It's still PSR-0.

Everything else is irrelevant, based on your misunderstandings of the above.

Except for:

You claimed PSR-0 allows mapping multiple directories to a single namespace tree quite fine.

NOPE. I never did.

I said I could map two different sub-namespaces to two different folders, and it still be perfectly PSR-0 compliant, which is definitely is no matter if I choose to use Composer, SplClassLoader or the example __autoload() function.

@philsturgeon
Copy link
Author

Sure, hind sight is 20-20, but foresight is usually blind. And how arrogant it is of you to make a statement like that is not how any vaugely right minded person would ever do it.

A better question is WHY would anyone ever do that?

Install with Composer, and then ignore the Composer autoloader, even ignore the manual class loader, ignore class map and instead choose to manually specify autoloadable points?

It's not arrogant to assume nobody would bother, it's just about as sane as taking a lightbulb out of the socket when you go home instead of turning it off at the switch. Don't go mad on that metafore and say something like "but what if t he switch is broken". I know what you're like :)

@simensen
Copy link

simensen commented May 7, 2013

Which indicated that while Composer does allow multiple paths within a namespace

No it doesn't. It maps different locations with different namespace prefixes.

Composer allows multiple paths within a namespace. We are using this for Stack to ensure that we can use classes named like Stack\Builder, Stack\Session, and Stack\OAuth for individual middlewares. This is accomplished by setting Stack as the PSR-0 root for each Composer package. Works like a charm.

To see this in action, here is the vendor/composer/autoload_classmap.php generated when you composer install Stack\OAuth:

<?php

// autoload_namespaces.php generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'Symfony\\Component\\Routing\\' => $vendorDir . '/symfony/routing',
    'Symfony\\Component\\HttpKernel\\' => $vendorDir . '/symfony/http-kernel',
    'Symfony\\Component\\HttpFoundation\\' => $vendorDir . '/symfony/http-foundation',
    'Symfony\\Component\\EventDispatcher\\' => $vendorDir . '/symfony/event-dispatcher',
    'Stack' => array(
        $baseDir . '/src',
        $vendorDir . '/stack/builder/src',
        $vendorDir . '/stack/session/src',
        $vendorDir . '/stack/callable-http-kernel/src'
    ),
    'Silex' => $vendorDir . '/silex/silex/src',
    'Psr\\Log\\' => $vendorDir . '/psr/log',
    'Pimple' => $vendorDir . '/pimple/pimple/lib',
    'OAuth' => $vendorDir . '/lusitanian/oauth/src',
);

@philsturgeon
Copy link
Author

Cool!

Well in this case, this would be considered a Composer specific feature, which has been added as a super-set to the "sample SplClassLoader implementation" linked in the spec, and not - as Anthony has been insisting - a fault with PSR-0.

And again, thats not what I was even talking about in the first place :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment