Running background PHP script on Heroku with Telegram bot integration

I’m working on a Telegram bot hosted on Heroku and I need to trigger a background PHP script when users send specific commands. My main script (main.php) should launch a separate PHP file (background.php) using exec() without blocking the bot response.

I want to avoid the complex setup from Heroku’s official documentation and keep things simple. The background script also needs to send messages back to the Telegram chat.

Here’s my current implementation in the main script:

$data_json = json_encode($data);

$bot_response = $telegram->sendMessage([
    'chat_id' => $data->message->chat->id,
    'text' => $data_json
]);

exec('php /app/background.php?data=' . $data_json, $output, $status);

The JSON data structure looks like this:

{"update_id":123456789,"message":{"message_id":456,"from":{"id":789012345,"first_name":"User"},"chat":{"id":789012345,"first_name":"User","type":"private"},"date":1234567890,"text":"/start","entities":[{"type":"bot_command","offset":0,"length":6}]}}

My background.php file:

<?php

require 'vendor/autoload.php';
require 'config.php';

try {
    $input = file_get_contents("php://input");
    $parsed = json_decode($input, true);
    $message_data = $parsed->{'data'};
    
    $telegram->sendChatAction([
        'chat_id' => $message_data->message->chat->id, 
        'action' => 'typing'
    ]);
    
    $telegram->sendMessage([
        'chat_id' => $message_data->message->chat->id,
        'text' => "Debug info: " . print_r($parsed, true)
    ]);
    
} catch (Exception $error) {}

The exec() returns status code 1 and I don’t receive any debug messages. What am I doing wrong here?

your background.php is trying to read from php://input but exec() doesnt send data that way. try passing the json as a command line argument instead and use $argv[1] to retreive it in background.php. also make sure to escapeshellarg() the json data before passing it to exec to avoid issues with special characters

The main issue is that php://input only works for HTTP requests, not command line execution. When you use exec(), the background script runs in CLI mode where php://input will be empty. Instead of passing data through the URL query string (which doesn’t work with exec either), you should use command line arguments or temporary files. Here’s a better approach:

$data_json = json_encode($data);
$encoded_data = base64_encode($data_json);
exec('php /app/background.php ' . escapeshellarg($encoded_data) . ' > /dev/null 2>&1 &', $output, $status);

Then in background.php:

$encoded_data = $argv[1];
$data_json = base64_decode($encoded_data);
$message_data = json_decode($data_json);

The > /dev/null 2>&1 & part runs the process in background without blocking. Base64 encoding prevents issues with special characters in shell arguments.

I’ve run into similar issues with Heroku’s dyno filesystem restrictions. The problem might be related to how Heroku handles background processes and file permissions. Since you’re getting status code 1, try adding the full path to the PHP binary and check if your background script has proper execution permissions. Something like /app/.heroku/php/bin/php /app/background.php instead of just php. Also, Heroku’s ephemeral filesystem can cause weird behavior with background processes - they might get killed unexpectedly. Consider writing error logs to a file or using Heroku’s logging to debug what’s actually happening when the exec() fails. The CLI environment on Heroku can be quite different from the web request environment, so PATH variables and available binaries might not be what you expect.