2

So this is my question, I wrote my own class autoloader that fallows PSR-0 standard. That's easy. Now I have my PHP MVC Frameowrk and I would like to use Twig, but Twig fallows PEAR naming standard that use _ instead of \.

Now is it true that PSR-0 Autoloaders should also be able to load PEAR libs? Since inside those Autoloaders _ is transformed into \? My loader can't load Twig if I register it, but it may be I made a mistake somewhere.

Should PSR-0 compatible class loader be able to load PEAR libs too?

Kjuly
  • 34,476
  • 22
  • 104
  • 118
otporan
  • 1,345
  • 2
  • 12
  • 29
  • possible duplicate of [PHP - most lightweight psr-0 compliant autoloader](http://stackoverflow.com/questions/12082507/php-most-lightweight-psr-0-compliant-autoloader) – hakre Oct 11 '12 at 02:10

2 Answers2

1

Should PSR-0 compatible class loader be able to load PEAR libs too?

Yes, PSR-0 is interoperable with the PEAR naming conventions of classes and files.

This is not publicly documented any longer, but it is commonly known.

If you can not load the classes, your autoloader is not PSR-0 compliant.

Double-Check with the specification. You will also find a SplClassLoader class linked there that is PSR-0 compatible and you can use instead of your own.

A probably better loader is The Symfony2 ClassLoader Component. You can install it easily via Pear or Composer (symfony/class-loader on Packagist).

If you write your own classloader, take care you work with spl_autoload_register, see


Bonus Function:

To have spl_autoload_register behaving just like in PHP but with PSR-0 resolution to include-path:

$spl_autoload_register_psr0 = function ($extensions = null) {
    $callback = function ($className, $extensions = null) {
        if (!preg_match('~^[a-z0-9\\_]{2,}$~i', $className)) {
            return;
        }
        null !== $extensions || $extensions = spl_autoload_extensions();
        $extensions = array_map('trim', explode(',', $extensions));
        $dirs = array_map('realpath', explode(PATH_SEPARATOR, get_include_path()));

        $classStub = strtr($className, array('_' => '/', '\\' => '/'));

        foreach ($dirs as $dir) {
            foreach ($extensions as $extension) {
                $file = sprintf('%s/%s%s', $dir, $classStub, $extension);
                if (!is_readable($file)) {
                    continue;
                }
                include $file;
                return;
            }
        }
    };
    return spl_autoload_register($callback);
};

var_dump(get_include_path());
var_dump(spl_autoload_extensions());
var_dump($spl_autoload_register_psr0());
Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836
  • 1
    Yes I am using spl_autoload_register and spl_autoload_unregister. My Autoload was not compatible, but now it is :) I already got the answer, the only difference in PEAR and PSR0 should be namespace separator. +1 for you and your effort! – otporan Oct 11 '12 at 09:23
  • 1
    @otporan: No, you are wrong. PSR-0 supports `_` as pseudo namespace separator out of the box. Read the specs. You just have create a PSR-0 incompatible classloader in the first place. – hakre Oct 11 '12 at 09:32
  • 1
    No, you are wrong. PSR-0 does not support PEAR autoloading from the box. I joust tester with SplClassLoader you linked in your answare. Lets say to load Twig that is PEAR, you cant do this: $classLoader = new SplClassLoader('Twig', '../vendor'); $classLoader->register(); It wont work, but if you change namespace seperator like this: $classLoader->setNamespaceSeparator('_'); Then its ok. The PSR-0 states that you should change _ for DIRECTORY_SEPERATOR in your class name. But thats not enough for the loadclass method to know where the file is, and thats why you must tell what NS you use! – otporan Oct 11 '12 at 09:44
  • 1
    And look for example at Smyfony2 UniversalClassLoader where you have clear distinction between how you register PSR0 and PEAR packages. And even UniversalClassLoader findFile method checks if current class has PEAR or PSR0 standard by doing this: if (false !== $pos = strrpos($class, '\\')) { // PSR0 } else { PEAR } And I didnt want to check for standard inside my loadclass. I wanted to have 2 instances of class loaders, one for PSR0, the other one for PEAR, and the only difference is _ vs \, that can be set through setNS method, joust as it is in SplClassLoader you linked. Thanks for help. – otporan Oct 11 '12 at 09:56
  • 1
    @otporan: But that does not change any of the meaning of the [PSR-0 specs](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) which does not differ. If you say you create something PSR-0 compatbile it *must support both ways*. As you've asked for that in your question I answered it. Apart from the standard you can do whatever you want, but then you should not call it PSR-0 compatible. See the **Mandatory** section of the spec. – hakre Oct 11 '12 at 09:57
  • 1
    It doas support both ways. But if using PEAR you must tell loader to use _ as namespace seperator, joust as with yours SplClassLoader. If my Autoload class is not PSR-0 compatible, then SplClassLoader is not compatible too. – otporan Oct 11 '12 at 09:59
  • 1
    @otporan: No, you are wrong again. The `SplClassLoader` has support for that, [see line 114 on ongoing](https://gist.github.com/221634#L114), the `_` separator is supported out of the box. – hakre Oct 11 '12 at 10:01
  • 1
    I have tested this 3 times now. Did you tested this? Try using SplClassLoader class, and register Twig that is PEAR. If you do joust this: $loader = new SplClassLoader('Twig', ../vendor/'); $loader->register(); IT WONT WORK! But if you do this: $loader = new SplClassLoader('Twig', '../vendor/'); $loader->setNamespaceSeparator('_'); $loader->register(); Than it will work! – otporan Oct 11 '12 at 10:09
  • 1
    you are right, it is not compatible per-se, seeing it now, I stand corrected. – hakre Oct 11 '12 at 10:13
  • 1
    + as much as I can for this conversation – otporan Oct 11 '12 at 10:15
0

PSR-0 should be able to work with both full-qualified namespaces and underscored classes. All you should need to do is replace the underscores with a directory seperator. A good example would be:

function autoload($className)
{
    $className = ltrim($className, '\\');
    $fileName  = '';
    $namespace = '';
    if ($lastNsPos = strripos($className, '\\')) {
        $namespace = substr($className, 0, $lastNsPos);
        $className = substr($className, $lastNsPos + 1);
        $fileName  = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
    }
    $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';

    require $fileName;
}

Hope this helps!

jleck
  • 821
  • 1
  • 8
  • 14
  • Thanks for the reply! The answer is YES! :) PSR-0 compatible class loader can load PEAR libraries. Thats awsome! What I did with my autoload class is that I can instanciate one class for PSR0, and one class for PEAR. The only difference in PSR-0 and PEAR is namespace separator. So I added method to my autoload class that can change default namespace separator. $autoload->setNS('\\') for PSR-0, and $autoload->setNS('_') for PEAR, and it works like a charm :) Huh! Thanks again, you got me thinking the right way! – otporan Oct 10 '12 at 23:46
  • @otporan: It is very likely that you're doing it wrong that way. I've added an answer, take a look into the Symfony2 loader and into the [`UniversalClassLoader`](https://github.com/symfony/ClassLoader/blob/master/UniversalClassLoader.php) in specific. – hakre Oct 11 '12 at 02:07
  • 1
    @otporan you shouldn't really need to set a setNS function, as both slashes and underscores should convert to directory seperators. I presume you are using the string defined in setNS function in the string replace, just replace that with array('\\', '_') and it should work – jleck Oct 11 '12 at 13:24