Lumen loaded wrong controller when vendor directory is softlinked

In my example setup, I have 2 directory (each is lumen 8.x project, created by running composer create-project --prefer-dist laravel/lumen lumenNUMBER)

  • ~/www/lumen1/
  • ~/www/lumen2/

In my setup, webserver is apache/httpd with document root set to ~/www/

In lumen1 project:

  • file edited: routes/web.php
$router->get('/route1', function () use ($router) {
    return "this is first route";
});
$router->get('/route2', function () use ($router) {
    return "this is second route";
});
$router->get("/route5", ["uses"=>"[email protected]"] );

  • file added: app/Http/Controllers/CommonController.php
<?php
namespace AppHttpControllers;
class CommonController extends Controller
{
    public function route5() { "this is fifth route"; }
}

In lumen2 project:

  • file edited: routes/web.php
$router->get('/route1', function () use ($router) {
    return "this is first route";
});
$router->get('/route2', function () use ($router) {
    return "this is second route";
});
$router->get('/route3', function () use ($router) {
    return "this is third route";
});
$router->get("/route4", ["uses"=>"[email protected]"] );
$router->get("/route5", ["uses"=>"[email protected]"] );
$router->get("/route6", ["uses"=>"[email protected]"] );
  • file added: app/Http/Controllers/CommonController.php
<?php
namespace AppHttpControllers;
class CommonController extends Controller
{
    public function route5() { "this is fifth route"; }
    public function route6() { "this is sixth route"; }
}
  • file added: app/Http/Controllers/SomeController.php
<?php
namespace AppHttpControllers;
class SomeController extends Controller
{
    public function route4() { "this is fourth route"; }
}

Problem

Everything is running fine and good, I can make such request and get expected response:

For lumen1:

  • request GET http://localhost/lumen1/public/route1 response this is first route
  • request GET http://localhost/lumen1/public/route2 response this is second route
  • request GET http://localhost/lumen1/public/route3 response error (expected): 404
  • request GET http://localhost/lumen1/public/route4 response error (expected): 404
  • request GET http://localhost/lumen1/public/route5 response this is fifth route
  • request GET http://localhost/lumen1/public/route6 response error (expected): 404

For lumen2:

  • request GET http://localhost/lumen2/public/route1 response this is first route
  • request GET http://localhost/lumen2/public/route2 response this is second route
  • request GET http://localhost/lumen2/public/route3 response this is third route
  • request GET http://localhost/lumen2/public/route4 response this is fourth route
  • request GET http://localhost/lumen2/public/route5 response this is fifth route
  • request GET http://localhost/lumen2/public/route6 response this is sixth route

But now I tried to get creative and deleted lumen2/vendor directory, then make symlink in lumen2/vendor to point to ../lumen1/vendor

  • ln -s ../lumen1/vendor vendor
  • output of ls -al on ~/www/lumen2:
    • lrwxrwxrwx 1 kristian kristian 16 Apr 21 08:49 vendor -> ../lumen1/vendor

The reason is that I’m low on disk space (this is not the only lumen project, I know it’s only 40-50mb but the size is multiplied by number of project)

Now the request and their responses is (note that lumen1 is omitted since it’s same with above):

  • request GET http://localhost/lumen2/public/route1 response this is first route
  • request GET http://localhost/lumen2/public/route2 response this is second route
  • request GET http://localhost/lumen2/public/route3 response this is third route
  • request GET http://localhost/lumen2/public/route4 response error (NOT expected): 500 Target class [AppHttpControllersSomeController] does not exist.
  • request GET http://localhost/lumen2/public/route5 response this is fifth route
  • request GET http://localhost/lumen2/public/route6 response error (NOT expected): 404

My analysis

  • closure routes do not get affected with softlinked vendor directory
  • route that use softlinked vendor directory will read it’s controller from link target’s controller instead of it’s own

The question is:

  • why softlinked vendor directory makes lumen reads it’s controller from ‘lumen1’ directory?
  • if this approach is not feasible, then how to reduce disk space usage on lumen and/or laravel vendor directory?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

I have found the problem:

I created 4 directory, each called a, b, c, d, each contains such php files:

  • view.php: <?php require_once("vendor/autoload.php"); echo "view-[x]";
  • vendor/autoload.php: <?php require_once(dirname( __FILE__ )."/../controller.php");
  • controller.php: <?php echo "app-[x]";
    In which [x] is replaced with directory name (so a/controller.php contains <?php echo "app-a"; , b/controller.php contains <?php echo "app-b"; , etc…)
  • directory a is the ‘master’/’main’
  • directory b is copied from directory a cp -r a b
  • directory c is copied from directory a cp -r a c, then I removed c/vendor and then softlink it from a’s vendor directory: cd c; ln -s ../a/vendor vendor
  • directory d is copied from directory a cp -r a d, then I removed d/vendor and then hardlink it from a’s vendor directory: cp -al a/vendor d/vendor

I found out that output from:

  • GET http://localhost/a/view.php is app-a view-a
  • GET http://localhost/b/view.php is app-b view-b
  • GET http://localhost/c/view.php is app-a view-c
  • GET http://localhost/d/view.php is app-d view-d

The solution: don’t softlink, just hardlink.

This solves:

  • lower disk usage compared to plainly copying vendor directory (it still consume inodes though, my laravel project have 80mb+ vendor directory, hardlinking consume about 6mb when observed via df -h)
  • http request is served by correct controllers

Why the trouble?

As stated by NicoHaase in comment of my question, two separate application HAVE TO stay on two seperate vendor folders.

Yes, I know, I know this and I strongly agree. But in case it’s not two separate applications. It’s one application inside git repository that’s made into multiple directory (via git worktree). And I have an ironclad rule that when I update one branch/worktree’s composer.lock/composer.json (dependency), then I have to update the others. So the goal here is to save disk space.

Why don’t I just use git’s checkout command then? That’s because I want to be able to simultaneously work on multiple branch at same time. Stashing and/or committing unfinished changes is out of the question.

Why bother about saving disk space? When you have this much free space you will try to save disk space:

$ # this is the size of my vendor directory
du -sh vendor
83M     vendor
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       XXXX  XXX  413M  XXX /

$ rm -r vendor
$ # this is my free space when I deleted vendor directory
$ df -h       
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       XXXX  XXX  419M  XXX /

$ cp -r ../../myproject/vendor/ vendor
$ # this is my free space when copied vendor directory
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       XXXX  XXX  336M  XXX /

$ cp -al ../myproject/vendor/ vendor
$ # this is my free space when using hardlinked vendor directory
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       XXXX  XXX  413M  XXX /

TL;DR: don’t softlink, do hardlink instead

instead of ln -s [target] [link], do cp -al [target] [link]


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x