Introduction
Sending a simple message from a Telegram bot using PHP typically involves making an HTTPS request to the Telegram Bot API's sendMessage method. While straightforward, robust implementations require careful handling of potential issues. PHP's cURL extension offers granular control over these requests, enabling detailed inspection of HTTP status codes, graceful management of JSON decoding errors, and precise reaction to Telegram's own ok field in its responses. This guide details a production-ready helper function designed to encapsulate these critical checks. It deliberately omits features like webhook management or state persistence (e.g., update_id for idempotency), as these are considered out of scope for this focused utility and are left to the calling application or a separate service layer.
Core Helper Function for Sending Messages
The following self-contained PHP function, sendTelegramMessage, consolidates essential safety checks for invoking the sendMessage API method. It retrieves the bot token from an environment variable (TELEGRAM_BOT_TOKEN), constructs the API request payload, configures sensible timeouts to prevent hangs, and meticulously verifies the HTTP response status code. Crucially, it also decodes the JSON response, checks for any decoding errors, and examines the ok field within the Telegram API's response structure. If any of these checks fail, the function returns false; otherwise, it returns the decoded JSON response array, providing the caller with detailed success or error information from Telegram.
function sendTelegramMessage(string $chatId, string $text): array|bool
{
$botToken = getenv('TELEGRAM_BOT_TOKEN');
if (!$botToken) {
error_log('Telegram Bot Token not set in environment variables.');
return false;
}
$url = "https://api.telegram.org/bot{$botToken}/sendMessage";
$postFields = [
'chat_id' => $chatId,
'text' => $text,
'parse_mode' => 'HTML' // Or 'MarkdownV2', depending on your needs
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postFields));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // Connection timeout in seconds
curl_setopt($ch, CURLOPT_TIMEOUT, 30); // Total execution timeout in seconds
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // Verify SSL certificate
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // Verify SSL hostname
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErrorNum = curl_errno($ch);
$curlError = curl_error($ch);
curl_close($ch);
// 1. Check for cURL errors
if ($curlErrorNum !== 0) {
error_log("cURL Error ({$curlErrorNum}): {$curlError}");
return false;
}
// 2. Check HTTP status code
if ($httpCode !== 200) {
error_log("Telegram API HTTP Error: {$httpCode} - Response: {$response}");
return false;
}
// 3. Decode JSON response
$responseData = json_decode($response, true);
// 4. Check for JSON decoding errors
if (json_last_error() !== JSON_ERROR_NONE) {
error_log("Telegram API JSON Decode Error: " . json_last_error_msg() . " - Response: {$response}");
return false;
}
// 5. Check Telegram's 'ok' field
if (!isset($responseData['ok']) || $responseData['ok'] !== true) {
$description = $responseData['description'] ?? 'Unknown error';
error_log("Telegram API Error: {$description} (Response: " . print_r($responseData, true) . ")");
return false;
}
// Success
return $responseData;
}
// Example Usage:
// $chatId = '123456789'; // Replace with a valid chat ID
// $message = 'Hello from your PHP cURL bot!';
// $result = sendTelegramMessage($chatId, $message);
// if ($result !== false) {
// echo "Message sent successfully!";
// } else {
// echo "Failed to send message.";
// }
Detailed Error Handling Breakdown
The robustness of the sendTelegramMessage function stems from its multi-layered error checking. Each layer addresses a distinct potential failure point in the communication with the Telegram API.
1. cURL Execution Errors
The first line of defense is checking if curl_exec() encountered any low-level network or cURL-specific problems. This is done by examining curl_errno($ch). A non-zero value indicates an issue such as a DNS resolution failure, a connection timeout before receiving any response, or an inability to establish a connection. The specific error message from curl_error($ch) provides valuable context for debugging. If such an error occurs, the function logs it and returns false, preventing further processing of a non-existent or corrupted response.
2. HTTP Status Code Verification
Even if cURL successfully completes, the HTTP status code returned by the Telegram server is crucial. The Telegram Bot API generally returns a 200 OK status for successful requests. Any other status code, such as 400 Bad Request (e.g., invalid chat ID, malformed request), 401 Unauthorized (invalid bot token), or 5xx Server Error, signifies a problem on Telegram's end or with the request parameters. curl_getinfo($ch, CURLINFO_HTTP_CODE) retrieves this code. The helper logs the code and the raw response body for inspection if it's not 200, returning false.
3. JSON Decoding Errors
The Telegram API consistently returns responses in JSON format. PHP's json_decode() function attempts to parse this response. However, if the response is not valid JSON (perhaps due to an incomplete transmission, a server error page that isn't JSON, or unexpected content), json_decode() will return null, and json_last_error() will report an error. The function checks for JSON_ERROR_NONE. If decoding fails, it logs the specific JSON error message using json_last_error_msg() along with the raw response, and returns false.
4. Telegram's 'ok' Field Check
This is Telegram's internal mechanism for indicating the success or failure of an API method call, distinct from the HTTP status code. A successful API operation will have an ok: true field in the JSON response body. If ok is missing or set to false, it means the API call itself failed, even if the HTTP request was technically successful (e.g., HTTP 200 OK). The response body will typically contain a description field explaining the error (e.g., "chat not found", "message is not modified"). The helper checks for the existence and truthiness of this field. If it's not present or is false, it logs the error description from Telegram's response and returns false.
Why This Approach Matters
Relying solely on the HTTP status code is insufficient for Telegram bots. The API can return an HTTP 200 OK even when a specific method call fails internally, as indicated by the ok: false field. This distinction is critical. For example, attempting to send a message to a user who has blocked your bot might result in an HTTP 200 OK response, but the JSON payload will contain ok: false and a description like "Forbidden: bot was blocked by the user". Without checking the ok field, your application might incorrectly assume the message was sent, leading to silent failures and user frustration. Similarly, ignoring cURL errors or JSON decoding issues leaves your application vulnerable to crashes or unpredictable behavior when network conditions fluctuate or the API's response format unexpectedly changes.
This layered checking strategy ensures that your PHP application has a clear, actionable understanding of whether a Telegram message was successfully delivered or why it failed. It transforms a potentially brittle integration into a resilient component of your bot's infrastructure. The function provides a solid foundation, allowing developers to focus on bot logic rather than the intricacies of HTTP communication and API response parsing.
