Xdebug support
Why there is no xdebug support on heroku? How do i get xdebug running on heroku? I usually use heroku for testing so I need xdebug
It can be a very nice feature because it is impossible de debug dyno application. The only way to debug is to add log, but for my problem the bug is in third part code so i can't put log on it. I am really lost, i don't know how to find my bug. Everything goes well in dev environment.
I didn't try it myself, but you can build custom packages. I think that's the way to add xdebug to your app.
Yeah, that's the recommended way for the moment. Heroku Exec currently only has the ability to forward local ports to a dyno, not the inverse, which would be needed for Xdebug remote debugging (as Xdebug's only mode for that is having PHP connect to the IDE, not vice versa as with e.g. Java or Node).
https://github.com/Incenteev/heroku-php-exts is an example repo with several additional extensions (although they're all for cedar-14 only, and ext-amqp is now supported out of the box on heroku-16).
Note that you do not have to run a forked buildpack to use your own packages.
In our use case xdebug is required by phpunit to generate code coverage reports: https://stackoverflow.com/questions/35811503/laravel-5-phpunit-and-no-code-coverage-driver-available
Would it be simple enough to simply make the xdebug.so extension available on the heroku-18 stack when building it? or perhaps to post install the extension via sudo apt-get install php-xdebug ?
Perhaps a new environment variable could be implemented?
e.g. HEROKU_POST_INSTALL=sudo apt-get install php-xdebug
This would need to be executed during build time due to the permissions required I assume?
Thinking out of the box... couldn't one use the postdeploy script in the app.json or the post-install-cmd from composer.json to solve this? Or would this not allow sudo operations?
I recently tried this extension for VSCode to remote debug a Python app... if this would be possible using heroku exec this would be a killer feature for remote debugging Python, Java, PHP etc. in heroku containers or docker containers as the VSCode server resides in the container and forwards the data back over the SSH connection to your IDE: https://code.visualstudio.com/docs/remote/ssh
Unfortunately for PHP debugging it is necessary that the server calls the IDE and not vice versa as for the other languages mentioned. At the moment it is not allowed by heroku exec, so the addition of the extension would not be enough.
Yes however my primary use case is that xdebug is used as a dependency for phpunit that doesnt require the remote debug port as far as I know or perhaps it uses it locally.
Question: Are heroku containers not allowed to open remote ports? I doubt that is the case otherwise I wouldn't be able to connect to my redis instance.
Now for the other use case of remote debugging using an IDE it should be enough to setup a SSH tunnel to the container and then running the VSCode server there. This means that the remote port would not be neccessary... the remote ip would be set to 127.0.0.1 and port 9000 as the VSCode server is running locally. The VSCode server would then proxy back the data via the SSH tunnel using whatever method it does. It would be an interesting PoC for Heroku im sure.
Some people have already had success over SSH: https://github.com/felixfbecker/vscode-php-debug/issues/360
What you say is correct, but it is not possible to forward the local (non-public) port of the IDE. To make it work, the PC port 9000 would have to be published on the internet.
Indeed. However it would nice to see a PoC of this VSCode Remote SSH extension working on heroku using exec and that would require that xdebug is installed. This would definitely be an elegant solution to debugging php applications remotely: https://code.visualstudio.com/docs/remote/ssh
The other reason for the extension as mentioned above would be phpunit.
@dzuelke isn't there a more elegant solution to post installing pecl extensions? Seems bizzare.
Why doesn't heroku simply allow "ext-xdebug": "*", in the composer.json's require-dev section? For prod builds this is not used and xdebug can be disallowed, but for CI tests, composer is installed with --dev and then xdebug should be allowed as well
The issue is that we want to, under normal circumstances, avoid people having ext-xdebug running in their production environments. People will specify it in require, forget about it, and push.
I'll have a ponder. The really useful case would be an ability to debug remotely, but as has been pointed out, heroku ps:forward can only forward a local port to a dyno, not vice versa, so that won't work for PHP.
The issue is that we want to, under normal circumstances, avoid people having
ext-xdebugrunning in their production environments. People will specify it inrequire, forget about it, and push.I'll have a ponder. The really useful case would be an ability to debug remotely, but as has been pointed out,
heroku ps:forwardcan only forward a local port to a dyno, not vice versa, so that won't work for PHP.
Does this also apply to the VS Code server running remotely on the server? In this use case xdebug and vs code server will both be running on the remote server and the ports xdebug uses for debugging will not need to leave the server/container. The question is, does the VS code server work over heroku exec? Im not aware of what ports/architecture/requirements that has.
Another use case has just popped up. We are currently using buildpacks.io pack cli to launch a docker container for local development. Setting up the remote php interpreter in the docker container is easy with PhpStorm, however the only thing missing now is the xdebug extension again! Can someone pls pls pls implement this as is the missing part of the puzzle currently for us 🙂
I also would like to use xdebug in dev only, perhaps strictly specify under require-dev and then enable it. xdebug simply initiaties the request to remote server so I dont see what is the issue of enabling it ? can someone provides an update on this please ? also you one can use a solution such as ngrok to forward tcp to the local running IDE @abarke
Hi all!
My use case is in below:
I built a docker image for heroku debugging and I built xdebug from source.
Dockerfile:
ARG STACK_VERSION=20
ARG USER_NAME=hvg-dev
ARG UID=1001
FROM heroku/heroku:${STACK_VERSION}-build
ARG STACK_VERSION
ARG USER_NAME
ARG UID
ARG STACK=heroku-${STACK_VERSION}
ARG JQ_VERSION=1.6
ARG YQ_VERSION=4.11.1
ARG XDEBUG_VERSION=3.0.4
COPY ./bashrc /tmp/bashrc
COPY ./entrypoint.sh /
RUN useradd -M ${USER_NAME} -d /app --uid ${UID}; \
cat /tmp/bashrc >> /etc/profile; rm /tmp/bashrc; \
mkdir -p /app /workspace; chown -R ${USER_NAME}:${USER_NAME} /app /workspace; \
chmod a+x /entrypoint.sh;
USER ${USER_NAME}
WORKDIR /workspace
RUN mkdir -p bin; \
curl -fsSL https://github.com/stedolan/jq/releases/download/jq-${JQ_VERSION}/jq-linux64 > bin/jq; \
chmod a+x bin/jq; \
curl -fsSL https://github.com/mikefarah/yq/releases/download/v${YQ_VERSION}/yq_linux_amd64 > bin/yq; \
chmod a+x bin/yq;
COPY --chown=${USER_NAME}:${USER_NAME} ./composer.json /tmp/composer.json
COPY --chown=${USER_NAME}:${USER_NAME} ./xdebug-install.sh /tmp/xdebug-install.sh
RUN s3_url="https://lang-php.s3.amazonaws.com/dist-${STACK}-stable/"; \
curl -fsSL "${s3_url}php-min-7.4.21.tar.gz" | tar -xz -C .; \
PATH="$(pwd)/bin:$PATH"; \
curl -fsSL -o bin/composer https://getcomposer.org/download/2.1.4/composer.phar; \
chmod a+x bin/composer; \
chmod a+x /tmp/xdebug-install.sh; \
php_version=$(cat /tmp/composer.json | bin/jq '.require .php' --raw-output); \
composer init --name "heroku/dev" --type project --require="php:$php_version" --stability=stable --no-interaction 2>/dev/stdout; \
composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction 2>/dev/stdout; \
rm -rf ./vendor; \
php_modules=$(cat /tmp/composer.json | bin/jq '.require | keys []' --raw-output | grep ext-); \
for m in $php_modules; do composer require --no-install --ignore-platform-reqs $m:* 2>/dev/stdout; done; \
mkdir -p /tmp/buildpack/php /tmp/build_cache /tmp/env; \
curl -fsSL https://buildpack-registry.s3.amazonaws.com/buildpacks/heroku/php.tgz | tar -xz -C /tmp/buildpack/php; \
echo "web: tail -f /dev/null" >> Procfile; \
/tmp/buildpack/php/bin/compile $(pwd) /tmp/build_cache /tmp/env 2>/dev/stdout; \
PATH="/workspace/.heroku/php/bin:$PATH"; \
/tmp/xdebug-install.sh 2>/dev/stdout; \
rm -rf /tmp/* /app/*
WORKDIR /app
ENV STACK=${STACK}
ENV PORT=3000
ENTRYPOINT [ "/entrypoint.sh" ]
xdebug-install.sh:
#!/usr/bin/env bash
# Define directories.
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
XDEBUG_VERSION=${XDEBUG_VERSION-3.0.4}
if [ "$STACK" != "heroku-20" ] && [ "$STACK" != "heroku-18" ]; then
echo "Need heroku-20 or heroku-18 stack"
exit 0
fi
# echo colors
LED='\033[0;34m'
OK='\033[0;32m'
NC='\033[0m' # No Color
#############
###########################################################################
# COMPILE PHP xDebug
###########################################################################
XDEBUG_RELEASE_URI="https://xdebug.org/files/xdebug-$XDEBUG_VERSION.tgz";
SRC_PATH="/tmp";
XDEBUG_SRC_PATH=$SRC_PATH/xdebug-$XDEBUG_VERSION
echo -e "${LED}XDEBUG${NC} Download $XDEBUG_RELEASE_URI ..."
mkdir -p $XDEBUG_SRC_PATH
curl -# $XDEBUG_RELEASE_URI| tar --warning=none -xz -C $SRC_PATH
echo -e "${OK}done${NC}"
# Build from repo
#echo -e "${LED}XDEBUG${NC} Cloning source to $XDEBUG_SRC_PATH ..."
#git clone git://github.com/xdebug/xdebug.git $XDEBUG_SRC_PATH
#echo -e "${OK}done${NC}"
echo -e "${LED}XDEBUG${NC} Compile at $XDEBUG_SRC_PATH ..."
cd $XDEBUG_SRC_PATH
echo $PWD
LOGFILE=/tmp/xdebug_build.log
echo -e "`date +%H:%M:%S` : Starting work" >> $LOGFILE
phpize > $LOGFILE 2>&1
./configure --enable-xdebug > $LOGFILE 2>&1
make -k > $LOGFILE 2>&1
echo -e "${OK}done${NC}"
###########################################################################
# INSTALL PHP xDebug
###########################################################################
EXTENSION_PATH=$( php-config --extension-dir );
LIB_PATH="$( php-config --prefix )/lib"
INSTALL_PATH=$( php-config --ini-dir );
XDEBUG_INI="020-xdebug.ini";
XDEBUG_INI_PATH=$SCRIPT_DIR/$XDEBUG_INI;
echo -e "${LED}XDEBUG${NC} Install bin to $EXTENSION_PATH ..."
cp -v modules/*.so $EXTENSION_PATH
cp -v modules/*.la $LIB_PATH
echo -e "${OK}done${NC}"
echo -e "${LED}XDEBUG${NC} Install ini to $INSTALL_PATH ..."
echo "zend_extension = xdebug.so" >> $XDEBUG_INI_PATH
cp -v $XDEBUG_INI_PATH $INSTALL_PATH/$XDEBUG_INI
echo -e "${OK}done${NC}"
echo -e "${LED}XDEBUG${NC} All Install ${OK}done${NC}"
###########################################################################
composer.json:
{
"name": "heroku/dev",
"type": "project",
"require": {
"php": "^7.4.0"
},
"minimum-stability": "stable",
"require-dev": {
"heroku/heroku-buildpack-php": "*"
}
}
xdebug.ini:
; Xdebug
[XDebug]
xdebug.max_nesting_level = 256
xdebug.show_exception_trace = 0
xdebug.mode = debug
xdebug.start_with_request = yes
#Replace host.docker.internal to your computers IP address if linux
xdebug.client_host = ${XDEBUG_HOST}
bashrc:
# prompt
export PS1='\[\033[01;34m\]\w\[\033[00m\] \[\033[01;32m\]$ \[\033[00m\]'
# profile feature switch
if [ -d $HOME/.profile.d ]; then
for i in $HOME/.profile.d/*.sh; do
if [ -r $i ]; then
. $i
fi
done
unset i
fi
entrypoint.sh:
#!/usr/bin/env sh
COMMAND=$*
#BASEDIR=$(dirname "$0")
if [ ! -d /app/.heroku ]; then
cp -r /workspace/.heroku /app
fi
if [ ! -d /app/.profile.d ]; then
cp -r /workspace/.profile.d /app
fi
XDEBUG_ENABLE=${XDEBUG_ENABLE-0}
export XDEBUG_ENABLE
rm -f /app/.heroku/php/etc/php/conf.d/999-xdebug.ini
if [ "$XDEBUG_ENABLE" = 1 ]; then
ln -sn $(pwd)/xdebug.ini /app/.heroku/php/etc/php/conf.d/999-xdebug.ini
fi
PATH="/workspace/bin:$PATH"
export PATH
HISTFILE=${HISTFILE-/tmp/.bash_history}
export HISTFILE
keys=$(yq e '. | keys' Procfile | cut -c 3-)
for key in $keys; do
echo '#!/usr/bin/env bash' > "/workspace/bin/$key"
yq e ".$key" Procfile >> "/workspace/bin/$key"
chmod +x "/workspace/bin/$key"
done
bash -l -c "$COMMAND"
Procfile:
web: vendor/bin/heroku-php-nginx .
docker-compose.yml:
version: "3.8"
services:
app:
ports:
- ${PORT-3000}:${PORT-3000}
command: web
build: .
docker-compose.override.yml:
version: "3.8"
services:
app:
build:
args:
UID: ${UID-1001}
environment:
PORT: ${PORT-3000}
XDEBUG_HOST: ${XDEBUG_HOST-host.docker.internal}
XDEBUG_ENABLE: ${XDEBUG_ENABLE-1}
volumes:
- ./:/app
In Linux Host requires settings below:
echo "UID=$(id -u)" >> .env
echo "XDEBUG_HOST=$(hostname -I | awk '{print $1}')" >> .env
For using as heroku build step modify composer.json like below:
{
"name": "heroku/dev",
"type": "project",
"require": {
"php": "^7.4.0"
},
"minimum-stability": "stable",
"require-dev": {
"heroku/heroku-buildpack-php": "*"
},
"scripts": {
"post-install-cmd": [
"bash xdebug-install.sh"
]
}
}
And now push to heroku:
git push heroku main
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 2 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 675 bytes | 675.00 KiB/s, done.
Total 6 (delta 4), reused 0 (delta 0), pack-reused 0
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Building on the Heroku-20 stack
remote: -----> Using buildpack: heroku/php
remote: -----> PHP app detected
remote: -----> Bootstrapping...
remote: -----> Installing platform packages...
remote: - php (7.4.22)
remote: - composer (2.1.5)
remote: - apache (2.4.48)
remote: - nginx (1.20.1)
remote: -----> Installing dependencies...
remote: Composer version 2.1.5 2021-07-23 10:35:47
remote: Installing dependencies from lock file
remote: Verifying lock file contents can be installed on current platform.
remote: Nothing to install, update or remove
remote: Generating optimized autoload files
remote: > bash xdebug-install.sh
remote: XDEBUG Download https://xdebug.org/files/xdebug-3.0.4.tgz ...
remote: ######################################################################## 100.0%
remote: done
remote: XDEBUG Compile at /tmp/xdebug-3.0.4 ...
remote: /tmp/xdebug-3.0.4
remote: done
remote: XDEBUG Install bin to /app/.heroku/php/lib/php/extensions/no-debug-non-zts-20190902 ...
remote: 'modules/xdebug.so' -> '/app/.heroku/php/lib/php/extensions/no-debug-non-zts-20190902/xdebug.so'
remote: 'modules/xdebug.la' -> '/app/.heroku/php/lib/xdebug.la'
remote: done
remote: XDEBUG Install ini to /app/.heroku/php/etc/php/conf.d ...
remote: '/tmp/build_f8f51b83/020-xdebug.ini' -> '/app/.heroku/php/etc/php/conf.d/020-xdebug.ini'
remote: done
remote: XDEBUG All Install done
remote: -----> Preparing runtime environment...
remote: -----> Checking for additional extensions to install...
remote: -----> Discovering process types
remote: Procfile declares types -> web
remote:
remote: -----> Compressing...
remote: Done: 15.5M
remote: -----> Launching...
remote: Released v6
remote: https://felegy-heroku-xdebug.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/felegy-heroku-xdebug.git
1ff79a8..0ae0c7d main -> main
Check my phpinfo: https://felegy-heroku-xdebug.herokuapp.com/phpinfo.php
@felegy thanks for sharing. what i did in dokku is to use a plugin calld apt-packages which enable you to inject additional binaries inside the container - i used it to inject php-xdebug which automatically installs xdebug 2.9 still compatible with php 7.4 , and then used pre-deploy scripts to copy the final xdebug.so file to the correct heroku php ini location. It works perfectly but yeah it is a bit hacky! advantage is I didnt have to build from scratch
thanks for sharing your setup, this is great help