I'm having a hell of a time trying to get X Autoload to pull in Mockery via hook_libraries_info() leveraging its composer.json.

I've tried this:

/**
 * Implements hook_libraries_info().
 */
function hammock_libraries_info() {
  $libraries = array();
  
  $libraries['mockery'] = array(
    'name'              => 'Mockery',
    'vendor url'        => 'http://docs.mockery.io/',
    'download url'      => 'https://github.com/padraic/mockery/releases',
    'download file url' => 'https://github.com/padraic/mockery/archive/master.zip',
    'version arguments' => array(
      'file'    => 'CHANGELOG.md',
      'pattern' => '@##\s+([0-9a-zA-Z\.-]+)\s+\([0-9X]{4}-[0-9X]{2}-[0-9X]{2}\)@',
      'lines'   => 5,
      'cols'    => 64,
    ),
    'xautoload' => function($adapter) {
      /** @var \Drupal\xautoload\Adapter\LocalDirectoryAdapter $adapter */
      $adapter->composerJson('composer.json');
    },
  );

This works for everything inside the \Mockery namespace (library/Mockery/*), but causes the Mockery class that exists outside a namespace, at the root (library/Mockery.php), to be missed by the auto-loader. It strikes me as odd that the root class is just chilling outside a namespace like that.

How do I reconcile this? Is this an error with Mockery itself, or just natural ambiguity from PSR-0?

Comments

GuyPaddock created an issue. See original summary.

GuyPaddock’s picture

Issue summary: View changes
GuyPaddock’s picture

Changing the xautoload to this seems to have worked, but seems like a hack:

...
    'xautoload' => function($adapter) {
      /** @var \Drupal\xautoload\Adapter\LocalDirectoryAdapter $adapter */
      $adapter->composerJson('composer.json');

      $adapter->addClassMap(array(
        'Mockery' => 'library/Mockery.php',
      ));
    },
...

Is there a better approach?

GuyPaddock’s picture

For anyone coming across this ticket for reference, though I still haven't found a way, the complete module is here:
https://www.drupal.org/sandbox/guypaddock/2870229

donquixote’s picture

Yes, your quick solution works.

But we should also fix this in xautoload.

From https://github.com/composer/composer/blob/master/src/Composer/Autoload/C... I conclude:
- "Mockery": "library/" does make library/Mockery.php autoloadable.
- "Mockery\\": "library/" does NOT make library/Mockery.php autoloadable, but is otherwise equivalent.

In xautoload, the behavior is currently the same in both cases. Mockery.php is never autoloadable, unless it is explicitly added.

The correct behavior needs to be implemented in ClassFinderAdapter::add() and ClassFinderAdapter::addMultiplePsr0().
We need to:
- Detect if the prefix/namespace ends with a separator.
- If yes, remove the separator and register the prefix/namespace directory mapping.
- If no, register the refix/namespace directory mapping AND register the toplevel class file in the classmap. I would expect this to be fine even if the class file does not exist, because the xautoload class loader does contain a file_exists() when loading classes from the classmap.

If you are interested in producing a patch, please consider
- first confirm that Composer actually works as described above!
- add test cases to cover this edge case.

donquixote’s picture

Btw, the main reason I added this composer.json functionality was "because I can".
At the time nobody did ask for it, and I did not have an actual use case.
There are alternative to deal with Composer libraries in Drupal, e.g. composer_manager.

But still, any existing functionality in xautoload should behave according to expectations!

- first confirm that Composer actually works as described above!

Just saying if this is confirmed then this can be elevated to "bug".
But make it "minor" because it is not more like a "bonus" functionality.