PHP-библиотека для работы с API Яндекс.Денег. Демоны — очереди сообщений Пользуйтесь тернарными операторами


    САМОВОЛЬНИЧАТЬ, самовольничаю, самовольничаешь, несовер. (разг.). Поступать самовольно, вести себя самовольно. Толковый словарь Ушакова. Д.Н. Ушаков. 1935 1940 … Толковый словарь Ушакова

    САМОВОЛЬНИЧАТЬ, аю, аешь; несовер. (разг.). Поступать самовольно. Толковый словарь Ожегова. С.И. Ожегов, Н.Ю. Шведова. 1949 1992 … Толковый словарь Ожегова

    Несов. неперех. разг. Поступать по своему желанию, по своей прихоти. Толковый словарь Ефремовой. Т. Ф. Ефремова. 2000 … Современный толковый словарь русского языка Ефремовой

    Самовольничать, самовольничаю, самовольничаем, самовольничаешь, самовольничаете, самовольничает, самовольничают, самовольничая, самовольничал, самовольничала, самовольничало, самовольничали, самовольничай, самовольничайте, самовольничающий,… … Формы слов

    самовольничать - самов ольничать, аю, ает … Русский орфографический словарь

    самовольничать - (I), самово/льничаю, чаешь, чают … Орфографический словарь русского языка

    Аю, аешь; нсв. Разг. Поступать по своему желанию, по собственной прихоти, не спрашивая разрешения у кого л. С. в гостях. С. на работе. Самовольничай у себя дома. ◁ Самовольничанье, я; ср. Без самовольничанья, пожалуйста, а то уволю! … Энциклопедический словарь

    самовольничать - аю, аешь; нсв.; разг. см. тж. самовольничанье Поступать по своему желанию, по собственной прихоти, не спрашивая разрешения у кого л. Самово/льничать в гостях. Самово/льничать на работе. Самовольничай у себя дома … Словарь многих выражений

3.3K

Вывод сообщений пользователю - довольно распространенное действие, которое должно выполнять веб-приложение. Оно может происходить при обработке форм, это могут быть сообщения об ошибках, а также сообщения, которые говорят, что надо зарегистрироваться, когда пользователь пытается получить доступ к ограниченной части сайта, и во многих других случаях.

Очень часто создание и вывод сообщений разнесены по разным HTTP-запросам. Как правило, удобно бывает использовать редирект после обработки форм (чтобы избежать проблем с кнопками Back и Refresh), но в то же время естественный момент для создания сообщения - это именно момент обработки форм и совершения действий, ему сопутствующих. Почему? Представьте, что текст сообщения должен выглядеть примерно так: "Количество заказываемых единиц товара ‘Коврик для мыши’ успешно изменено с 7 до 12". После редиректа, возможно, на совершенно другую с точки зрения функциональности страницу, это будет лишняя головная - определить, что же было совершено до этого.

Чаще всего сообщения выводят именно в POST-запросе, который занимается обработкой формы - это нехорошо, надписи "эта страница устарела" портят жизнь (когда пользователю вздумается попробовать кнопку Back).

Кто-то использует редирект, махнув рукой на дружелюбные сообщения.

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

Итак, имеем проблему - сообщение должно "жить" в разных запросах. Нам нужен механизм передачи текста сообщения на страницу, которая должна его выводить. Вы уже, наверное, вспомнили про сессии.

Да, вобщем-то вы правы. Прочие способы, например через глобальную переменную, не позволяют сохранить данные в случае, когда используется редирект (замечание Максима Науменко). Плюс еще я обычно делаю так, чтобы каждый экран в приложении имел возможность, наряду с прочей информацией, выводить сообщения, которые были сформированы на предыдущих экранах. Это удобно, потому что не потребуется готовить отдельные экраны для вывода сообщений, а пользователю не придется лишний раз щелкать мышью. Но, правда, здесь надо подумать дизайнеру - выделить область, в которой бы появлялись сообщения.

Идея очень простая, и ее можно реализовать с помощью пары классов.

Первое, что приходит в голову - создать класс Message, который бы, собственно, и представлял собой сообщение на нашей нехитрой схеме классов. Сообщение должно уметь сохранять себя в сессии, а также выводить себя на экран.

class Message { /** * Содержание сообщения. */ var $content; /** * Конструктор для инициализации текста сообщения. * * @param content содержание сообщения */ function Message($content) { $this->content = $content; } /** * Запись сообщения в сессию. */ function send() { $_SESSION["session_messages"] = $this->content; } /** * Вывод сообщения на страницу. */ function toPage() { echo " - " . $this->content . "
"; } }

Для доступа к сессии используется переменная $_SESSION.

Замечу, что $_SESSION - это массив, мы используем всего лишь один элемент этого массива с индексом ‘session_message’.

В данном случае имеем дело с "массивом массивов" - то, что мы храним в элементе ‘session_message’, представляет собой массив, это и есть список передаваемых сообщений (их, конечно, может быть несколько).

Если вы не смогли нащупать нить, самое время освежить в памяти разделы мануала, посвященные сессиям и массивам.

У вас может возникнуть вопрос. А зачем здесь нужны классы? Вполне можно было бы обойтись двумя функциями. Но давайте заглянем дальше. Нам может понадобиться создавать сообщения с различными типами (info, error, warning), определять адресатов сообщений.

Заметьте, что в данный момент в сессию кладется не сам объект, а только текст сообщения. ООП позволяет нам в дальнейшем поменять поведение метода send(), не меняя клиенский код, который обращается к этому методу (например, в будущем в сессию можно записывать полностью объект Message, если в нем будет много полей).

Представим, что мы бы это делали с помощью функций. Наверное, у нас была бы функция message_send($txt), еще была бы функция message_to_page($txt). Теперь надо добавить возможность различного поведения для различных видов сообщений. Вызовы функций меняются: message_send($txt, $kind), message_to_page($txt, $kind). Придется прочесать весь код приложения в поисках таких функций, делая исправления.

Этого можно избежать, заранее предвидя ситуацию, представив сообщение в виде ассоциативного массива: $msg[‘txt’], $msg[‘kind’], тогда в вызовах функций будет только один параметр. Чувствуете, как это стремится превратиться в класс?

Так вот, ООП дает возможность позволить себе роскошь не продумывать все заранее.

Следующий класс - Inbox - как раз для этого и предназначен.

class Inbox { /** * Массив поступивших сообщений. */ var $messages = array(); /** * В конструкторе получаем все поступившие сообщения * и удаляем их из сессии. */ function Inbox() { if (is_array($_SESSION["session_messages"])) { $messages = $_SESSION["session_messages"]; $co = sizeof($messages); for ($i = 0; $i < $co; $i++) { $this->messages = new Message($messages[$i]); } } /* очищаем массив сообщений */ $_SESSION["session_messages"] = array(); } /** * Выводим на страницу содержимое Inbox. */ function toPage() { $co = sizeof($this->messages); if ($co > 0) { echo "Сообщение от системы:
"; } for ($i = 0; $i < $co; $i++) { $this->messages[$i]->ToPage(); } } }

Давайте испытаем нашу систему сообщений.

Создадим очень простой пример, который в ответ на отправку формы будет сообщать количество секунд в текущей минуте.

send(); /* перенаправление на себя же */ header("location:"); } else { $inbox = new Inbox(); $inbox->toPage(); } ?>

Всю работу с массивами и сессиями мы спрятали внутри классов, и конечный код выглядит просто и красиво.

Создайте каталог на веб-сервере, затем создайте в нем эти три файла и попробуйте скрипт в работе. Заметьте, проблем с кнопками Back и Refresh не возникает.

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

Здесь мы встречаем два затруднения:

* Хотелось бы, чтобы список сообщений появлялся в определенной части страницы, и вы уже подобрали хорошее местечко для этого.
Проблема в том, что надо запустить команду $inbox->toPage() именно в тот момент, который бы соответствовал положению списка сообщений на странице. Если мы захотим поменять положение этого списка, придется лезть в код, но нехорошо постоянно для этого изменять каркас портала. Наилучшим решением было бы сделать вывод сообщений в виде отдельного модуля, о котором известно лишь только, что его надо подключить к каркасу.
То есть освободиться от строгой последовательности запуска модулей. Действительно, раз результат работы вывода Inbox не зависит от работы системы (на данном шаге - все данные у нас уже есть в сессии), то зачем лишние сложности?
* Чтобы поддерживать внешний вид (дизайн) списка сообщений надо заботиться об HTML-коде, который у нас зашит в методах toPage() классов Message и Inbox. Как правило, придется изменять PHP-код для того, чтобы изменить дизайн.

Чтобы попытаться решить первую проблему, можно создать буфер, в котором бы хранился результат работы вывода Inbox.

Возможно, у нас еще будет несколько похожих (на Inbox) вещей, и надо создать систему буферов. Для того, чтобы не перепутать где чей вывод, мы, наверное придем к именованию буферов. У нас будет где-то храниться последовательность, в соответствии с которой должен происходить вывод буферов - желательно во внешнем файле, чтобы легче было вносить изменения.

Уже эта попытка решения дает нам идею использовать XML как средство хранения промежуточных данных. А использование стилей XSLT поможет справиться и со втором проблемой.

Я не буду останавливаться на том, что такое XML, и что такое XSLT. Если вы не знакомы с этими вещами, zvon.org станет хорошей отправной точкой для изучения.

Идея в том, чтобы в методах toPage() формировать не HTML-код, а XML структуру. Документ страницы будет создаваться в виде стринга с XML-кодом (он будет служить в качестве "буфера"), а на последней стадии работы скрипта мы будем использовать XSL-трансформацию.

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

minute 57 second: 45

Что это такое - догадаться довольно просто - два сообщения и форма. Заметьте, PHP-скрипт должен подготовить только такой стринг - он очень простой. Причем порядок следования основных тегов неважен - можно поставить вначале, например, как будет удобно программисту. Как это реализовать. Можно, почти ничего не меняя, использовать output buffering, вместо HTML-кода выводить XML, а в конце просто захватить вывод в стринг. Но тогда мы потеряем в гибкости - например, хочется иногда выводить отладочную информацию прямо на страницу (с помощью echo). В то же время, разработчики PHP работают над DOM-модулем, который предлагает более продвинутый способ создания и передачи древовидных документов. Если мы захотим внедрить DOM, то придется перекраивать все приложение, изменяя вывод стрингов на создание DOM-элементов. Поэтому я предпочитаю хранить XML-представление объектов внутри самих объектов, последовательно собирая общий XML-документ. Это не так сложно, нужна всего лишь небольшая модификация. Вы увидите, что такой прием не привязан жестко к конкретному способу хранения XML-данных, и это позволит совершить переход к использованию DOM "малой кровью". Прежде всего заметим, что у каждого нашего объекта есть метод toPage(). Эта похожесть должна нас заставить задуматься о том, чтобы ввести новый общий родительский класс. Пусть каждый класс, который способен создавать кусочки XML-документа для страницы, будет наследоваться от класса, который будет заботиться об XML-представлении объекта. Назовем его Outputable.

class Outputable { /** * XML контейнер (стринг). */ var $output = ""; /** * Отдать содержимое контейнера и очистить контейнер. * * @return стринг с XML-данными */ function getOutput() { $out = $this->output; $this->output = ""; return $out; } /** * Добавить порцию к содержимому контейнера. * * @param string добавляемый стринг */ function appendOutput($string) { $this->output .= $string . "n"; } /** * "Абстрактный" метод. */ function toPage() { } }

Метод toPage() сделан пустым - в данном случае он нужен как индикатор того, как должны внешние "матрешки"-классы общаться с внутренним классом. Впрочем, здесь можно было бы предложить реализацию по умолчанию, если бы мы заметили, что есть много объектов, которые одинаково выводят себя на страницу.

Классы Message и Inbox несколько изменятся - теперь оба они должны наследоваться от Outputable, а также изменятся и методы toPage()
Message.php

class Message extends Outputable { /** * Содержание сообщения. */ var $content; /** * Конструктор для инициализации текста сообщения. * * @param content содержание сообщения */ function Message($content) { $this->content = $content; } /** * Запись сообщения в сессию. */ function send() { $_SESSION["session_messages"] = $this->content; } /** * Вывод сообщения на страницу. */ function toPage() { $this->appendOutput("".$this->content.""); } }

class Inbox extends Outputable { /** * Массив поступивших сообщений. */ var $messages = array(); /** * В конструкторе получаем все поступившие сообщения * и удаляем их из сессии. */ function Inbox() { if (is_array($_SESSION["session_messages"])) { $messages = $_SESSION["session_messages"]; $co = sizeof($messages); for ($i = 0; $i < $co; $i++) { $this->messages = new Message($messages[$i]); } } /* очищаем массив сообщений */ $_SESSION["session_messages"] = array(); } /** * Выводим на страницу содержимое Inbox. */ function toPage() { $co = sizeof($this->messages); $this->appendOutput(""); for ($i = 0; $i < $co; $i++) { $this->messages[$i]->toPage(); $this->appendOutput($this->messages[$i]->getOutput()); } $this->appendOutput(""); } }

Изменился способ вывода - теперь вместо непосредственного вывода на страницу внешнее представление до поры до времени хранится в Outputable, который "сидит" в каждом из объектов. Метод appendOutput() служит некоторой заменой конструкции echo(). Чтобы забрать вывод объекта, используется метод getOutput().

Теперь посмотрим, что собой представляет клиентская часть кода, которая будет решать ту же задачу, что и раньше.
index.php

send(); /* текущая секунда */ $msg_sec = new Message("second: " . date("s")); $msg_sec->send(); /* перенаправление на себя же */ header("location:"); exit; } else { /* подготавливаем список сообщений в виде XML */ $inbox = new Inbox(); $inbox->toPage(); $global_content->appendOutput($inbox->getOutput()); } $global_content->appendOutput(""); $xml_string = $global_content->getOutput(); $xh = xslt_create(); $xarg = array(); /* заголовок XML-документа */ $xarg["xml"] = ""."n"; /* тело XML-документа */ $xarg["xml"] .= "" . $xml_string . ""; /* XSL-шаблон */ $xarg["xsl"] = implode("", file("style.xsl")); /* выводим HTML-код - результат XSL-трансформации */ echo xslt_process($xh, "arg:xml", "arg:xsl", NULL, $xarg); /* выводим XML-исходник (debug) */ echo "


" . htmlspecialchars($xml_string) . "
"; ?>

Главное новшество - в объекте $global_content, название которого говорит само за себя. В данном случае он принадлежит классу Outputable, в реальных задачах вы, наверное, создадите отдельный класс для контента страницы.

Если внимательно присмотреться, то содержательная часть скрипта практически не изменилась - тот же inbox, тот же toPage(). Добавлена инструкция, которая содержимое списка сообщений выводит в контент страницы. Для разнообразия теперь генерируется два сообщения.

Для того, чтобы посмотреть на результат, осталось только подготовить XSL-шаблон.
style.xsl

XSLT Example

message

Чего же мы добились?

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

Любой модуль, который генерирует XML-данные в качестве результата своей работы, может быть использован в проекте. Кстати, это одно из преимуществ перед template-движками, в которых создание данных заключается в последовательности вызова методов (assign и т.п.) конкретного движка, на которых нет общего стандарта.

Еще одно преимущество - легкость отладки. Если вы запустите скрипт, то заметите, что на каждой странице присутствует debug-вывод - XML-прообраз, который здорово упрощает отладку приложений.

Над чем надо еще подумать - как создавать объекты-сообщения. Не всегда удобно использовать new непосредственно в клиентском коде. Но, пожалуй, это тема для отдельной статьи.

Напоследок, галопом о перспективах:

* всплывающие окна для списка важных сообщений
* "страницы-отправители" и "страницы-адресаты" в сообщениях
* ведение лога сообщений в базе данных
* кнопка "показать историю моих действий"
* статистический анализ действий пользователей в пределах сессий
* "интеллектуальные помощники" в веб-приложениях

Мне давно хотелось попробовать что-нибудь новенькое, и вот, когда на работе предложили написать примеры использования API Яндекс.Денег на разных языках, я с удовольствием принял это предложение-challenge. Так как чаще всего подобный функционал используют в приложениях на различных хостингах, то было принято волевое решение первым делом попробовать написать сие на PHP. Причем API до этого я даже не видел; более того, никакого опыта работы с PHP, кроме как лабораторной в ВУЗе, у меня не было. Дело обещало быть интересным.

Выбор окружения

Первое, с чем я столкнулся: разрабатывать и деплоить тут же на хостинг не очень удобно. И я решил скачать какой-нибудь джентльменский набор для web-разработчика. Из студенческих времен в памяти маячило название Denwer. Попробовал, но быстро отказался от него. Он живет какой-то своей жизнью, и когда я пытался настроить его, переписав что-нибудь в conf-файле Apache, он затирал или не затирал это по своему усмотрению, не давая все настроить руками. То есть получилось, что не я его настраивал, а он меня. Напомнило «если б я имел коня - это был бы номер...». В итоге я его, конечно, победил, но решил при этом найти более простой WAMP-server. Хочу отметить, что Denwer хороший продукт и ничего против я не имею, но читать факи и мануалы по нему мне не захотелось.

Нашел страничку со списком WAMP"ов в вики и стал перебирать их. Основными критерием для выбора были поддерживаемость проекта, чтобы версия сборки была более-менее свежей, и простота установки/запуска. В итоге смело могу рекомендовать The Uniform Server . Он не требует установки (только распаковать архив), при запуске висит в трее и запускается легким нажатием =). Самовольничать он не стал, и я остановился на нем.

Аутентификация OAuth

Почитал инструкции , скачал документацию , оголил шашку и помчался в бой. Но в бою я был быстро повержен OAuth-аутентификацией. OAuth - это способ получить доступ к какому-либо сервису/аккаунту пользователя без ввода и хранения его логина и пароля в своем приложении. Пришло к нам это от создателя твиттера и выглядит примерно так: мы делаем запрос к сервису (в нашем случае – к Яндекс.Деньгам), пользователь вводит на сервере Яндекс.Денег свой логин/пароль и дает нашему приложению разрешения пользоваться его аккаунтом. После этого сервер Яндекс.Денег делает редирект на наше приложение, и мы получаем временный код, время жизни которого очень мало. Затем посредством еще одного запроса к Яндекс.Деньгам меняем этот временный код на постоянный токен пользователя и дальше для нас, как для дембеля, все дороги открыты.

Правда, пока я проходил OAuth-аутентификацию, наткнулся на проблему безопасности. Попробовал обращаться к серверу Яндекс.Денег, но PHP стал грязно ругаться и говорить что-то про сертификаты. Порыскал немного в интернете и понял, что нужно, чтобы наше приложение проверяло SSL-сертификат сервера. Хотелось сделать хорошо, чтобы гарантировать пользователям безопасность, и я продолжил поиски. Но вменяемых примеров реализации проверки сертификата сервера в рунете почти нет. Расскажу по порядку.

Сначала нужно было прописать проверку сертификатов в коде при отправке запросов. Как заставить cURL, которым я пользуюсь, отправлять запросы с проверкой сертификата? Начал искать и был поражен тем, что самый популярный совет при ошибках сертификатов: ОТКЛЮЧИТЬ проверку. И это предлагают на куче сайтов и форумов (к примеру, ). Ужас, в общем. Вот код, который чаще всего предлагают:

Curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);

Запомните его, дети, и никогда не пишите. «А теперь, сынок, повторим все те плохие слова, которые ты должен забыть». Слава богу, что в англоязычном интернете не все так плохо и я нашел такую ссылочку , которая все объяснила. Сделал, как там написано, и все заработало.

Затем нужно было сохранить публичный сертификат Яндекс.Денег у себя, чтобы было с чем сравнивать при отправке запросов. Я вроде не совсем отсталый, но тем не менее мне это показалось довольно непростым. Может, вам в будущем поможет при работе с SSL. Заходим на нужный сайт через https, тыкаем в сертификаты и экспортируем. Но их там 3, какой из них нужен? «Возьми с полки пирожок; их там два, возьми средний». Оказывается, нужно экспортировать сертификат центра сертификации (корневой) и промежуточный (Яндекса). Конечный сертификат меняется раз в год и, если впишем его в цепочку и забудем поменять, когда он протухнет, то все в нашем приложении поломается. Поэтому экспортируем только указанные 2 сертификата и просто сохраняем их в один текстовый файл (скриншот). В моей библиотеке он фигурирет как ym.crt .

После этого запросы у меня заработали. Ура! Дальнейшее было делом техники. В документации все было понятно; собственно, поэтому я ее фактически и закопипастил в документацию кода. Оговорюсь еще, что писал я ее как объектную; на мой взгляд, с объектами работать хорошо и удобно.

Шифрование

Еще были небольшие трудности с шифрованием. Сначала сохранение/восстановление токенов пользователей было реализовано стандартной PHP-библиотекой Mcrypt. Но, как выяснилось, с ней есть проблемы. Например, из экзамплов, взятых в мануале, сразу заработала только одна deprecated-функция. Остальные функции плевали на мое желание заставить их работать, и лишь говорили что-то о неудачной инициализации модуля. Надо было разбираться. Затем выяснилось, что хостеры не очень жалуют эту библиотеку. Спросил у поддержки своего хостера , почему при создании сайта на PHP 5.3 нет модуля Mcrypt. Мне ответили (дословно): «Он годами глючил в 5.2 - автоматом не внесли в 5.3 как модуль, о котором вспоминают раз в пять лет, а проблемы с ним были». Других удобных стандартных библиотек с реализацией симметричного шифрования я в PHP не нашел (есть библиотека OpenSSL, но она не совсем для этого). После этого библиотеку шифрования я решил сменить на , которая является открытой и поддерживает популярный алгоритм AES. Она заработала сразу и без проблем.

Функционал и примеры использования

Также для «тренировки на кошках», а точнее — тестирования и отладки, были написаны вызовы функций библиотеки, которые я впоследствии облагородил версткой для приятного глазу вида, наполнил комментариями и обозвал примерами использования библиотеки.

В результате реализованы вызовы следующих функций API Яндекс.Денег: информация о счете, история операций, детальная информация по операциям, переводы денег другим пользователям. Плюсы и возможности:

  • OAuth-авторизация пользователя;
  • безопасность работы (поддерживается проверка цепочки сертификатов);
  • удобство работы (response"ы сервера представлены в виде объектов) и быстрый старт;
  • относительно безопасное и простое решение хранения токенов пользователей с использованием шифрования и без использования БД. Реализацию этого решения вы сможете легко переписать под свои хранилища.
Сама библиотека представляет собой файл цепочки сертификатов ym.crt и файл ym.php , который содержит:
  • программный интерфейс IYandexMoney;
  • главный класс YandexMoney (реализация интерфейса);
  • класс-перечисление с правами доступа (scope);
  • вспомогательные классы (response-объекты вывода результатов запросов к API).
В комплект библиотеки приложены 2 файла, которые реализуют шифрование: Rijndael.php и AES.php. Данные файлы взяты из библиотеки . Они нужны на случай, если вы будете использовать методы сохранения и восстановления токенов.
Внимание: использует PHP версии 5, а также стандартную библиотеку cUrl для http-запросов.

Для тех, кто не будет устанавливать и смотреть подробные примеры, покажем пару вызовов.
Для выполнения операций со счетом через API необходимо получить разрешение пользователя, то есть токен. Его можно получить следующими вызовами (к примеру, с доступом на просмотр информации о счете и истории операций):

YandexMoney::authorize(Consts::CLIENT_ID, "account-info operation-history", Consts::REDIRECT_URL); // затем на странице редиректа инициировать создание объекта и получение токена $ym = new YandexMoney(Consts::CLIENT_ID, Consts::CERTIFICATE_CHAIN_PATH); $token = $ym->receiveOAuthToken($_GET["code"], Consts::REDIRECT_URL);
При создании объекта $ym ему передается идентификатор приложения и абсолютный путь на сервере к цепочке сертификатов (файл ym.crt). И то, и другое обычно прописывается в константах в каком-нибудь модуле (consts.php в наших примерах).
Ну и покажем, как получить информацию о счете пользователя. Таким же образом создаем объект и затем вызываем метод, передав ему токен пользователя:

$ym = new YandexMoney(Consts::CLIENT_ID, Consts::CERTIFICATE_CHAIN_PATH); $accountInfoResponse = $ym->accountInfo($token); echo "Номер счета: " . $accountInfoResponse->getAccount() . "\n"; echo "Баланс: " . $accountInfoResponse->getBalance() . "\n"; echo "Код валюты: " . $accountInfoResponse->getCurrency() . "\n";
Информация о счете получена.

Примерно так же обстоят дела и с другими вызовами.

В итоге, библиотеку решили назвать громким именем SDK и выложить на

PHP - самый популярный язык для написания кода серверной части. Одной и той же цели на нем можно достичь несколькими путями: можно спроектировать красивую и легко поддерживаемую систему, а можно быстро слепить вместе куски кода со Stack Overflow, не забивая себе чересчур голову такими вещами, как правила проектирования и читаемость кода.

Конечно, даже если вы пока еще не гуру разработки, вы все равно захотите писать код, из-за которого окружающим не захочется плакать. Tproger вас выручит - в этой статье собрано 20 советов, каждый из которых поможет вам улучшить читаемость кода и за следование которым вам потом скажут спасибо. А в перспективе прилежное следование этим советам поможет вам стать ближе к статусу опытного и внимательно относящегося к деталям разработчика.

Используйте

Не используйте и прочие способы размещения в файле php-скриптов. Да, может быть, у вас все работает, а вот на другом сервере - не факт. На некоторых серверах короткие тэги вообще отключены. Не рискуйте, используйте только

// Неправильно // Неправильно

Отделяйте файлы с параметрами

Нет ни одной достаточно веской причины, которая бы оправдала хранение настроек в одном файле со скриптом. Всегда создавайте отдельный файл и в самом начале скрипта его подключайте. Вы поймете, как это важно, когда вам придется редактировать кучу файлов из-за изменения одного параметра. Это ведь совсем несложно:

include("config.php");

Комментарии - ваши друзья

Конечно, если вы спешите или набиваете код на волне вдохновения, становится как-то не до комментариев. Но код, написанный давно, понимать трудно - даже если писали его вы. Лучше помогите себе парой комментариев в ключевых местах, чтобы потом сократить время на понимание очередного скрипта.

// Однострочный комментарий /** Многострочный комментарий **/

Правда, просто? Еще стоит обратить внимание на PHPDoc .

Грамотно форматируйте код

Нет ничего хуже огромной стены кода, автор которого явно не знал о существовании кнопки Tab. Не спасут даже комментарии - отступы придумали для того, чтобы логически отделять друг от друга фрагменты кода. А использование или неиспользование отступов много говорит о вас как о программисте. Вот этот код отформатирован неправильно:

Function dothisformula($a,$b) { $c = $b+$a; $e=3; while ($c < 20) { $e = $e - 1; $c = $c + 1; } return $e; }

А вот это - тот же самый код, но с правильным форматированием:

Function dothisformula($a, $b) { $c = $b + $a; $e = 3; while ($c < 20) { $e = $e - 1; $c = $c + 1; } return $e; }

Если вы хотите точно знать, какой способ форматирования кода правильный - почитайте PSR-ы .

Давайте переменным понятные имена

Конечно, к единому мнению по этому вопросу прийти нельзя. camelCase или under_score? Нужна ли Венгерская нотация ? Важно здесь одно: раз и навсегда определитесь, что вам ближе. А даже если вам и захочется изменить стиль, то пожалуйста, не делайте это посреди проекта! Разные варианты именования переменных в одном проекте или, что еще хуже, в одном файле - это ужасно. И никаких магических чисел! Не поленитесь использовать константы.

Инициализируйте переменные

Конечно, PHP автоматически создает переменные при попытке к ним обратиться. Но вам не кажется, что пользоваться этой особенностью довольно рискованно? Хорошей практикой считается всегда инициализировать переменные еще перед первым использованием. Это сделает код понятнее и поможет убедиться, что вы не обращаетесь случайно к неинициализированной переменной.

$foo = array(); $bar = 0; $baz = false;

Булева переменная ложна, иначе - истинна

Если вы проверяете какое-то условие и потом результат сравнения сохраняете в булевом значении, при инициализации переменной для результата (мы ведь всегда заранее инициализируем переменную, помните?) сначала присваивайте ей false . То есть вот так делать не надо:

Function getStatus() { $result = true; if ($i != 2) $result = false; return $result; }

Лучше вот так:

Function getStatus() { $result = false; if ($i == 2) $result = true; return $result; }

Зачем это? Представьте, что вы проверяете данные перед запросом к базе. Если по какой-то причине блок if вдруг не выполнится, то значение переменной останется false - так вы подстрахуете себя от попадания в базу ошибочных данных. В коде порой приходится иметь дело с конфиденциальными данными, так что лучше сначала исходить из того, что все данные ошибочны, пока не доказано обратное. Это правило практически написано кровью.

Кстати, лучше проверять, являются ли данные такими, как надо нам, чем являются ли они такими, как нам не надо.

Используйте кавычки, когда обращаетесь к элементам массива

В коде разных разработчиков можно увидеть два варианта доступа к элементам ассоциативного массива:

$name = $names["marc"]; $name = $names;

Во время выполнения второго варианта PHP сначала попытается найти константу с именем marc . И только если такой не найдется, marc будет сконвертировано в строку и передано в таком виде. Лучше не думать о том, что может случиться, если вдруг такая константа будет существовать… Всегда ставьте кавычки, чтобы такого не происходило.

Используйте запятые, чтобы обратиться к нескольким строкам в одном вызове

Если вам надо, например, в одном вызове функции обратиться к значению переменной и к строке, лучше используйте запятые, а не точки. Почему? Точка - оператор конкатенации строк, эта операция будет выполняться медленнее. Доказательство .

Еще раз. Так надо:

echo "Hello, my name is ", $name;

А вот так - не надо:

echo "Hello, my name is " . $name;

Пользуйтесь тернарными операторами

Если у вас в коде какое-то очень простое сравнение, желательно использовать тернарный оператор, чтобы не растягивать простой код на несколько строк. Вот так может выглядеть ваш простой if:

If ($a == 1) $b = 2; else $b = 3;

Но можно записать его и так:

$b = ($a == 1) ? 2: 3;

Обобщенно тернарный оператор работает следующим образом:

$variable = ($condition) ? true: false;

Используйте для сравнения с булевыми значениями строгое сравнение

Если вы проверяете переменную на true или false , используйте === , а не == , которое задействуется в остальных сравнениях. Строгое сравнение из трех знаков равенства сравнит еще и типы переменных.

If ($isMember == true) { ... } // Неправильно if ($isMember === true) { ... } // Правильно

Используйте инкремент и декремент

Если вам нужно просто увеличить или уменьшить на 1 значение переменной, ни к чему писать эту громоздкую конструкцию:

$number = $number + 1; $number2 = $number2 - 1;

Куда лаконичнее такой вариант:

$number++; // Инкремент $number2--; // Декремент

А главная прелесть этих операций - в том, что одновременно с ними можно выполнять еще какое-нибудь действие (например, if или while). В зависимости от того, написано ли -- или ++ до или после самого названия переменной, очередность операций изменяется.

// Инкремент произойдет после сравнения: $number = 10; while ($number++ == 10) { ... } // А здесь декремент произойдет перед сравнением: $number = 10; if (--$number == 10) { ... } $number = 10; $total = $number++; // $total будет равен 10. $number = 10; $total = --$number; // $total будет равен 9.

Используйте сокращенные операторы присваивания

Если переменную надо увеличить или уменьшить на число, не равное 1, то и в этом случае код можно сократить. Например, у вас в коде может быть такое:

$a = $a + $b; $a = $a * 5; $a = $a - $d; $a = $a / 3;

А используя сокращенные операторы, этот же код можно будет записать так:

$a += $b; $a *= 5; $a -= $d; $a /= 3;

Со строками, кстати, это тоже работает. Если понадобится присоединить к существующей строке какую-то часть, сделайте это так:

$string = $string . "hello"; $string .= "hello";

Создайте отдельную функцию для var_dump

Иногда бывает нужно во время активной отладки часто выводить прямо на страницу значение какой-то переменной. Конечно, лучше лишний раз так не делать, но если все-таки нужно, то имеет смысл себе помочь. Функция может выглядеть так:

Function d($var) { echo "

";
 var_dump($var);
 echo "
"; }

Тэг pre используется, чтобы сделать значение более читаемым. Кстати, такое странное имя функции дано неслучайно - оно короткое, хоть и неочевидное. Набрать ее название будет быстро. Только потом не забудьте убрать эту функцию, когда будете подчищать код в конце разработки.

Но лучше всего забыть этот совет и научиться пользоваться XDebug.

Пользуйтесь константами

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

Define ("DIRECTORY", "/path/to/site/"); echo DIRECTORY;

Пользуйтесь $_GET и $_POST

$_REQUEST лучше не использовать. Четко разделяйте данные: $_GET - это параметры, переданные из адресной строки, $_POST - это, например, полученные из формы данные. И не вздумайте передавать в скрипт пароли, особенно незашифрованные, GET-запросом!

$action = $_GET["actions"]; $name = $_POST["name"];

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

Используйте объекты, а не функции

PHP - все-таки в том числе и объектно-ориентированный язык. Если вы работаете над большим проектом, сотни строк функционального кода вам точно быстро надоедят. А если вам нужно хранить множество однотипных параметров, то тем более задумайтесь об использовании соответствующего класса.

Вызывайте методы цепочками

Если вы подряд вызываете несколько методов одного объекта, имеет смысл сократить код, не выписывая каждый раз название этого объекта. Если есть вот такой класс:

Class User { function setThis($var) { .... return $this; } function setThis2($var2) { .... return $this; } }

… то обращаться к его методам можно так:

$user = new User(); $user->setThis(...) ->setThis2(...);

Чтобы лучше понять, как это работает и при каких обстоятельствах можно это пользоваться, почитайте .

Не повторяйте себя

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

Стремитесь к слабой связанности и сильной сцепленности

Связанность - это способ проектирования проекта, когда изменение одного компонента требует изменения другого. Другими словами, если вы измените одну функцию, придется ли менять другую? Чем свободнее связь, тем проще будет менять компоненты, не перелопачивая половину кода.

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

Как опытный программист, вы должны стремиться к тому, чтобы компоненты мало зависели друг от друга и не брали на себя слишком много работы. Такой код будет куда проще расширять и поддерживать. Создавая функцию, задумывайтесь о том, насколько она удобна для использования в будущих проектах.

Проиллюстрировать этот совет можно так. Возьмем этот код:

/* Мы хотим сложить два числа, только если первое число меньше 5 */ function add($var, $var2) { if ($var < 5) return false; else $result = $var + $var2; echo "The Result is ", $result; } $var = 3; $var2 = 2; add($var, $var2);

Главный минус этой функции в том, что она сама осуществляет проверку данных, а потом выводит на экран значение. Да и такое поведение не совсем очевидно - согласитесь, столь избирательно работающее сложение вам вряд ли еще где-то повторно понадобится. А если вам еще и захочется поменять сообщение для вывода? Этот код лучше переписать так:

Function add($var, $var2) { return $var + $var2; } function result($message) { return "The Result is " . $message; } $var = 3; $var2 = 2; if ($var < 5) { echo result(add($var, $var2)); }

Теперь каждая функция выполняет только свою работу: add складывает числа, result выводит результат.