Обработка ошибки 152 «Недостаточно баллов»
PHP версии 5, формат JSON, используется функция file_get_contents
Баллы API Директа версии 5 начисляются всем рекламодателям один раз в 60 минут, но для каждого рекламодателя используется собственное расписание начисления баллов без привязки к началу астрономического часа. Пример демонстрирует логику повторного выполнения запросов, если первый запрос завершается ошибкой с кодом 152.
Для демонстрации используется функция sleep
, которая позволяет устанавливать задержки выполнения скрипта. Задержки задаются в массиве set_DELAY
в секундах, так чтобы с момента первого запуска было выполнено не более 5 повторных запросов с увеличивающимися интервалами, при этом были покрыты ближайшие 60 минут с момента первого получения ошибки.
При разработке приложения мы рекомендуем использовать более надежные механизмы повторного выполнения запросов.
Вы можете организовать логику выполнения запросов с использованием планировщика задач (например, cron
) или системы очередей (например, Gearman
).
<?php
//--- Настройки ---------------------------------------------------------//
$set_DEBUG = true; // Вывод отладочной информации (html): false - не выводить; true - выводить
$set_DELAY = array(0, 360, 540, 720, 900, 1080); // Массив задержек выполнения повторных запросов (в секундах)
$err_NOT_ENOUGH_UNITS = 152; // Ошибка "Недостаточно баллов"
ini_set('max_execution_time', 5400); // Максимальное время выполнения скрипта - 5400 секунд (90 минут)
// Настройки для вывода содержимого буфера, которые позволяют делать вывод на экран при использовании функции sleep
ob_implicit_flush();
//--- Входные данные запроса --------------------------------------------//
// Адрес сервиса Campaigns для отправки JSON-запросов (регистрозависимый)
$url = 'https://api.direct.yandex.com/json/v5/campaigns';
// OAuth-токен пользователя, от имени которого будут выполняться запросы
$token = 'ТОКЕН';
// Логин клиента рекламного агентства
// Обязательный параметр, если запросы выполняются от имени рекламного агентства
$clientLogin = 'ЛОГИН_КЛИЕНТА';
//--- Подготовка запроса ------------------------------------------------//
// Установка HTTP-заголовков запроса
$headers = array(
"Authorization: Bearer $token", // OAuth-токен. Использование слова Bearer обязательно
"Client-Login: $clientLogin", // Логин клиента рекламного агентства
"Accept-Language: ru", // Язык ответных сообщений
"Content-Type: application/json; charset=utf-8" // Тип данных и кодировка запроса
);
// Параметры запроса к серверу API Директа
$params = array(
'method' => 'get', // Используемый метод сервиса Campaigns
'params' => array(
'SelectionCriteria' => (object) array(), // Критерий отбора кампаний. Для получения всех кампаний должен быть пустым
'FieldNames' => array('Id', 'Name') // Названия параметров, которые требуется получить
)
);
// Преобразование входных параметров запроса в формат JSON
$body = json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
// Создание контекста потока: установка HTTP-заголовков и тела запроса
$streamOptions = stream_context_create(array(
'http' => array(
'method' => 'POST',
'ignore_errors' => true, // Извлечь содержимое ответа даже в случае, если HTTP-код ответа отличен от 200
'header' => $headers,
'content' => $body
)
));
//--- Выполнение задачи -------------------------------------------------//
/*
Запуск цикла для выполнения запросов. Если первый запрос завершен успешно, т.е. не привел к возникновению ошибки 152, то выводится список кампаний.
Если же первый запрос завершен с этой ошибкой, то выполняются повторные запросы с задержками, заданными в массиве set_DELAY.
*/
foreach ($set_DELAY as $currentAttempt => $delay) {
// Выполнение запроса, получение результата
$result = file_get_contents($url, 0, $streamOptions);
//--- Обработка результата выполнения запроса ---------------------//
// Извлекаем HTTP-заголовки ответа, если они есть
if (isset($http_response_header)) {
foreach ($http_response_header as $header) {
if (preg_match('#HTTP/[0-9\.]+\s+([0-9]+)#', $header, $arr)) {
$httpStr = $header; // Пояснение к HTTP-коду ответа
$httpCode = intval($arr[1]); // Числовое значение HTTP-кода
}
if (preg_match('/RequestId: (\d+)/', $header, $arr)) {
$requestId = $arr[1]; // Идентификатор запроса
}
if (preg_match('/Units: (\d+)\/(\d+)\/(\d+)/', $header, $arr)) {
$unitsSpent = $arr[1]; // Количество потраченых баллов
$unitsAvailable = $arr[2]; // Количество доступных баллов
$unitsLimit = $arr[3]; // Суточный лимит
}
}
}
else {
// Если заголовков нет, значит, запрос не был выполнен - выводим ошибку
echo date("d.m.Y H:i:s > ").'Не удалось выполнить запрос к серверу API';
}
// Обработка ответа, если получен HTTP-код
if (isset($httpCode)) {
// Если получен HTTP-код 200, то обрабатываем тело ответа
if ($httpCode == 200) {
// Вывод информации по баллам
if (isset($unitsSpent)) {
echo date("d.m.Y H:i:s > ")."Израсходовано баллов за запрос: $unitsSpent Доступно сейчас: $unitsAvailable Суточный лимит: $unitsLimit<br>";
}
// Преобразование ответа из формата JSON
$response = json_decode($result);
// Если сервер API вернул ошибку в теле ответа, то выводим ее
if (isset($response->error)) {
$apiErr = $response->error;
echo date("d.m.Y H:i:s > ")."Ошибка API {$apiErr->error_code}: {$apiErr->error_string} - {$apiErr->error_detail} (RequestId: {$apiErr->request_id})<br>";
}
}
else { echo date("d.m.Y H:i:s > ").'Ошибка выполнения запроса к серверу API: '.$httpStr; }
}
//--- Вывод отладочной информации ---------------------------------//
if ($set_DEBUG) {
echo "<hr>URL запроса: $url<br>";
echo "Заголовки запроса: <pre>".implode($headers, '<br>')."</pre>";
echo "Тело запроса (JSON): <pre>".json_encode($params, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)."</pre>";
if (isset($http_response_header)) { echo "Заголовки ответа: <pre>".implode($http_response_header, '<br>')."</pre>"; }
if (isset($response)) { echo "Тело ответа (JSON): <pre>".json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)."</pre>"; }
if (isset($httpCode) and $httpCode != 200) { echo "Тело ответа: <pre>".htmlspecialchars($result)."</pre>"; }
echo "<hr>";
}
//--- Обработка результата ----------------------------------------//
// Вывод списка кампаний при наличии результата
if (isset($response->result)) {
foreach ($response->result->Campaigns as $campaign) {
echo "{$campaign->Name} (№ {$campaign->Id})<br>";
}
if ($response->result->LimitedBy) {
// Если ответ содержит параметр LimitedBy, значит, были получены не все доступные объекты.
// В этом случае следует выполнить дополнительные запросы для получения всех объектов.
// Подробное описание постраничной выборки - https://tech.yandex.ru/direct/doc/dg/best-practice/get-docpage/#page
echo '<br>Получены не все доступные объекты.';
}
}
//--- Действия при возникновении ошибки 152 "Недостаточно баллов" и ее отсутствии ---//
if (isset($apiErr->error_code) and $apiErr->error_code == $err_NOT_ENOUGH_UNITS) {
// Если в массиве set_DELAY значений больше, чем текущая итерация цикла, то ставим задержку перед следующей итерацией выполнения цикла
if (isset($set_DELAY[$currentAttempt+1])) {
unset ($apiErr); // Удаляем переменную, содержащую данные об ошибке
echo date("d.m.Y H:i:s > ")."Повторный запрос через ".$set_DELAY[$currentAttempt+1]." секунд<br>";
sleep ($set_DELAY[$currentAttempt+1]);
}
}
// Если не была получена ошибка 152 "Недостаточно баллов", то считаем задачу выполненной и завершаем работу скрипта
else { die(); }
}
?>