biplane / yandex-direct

PHP library for Yandex.Direct API.
MIT License
44 stars 23 forks source link

Неправильные ID у товарных объявлений из ЕПК #61

Closed AntsHere closed 4 months ago

AntsHere commented 4 months ago

Добрый день!

Никак не могу понять, в чем проблема. Для товарных объявлений, созданных в новой ЕПК, Директ определяет очень длинные ID.

К примеру, для одного из таких объявлений Директ в своем интерфейсе показывает значение - 1842497672419288604

Если мы запросим информацию через API и просто выведем ID объявления на экран, то получим 1.8424976724193E+18

Если выполнить number_format($id, 0, '', '') и вывести ID на экран, получим 1842497672419288576

Получается, в web интерфейсе Директа ID равен 1842497672419288604, а в PHP скрипте, который получил данные через API - 1842497672419288576 . Три последние цифры отличаются. Выходит, либо через API передаются неправильные данные, либо после их получения где то на уровне PHP они бьются.

Что нужно сделать, чтобы получить таки правильный ID объявления?

yethee commented 4 months ago

Какая версия PHP используется, 32 или 64-битная?

Можете предоставить дамп ответа (XML)? Если настроен SoapLogger, то можно из логов получить. Либо же через вызов __getLastRequest() метод, после вызова соответствующего API метода.

$apiResponse = $apiService->get($request);
$xml = $apiService->__getLastRequest();
// echo $xml;
// file_put_contents('dump.xm', $xml);
// etc...

PS: При необходимости, XML можно отредактировать. Главное чтобы поля с ID были без изменений.

AntsHere commented 4 months ago

Версия, к сожалению, 32-битная.

Я выполнил ваш код, только, если я правильно понял, вы имели ввиду не getLastRequest, а getLastResponse. И да, он выдает правильный ID:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><ns3:GetResponse xmlns:ns3="http://api.direct.yandex.com/v5/ads" xmlns:ns4="http://api.direct.yandex.com/v5/lifts" xmlns:ns5="http://api.direct.yandex.com/v5/general" xmlns=""><Ads><Id>1842497672419288604</Id>...

yethee commented 4 months ago

32-битная версия не поддерживается. Об этом упоминается в README:

NOTE: Библиотека совместима только с 64-битной версией PHP.

При установке через composer, последний должен был предупредить о несовместимости, по идее.

Честно сказать, не вижу мотивации для поддержки 32-битной архитектуры. Это потребует все идентификаторы хранить как строки на уровне DTO. Что отрицательно скажется на поддержке статического анализа кода и прочих аспектах. Контракты станут менее явными.

У вас есть какие-то непреодолимые обстоятельства, требующие именно 32-битной версии рантайма?

AntsHere commented 4 months ago

Прошу прощения, надо было самому догадаться про версию PHP.

У меня для одного проекта используется локальный сервер. Там все работает на базе 32-битных версий (включая Apache, PHP. MySQL и пр.). Чтобы поменять версию PHP на 64 битную, нужно переустанавливать и настраивать весь набор ПО. В настоящее время не хочется этим заниматься, так как и так все работает достаточно стабильно.

И спасибо за подсказку. Понимаю, что это костыль, но в моем случае вполне норм. Правильные ID достаю из ответа от __getLastResponse.

yethee commented 4 months ago

Понимаю, что это костыль, но в моем случае вполне норм. Правильные ID достаю из ответа от __getLastResponse.

Ещё, как вариант, можно посмотреть на TypeConverter, чтобы xsd:long конвертировать в строку. Что-то вроде такого:

use Biplane\YandexDirect\Soap\TypeConverter;

final class LongTypeConverter implements TypeConverter
{
    public function getTypeNamespace(): string
    {
        return 'http://www.w3.org/2001/XMLSchema';
    }

    public function getTypeName(): string
    {
        return 'long';
    }

    public function fromXml(string $xml)
    {
        if (preg_match('@^<([^>]+)>(?<content>.+)</\\1>$@', $xml, $m)) {
            return $m['content']; // Convert xsd:long to PHP string
        }

        return null;
    }

    public function toXml($data): string
    {
        return sprintf('<%1$s>%2$s</%1$s>', $this->getTypeName(), $data);
    }
}

Только в этом случае нужно принять несколько нюансов,

Но нужно понимать, что такое решение не гарантирует совместимости с будущими релизами. As is, так сказать.