doctrine / DoctrineModule

Doctrine Module for Laminas
http://www.doctrine-project.org/projects/doctrine-module.html
MIT License
398 stars 270 forks source link

vendor/bin/doctrine-module only works if it's a symlink #729

Closed bitwombat closed 2 years ago

bitwombat commented 3 years ago
$ doctrine-module orm:generate-proxies
PHP Warning:  include(doctrine-module.php): failed to open stream: No such file or directory in /app/platform/product-app/vendor/bin/doctrine-module on line 4

Warning: include(doctrine-module.php): failed to open stream: No such file or directory in /app/platform/product-app/vendor/bin/doctrine-module on line 4
PHP Warning:  include(): Failed opening 'doctrine-module.php' for inclusion (include_path='.:/usr/local/lib/php') in /app/platform/product-app/vendor/bin/doctrine-module on line 4

Warning: include(): Failed opening 'doctrine-module.php' for inclusion (include_path='.:/usr/local/lib/php') in /app/platform/product-app/vendor/bin/doctrine-module on line 4

This happens because Composer detects it's running in WSL2 and copies files into vendor/bin/ for some reason. The logic in vendor/bin/doctrine-module expects __DIR__ to be ../doctrine-module/bin, which it is when vendor/bin/doctrine-module is a symlink (ie. it gets followed).

A workaround is to configure Composer to always make symlinks:

    "config": {
        "bin-compat": "symlink"
    },

I don't believe this is a composer issue. doctrine-module should not be relying on symlink resolution to ../doctrine-module/bin/doctrine-module in order to work. Other vendor/bin files seem to work by loading autoload.php and newing an object.

TomHAnderson commented 3 years ago

In order to correct this, we'll need someone with access to WSL2 and a vested interest in correcting it. Know anyone? I'm sure that may come across as smart-assed but that's really who would be best to submit a PR on this.

bitwombat commented 3 years ago

Hi Tom - Have enjoyed Doctrine for years, so would like to try to contribute. I'm not familiar with the codebase but could help by testing.

If that's not useful, I'll be able to have a crack at it in a few weeks.

TomHAnderson commented 3 years ago

I hope I didn't come across as short. In retrospect, I was worried I did.

This enhancement doesn't NEED WSL2 but would benefit from a user who can duplicate the issue. I'm worried the issue isn't duplicatable on standard *nix distributions.

bitwombat commented 3 years ago

Not short! You included a caveat anyway :) Plus, as saints, open source maintainers get extra leeway and benefit of the doubt.

I just duplicated it on my Ubuntu setup.

$ rm vendor/bin/doctrine-module
$ cp vendor/doctrine/doctrine-module/bin/doctrine-module vendor/bin
$ doctrine-module
PHP Warning:  include(doctrine-module.php): failed to open stream: No such file or directory in /app/platform/product-app/vendor/bin/doctrine-module on line 3

Warning: include(doctrine-module.php): failed to open stream: No such file or directory in /app/platform/product-app/vendor/bin/doctrine-module on line 3
PHP Warning:  include(): Failed opening 'doctrine-module.php' for inclusion (include_path='.:/usr/local/lib/php') in /app/platform/product-app/vendor/bin/doctrine-module on line 3

Warning: include(): Failed opening 'doctrine-module.php' for inclusion (include_path='.:/usr/local/lib/php') in /app/platform/product-app/vendor/bin/doctrine-module on line 3

It looks to me like a simple "include failed because I'm not in the dir I thought I was in".

I'm fibbing a little - I'm running PHP inside a docker container. But I don't think that's contributing, since the include is path-less, and the doctrine-module.php file is not in vendor/bin, so that's what's making the include fail.

https://github.com/doctrine/DoctrineModule/blob/b0a1f82690b69e0c075cf6db4527a25d5a6ec633/bin/doctrine-module#L4

TomHAnderson commented 3 years ago

Can you duplicate it without manually moving it? Can composer put it in the bin dir as a non-symlink?

bitwombat commented 3 years ago

Doesn't look like it: https://getcomposer.org/doc/06-config.md#bin-compat The manual/composer part doesn't really matter. Doctrine-module can't load if it's not a symlink, unlike the other inhabitants of vendor/bin/. This seems to be due to its direct include, instead of relying on autoload.

CardboardAgent commented 3 years ago

Don't know if it is actually the same but I do believe this is at least similar to https://github.com/doctrine/orm/issues/8563 where changing bin/doctrine to

#!/usr/bin/env php
<?php

include(__DIR__ . '/doctrine.php');

Solved the issue.

Changing bin/doctrine-module to

#!/usr/bin/env php
<?php

include(__DIR__ . '/doctrine-module.php');

Fixes the issue in my case, though I did not invest any time trying to recreate the environment described here (setting up symlinks). I am using WSL2, though the application is running in a docker container.

@bitwombat could you perhaps try this solution in your setup?

bitwombat commented 2 years ago

That didn't work. Because composer is copying vendor/doctrine/doctrine-module/bin into vendor/bin, it has to be:

include(__DIR__ . '/../doctrine/doctrine-module/bin/doctrine-module.php');                           

Taking a sample of my vendor/bin dir, I see that doctrine and doctrine-dbal both do this too, as well as codecept. But phpmd, phpcs, codecept and laminas-development-mode all load autoload and then new something, which would be robust to them being symlinks or copied in.

TomHAnderson commented 2 years ago

I appreciate your investigation into this windows issue. Since you have examples of working bin dir code, how about putting in a PR with the modified doctrine-module?

driehle commented 2 years ago

@bitwombat Could it be that you are using an old version of composer? For me with composer 2.1.8 the solution with __DIR__ works on WSL2. No need for bin-compat: symlink. This is the same settings as it is used in doctrine/orm.

Composer automatically creates a proxy file. For doctrine/orm, i.e. for vendor/bin/doctrine this looks like this:

#!/usr/bin/env php
<?php

/**
 * Proxy PHP file generated by Composer
 *
 * This file includes the referenced bin path (../doctrine/orm/bin/doctrine) using eval to remove the shebang if present
 *
 * @generated
 */

$binPath = realpath(__DIR__ . "/" . '../doctrine/orm/bin/doctrine');
$contents = file_get_contents($binPath);
$contents = preg_replace('{^#!/.+\r?\n<\?(php)?}', '', $contents, 1, $replaced);
if ($replaced) {
    $contents = strtr($contents, array(
        '__FILE__' => var_export($binPath, true),
        '__DIR__' => var_export(dirname($binPath), true),
    ));

    eval($contents);
    exit(0);
}
include $binPath;

I think for DoctrineModule we should go the same way, i.e. changing it to include(__DIR__ . '/doctrine-module.php');.

bitwombat commented 2 years ago

"Composer automatically creates a proxy file"

Yeah, you lost me.

I was using composer 2.1.5.

driehle commented 2 years ago

Good, then I suggest we use include(__DIR__ . '/doctrine-module.php');. I'll create a PR in a second.