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
responsethis is first route
- request
GET http://localhost/lumen1/public/route2
responsethis 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
responsethis is fifth route
- request
GET http://localhost/lumen1/public/route6
response error (expected): 404
For lumen2:
- request
GET http://localhost/lumen2/public/route1
responsethis is first route
- request
GET http://localhost/lumen2/public/route2
responsethis is second route
- request
GET http://localhost/lumen2/public/route3
responsethis is third route
- request
GET http://localhost/lumen2/public/route4
responsethis is fourth route
- request
GET http://localhost/lumen2/public/route5
responsethis is fifth route
- request
GET http://localhost/lumen2/public/route6
responsethis 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
responsethis is first route
- request
GET http://localhost/lumen2/public/route2
responsethis is second route
- request
GET http://localhost/lumen2/public/route3
responsethis 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
responsethis 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
isapp-a view-a
GET http://localhost/b/view.php
isapp-b view-b
GET http://localhost/c/view.php
isapp-a view-c
GET http://localhost/d/view.php
isapp-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