heroku-buildpack-php
heroku-buildpack-php copied to clipboard
Support Debianisms for local execution
It has php5
and php5-fpm
instead, and apache2
instead of httpd
(with crazy sourcing of an env vars file).
https://wiki.apache.org/httpd/DistrosDefaultLayout :(
For anyone else struggling with this, I created local-heroku-php-nginx to show a setup with heroku-php-nginx
on Ubuntu 14.04.
@dzuelke I haven't quite figured out why, but something in v93
of the buildpack is preventing local execution (but only Ubuntu 14.04). Specifically, it is throwing this error:
Unable to determine Composer vendor-dir setting; is 'composer' executable on path or 'composer.phar' in current working directory?
I'll be digging around more today in preparation for v96
but if you have any ideas, I'm all ears.
How did you install Composer, @jonahgeorge? Is there a local composer.phar
?
Please run this and report what it returns:
file $(which composer)
Also, what does composer config vendor-dir
yield if you just run it in your project?
Also, do you have a "php" executable, or is it "php5" or "php5-cli"?
Installing composer using the following commands; however, this issues is also present when using their new installation instructions.
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ file $(which composer)
/usr/local/bin/composer: data
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ composer config vendor-dir
vendor
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ composer config bin-dir
vendor/bin
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ php -v
PHP 7.0.4-4+deb.sury.org~trusty+1 (cli) ( NTS )
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ ls /usr/bin | grep php
php
php7.0
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ ls /usr/sbin | grep php
phpdismod
phpenmod
php-fpm
php-fpm7.0
phpquery
While I'm not entirely sure, it seems to be related to the variables that heroku-php-nginx
is setting. If I hardcode the values on lines 211 and 212, I get the following error:
211: COMPOSER_VENDOR_DIR="vendor"
212: COMPOSER_BIN_DIR="vendor/bin"
213: # COMPOSER_VENDOR_DIR=$(composer config vendor-dir 2> /dev/null | tail -n 1) && export COMPOSER_VENDOR_DIR || { echo "Unable to determine Composer vendor-dir setting; is 'composer' executable on path or 'composer.phar' in current working directory?" >&2; exit 1; } # tail, as composer echos outdated version warnings to STDOUT; export after the assignment or exit status will that be of 'export
214: # COMPOSER_BIN_DIR=$(composer config bin-dir 2> /dev/null | tail -n 1) && export COMPOSER_BIN_DIR || { echo "Unable to determine Composer vendor-dir setting; is 'composer' executable on path or 'composer.phar' in current working directory?" >&2; exit 1; } # tail, as composer echos outdated version warnings to STDOUT; export after the assignment or exit status will that be of 'export
vendor/bin/heroku-php-nginx: line 280: Could: unbound variable
Which is:
277: # determine number of FPM processes to run
278: # prevent loading of extension INIs (and thus e.g. newrelic) using empty PHP_INI_SCAN_DIR
279: read WEB_CONCURRENCY php_memory_limit <<<$(PHP_INI_SCAN_DIR= php ${php_config:+-c "$php_config"} $(composer config vendor-dir 2> /dev/null | tail -n 1)/heroku/heroku-buildpack-php/bin/util/autotune.php -y "$fpm_config" -t "$DOCUMENT_ROOT" "$ram") # tail, as composer echos outdated version warnings to STDOUT
280: [[ $WEB_CONCURRENCY -lt 1 ]] && WEB_CONCURRENCY=1
281: export WEB_CONCURRENCY
Update
By switching $(composer config vendor-dir 2> /dev/null | tail -n 1)
to vendor
on line 279, it starts up but is not binding to the correct port (attempts and fails to bind to 8080, rather than foreman's default of 5000). This also happens when hardcoding the PORT
variable on line 101:
100: # set a default port if none is given
101: # export PORT=${PORT:-$(( $RANDOM+1024 ))}
102: export PORT=5000
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ foreman start
17:19:13 web.1 | started with pid 2889
17:19:13 web.1 | DOCUMENT_ROOT changed to './'
17:19:13 web.1 | Using Nginx server-level configuration include 'nginx_app.conf'
17:19:13 web.1 | 1 processes at -1B memory limit.
17:19:13 web.1 | Starting php-fpm...
17:19:15 web.1 | Starting nginx...
17:19:15 web.1 | Application ready for connections on port 5000.
17:19:15 web.1 | nginx: [emerg] bind() to 0.0.0.0:8080 failed (98: Address already in use)
What happens if you run composer config vendor-dir 2> /dev/null | tail -n 1
, and what happens if you run echo $(composer config vendor-dir 2> /dev/null | tail -n 1)
? And, in both cases, without the 2> /dev/null
redirect?
Ah, this has to do with the alias we make that uses a local composer.phar
Please help me by running the following and reporting:
$ which ./composer.phar composer
$ composer_bin=$(which ./composer.phar composer | head -n1)
$ echo $composer_bin
$ file --brief --dereference $composer_bin
$ php -n $composer_bin
Well, you beat me to it! :D Here's the debug info
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ which ./composer.phar composer
/usr/local/bin/composer
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ composer_bin=$(which ./composer.phar composer | head -n1)
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ echo $composer_bin
/usr/local/bin/composer
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ file --brief --dereference $composer_bin
data
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ php -n $composer_bin
Fatal error: Uncaught Error: Class 'Phar' not found in /usr/local/bin/composer:23
Stack trace:
#0 {main}
thrown in /usr/local/bin/composer on line 23
Below is how I found it:
echo $(composer config vendor-dir)
# make sure we run a local composer.phar if present, or global composer if not
composer() {
local composer_bin=$(which ./composer.phar composer | head -n1)
# check if we the composer binary is executable by PHP
if file --brief --dereference $composer_bin | grep "bash" > /dev/null ; then # newer versions of file return "data" for .phar
# run it directly; it's probably a bash script or similar (homebrew-php does this)
$composer_bin "$@"
else
php -n $composer_bin "$@"
fi
}
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ foreman start
18:10:51 web.1 | started with pid 4104
18:10:51 web.1 | vendor
18:10:51 web.1 | Unable to determine Composer vendor-dir setting; is 'composer' executable on path or 'composer.phar' in current working directory?
18:10:51 web.1 | exited with code 1
18:10:51 system | sending SIGTERM to all processes
When I move it below the composer()
function:
# make sure we run a local composer.phar if present, or global composer if not
composer() {
local composer_bin=$(which ./composer.phar composer | head -n1)
# check if we the composer binary is executable by PHP
if file --brief --dereference $composer_bin | grep "bash" > /dev/null ; then # newer versions of file return "data" for .phar
# run it directly; it's probably a bash script or similar (homebrew-php does this)
$composer_bin "$@"
else
php -n $composer_bin "$@"
fi
}
echo $(composer config vendor-dir)
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ foreman start
18:12:30 web.1 | started with pid 4149
18:12:30 web.1 | Fatal error: Uncaught Error: Class 'Phar' not found in /usr/local/bin/composer:23 Stack trace: #0 {main} thrown in /usr/local/bin/composer on line 23
18:12:30 web.1 | Unable to determine Composer vendor-dir setting; is 'composer' executable on path or 'composer.phar' in current working directory?
18:12:30 web.1 | exited with code 1
18:12:30 system | sending SIGTERM to all processes
Wow, even phar is compiled as shared on Ubuntu? :(
The reason we do php -n
(which prevents loading of INIs, so it also won't load conf.d/
configs that enable extensions) is that we don't want new relic to start and spew debug info on dyno startup (or even send telemetry about it to the server as "console events").
That makes sense-- Do you think building php from source would be a workaround or is this a hard block?
Oh yeah sure, that'll work, otherwise dynos would not boot on Heroku ;) I can't think of a quick and clean workaround from my side right now...
Ok, well in the meantime I'm updating our Vagrantfiles to compile PHP. Thank you a ton for resolving that
What about putting the extensions on the command line ? Would it be a possible workaround ? :
php -n -d extension=phar.so -d extension=json.so $composer_bin "$@"
It seems to work on my end.... I just encountered the same issue with the apache2 buildpack on ubuntu 16.04, but I don't use new relic and don't find any way to check if using these inline provided extensions actually loads the conf.d/ folder. Any idea ?
That works for you @theobat, but if there is no phar.so
because it's built into core then that'd produce an error... :/
I was able to deal with some of these challenges by adding a bin directory within my project to PATH that contains:
- httpd which just calls apache2
- php-fpm which just calls php7.0-fpm
- composer which just calls /usr/local/bin/composer
The last one bypasses the "php -n" because heroku-php-apache2 only calls "composer" that way if it is a php script and not a shell script.
I'm calling heroku-php-apache2 like this:
#!/bin/bash
set -e
cd /vagrant
PATH="/vagrant/bin:$PATH"
# envvars unsets HOME
orig_home="$HOME"
source /etc/apache2/envvars
HOME="$orig_home"
apache_dir=/vagrant/apache2
[ -d "$apache_dir" ] || mkdir "$apache_dir"
export APACHE_LOG_DIR="$apache_dir"
export APACHE_RUN_DIR="$apache_dir"
export APACHE_PID_FILE="$APACHE_RUN_DIR/apache2.pid"
export APACHE_LOCK_DIR="$apache_dir"
# Simulate a 1X dyno.
ulimit -S -u 256
export DYNO=512M
source .env
vendor/heroku-buildpack-php/bin/heroku-php-apache2 -i ./php.ini public/
... and Apache seems to start ok and respond with my app's index page.
Very interesting. I wonder how feasible it would be for the buildpack to do that out of the box.
My biggest worry is changes to the Debian or Ubuntu config that'd eventually make this unusable.
I'm having the same issue trying to run my app locally.
First was the php-fpm
that in Ubuntu is now php-fpm7.0
, now is the Unable to determine Composer vendor-dir setting; is 'composer' executable on path or 'composer.phar' in current working directory?
Instead of hacking the heroku-php-nginx
I can just change my Vagrant image. With what distro this would work out-of-box? CentOS?
I was able to deal with some of these challenges by adding a bin directory within my project to PATH that contains:
- httpd which just calls apache2
- php-fpm which just calls php7.0-fpm
For these two, I made symlinks
- composer which just calls /usr/local/bin/composer
Because I'm a noob and it took me longer than it should have to figure out how to do this:
contents of /usr/bin/local/composer
#!/usr/bin/env bash
/usr/bin/composer "$@"
don't forget chmod +x /usr/bin/local/composer
lol I forgot to have the call to composer
in the shell script be an absolute path, so it was recursively calling itself over and over. Easy to see once I did set -x
.