среда, 13 апреля 2016 г.

Отправка GuzzleHttp POST параметров с одинаковыми именами через CURLOPT_POSTFIELDS

Понадобилось отправлять данные в один сторонний API методом POST. Тривиальная задача, которая потребовала довольно напряженных поисков решения.

Дело в том, что API, c которыми мне часто приходится работать, обычно довольно сложные и реализовать отправку множества разнообразных запросов голым curl крайне непродуктивно. Поэтому я с самого начала решил работать с API через библиотеку GuzzleHttp, которая взяла на себя все вопросы базовой подготовки запросов и их отправки различными способами, в зависимости от метода API. Такой подход позволяет сконцентрироваться на подготовке параметров запроса и главным образом всё, что оставалось для комфортной разработки — прописать соответствия методов HTTP методам API.

Грабли нашлись в тот момент, когда в спецификации API обнаружилось, что в POST-метод надо отправлять данные в формате application/x-www-form-urlencoded, т.е. сформированные так же, как обычно формируются GET-строки параметров. Но при этом все параметры должны иметь одно и то же имя. То есть POST запрос должен был выглядеть примерно так:

param=value1&param=value2&param=value3

Привыкнув к тому, что в подавляющем большинстве систем в POST всегда отдаются параметры с разными именами и достаточно отправить их в виде обычного ассоциативного массива PHP, меня этот формат передачи параметров поначалу удивил, но уже совсем скоро начал серьезно раздражать, т.к. очевидного решения для использования в связке с GuzzleHttp не было ни в голове, ни в гугле. GET-запрос с такой строкой передать проще простого - достаточно лишь собрать строку не через build_http_query, а самостоятельно, ручками. Но с POST такое не прокатывает. Одна из обычных практик отправки POST через GuzzleHttp - это создать клиента, настроенного на нужный адрес метода API, а затем передать ему параметр URI и сам запрос, примерно следующим образом:

$client = new GuzzleHttp\Client(['base_uri' => 'https://foo.com/api/']); 
$client->request('POST', $uri, ['body' => $requestArray]);
//или, что то же самое:
$client->post($uri, ['body' => $requestArray']);  
  
Однако все-таки гугл не был совсем бесполезен - было найдено решение, которое позволяет передавать POST в виде строки url-encoded параметров. Общая идея была в том, чтобы передавать данные не в формате miltipart/form-data, а в application/x-www-form-urlencoded. При таком раскладе можно отправить POST'ом предварительно сформированную строку параметров через

curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);

Мысль о том, что придется переделывать всё на использование Curl, очень не понравилась - уже была написана большая часть кода, да и отказываться от использования удобной библиотеки совсем не хотелось. Однако надо помнить о том, что GuzzleHttp, как и многие другие компоненты - всего лишь обертка над более-менее низкоуровневыми интерфейсами. Хорошенько покопавшись в коде пакета, я нашел место, в котором устанавливался параметр CURLOPT_POSTFIELDS. В итоге работающее решение нашлось, хотя и является, по сути, хаком (подробнее о вытекающих из этого последствиях читайте в последнем абзаце) и выглядит несколько неуклюжим по сравнению с обычными вызовами, которые я приводил выше:

$client->post($uri, [
   'body' => $requestString,
   'curl' => [
      'body_as_string' => true
   ],
   '_conditional' => [
      'Content-Type' => 'application/x-www-form-urlencoded'
   ]
]);


Когда решение нашлось, стало грустно от осознания того количества времени, которое было потрачено на его поиски. Как, впрочем, всегда бывает, когда оно умещается в пару строчек. Есть подозрение, что такая ситуация встречается редко, но тем не менее, вполне вероятна и еще у кого-то. Именно поэтому я и решил опубликовать работающее решение для тех, кто на эти грабли в будущем наткнется.

На всякий случай добавлю, что версия пакета GuzzleHttp, которую я использовал - 6.2.2. Параметры body_as_string и _conditional не относятся к задокументированным, поэтому в более поздних версиях этот хак может уже не работать. На это также намекает и то, что в прежних версиях работа с подобными запросами была организована немного иначе. Постараюсь обновить здесь информацию, если появится что-то новое.

понедельник, 21 марта 2016 г.

ВНЕЗАПНО решил поиграть с Canvas

Если вдруг работаю с примитивами без особой цели, то порой люблю заморочиться генерацией более-менее замысловатых картинок. В этот раз получилось нечто типа камеры Вильсона (хотя без "опадания" треков это больше похоже на результат применения фотоэмульсионного метода, переведенный в позитив %)). Без разнообразия было скучновато, поэтому решил добавить цвет, поперечные штрихи и прочие штуки. Раньше не задумывался, но оказывается, не так-то просто сделать плавный переход цвета от одного к другому по мере рисования трека, если цвет надо генерить в шестнадцатиричном формате, как в CSS (а здесь для каждого трека цвет задается индивидуально). И поперечные штрихи на треках меня долго не удовлетворяли своей недостаточной перпендикулярностью к треку. Пришлось даже вспоминать тригонометрию, — но обошлось в итоге без нее.

Код, конечно же, тут просто так не разместить. Поэтому сохранил на jsfiddle.

вторник, 17 декабря 2013 г.

Пиши код блять!

Сегодня я расскажу вам об уникальной методологии разработки ПО. Уверяю вас, она произведёт настоящую революцию в нашей индустрии. Почему? Да потому что она разительно отличается от тех теорий, которыми вас досыта накормили заумные книжки и дурацкие статьи в интернетах. Существующие методологии заставляют вас запоминать сотни баззвордов, высчитывать какие-то непонятные метрики или даже (о ужас!) предлагают пустить работу на самотёк и сделать вашу команду самоорганизующейся.

Наша методология гораздо проще и эффективнее. Она предлагает вам сконцентрироваться всего на одной, но очень важной вещи (чтобы случайно не забыть, на какой именно, мы даже вынесли эту вещь в название методологии). Итак, встречайте: инновационная методология разработки под названием…
Пиши код блять!

Именно так. Пиши код блять!

Как мы добавляем фичи в проект? Мы блять пишем код!

Как у нас поставлено тестирование? Мы блять пишем автотесты!

Как мы умудряемся поставлять релизы в срок и в рамках бюджета? Мы блять пишем код качественно и быстро!

Почему наши программисты счастливы и с удовольствием ходят на работу? Потому что они блять пишут код и не отвлекаются на всякую херню!

Конечно, на деле не все так просто, Одной идеи совершенно недостаточно для того, чтобы перестроить всю вашу команду. Поэтому вот вам пошаговая инструкция для внедрения нашей замечательной методологии:

    Выпишите список всей той хрени, которая должна входить в ваш продукт
    Реализуйте всю эту хрень путем блять написания кода
    Протестируйте написанное
    Если что-то работает неправильно — то напишите блять правильный код!


После п.4 немедленно переходите к п.1. Повторять до достижения необходимого результата.

Здорово, правда? Помните, что написание кода — это блять самая важная вещь во всём процессе разработки! А все прочие идеи, которые вы могли видеть в конкурирующих методологиях, я предлагаю подытожить вот таким принципом:


Менеджер, твою мать, облегчи жизнь программисту!

Чтобы программисты могли блять писать код, менеджеры, мать их, должны по возможности облегчать программистам жизнь. Менеджер, мать его, должен делать всё возможное, чтобы код блять писался хорошо: отслеживать процесс написания блять кода, заботиться о том, чтобы заказчики вовремя платили за всю написанную нами хрень, и (самое главное!) вовремя пополнять запасы чая и блять печенек.

Короче говоря: менеджер, мать его, должен заниматься всем, что напрямую не относится к написанию блять кода.

Подробная пошаговая инструкция для, мать их, менеджеров, выглядит следующим образом:

    Понять, что вообще хочет заказчик, и рассказать об этом программистам
    Сделать всё, чтобы программистам было блять комфортно писать код
    Если тот код, что мы блять написали — это не совсем то, что хочет заказчик, то нужно снова сказать об этом программистам


Уверяю вас, эта новая методология изменит жизнь вашей команды. Я искренне верю в неё и считаю её единственно правильной. Почему? Да потому что в нашей индустрии есть только одно действительно важное занятие: писать код блять!

http://factorized.tumblr.com/post/4180288873/programming-motherfucker

вторник, 16 апреля 2013 г.

Хрен с ним, со смыслом, — главное, код красиво написан. Интересно было бы еще и на sorted() посмотреть )

понедельник, 19 ноября 2012 г.

Полезный скриптик для Chrome Gestures

В интернете порой попадаются своеобразно сверстанные сайты, в дизайне которых отсутствуют ссылки постраничной навигации вроде "← Назад | Вперед →" и т.п. Однако эти сайты иногда интересны настолько, что я мирюсь с необходимостью постоянно клацать в адресную строку и менять цифру, означающую номер страницы, чтобы попасть туда, куда нужно.
Но рано или поздно это надоедает (а в последнее время я видел немало таких сайтов на tumblr'e) и хочется упростить себе жизнь. Раньше, когда браузером для серфинга у меня был лишь Firefox, я пользовался в таких случаях расширением All-in-One-Gestures, которое имеет функцию "Increment/decrement digit in URL", срабатывающую при выполнении мышью определенного жеста и меняющую последнюю цифру в адресе страницы автоматически.
Теперь же, когда для серфинга я очень часто использую Chrome и соответствующее расширение для жестов — Gestures for Chrome, мне захотелось иметь подобный функционал и в нем. Плагин сам по себе эту функцию не предлагает, однако в нем имеется возможность использовать пользовательские скрипты. Собственно, после того, как я захотел иметь такую штуку, оставалось только придумать удобные жесты и назначить им следующие коды:
m=/(.*\D[0]*)([\d]+)(.*)/g.exec(location.href);if(m)document.location=m[1]+(~~m[2]-1)+m[3];
и
m=/(.*\D[0]*)([\d]+)(.*)/g.exec(location.href);if(m)document.location=m[1]+(~~m[2]+1)+m[3];
Возможно, кому-нибудь такая штука также пригодится.

UPD: кстати, ничто не мешает эти же скрипты использовать в виде обычных букмарклетов - достаточно создать закладки, вместо URL у которых указать вышеприведенные скрипты, только в URL вместо http:// указать протокол javascript:, чтобы получилось
javascript:m=/(.*\D[0]*)([\d]+)(.*)/g.exec(location.href);if(m)document.location=m[1]+(~~m[2]+1)+m[3];