PHP-мышление ООП: отправка и получение сообщения: я правильно понял? Основы основ PHP: обзор для начинающих

Приветствуем наших читателей. К сожалению, очень много дел и проблем навалилось, поэтому частота обновление блога не такая, как хотелось бы, но это поправимо. Параллельно к основной работе, я в "фоне" обдумываю и прикидываю реализации архитектуры для игровых проектов (напомню, что основная область моих интересов и работ - создание онлайновых браузерных игр). Последнее время я все чаще и чаще возвращаюсь к мысли, что интересно было бы реализовать основной игровой сервер на основе очередей сообщений (MQ или Messages queue). То есть, движок такой игры будет представлять собой набор компонентов, которые будут общаться между собой посредством асинхронных сообщений, а каждый компонент может быть как генератором сообщений, так и подписчиком, то есть исполнять другие сообщения.

Такой подход, насколько я понимаю, широко применяется в мире Java, там для этого есть стандарт Java Message Service (JMS) и применяются брокеры сообщений и на этом базируется архитектура Enterprise service bus (ESB), например, Apache ServiceMix . Но для нас это пока высокая сфера крупных проектов, а в специфике веба и веб-ориентированных приложений я бы хотел рассмотреть, можно ли что-то сделать подобное, но с меньшими затратами и обеспечить приложению отказоустойчивость, распределение нагрузки и асинхронную обработку. И конечно, очень желательно, чтобы это было реализовано на РНР как основном языке реализации всех компонентов сервера.

И так, еще раз - MQ, это архитектура и ПО промежуточного уровня, которое занимается сбором, хранение и маршрутизацией (распределением) сообщений между компонентами. Я не претендую на полноту описания, и, вполне возможно, не учитываю множества нюансов, поэтому не рассматривайте мои определения как аксиомы, лучше всего почитать дополнительную литературу, если вы хотите поглубже разобраться в архитектуре MQ (например, вот эти статьи: , , ) и определение в Wikipedia - Message queue

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

Apache ActiveMQ - открытая реализация брокера сообщений (Message Broker) и Enterprise Integration Patterns (если сейчас и очень кратко - расширение для реализации дополнительной обработки согласно правилам). Этот проект, по моему мнению, из всех открытых, самый мощный и развивающийся, недавно вышла версия 5.1. Он реализовывает множество стандартов и обеспечивает все возможности, необходимые для решений уровня Enterprise, входит в стек Java-технологий от Apache. Что меня заинтересовало, так это возможность кросс-языкового обмена сообщениями, а значит - клиенты могут быть реализованы на любом языке. Для платформ Java, C, C++, C# это библиотека OpenWire , реализующая Wire протокол для нативного доступа к MQ, для остальных языков, включая, что нам и интересно, РНР, есть Stomp - реализация библиотек для разных скриптовых языков, которая превращает сообщения в формат JMS. Кстати, если необходимо обеспечить безопасную коммуникацию и передачу сообщений, можно использовать SSL.

MQS (Minimalist Queue Services) - проект, если можно так сказать, с друго конца. Это небольшая система, написанная на Perl, организовывает систему очередей сообщений, используя XML-RPC протокол, сами сообщения могут хранится как в любой базе данных, так и в файлах. К сожалению, по всей видимости, проект заброшен, так как последняя новость на сайте датирована апрелем 2005 года, а текущая версия - 0.0.14.

Spread - еще одна реализация очереди сообщений, на этот раз на С++, и версии есть для различных платформ, включая Win32, Linux, BSD и MacOS. Текущая версия - 4.0. Система распределенная и ориентирована на высокопроизводительные системы, где клиентов и, соответственно, сообщений, очень много. Заявлена поддержка, в последней 4.0 версии, технологии Virtual Synchrony . Что интересно - в поставку сразу включены бинарные версии для нескольких платформ, а также встроенные интерфейсы для некоторых языков - C/C++, Java, Perl, Python, Ruby. Странно, что четвёртого Р - РНР, среди них нет, но существует расширение в PECL , которое реализовывает весь интерфейс Spread API. Текущая версия 2.1 и достаточно новая, значит проект развивается. Существуют и реализации для других языков, в том числе и альтернативы встроенным интерфейсам, поэтому посмотрите на , там даже для MS Excel есть расширение. Среди интересных проектов - модуль mod_log_spread для Apache , позволяющий собирать логи доступа с нескольких веб-серверов.

RabbitMQ - высокопроизводительная платформа, написанная на Erlang, основанная на Open Telecom Platform, а значит - очень надежная и масштабированная система, часто применяемая в телекоммуникационных приложениях и других подобных системах. Есть интерфейс только для Java и C++. Система поддерживает стандарт AMQP (Open Standard for Messaging Middleware). Система интересная, знать бы только Erlang, хотя что-то мне подсказывает, что проектируя весь серверный модуль на этой платформе, мы получили бы много "плюшек", в частности, на этой же платформе написан самый популярный Jabber-сервер ejabberd, который также можно применять в он-лайн игровых проектах.

Beanstalkd - также интересный проект, созданный в рамках разработки одного из приложений для социальной сети Facebook, которым пользуется около 10 млн человек (приложением, не сетью). Это специализированный сервер для хранения и обработки очередей заданий, использующий хранение данных в памяти для обеспечения скорости, однако в ущерб отказоустойчивости. Этот проект очень похож на уже ранее , да и сами разработчики выражают благодарность создателям memcached за принципы архитектуры и протокол. Система предназначена для создания асинхронной очереди обработчиков пользовательских задач, которые не требуют немедленного ответа, например, отсылки писем, фоновые обработки и т.п. Существуют клиентские API для различных языков, в том числе для Erlang, OCaml, Perl, PHP, Python, Ruby. Для РНР библиотека и пока имеет версию 0.11, сама разработка началась всего пару месяцев назад (судя по регистрации проекта). Детальнее можно почитать отличный обзор: Beanstalk Messaging Queue Сервер написан на С, что обеспечивает высокую скорость работы, однако специфика проекта в плане хранения всех данных в оперативной памяти не подойдет для тех сфер, где остро необходима максимальная отказоустойчивость, пусть даже ценой дополнительного ПО и затрат на хранение данных в базе.

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

См. «Обновления» в конце:

Текущая база кода имеет 1.4k-строку чисто процедурного кода, который отправляет sms (имеет бизнес-логику, db-связь и все в одном гигантском, if условно вложенное бесчисленное множество, if s, никаких функций, полных литералов, подлинного кандидата DailyWTF?). И я решил укусить пулю и переписать всю чертову с нуля.
Дело в том, что это будет мой первый опыт ООП. Я читал как можно больше об OOD и о хорошей практике и решил начать с чего-то простого. Я хочу реализовать отправку / получение сообщений (в основном текст / SMS, но MMS, электронная почта должны быть включены в будущем). Поэтому я написал следующее в качестве своего первого сообщения

interface MessageInterface { public function setType($type); public function getType(); public function setContent($content); public function getContent(); public function sendMessage(); //add more functionalities later } class Message implements MessageInterface { private $_type; private $_content; public function setType($type) { $this->_type = $type; } public function getType() { return $this->_type; } public function setContent($content) { if ($this->_type = "text") { $this->_content = $content; return TRUE; // report success } else { return FALSE; } // report failure } public function getContent() { return $this->_content; } public function sendMessage() { if ($this->_type == "text") { print "Sending ".$this->getContent()." as ".$this->getType()." message\n"; //do the actual implementation later return TRUE; // report success } else { return FALSE; } // report failure } } $msg = new Message(); $msg->setType("text"); print $msg->getType() . "\n"; //text $result = $msg->setContent("Hello World!"); if($result) $result2 = $msg->sendMessage(); //Sending Hello World! as text message if($result2) print "Hurray ! Mission accomplished !!";

Я не думаю, что правильно применяю концепцию полиморфизма. Я чувствую, что if бы не было, верно? Возможно, они необходимы для setContent() но как насчет sendMessage() ? Поэтому я подумал, что отделив отправляющую часть в свой class SendMessage implements SendMessageInterface . который будет иметь свои собственные переменные для $server, $protocol и методов для отправки электронной почты и текста и т. д. Но при написании этого класса я понял, что if s снова ползут, как if($msg->getType() == "text") условные обозначения. Чтобы добавить к этому, я создаю новый класс, который отделяет часть действия моего объекта, который меня путает (например, class door должна отвечать за реализацию методов close() и open()).

Теперь либо я принимаю, что if всегда будет там (что, похоже, побеждает всю цель полиморфизма), или я должен делать что-то неправильно .
С точки зрения пользователя, я представляю себе что-то вроде:

$msg = new Message(); $msg->setType("email"); //or "text" or "mms" etc. $msg->setContent($content); //eg $content=array("subject"=>"foo","body"=>"bar") $msg->sendMessage(); //if the last line is not possible, then perhaps //$sender = new SendMessage($msg); //$sender->send();

что мне здесь не хватает? невозможно достичь $msg->sendMessage(); ? Должны ли / нужны ли мне разные классы сообщений (MessageEmail , MessageText и т. Д.)? Должен ли я отделить SendMessage (и, возможно, иметь $msg->sendMessage(); называть его?)

// и это когда я даже не думал о получении сообщения ! Боже, помоги мне!! 🙁

Обновление 15 августа 2011 года. Подумав обо всех аспектах текущей базы кода, я определил следующие части, которые мне нужно будет реализовать. a. Message Class(es) (type, content, sender, receiver, DateTime of send/receive etc.) Responsibilities: creating and modifying messages ascribing consistent and appropriate characteristics of a message b. Send Class(es) (protocol, header info, server/operator to use) Responsibilities: Sending messages Changing the state of Message (for setting send DateTime of Message) e. Database Class(es) (id, content, to, from, time etc.) Responsibilities: Represent Message for storage. CRUD (Create, Read, Update, Delete) actions on this representation for DBMS. e. Interfaces (MAX_MESSAGE_LENGTH, TIMEOUT etc.) Responsibilities: Provide interface for communication between various modules.

Я считаю, что моя основная причина путаницы заключалась в смешивании интерфейсов с полиморфизмом (см. Комментарий). Как вы оцениваете это?

Обновление 16 августа 2011 г.
В основном я использовал интерфейсы, чтобы навязывать функциональность. Вот краткая версия файла interface. interface MessageInterface { //omitting getters for clarity public function setType($type); public function setSender(IdentityInterface $sender); public function setReceiver(IdentityInterface $receiver); public function setSendGateway(GatewayInterface $sendGateway); } interface IdentityInterface { public function setName($name); public function setAddress($address); } interface GatewayInterface { public function setProtocol($protocol); public function send(IdentityInterface $sender, IdentityInterface $receiver, ContentInterface $content); }

(нет причудливых вещей, поскольку я еще не интегрировал class GatewaySMPP implements GatewayInterface в мой основной класс Message который выглядит так:

Class Message implements MessageInterface { private $_type; private $_content; private $_sender; private $_receiver; private $_sendGateway; //private $_receiveGateway; private $_dataStorage; public function __construct($type = NULL, $content = NULL, IdentityInterface $sender = NULL, IdentityInterface $receiver = NULL, GatewayInterface $sendGateway = NULL) { $this->setType($type); $this->setContent($content); ($sender === NULL) ? $this->setSender(new Identity()) : $this->setSender($sender); ($receiver === NULL) ? $this->setReceiver(new Identity()) : $this->setReceiver($receiver); //similarly for $setSendGateway etc. } //setters and getters, omitting for clarity public function send(...) { //testing pending $this->_sendGateway->send($this->getSender(), $this->getReceiver(), $this->getContent ...) }

Интересной частью было внедрение GatewaySMPP, которое включало множество операций сокета и проверку ответов. Мне просто нужно написать public function send() обёртки public function send() вокруг private function _send{PDU,SM} .

Хотя я думал об интеграции GatewaySMPP, я понял, что буду открывать / закрывать сокеты для соединения SMPP для каждой операции отправки сообщений. Это нормально для упражнений / тестирования, но на практике мне кажется, что мне может понадобиться изменить мою логику, чтобы использовать существующее соединение. Вопрос в том, как? Вот текущая логика:

Class GatewaySMPP { private $_socket,$_port,$_host //etc. public function __construct($host,$port,$user,$passwd) { $this->_socket = FALSE; $this->_host = $host; //initialize other private variables } public function init() { if($this->_socket !== FALSE) return FALSE; //socket already in use $this->_socket = fsockopen($this->_host, $this->_port ...) //prepare bind statement for initiating SMPP connection and fwrite to socket $this->_sendPDU(BIND, $data) } public function send($receiver, $sender, $message, ...) { //use private functions which do actual socket operations $this->_sendSM($receiver, $sender, $message, ...) } public function end() { if($this->_socket === FALSE) return; //socket already closed this->_sendPDU(UNBIND, ""); //omitting response check $result = fclose($this->_socket); //omitting response check }

Q. Проблема, с которой я столкнулся, состоит в том, что каждый объект GatewaySMPP будет иметь свой собственный $ _socket, поэтому я подумал о том, чтобы сделать singleton (содрогание ) GatewaySMPP или использовать какую-либо переменную global / state для отслеживания сокетов для повторного использования. Лучшая идея, которая приходит мне на ум, заключается в том, что потребитель этих классов использует следующую логику. 1. Создайте и используйте одиночный $objGatewaySMPP для всех $objectMessage 2. objGatewaySMPP->init(); 3. foreach($objMessage as $msg) $msg->send(); 4. objGatewaySMPP->end(); , Это все еще оставляет проблему одновременных вызовов разных пользователей класса? Предложения / комментарии, пожалуйста.

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

Сообщение "Hello world!" выводится в заголовке web-страницы. Интересно то, что команда print внутри конструкции, которая обычно называется экранирующими последовательностями PHP (), представляет собой законченную программу. Ни длинного кода инициализации, ни включения библиотек -- программа состоит лишь из того кода, который непосредственно решает поставленную задачу!

Конечно, для выполнения сценариев PHP необходимо предварительно установить и настроить программное обеспечение PHP на сервере. Этот процесс описан в разделе «Загрузка и установка PHP/Apache» настоящей главы. Разделу предшествуют фрагменты из отзывов нескольких пользователей, выступающих в пользу PHP, с кратким обзором языка и его истории. Но прежде чем браться за процесс установки, мы познакомимся с некоторыми характеристиками PHP. Этой теме посвящен следующий раздел.

Характеристики PHP

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

  • традиционностью;
  • простотой;
  • эффективностью;
  • безопасностью;
  • гибкостью.

Существует еще одна «характеристика», которая делает PHP особенно привлекательным: он распространяется бесплатно!

Традиционность

Язык PHP кажется знакомым программистам, работающим в разных областях. Многие конструкции языка позаимствованы из Си Perl, а нередко код PHP практически неотличим от того, что встречается в типичных программах С или Pascal. Это заметно снижает начальные усилия при изучении PHP.

Простота

Сценарий PHP может состоять из 10 000 строк или из одной строки -- все зависит от специфики вашей задачи. Вам не придется подгружать библиотеки, указывать специальные параметры компиляции или что-нибудь в этом роде. Механизм PHP просто начинает выполнять код после первой экранирующей последовательности (). Если код имеет правильный синтаксис, он исполняется в точности так, как указал программист.

Эффективность

Эффективность является исключительно важным фактором при программировании для многопользовательских сред, к числу которых относится и WWW. В PHP 4.0 был реализован механизм выделения ресурсов и обеспечена улучшенная поддержка объектно-ориентированного программирования, а также средства управления сеансом. В последней версии появился и механизм подсчета ссылок (reference counting), предотвращающий выделение лишней памяти.

Безопасность

PHP предоставляет в распоряжение разработчиков и администраторов гибкие и эффективные средства безопасности, которые условно делятся на две категории: средства системного уровня и средства уровня приложения.

Средства безопасности системного уровня

В PHP реализованы механизмы безопасности, находящиеся под управлением администраторов; при правильной настройке PHP это обеспечивает максимальную свободу действий и безопасность. PHP может работать в так называемом безопасном режиме (safe mode), который ограничивает возможности применения PHP пользователями по ряду важных показателей. Например, можно ограничить максимальное время выполнения и использование памяти (неконтролируемый расход памяти отрицательно влияет на быстродействие сервера). По аналогии с cgi-bin администратор также может устанавливать ограничения на каталоги, в которых пользователь может просматривать и исполнять сценарии PHP, а также использовать сценарии PHP для просмотра конфиденциальной информации на сервере (например, файла passwd).

Средства безопасности уровня приложения

В стандартный набор функций PHP входит ряд надежных механизмов шифрования. PHP также совместим с многими приложениями независимых фирм, что позволяет легко интегрировать его с защищенными технологиями электронной коммерции (e-commerce). Другое преимущество заключается в том, что исходный текст сценариев PHP нельзя просмотреть в браузере, поскольку сценарий компилируется до его отправки по запросу пользователя. Реализация PHP на стороне сервера предотвращает похищение нетривиальных сценариев пользователями, знаний которых хватает хотя бы для выполнения команды View Source.

Тема безопасности настолько важна, что ей посвящена целая глава. За подробной информацией о средствах безопасности PHP обращайтесь к главе 16.

Гибкость

Поскольку PHP является встраиваемым (embedded) языком, он отличается исключительной гибкостью по отношению к потребностям разработчика. Хотя PHP обычно рекомендуется использовать в сочетании с HTML, он с таким же успехом интегрируется и в JavaScript, WML, XML и другие языки. Кроме того, хорошо структурированные приложения PHP легко расширяются по мере необходимости (впрочем, это относится ко всем основным языкам программирования).

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

Поскольку PHP не содержит кода, ориентированного на конкретный web-сервер, пользователи не ограничиваются определенными серверами (возможно, незнакомыми для них). Apache, Microsoft IIS, Netscape Enterprise Server, Stronghold и Zeus -- PHP работает на всех перечисленных серверах. Поскольку эти серверы работают на разных платформах, PHP в целом является платформенно-незави-симым языком и существует на таких платформах, как UNIX, Solaris, FreeBSD и Windows 95/98/NT.

Наконец, средства PHP позволяют программисту работать с внешними компонентами, такими как Enterprise Java Beans или СОМ-объекты Win32. Благодаря

этим новым возможностям PHP занимает достойное место среди современных технологий и обеспечивает масштабирование проектов до необходимых пределов.

Бесплатное распространение

Стратегия Open Source наделала немало шуму в программной отрасли. Распространение исходных текстов программ в массах оказало несомненно благотворное влияние на многие проекты, в первую очередь -- Linux, хотя и успех проекта Apache сильно подкрепил позиции сторонников Open Source. Сказанное относится и к истории создания PHP, поскольку поддержка пользователей со всего мира оказалась очень важным фактором в развитии проекта PHP.

Принятие стратегии Open Source и бесплатное распространение исходных текстов PHP оказало неоценимую услугу пользователям. Вдобавок, отзывчивое сообщество пользователей PHP является своего рода «коллективной службой поддержки», и в популярных электронных конференциях можно найти ответы даже на самые сложные вопросы.

«Мы в течение долгого времени поддерживали личные контакты с некоторыми разработчиками PHP и вели с ними обширную переписку. Когда у разработчиков PHP возникали какие-то проблемы, относящиеся к MySQL, мы всегда были готовы помочь им в поиске решения. Кроме того, мы включили в MySQL несколько новых возможностей лишь для того, чтобы улучшить его интеграцию с PHP. Результатом наших усилий стало то, что MySQL превосходно работает с PHP, -- и мы позаботимся о том, чтобы это положение сохранилось и в будущем!»

Майкл «Монти» Видениус (Michael «Monty» Widenius),

«Выбор PHP для реализации mp3.lycos.com был обусловлен несколькими причинами. Главной причиной стали сжатые сроки работы над проектом -- ведь PHP ускоряет процесс разработки. Другой причиной была высокая эффективность -- мы перешли от 0 к 1,4 миллиона посещений в сутки, и PHP с этим прекрасно справился. Третья причина заключалась в том, что я твердо знал: если на стадии тестирования с повышенной нагрузкой в PHP обнаружатся какие-либо ошибки, я смогу их самостоятельно исправить, поскольку PHP распространяется вместе с исходными текстами».

Стиг Баккен (Stig Bakken),

«Я использовал PHP с первых дней, еще с версии PHP/FI 1.x. Мне понравилось, что я могу обрабатывать формы и настраивать страницы «на ходу» при помощи такого простого языка. Вместе с потребностями моей компании развивался и PHP.

В наши дни PHP обладает исключительно богатыми возможностями. Мы используем его практически во всех создаваемых web-сайтах, включая 32bit.com и DevShed.com. Мы даже воспользовались им в Info West для реализации службы поддержки, управления учетными записями и отслеживания портов.

Эволюция PHP и признание его мировым сообществом -- классический пример успешного ведения проекта с открытыми исходными текстами. Широта взглядов создателей, поддержка сообщества и хорошее сопровождение кодовой базы привели PHP к успеху, о котором многие коммерческие проекты могут лишь мечтать. Я с оптимизмом смотрю в будущее PHP и рекомендую каждому web-разработчику попробовать его в деле. Возможно, вы, как и я, уже не расстанетесь с ним».

Рэнди Косби (Randy Cosby),

Вводный пример

Пример, приведенный в листинге 1.1, наглядно показывает, как легко PHP интегрируется с HTML-кодом.

Листинг 1.1. Создание динамической страницы PHP

// Присвоить значения нескольким переменным

$site_title = "PHP Recipes";

$bg_color = "white";

$user_name = "Chef Luigi";

Понравилась статья? Поделиться с друзьями: