Running composer dump-autoload in a controller does nothing - php

I'm attempting to run composer dump-autoload -o -d C:\laragon\www\site inside a Laravel controller using the following (or similar - see below):
$command = 'composer dump-autoload -o -d ' . base_path();
exec($command, $output);
But when doing this, output is always null/empty, and the status code is always 1. No errors in either the Laravel or PHP logs. The composer files in vendor/composer do not get updated. I have also triple checked that the directory exists, even removing the -d parameter, which made no difference. Running composer dump-autoload in the terminal manually, works as expected, and with no errors.
I have also tried system(), passthru(), shell_exec, but all exhibit similar behaviour.
Additionally, since I'm running the latest Laravel, I have access to Illuminate\Support\Composer. I've tried the below code, but again it behaves exactly as the above code:
use Illuminate\Support\Composer;
// Attempt 1
app(Composer::class)->dumpAutoloads();
// Attempt 2
app(Composer::class)->dumpOptimized();
I've been playing with this for a couple hours now, and have been using these sources as reference:
https://laracasts.com/discuss/channels/laravel/how-to-composer-dump-autload-in-php
Run composer dump-autoload from controller in laravel 5
EDIT:
After manually adding logging to the $callback parameter inside the Composer::dumpAutoloads() helper in laravel, I've found that I'm getting the following error:
local.DEBUG: 'composer' is not recognized as an internal or external command, operable program or batch file.
Composer works in all my other terminals (Windows Terminal, and the embedded PHPStorm terminal). I've also confirmed that Composer is in my environment variables, and that the path for that variable is valid and is the composer bin folder: C:\ProgramData\ComposerSetup\bin, Composer is also installed for all users.
Right now I'm wondering if this is some "weird" behaviour on Windows and how it determines what user runs the process when initiated by PHP (or what environment variables the process can see). Unfortunately, I don't have a ready-to-go Linux environment to test this against. I'm aware the Composer helper in Laravel uses the Symfony process classes under the hood, perhaps I'm missing something here? Not sure why the PHP exec functions, etc, also didn't work though.

Related

Setting up PHPunit - PHP CLI is not executing the shell script

I'm in a multi developer program. The other developer installed PHPUnit via composer and successfully is running tests. I took the composer files, ran composer update, but when I try to run the tests, I am receiving this error:
C:\Repos\Project\TDD>php vendor/bin/phpunit
Output:
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../phpunit/phpunit" && pwd)
if [ -d /proc/cygdrive ]; then
case $(which php) in
$(readlink -n /proc/cygdrive)/*)
# We are in Cygwin using Windows php, so the path must be translated
dir=$(cygpath -m "$dir");
;;
esac
fi
"${dir}/phpunit" "$#"
This is the content of the phpunit shell script. The shebang first line #!/usr/bin/env sh is not being output, but this is the rest of the file. The problem appears to be that the PHP CLI is not executing the script, just reading it. I can't figure out how to fix that.
I am on a windows environment, and I am not running apache locally.
Things I have tried:
I have tried using full paths (eg, C:/php/php.exe) for both the php and the shell script, and that didn't matter.
I have tried reversing the slashes vendor\bin\phpunit
I can run php -v successfully, but php -f vendor/bin/phpunit still just outputs the file contents.
I have confirmed the PATH for php is accurate and restarted.
All expected vendor files do appear to be present.
Composer update is not throwing errors of any kind.
I've changed my terminal / CLI processor to use powershell instead of the default cmd.exe (same result as above)
I've confirmed encoding of the phpunit file is UTF-8 without BOM
I've tried making the php command execute the phpunit script in powershell and git bash directly with no luck --- it outputs the shell file. It seems to be that php is just not executing it, which makes me think there is a problem with my php.ini or something.
I CAN run the phpunit shell directly, such as phpunit --version, but I can't get it to run my tests.
I set the composer platform to match my cli php -v
"config": {
"platform": {
"php": "7.4.20"
}
}
Edit: Interestingly, I uploaded it all to my LAMP webserver and ran the same script there, and it is not executing there either. It's outputting the files...
There is a lot of stuff you're fiddling with while trouble-shooting your issue, so there can be only giving extended comments on it. Take it with a grain of salt and pick the pieces that make sense to you, it looks to me you're clever and should be close to the point where you connect the dots more soon than later (don't give up):
C:\Repos\Project\TDD>php vendor/bin/phpunit
This on your windows shell prompt command the php binary to execute the (php) script vendor/bin/phpunit.
And PHP does exactly this (everything else would be a surprise, right?), it executes that script.
The output you see confirms it. As php is the PHP CLI SAPI (PHP Command Line Binary), the first line starting with # is not output (you can find this behaviour described in the PHP documentation as well: https://www.php.net/manual/en/features.commandline.usage.php and on antoher place that I can't find right now that exactly describes this difference to the common mode https://www.php.net/manual/en/language.basic-syntax.phpmode.php).
You then correctly analyze:
This is the content of the phpunit shell script.
and make the note on the shebang line:
#!/usr/bin/env sh
So this looks to me that this shell script vendor/bin/phpunit is not to be executed with the given commandline:
php vendor/bin/phpunit
but instead just by:
C:\Repos\Project\TDD>vendor/bin/phpunit
Which won't (or might not) work on your windows system.
Some more background information (this applies to phpunit bin-stubs as well as other when installed as composer(1) dependencies):
composer manages these scripts (for you)
composer handles them for POSIX compatible systems as well as for others (compare the /usr/bin/env sh which is stupid, it's /bin/sh, always, may need a report on the composer project) incl. windows or windows with cygwin or WSL running on Windows, Vagrant box setups etc. pp. (yes they really care since years).
composer handles this at the time of install/update.
for windows (which I can't compare are not using it but from what I remember) composer is adding .bat or .cmd files next to the commands (as that is how Windows handles executables).
So mabye using:
C:\Repos\Project\TDD>composer exec phpunit
already solves your invocation problem.
Or
C:\Repos\Project\TDD>vendor\bin\phpunit.bat
Or
C:\Repos\Project\TDD>vendor\bin\phpunit.cmd
Or
C:\Repos\Project\TDD>vendor\bin\phpunit
does. Point in case is this: https://getcomposer.org/doc/articles/vendor-binaries.md
All in all this looks to me you got instructions from one developer working on a unixoide sytem (Mac, Linux) and you're running on Windows. This should not pose a problem on that level, however the onboarding in your team might be low or it's just the knowledge management (unfortunately after a decade or more of Stackoverflow this got worse) and you've been left alone with the trouble shooting.
It's not a programming problem, but merely getting the tooling to run.
Perhaps there is some regime about composer and running it which prevents you from looking there first. But this is hard to say on Stackoverflow.
I'd start fresh with the project, remove it from disk and install it anew. This must work, always. To not sink the current project you can do this a-new in a second directory (the project might support composer create-project to give it a quick start - that is cloning new and doing the composer install).
Similar it is that you could do a composer update in the existing project (which is intended to modify it and depending on how well the project you work with is integrated with composer and your development platform will enable or break things).
At the end of the day the common workflow is:
> rmdir vendor
(remove the vendor directory, that is the blank slate)
> composer install
(install the vendor directory, that is all PHP dependencies of the project)
> composer exec phpunit
(execute the phpunit test-runner)
Edit: Interestingly, I uploaded it all to my LAMP webserver and ran the same script there, and it is not executing there either. It's outputting the files...
Never install phpunit on a webserver system. Run it in development or CI, but not on the webserver. Period. (this has security implications, and it won't help you to execute it there, you need to run it where you develop when you're doing TDD - keep this focus for troubleshooting)
The true path to the PHP script that is represented by vendor/bin/phpunit is:
vendor/sebastianbergmann/phpunit/phpunit
(you can find it in the composer.json of phpunit https://github.com/sebastianbergmann/phpunit/blob/master/composer.json#L66)
Given your original command-line and from the various other information you give, this should work:
php vendor/sebastianbergmann/phpunit/phpunit

Why despite "drush" being installed via `composer global install` during image build, I cannot find the tool from within a running PHP script?

I am developing a PHP web application inside of a Docker container. Using volumes: inside of my docker-compose.yml file, I have specified a local directory so that any files generated are dumped and persist after the container is destroyed.
volumes:
- ./docroot:/var/www/html
Inside my Dockerfile, I RUN a command that installs a command line management tool:
RUN curl -sS https://getcomposer.org/installer | php && \
mv composer.phar /usr/local/bin/composer && \
ln -s /root/.composer/vendor/bin/drush /usr/local/bin/drush
RUN composer global require drush/drush:8.3.3 && \
composer global update
When the container comes up, I can use docker-compose exec -it <container> bash to get inside the container, and everything works fine. drush is in my path, and I can use it globally throughout the container to manage the app.
Now here is the strange part. Part of my application is that I have to run that command from a PHP script inside the container to help automatically manage some of the build process.
Using php, I run exec('drush dbupdate', $output, $retval); $retval returns a exit status of 127, or command not found and $output is empty. If I switch up the exec to use the full path I get an exit status 126.
If I go back into the container, I can run that command just fine. Note all other cli commands work as expected with exec (ls, whoami, etc but which drush returns exist status 1)
What am I missing? Why can I use it with no problems manually, but PHP exec() can't find it? passthru(), shell_exec(), and others have the same behavior.
composer global install will not install the command "globally" for all users, but "globally" as in "for all projects".
Generally, these packages are installed in the home directory for the user executing the command (e.g. ~/.composer), and if they are available in your path is because ~/.composer/vendor/bin is added to the session path.
But when you run composer global require (while building the image) or when you "log in" to the running container (using exec [...] bash) the user involved is root. But when your PHP script runs, it's being executed by another user (presumably www-data). And for that user, ~/.composer does not contain anything.
Maybe do not install drush using composer, but rather download the PHAR file directly or something like that while you are building the image, and put it in /usr/local/bin.
If you are using Drupal >= 8, the recommended way of installing Drush is not as a "global" dependency, but as "project" dependency, so that the appropriate drush version is installed. This comes straight from the docs:
It is recommended that Drupal 8 sites be built using Composer, with Drush listed as a dependency. That project already includes Drush in its composer.json. If your Composer project doesn't yet depend on Drush, run composer require drush/drush to add it. After this step, you may call Drush via vendor/bin/drush

Running the composer remove package name / composer require package name from bash file not working

I am facing a unique issue as i have created a script to install package using my api which is in php file and which used the shell_exec to run a bash file and this bash file is used to install package or remove the package.
But These two command are not working but in i run the composer -v or any other command it give output. so is there any permision for that.
$projectPath = './uninstall.sh '.$name.' '.$version;
$page = shell_exec($projectPath);
.sh file
composer remove $1 $2 --- not working
composer -v -- working
The command composer remove does indeed supposed to only remove the requirement from the compose.json file - it does not remove the directory.
Regarding install (which you didn't demonstrate), I'm guessing there is some error during installation which isn't showing.
Important: the shell_exec() command does not capture the "standard error" stream of the executed shell (this is poorly documented in the shell_exec() function reference). Instead, for commands that output only to stderr, it returns NULL - which it also returns if the executed shell command returned with a non-zero result code, discarding all of the actual output from that shell command, so you often miss the actual error.
If the shell command run using shell_exec() outputs to the standard error stream, that would be streamed to the terminal connected to the running PHP script, or to the server error stream (usually an error log) if running under a server API - so I recommend checking that.
That being said, you should probably not use shell_exec() for any non-trivial things, especially things that can fail. system() is much better as it doesn't return NULL for commands that end with a non-zero exit code, but the best choice is obviously the much more complicated to use proc_open().

Make Capistrano use alias on sever when running scripts

I have the following problem using Capistrano with laravel:
My hosting provider does not provide a cli php version via php but only via a usr/bin/local/.../PHP-CLI command
I did create an alias for it in my .bash_profile so running composer install from the cli is no problem.
However, Capistrano (as far as I understand due to it starting in a very basic shell http://capistranorb.com/documentation/faq/why-does-something-work-in-my-ssh-session-but-not-in-capistrano/) does not load this alias, so I get an error from the composer scripts e.g. php artisan.
However, on my dev machine I need to keep it as php, since this is where php is here.
How can I solve this problem best? Any more info you need? Thanks.
Just in case it helps, this is how I call the script:
desc 'Composer install'
task :composer_install do
on roles(:app), in: :groups, limit:1 do
execute "/usr/local/bin/php5-56STABLE-CLI composer.phar install --working-dir #{fetch(:release_path)}"
execute "cp #{fetch(:deploy_to)}/shared/.env #{fetch(:release_path)}/.env"
end
end
It sounds like your scenario is the perfect fit for Capistrano's "command map" feature, as documented here: https://github.com/capistrano/sshkit#the-command-map.
Here are the two main takeaways:
Write your Capistrano execute commands so that the binary name (php) is a separate argument. This will allow it to be substituted using the command map. For example:
execute :php, "composer.phar install --working-dir #{fetch(:release_path)}"
In your Capistrano deployment config, tell the command map how to substitute the :php command, like this:
SSHKit.config.command_map[:php] = "/usr/local/bin/php5-56STABLE-CLI"
If you want this substitution to affect all deployment environments, place it in deploy.rb. If it only applies to your production environment, then put it in production.rb.
Okay, my current workaround is the following:
in your capistrano deploy.rb in the script that you execute at deploy update.
desc 'Composer install'
task :composer_install do
on roles(:app), in: :groups, limit:1 do
execute "/usr/local/bin/php5-56STABLE-CLI /path/to/composer.phar install --working-dir #{fetch(:release_path)} --no-scripts"
execute "cd #{fetch(:release_path)} && /usr/local/bin/php5-56STABLE-CLI artisan clear-compiled"
execute "cd #{fetch(:release_path)} && /usr/local/bin/php5-56STABLE-CLI artisan optimize"
end
end
end
after "deploy:updated", "deploy:composer_install"
I am not 100% sure if the artisan clear-compiled is needed. Anyway, those 2 are composer scripts that would normally be called via composer, but the --no-scripts flag keeps them from being called, so that it does not fail on install. When calling them from capistrano, I can easily change which php to use, as you can see.
However if anyone has a better solution, please let me know.

phpunit command doesn't work for laravel 4 on windows 7

I've recently installed laravel and have written some tests in /tests directory but when I use phpunit at cmd in the same folder that phpunit.xml exists, it says 'phpunit' is not recognized as an internal or external command,operable program or batch file.. I'm using windows 7. what should I do?
The solution for me:
php vendor/phpunit/phpunit/phpunit
This, of course, assumes you've set up a php environment variable in Windows
As Unnawut said, it doesn't work because vendor/phpunit/phpunit/phpunit is not a native Windows executable. You need a .bat or .cmd file that will basically call 'php phpunit'. There should be one in vendor/bin, but to make life easy, try this - create a file phpunit.bat (or .cmd) at the root of your site, containing this:
#ECHO OFF
SET BIN_TARGET=%~dp0/vendor/phpunit/phpunit/phpunit
php "%BIN_TARGET%" %*
Now you can call phpunit from the command line at the root of the site.
If you are a window user and you are having this issue, do this:
You need to tell Window where to find PHPUnit command, you can first of all verify that this file exists in your Laravel project under /vendor/bin
Finally you need to append the full path to /vendor/bin in your window PATH variable,
To do this:
1. Right-click on 'Computer' then click properties
On the second window click Advanced system settings
On the next window under Advanced click Environmental Variables
On the next window double-click PATH then set PATH variable by appending
the full path to your laravel-project/vendor/bin; Notice the ; at the end.
NB: Other variables might already exists in the PATH, so ensure you don't overwrite them by appending your own at the very end
Finally click Ok on all the dialog boxes
alias phpunit="vendor/bin/phpunit"
I added this command in command line instead of just "phpunit"
vendor\bin\phpunit
That worked for me.
Install phpunit globally:
composer global require phpunit/phpunit
Afterwards you will be able to run phpunit ( even on Windows ):
phpunit
The phpunit executable is not in your project root folder, that's why it can't find it.
Now I assume that you already have phpunit in your composer.json file, something like this:
"require-dev": {
"phpunit/phpunit": "3.7.*"
}
When installed by composer, the package will be installed to vendor/vendor_name/package_name. So to run it at your project root, type this command:
vendor/phpunit/phpunit/phpunit
Borrowing from #Chris' excellent answer:
Even better, you can make vendor/phpunit/phpunit/phpunit an environment variable, say "phpunit" and whenever you want to run the test in any laravel project you just call php %phpunit%.
Demonstration
This working for me
In double quotes this command in console windows
"vendor/bin/phpunit"
If it says the following:
$ phpunit tests/Feature/ExampleTest.php
PHPUnit 3.7.21 by Sebastian Bergmann.
Class 'tests/Feature/ExampleTest' could not be found in 'C:\xampp\htdocs\blog1\tests\Feature\ExampleTest.php'.
Instead of typing tests/Feature/ExampleTest.php you say tests " \\Feature\\Example.test" because you're using windows, not mac. :) GL & HF
Using just \ or / will give errors :)
With Laravel phpunit is set up right out of the box. The easiest way to run it on Windows is to add an entry to scripts in your package.json file...
"scripts": {
...
"tests": "php vendor/phpunit/phpunit/phpunit"
},
Now you simply run unit tests with
npm run tests

Categories