Перейти к содержимому
Все статьи
· Обновлено 15 февраля 2026 г.

Отправка Discord Webhook из PHP

Полное руководство по отправке Discord webhook из PHP. Примеры с cURL и Guzzle, создание эмбедов, загрузка файлов и интеграция с Laravel.

phpwebhookтуториалlaravelphp discord 2026laravel discordwordpress discord
Отправка Discord Webhook из PHP

Discord webhook позволяет отправлять автоматические уведомления в каналы Discord из PHP-приложений. В этом руководстве мы рассмотрим все способы работы с webhook: от простых запросов через cURL до продвинутой интеграции с Laravel.

Основы Discord Webhook

Webhook в Discord — это специальный URL, который принимает POST-запросы с JSON-данными и публикует сообщения в канал. Это простой способ интеграции уведомлений без необходимости создавать полноценного бота.

Типичные сценарии использования:

  • Уведомления об ошибках в приложении
  • Алерты о новых заказах в интернет-магазине
  • Отчёты о результатах деплоя
  • Мониторинг серверов и сервисов
  • Интеграция с формами обратной связи
  • Отложенные сообщения по расписанию
  • Опросы для сбора обратной связи

Отправка простого сообщения через cURL

Начнём с базового примера отправки текстового сообщения используя встроенные функции PHP.

<?php

function sendDiscordMessage($webhookUrl, $message) {
    $data = json_encode([
        'content' => $message
    ]);

    $ch = curl_init($webhookUrl);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    return $httpCode >= 200 && $httpCode < 300;
}

// Использование
$webhookUrl = 'https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN';
$success = sendDiscordMessage($webhookUrl, 'Привет из PHP!');

if ($success) {
    echo "Сообщение отправлено успешно";
} else {
    echo "Ошибка отправки сообщения";
}

Создание класса для работы с webhook

Для удобства создадим класс, который инкапсулирует всю логику работы с Discord webhook.

<?php

class DiscordWebhook {
    private $webhookUrl;
    private $timeout = 10;

    public function __construct($webhookUrl) {
        $this->webhookUrl = $webhookUrl;
    }

    public function setTimeout($seconds) {
        $this->timeout = $seconds;
        return $this;
    }

    public function send($data) {
        $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

        $ch = curl_init($this->webhookUrl);
        curl_setopt_array($ch, [
            CURLOPT_HTTPHEADER => ['Content-Type: application/json; charset=utf-8'],
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $json,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_TIMEOUT => $this->timeout
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        if ($error) {
            throw new Exception("cURL error: $error");
        }

        if ($httpCode === 429) {
            throw new Exception("Rate limit exceeded");
        }

        if ($httpCode < 200 || $httpCode >= 300) {
            throw new Exception("HTTP error $httpCode: $response");
        }

        return true;
    }

    public function sendMessage($content, $username = null, $avatarUrl = null) {
        $data = ['content' => $content];
        
        if ($username) {
            $data['username'] = $username;
        }
        
        if ($avatarUrl) {
            $data['avatar_url'] = $avatarUrl;
        }

        return $this->send($data);
    }
}

Использование класса:

$webhook = new DiscordWebhook('https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN');

try {
    $webhook->sendMessage(
        'Новый заказ на сумму 5000 руб.',
        'Магазин Bot',
        'https://example.com/shop-icon.png'
    );
    echo "Уведомление отправлено";
} catch (Exception $e) {
    error_log("Discord webhook error: " . $e->getMessage());
}

Отправка эмбедов

Эмбеды позволяют создавать структурированные и красиво оформленные сообщения. Расширим наш класс для поддержки эмбедов.

class DiscordEmbed {
    private $data = [];

    public function setTitle($title) {
        $this->data['title'] = $title;
        return $this;
    }

    public function setDescription($description) {
        $this->data['description'] = $description;
        return $this;
    }

    public function setColor($color) {
        // Принимает hex (#FF5733) или decimal (16734003)
        if (is_string($color) && strpos($color, '#') === 0) {
            $color = hexdec(substr($color, 1));
        }
        $this->data['color'] = $color;
        return $this;
    }

    public function setUrl($url) {
        $this->data['url'] = $url;
        return $this;
    }

    public function setTimestamp($timestamp = null) {
        $this->data['timestamp'] = $timestamp ?? date('c');
        return $this;
    }

    public function setFooter($text, $iconUrl = null) {
        $this->data['footer'] = ['text' => $text];
        if ($iconUrl) {
            $this->data['footer']['icon_url'] = $iconUrl;
        }
        return $this;
    }

    public function setThumbnail($url) {
        $this->data['thumbnail'] = ['url' => $url];
        return $this;
    }

    public function setImage($url) {
        $this->data['image'] = ['url' => $url];
        return $this;
    }

    public function setAuthor($name, $url = null, $iconUrl = null) {
        $this->data['author'] = ['name' => $name];
        if ($url) {
            $this->data['author']['url'] = $url;
        }
        if ($iconUrl) {
            $this->data['author']['icon_url'] = $iconUrl;
        }
        return $this;
    }

    public function addField($name, $value, $inline = false) {
        if (!isset($this->data['fields'])) {
            $this->data['fields'] = [];
        }
        $this->data['fields'][] = [
            'name' => $name,
            'value' => $value,
            'inline' => $inline
        ];
        return $this;
    }

    public function toArray() {
        return $this->data;
    }
}

Добавим метод в класс DiscordWebhook:

public function sendEmbed($embed, $content = null) {
    $data = [];
    
    if ($content) {
        $data['content'] = $content;
    }
    
    if ($embed instanceof DiscordEmbed) {
        $data['embeds'] = [$embed->toArray()];
    } elseif (is_array($embed)) {
        $data['embeds'] = array_map(function($e) {
            return $e instanceof DiscordEmbed ? $e->toArray() : $e;
        }, $embed);
    }

    return $this->send($data);
}

Пример использования для уведомления об ошибке:

$webhook = new DiscordWebhook('https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN');

$embed = (new DiscordEmbed())
    ->setTitle('Критическая ошибка')
    ->setDescription('Обнаружена ошибка в модуле оплаты')
    ->setColor('#FF0000')
    ->addField('Файл', 'payment.php', true)
    ->addField('Строка', '142', true)
    ->addField('Сообщение', 'Division by zero', false)
    ->setFooter('Система мониторинга')
    ->setTimestamp();

try {
    $webhook->sendEmbed($embed, '@here Требуется внимание!');
} catch (Exception $e) {
    error_log($e->getMessage());
}

Подробнее о цветах эмбедов читайте в статье Цвета Discord Embed.

Отправка файлов

Discord webhook поддерживает загрузку файлов. Для этого используется multipart/form-data.

public function sendFile($filePath, $message = null, $filename = null) {
    if (!file_exists($filePath)) {
        throw new Exception("File not found: $filePath");
    }

    $filename = $filename ?? basename($filePath);
    $boundary = uniqid();
    $delimiter = '-------------' . $boundary;

    $postData = '';

    // Добавляем JSON payload если есть сообщение
    if ($message) {
        $postData .= "--$delimiter\r\n";
        $postData .= 'Content-Disposition: form-data; name="payload_json"' . "\r\n";
        $postData .= "Content-Type: application/json\r\n\r\n";
        $postData .= json_encode(['content' => $message]) . "\r\n";
    }

    // Добавляем файл
    $fileContents = file_get_contents($filePath);
    $postData .= "--$delimiter\r\n";
    $postData .= 'Content-Disposition: form-data; name="file"; filename="' . $filename . '"' . "\r\n";
    $postData .= "Content-Type: application/octet-stream\r\n\r\n";
    $postData .= $fileContents . "\r\n";
    $postData .= "--$delimiter--\r\n";

    $ch = curl_init($this->webhookUrl);
    curl_setopt_array($ch, [
        CURLOPT_HTTPHEADER => [
            "Content-Type: multipart/form-data; boundary=$delimiter",
            "Content-Length: " . strlen($postData)
        ],
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $postData,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_TIMEOUT => $this->timeout
    ]);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($httpCode < 200 || $httpCode >= 300) {
        throw new Exception("HTTP error $httpCode: $response");
    }

    return true;
}

Использование:

$webhook->sendFile(
    '/var/log/app-errors.log',
    'Лог-файл с ошибками за сегодня',
    'errors-' . date('Y-m-d') . '.log'
);

Использование Guzzle HTTP Client

Для более продвинутых сценариев рекомендуется использовать Guzzle — популярный HTTP-клиент для PHP.

Установка через Composer:

composer require guzzlehttp/guzzle

Класс с использованием Guzzle:

<?php

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;

class GuzzleDiscordWebhook {
    private $client;
    private $webhookUrl;

    public function __construct($webhookUrl) {
        $this->webhookUrl = $webhookUrl;
        $this->client = new Client([
            'timeout' => 10,
            'verify' => true
        ]);
    }

    public function send($data) {
        try {
            $response = $this->client->post($this->webhookUrl, [
                'json' => $data,
                'headers' => [
                    'Content-Type' => 'application/json'
                ]
            ]);

            return $response->getStatusCode() >= 200 && $response->getStatusCode() < 300;
        } catch (GuzzleException $e) {
            throw new Exception("Webhook error: " . $e->getMessage());
        }
    }

    public function sendMessage($content, $username = null) {
        $data = ['content' => $content];
        if ($username) {
            $data['username'] = $username;
        }
        return $this->send($data);
    }

    public function sendEmbed($embed) {
        return $this->send([
            'embeds' => [$embed instanceof DiscordEmbed ? $embed->toArray() : $embed]
        ]);
    }

    public function sendFile($filePath, $message = null) {
        $multipart = [
            [
                'name' => 'file',
                'contents' => fopen($filePath, 'r'),
                'filename' => basename($filePath)
            ]
        ];

        if ($message) {
            $multipart[] = [
                'name' => 'payload_json',
                'contents' => json_encode(['content' => $message])
            ];
        }

        try {
            $response = $this->client->post($this->webhookUrl, [
                'multipart' => $multipart
            ]);
            return $response->getStatusCode() >= 200 && $response->getStatusCode() < 300;
        } catch (GuzzleException $e) {
            throw new Exception("File upload error: " . $e->getMessage());
        }
    }
}

Интеграция с Laravel

Для Laravel-приложений создадим сервис-провайдер и фасад для удобной работы с webhook.

Создание сервиса

Создайте файл app/Services/DiscordWebhookService.php:

<?php

namespace App\Services;

use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;

class DiscordWebhookService {
    protected $client;
    protected $webhookUrl;

    public function __construct() {
        $this->webhookUrl = config('services.discord.webhook_url');
        $this->client = new Client(['timeout' => 10]);
    }

    public function notify($message, $level = 'info') {
        $colors = [
            'info' => 3447003,    // Синий
            'success' => 3066993, // Зелёный
            'warning' => 16776960, // Жёлтый
            'error' => 15158332   // Красный
        ];

        $embed = [
            'title' => ucfirst($level),
            'description' => $message,
            'color' => $colors[$level] ?? $colors['info'],
            'timestamp' => now()->toIso8601String(),
            'footer' => [
                'text' => config('app.name')
            ]
        ];

        return $this->send(['embeds' => [$embed]]);
    }

    public function error($exception, $context = []) {
        $embed = [
            'title' => 'Application Error',
            'description' => $exception->getMessage(),
            'color' => 15158332,
            'fields' => [
                [
                    'name' => 'Exception',
                    'value' => get_class($exception),
                    'inline' => true
                ],
                [
                    'name' => 'File',
                    'value' => $exception->getFile() . ':' . $exception->getLine(),
                    'inline' => false
                ]
            ],
            'timestamp' => now()->toIso8601String()
        ];

        if (!empty($context)) {
            $embed['fields'][] = [
                'name' => 'Context',
                'value' => '```json\n' . json_encode($context, JSON_PRETTY_PRINT) . '\n```',
                'inline' => false
            ];
        }

        return $this->send([
            'content' => '@here Critical error occurred!',
            'embeds' => [$embed]
        ]);
    }

    protected function send($data) {
        try {
            $response = $this->client->post($this->webhookUrl, [
                'json' => $data
            ]);
            return $response->getStatusCode() >= 200 && $response->getStatusCode() < 300;
        } catch (\Exception $e) {
            Log::error('Discord webhook failed: ' . $e->getMessage());
            return false;
        }
    }
}

Конфигурация

Добавьте в config/services.php:

'discord' => [
    'webhook_url' => env('DISCORD_WEBHOOK_URL'),
    'error_webhook_url' => env('DISCORD_ERROR_WEBHOOK_URL'),
],

В .env:

DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN
DISCORD_ERROR_WEBHOOK_URL=https://discord.com/api/webhooks/ERROR_ID/ERROR_TOKEN

Регистрация в Service Provider

В app/Providers/AppServiceProvider.php:

use App\Services\DiscordWebhookService;

public function register() {
    $this->app->singleton(DiscordWebhookService::class, function ($app) {
        return new DiscordWebhookService();
    });
}

Использование в контроллерах

<?php

namespace App\Http\Controllers;

use App\Services\DiscordWebhookService;
use Illuminate\Http\Request;

class OrderController extends Controller {
    protected $discord;

    public function __construct(DiscordWebhookService $discord) {
        $this->discord = $discord;
    }

    public function store(Request $request) {
        $order = Order::create($request->all());

        // Отправляем уведомление в Discord
        $this->discord->notify(
            "Новый заказ #{$order->id} на сумму {$order->total} руб.",
            'success'
        );

        return response()->json($order, 201);
    }
}

Обработчик исключений

В app/Exceptions/Handler.php:

use App\Services\DiscordWebhookService;

public function report(Throwable $exception) {
    if ($this->shouldReport($exception)) {
        $discord = app(DiscordWebhookService::class);
        $discord->error($exception, [
            'url' => request()->fullUrl(),
            'user_id' => auth()->id(),
            'ip' => request()->ip()
        ]);
    }

    parent::report($exception);
}

Обработка rate limits

Discord ограничивает количество запросов к webhook. Реализуем простую систему очередей для Laravel.

Создайте Job:

<?php

namespace App\Jobs;

use App\Services\DiscordWebhookService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class SendDiscordNotification implements ShouldQueue {
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $message;
    protected $level;

    public function __construct($message, $level = 'info') {
        $this->message = $message;
        $this->level = $level;
    }

    public function handle(DiscordWebhookService $discord) {
        $discord->notify($this->message, $this->level);
    }

    public function failed(\Throwable $exception) {
        \Log::error('Discord notification failed: ' . $exception->getMessage());
    }
}

Использование:

use App\Jobs\SendDiscordNotification;

SendDiscordNotification::dispatch('Новый заказ создан', 'success');

Практический пример: мониторинг формы обратной связи

Полный пример обработки формы с отправкой уведомления в Discord:

<?php

namespace App\Http\Controllers;

use App\Services\DiscordWebhookService;
use Illuminate\Http\Request;

class ContactController extends Controller {
    public function submit(Request $request, DiscordWebhookService $discord) {
        $validated = $request->validate([
            'name' => 'required|max:255',
            'email' => 'required|email',
            'subject' => 'required|max:255',
            'message' => 'required'
        ]);

        // Сохраняем в БД
        $contact = Contact::create($validated);

        // Формируем красивое уведомление
        $embed = [
            'title' => 'Новое обращение',
            'color' => 3447003,
            'fields' => [
                [
                    'name' => 'Имя',
                    'value' => $validated['name'],
                    'inline' => true
                ],
                [
                    'name' => 'Email',
                    'value' => $validated['email'],
                    'inline' => true
                ],
                [
                    'name' => 'Тема',
                    'value' => $validated['subject'],
                    'inline' => false
                ],
                [
                    'name' => 'Сообщение',
                    'value' => mb_substr($validated['message'], 0, 1000),
                    'inline' => false
                ]
            ],
            'footer' => [
                'text' => 'ID: ' . $contact->id
            ],
            'timestamp' => now()->toIso8601String()
        ];

        $discord->send(['embeds' => [$embed]]);

        return response()->json([
            'message' => 'Спасибо за обращение!'
        ]);
    }
}

Визуальный конструктор эмбедов

Создание сложных эмбедов вручную может быть трудоёмким. Используйте наш бесплатный визуальный конструктор для проектирования эмбедов и получения готового JSON-кода.

Больше информации о создании эмбедов в статье Руководство по Discord Embed Builder.

Лучшие практики

1. Валидация данных

Всегда проверяйте размеры полей перед отправкой:

class DiscordValidator {
    public static function truncate($text, $maxLength) {
        if (mb_strlen($text) <= $maxLength) {
            return $text;
        }
        return mb_substr($text, 0, $maxLength - 3) . '...';
    }

    public static function validateEmbed($embed) {
        $limits = [
            'title' => 256,
            'description' => 4096,
            'fields' => 25,
            'field_name' => 256,
            'field_value' => 1024
        ];

        if (isset($embed['title'])) {
            $embed['title'] = self::truncate($embed['title'], $limits['title']);
        }

        if (isset($embed['description'])) {
            $embed['description'] = self::truncate($embed['description'], $limits['description']);
        }

        if (isset($embed['fields'])) {
            $embed['fields'] = array_slice($embed['fields'], 0, $limits['fields']);
            foreach ($embed['fields'] as &$field) {
                $field['name'] = self::truncate($field['name'], $limits['field_name']);
                $field['value'] = self::truncate($field['value'], $limits['field_value']);
            }
        }

        return $embed;
    }
}

2. Безопасность webhook URL

Никогда не храните webhook URL в коде. Используйте переменные окружения и конфигурационные файлы.

3. Логирование ошибок

Всегда логируйте ошибки отправки webhook для отладки:

try {
    $webhook->send($data);
} catch (Exception $e) {
    error_log(sprintf(
        '[Discord Webhook] Failed to send: %s in %s:%d',
        $e->getMessage(),
        $e->getFile(),
        $e->getLine()
    ));
}

4. Асинхронная отправка

Для высоконагруженных приложений используйте очереди, чтобы не блокировать основной поток выполнения.

Заключение

Discord webhook в PHP — это простой и эффективный способ интеграции уведомлений в ваши приложения. Используя приведённые примеры, вы можете создать надёжную систему уведомлений для любых сценариев.

Основные выводы:

  • Используйте классы для инкапсуляции логики
  • Применяйте Guzzle для продвинутых сценариев
  • Интегрируйте с Laravel через сервис-провайдеры
  • Обрабатывайте ошибки и rate limits
  • Валидируйте данные перед отправкой

Для более сложных workflow автоматизации изучите статью Автоматизация Discord Webhook.

Помимо кода, discord-webhook.com предлагает визуальный конструктор с такими функциями как отложенные сообщения, поддержка тредов и форумов, опросы и интерактивные кнопки с действиями — всё без написания кода.

Похожие статьи

Попробуйте в нашем инструменте

Открыть конструктор Discord Webhook