Реалізація SOAP-сервісів за допомогою Zend Framework

  1. Часто використовувані скорочення
  2. Що таке SOAP
  3. Лістинг 1. Приклад SOAP-запиту
  4. Лістинг 2. Приклад SOAP-відповіді
  5. Установка прикладу додатки
  6. Крок 1: ініціалізація нового додатка
  7. Малюнок 1. Сторінка вітання Zend Framework за замовчуванням
  8. Крок 3: настройка простору імен додатки
  9. витяг даних
  10. Лістинг 3. Визначення soapAction ()
  11. Лістинг 4. Об'єкт сервісу з певними методами get * ()
  12. Лістинг 5. Ініціалізація адаптера бази даних
  13. Лістинг 6. Приклад SOAP-клієнта
  14. Лістинг 7. Приклад запиту методу SOAP getProducts ()
  15. Лістинг 8. Приклад відповіді SOAP на метод getProducts ()
  16. Малюнок 2. Результат запиту SOAP, перетворений в стандартний масив PHP
  17. Лістинг 9. Об'єкт SOAP-сервісу з певним методом addProduct ()
  18. Лістинг 10. Об'єкт SOAP-сервісу з певним методом deleteProduct ()
  19. Лістинг 11. Об'єкт SOAP-сервісу з певним методом updateProduct ()
  20. Лістінг 12. Приклад SOAP-клієнта
  21. Лістинг 13. Спеціальний клас винятків
  22. Лістинг 14. Переглянутий об'єкт SOAP-сервісу з перевіркою вхідних даних і видачею повідомлень
  23. Лістинг 15. Відредагована визначення SOAPAction () з підтримкою виклику спеціальних винятків як повідомлень...
  24. Лістинг 16. SOAP-запит з недійсними вхідними аргументами
  25. Лістинг 17. Створення повідомлення про помилку SOAP
  26. Додавання підтримки WSDL
  27. Лістинг 18. Визначення wsdlAction ()
  28. Малюнок 3. Динамічно генерується файл WSDL
  29. Висновок
  30. Ресурси для скачування

Як за допомогою Zend Framework швидко додати SOAP-сервіси до Web-додатком, написаному на PHP

Сьогодні Web-сервісів приділяється багато уваги, причому левова його частка припадає на сервіси на основі REST. REST став популярний завдяки своїй простій, інтуїтивно зрозумілою природі і здатності працювати з існуючими методами HTTP. Але варто пам'ятати, що REST - не найкращий варіант: SOAP (Simple Object Access Protocol) забезпечує більш формальний, стандартизований підхід до вирішення проблеми обміну інформацією через Інтернет.

Часто використовувані скорочення
  • API: Application programming interface - інтерфейс прикладних програм
  • HTTP: Hypertext Transfer Protocol
  • i18n: інтернаціоналізація
  • MVC: Model-View-Controller
  • OOP: Object-Oriented Programming - об'єктно-орієнтоване програмування
  • REST: Representational State Transfer
  • SQL: Structured Query Language - мова структурованих запитів
  • URI: Uniform Resource Identifier - універсальний ідентифікатор ресурсу
  • URL: Uniform Resource Locator - універсальний покажчик ресурсу
  • W3C: World Wide Web Consortium
  • WSDL: Web Services Description Language - мова опису Web-сервісів
  • XML: Extensible Markup Language - розширювана мова розмітки

Хоча SOAP-сервіси зазвичай вважають складними і трудомісткими в реалізації, існують інструменти, які значно спрощують процес. Один з них - Zend Framework, який представляє собою повну MVC-середовище для створення масштабованих Web-додатків на мові PHP. Поряд з масою інших корисних речей - ТМР-формами, підтримкою i18n, кешуванням запитів і сторінок, інтеграцією Dojo і т.п. - Zend Framework забезпечує повний набір інструментів для створення і розгортання SOAP-сервісів за допомогою компонента Zend_Soap.

У цій статті ми розглянемо процес створення простого Web-сервісу на основі SOAP за допомогою Zend Framework. Ми не тільки покажемо, як обробляти клієнтські запити і повертати SOAP-сумісні відповіді, але і досліджуємо процес обробки виключень і створення повідомлень про помилки SOAP. Нарешті, ми використовуємо Zend_Soap для автоматичного створення файлу WSDL, що описує сервіс SOAP, щоб наділити клієнти здатністю "автовизначення" API SOAP-сервісу.

Що таке SOAP

Спочатку кілька слів про SOAP. Це спосіб обміну інформацією через Інтернет за допомогою котрий залежить від мови коду XML, що дозволяє додаткам, написаним на різних мовах, встановлювати зв'язок один з одним. Цей код XML передається між клієнтом і сервером за допомогою HTTP в якості транспортного протоколу з використанням сильної типізації даних для забезпечення їх цілісності.

На відміну від REST, який пов'язаний з ресурсами і діями, SOAP заснований на методах і типах даних. Якщо REST-сервіс, як правило, обмежується чотирма операціями, відповідними чотирьом HTTP-методам: GET, POST, PUT і DELETE, то SOAP-сервіс не має таких обмежень; він здатний забезпечити стільки методів, скільки визначить розробник. Більш того, ці методи, як правило, викликаються за допомогою HTTP-методу POST, причому цей метод ніяк не прив'язаний до типу викликається операції.

Щоб проілюструвати, як працює SOAP, розглянемо простий приклад. Припустимо, що у вас є додаток для створення закладок в соціальній мережі, і ви хочете дозволити стороннім розробникам додавати і видаляти закладки з цього додатка за допомогою SOAP. Зазвичай створюють набір сервіс-об'єктів з такими методами, як getBookmark () і addBookmark (), і публікують ці сервіс-об'єкт на сервері SOAP. Сервер здійснює переклад типів даних SOAP в стандартні типи даних, аналізує пакети запитів SOAP, реалізує відповідний метод сервера, а також генерує пакети відповідей SOAP з результатами.

В лістингу 1 наведено приклад SOAP-запиту для процедури getBookmark ().

Лістинг 1. Приклад SOAP-запиту
POST / soap HTTP / 1.1 Host: localhost Connection: Keep-Alive User-Agent: PHP-SOAP / 5.3.1 Content-Type: application / soap + xml; charset = utf-8 Content-Length: 471 <? xml version = "1.0&quot; encoding = "UTF-8"?> <env: Envelope xmlns: env = "http://www.w3.org/2003/05/ soap-envelope "xmlns: ns1 =" http: //example.localhost/index/soap "xmlns: xsd =" http://www.w3.org/2001/XMLSchema "xmlns: xsi =" http: // www .w3.org / 2001 / XMLSchema-instance "xmlns: enc =" http://www.w3.org/2003/05/soap-encoding "> <env: Body> <ns1: getBookmark env: encodingStyle =" http : //www.w3.org/2003/05/soap-encoding "> <param0 xsi: type =" xsd: int "> 4682 </ param0> </ ns1: getBookmark> </ env: Body> </ env : Envelope>

А в лістингу 2 ілюструється приклад відповіді.

Лістинг 2. Приклад SOAP-відповіді
HTTP / 1.1 200 OK Date: Wed, 17 Mar 2010 17:13:28 GMT Server: Apache / 2.2.14 (Win32) PHP / 5.3.1 X-Powered-By: PHP / 5.3.1 Content-Length: 800 Keep -Alive: timeout = 5, max = 100 Connection: Keep-Alive Content-Type: application / soap + xml; charset = utf-8 <? xml version = "1.0&quot; encoding = "UTF-8"?> <env: Envelope xmlns: env = "http://www.w3.org/2003/05/soap-envelope" xmlns : ns1 = "http: //example.localhost/index/soap" xmlns: xsd = "http://www.w3.org/2001/XMLSchema" xmlns: enc = "http://www.w3.org/ 2003/05 / soap-encoding "xmlns: xsi =" http://www.w3.org/2001/XMLSchema-instance "> <env: Body xmlns: rpc =" http://www.w3.org/2003 / 05 / soap-rpc "> <ns1: getBookmarkResponse env: encodingStyle =" http://www.w3.org/2003/05/soap-encoding "> <rpc: result> return </ rpc: result> <return enc: itemType = "xsd: string" enc: arraySize = "3" xsi: type = "enc: Array"> <item xsi: type = "xsd: string"> http://www.google.com </ item > <item xsi: type = "xsd: string"> http://www.php-programming-solutions.com </ item> <item xsi: type = "xsd: string"> http: //www.mysql- tcr.com </ item> </ return> </ ns1: getBookmarkResponse> </ env: Body> </ env: Envelope>

У типовій операції SOAP сервер приймає запит в коді XML, як в лістингу 1 , Аналізує XML, виконує відповідний метод сервіс-об'єкта і повертає клієнту відповідь в коді XML, як в лістингу 2 . Клієнт, як правило, в змозі розібрати і перевести цей SOAP-відповідь в об'єкт конкретного мови або структури даних для подальшої обробки. Можна використовувати необов'язковий файл WSDL, щоб надати клієнтам інформацію про доступні методи, а також кількість і типи даних вхідних параметрів і значень.

У Zend Framework входять реалізації для клієнта і сервера SOAP, а також для автоматичної генерації файлів WSDL. Реалізації для сервера і клієнта - це оболонки навколо SOAP-розширення в PHP; це означає, що вони не будуть працювати, якщо збірка PHP не включає в себе підтримку SOAP-розширення. Тим не менше, використання бібліотеки Zend Framework поверх стандартного розширення трохи спрощує завдання, так як розробнику досить визначити набір об'єктів, який реалізує API сервісу, і прикріпити їх до сервера для обробки вхідних запитів. У наступних розділах ми обговоримо це докладніше.

Установка прикладу додатки

Перш ніж приступити до реалізації SOAP-сервісу, зробимо кілька зауважень і припущень. У цій статті я буду вважати, що у вас є діюча середовище розробки з Apache, PHP + SOAP і MySQL, що Zend Framework встановлений в каталозі PHP і що ви знайомі з основами SQL, XML і SOAP. Я також припускаю, що ви знайомі з основними принципами розробки додатків з використанням Zend Framework, розумієте зв'язок між діями і контролерами і знайомі з рівнем абстракції бази даних Zend_Db. Нарешті, я буду вважати, що ваш Web-сервер Apache налаштований для підтримки віртуального хостингу і перезапису URL за допомогою файлів .htaccess. Якщо ці предмети вам не знайомі, зверніться по посиланнях на додаткову інформацію, наведеним в розділі ресурси .

Приклад SOAP-сервісу, який буде реалізований в цій статті, дозволяє стороннім розробникам додавати, редагувати, видаляти і витягувати листинги з бази даних додатків. Він містить такі методи, до яких можна звертатися за допомогою стандартного клієнта SOAP:

  • getProducts (): повертає всі продукти з бази даних;
  • getProduct ($ id): повертає певний продукт з бази даних;
  • addProduct ($ data): додає новий продукт в базу даних;
  • deleteProduct ($ id): видаляє певний продукт з бази даних;
  • updateProduct ($ id, $ data): привласнює певного продукту в базі даних нових значень.

Крок 1: ініціалізація нового додатка

Для початку встановимо стандартний додаток Zend Framework, яке забезпечує контекст для коду, наведеного в цій статті. Щоб форматувати новий проект, використовуємо сценарій інструменту Zend Framework (zf.bat в Windows® або zf.sh в UNIX) ™, як показано нижче:

shell> zf.bat create project example

Тепер можна визначити новий віртуальний хост для цього додатка, такий як http: //example.localhost/ в конфігурації Apache, і вказати каталог public / додатки в якості кореневого каталогу документів віртуального хоста. Якщо тепер перейти в цей хост, ви побачите сторінку вітання Zend Framework за замовчуванням, як показано на малюнку 1 .

Малюнок 1. Сторінка вітання Zend Framework за замовчуванням

Крок 2: ініціалізація бази даних і моделі програми

Наступний крок полягає в ініціалізації бази даних програми. Створимо нову таблицю MySQL для зберігання інформації про продукти, як показано нижче.

mysql> CREATE TABLE IF NOT EXISTS products (-> id int (11) NOT NULL AUTO_INCREMENT, -> title varchar (200) NOT NULL, -> shortdesc text NOT NULL, -> price float NOT NULL, -> quantity int (11 ) NOT NULL, -> PRIMARY KEY (id) ->) ENGINE = InnoDB DEFAULT CHARSET = utf8;

Заповнимо цю таблицю прикладами записів, як показано нижче.

mysql> INSERT INTO products (id, title, shortdesc, price, quantity) VALUES (1, -> 'Ride Along Fire Engine', 'This red fire engine is ideal for toddlers who want -> to travel independently. Comes with flashing lights and beeping horn. ', -> 69.99, 11); Query OK, 1 row affected (0.08 sec) mysql> INSERT INTO products (id, title, shortdesc, price, quantity) VALUES (2, -> 'Wind-Up Crocodile Bath Toy', 'This wind-up toy is the perfect companion for hours -> of bathtub fun. ', 7.99, 67); Query OK, 1 row affected (0.08 sec)

Крок 3: настройка простору імен додатки

Останній крок полягає в налагодженні простору імен додатки для Автозавантажувач Zend Framework. Цей крок дозволяє автоматично по мірі необхідності завантажувати в додаток пов'язані з ним класи. В даному випадку я припускаю, що простором імен додатка є Example, а спеціалізовані класи (такі як класи SOAP-сервісу) зберігаються в $ PROJECT / library / Example /. Змінимо відповідним чином файл конфігурації програми $ PROJECT / application / configs / application.ini і додамо до нього наступний рядок:

autoloaderNamespaces [] = "Example_"

Тепер все готово, щоб приступити до створення SOAP-сервісу.

витяг даних

Оскільки це приклад, я для простоти створю дію для обробки SOAP-запитів всередині IndexController модуля за замовчуванням, однак в реальному додатку краще завести окремий контролер для обробки SOAP-запитів. Відредагуємо файл $ PROJECT / application / controllers / IndexController.php і додамо до нього нову дію, як в лістингу 3 .

Лістинг 3. Визначення soapAction ()
<? Php class IndexController extends Zend_Controller_Action {public function soapAction () {// відключення макетів і візуалізації $ this-> getHelper ( 'viewRenderer') -> setNoRender (true); // ініціалізація сервера і завдання URI $ server = new Zend_Soap_Server (null, array ( 'uri' => 'http: //example.localhost/index/soap')); // завдання класу SOAP-сервісу $ server-> setClass ( 'Example_Manager'); // обробка запиту $ server-> handle (); }}

лістинг 3 инициализирует новий об'єкт Zend_Soap_Server в не-WSDL режимі, передаючи конструктору об'єкта в якості першого аргументу нульове значення. Під час налаштування сервера в не-WSDL режимі необхідно вказати URI сервера; в лістингу 3 він вказаний в масиві опцій, переданому конструктору як другий аргумент.

Далі, метод об'єкта сервера setClass () використовується для підключення до сервера класу сервісу. Цей клас реалізує методи SOAP-сервісу; сервер буде автоматично викликати їх у відповідь на запити SOAP. До сервера можна додати спеціальні функції за допомогою методів addFunction () і loadFunctions (), замість приєднання класу методом setClass ().

Як зазначалося вище, клас Zend_Soap_Server не забезпечує реалізацію SOAP-сервера; він всього лише створює оболонку навколо вбудованого в PHP розширення SOAP. Так що коли всі приготування залишаться позаду, метод handle () в лістингу 3 подбає про ініціалізації вбудованого об'єкта PHP SoapServer, передаючи йому об'єкт запиту та викликаючи метод handle () цього об'єкта для обробки SOAP-запиту.

Все це добре, але ми поки не дуже далеко просунулися, так як ще не визначений клас сервісу. Створимо його, використовуючи код лістингу 4 і зберігши результуюче визначення класу в $ PROJECT / library / Example / Manager.php.

Лістинг 4. Об'єкт сервісу з певними методами get * ()
<? Php class Example_Manager {/ ** * Повертає список всіх продуктів в базі даних * * @return array * / public function getProducts () {$ db = Zend_Registry :: get ( 'Zend_Db'); $ Sql ​​= "SELECT * FROM products"; return $ db-> fetchAll ($ sql); } / ** * Повертає вказаний продукт в базі даних * * @param integer $ id * @return array | Exception * / public function getProduct ($ id) {if (! Zend_Validate :: is ($ id, 'Int')) {throw new Example_Exception ( 'Invalid input'); } $ Db = Zend_Registry :: get ( 'Zend_Db'); $ Sql ​​= "SELECT * FROM products WHERE id = '$ id'"; $ Result = $ db-> fetchAll ($ sql); if (count ($ result)! = 1) {throw new Exception ( 'Invalid product ID:'. $ id); } Return $ result; }}?>

лістинг 4 встановлює автономний клас сервісу, що містить два методи. Метод getProducts () використовує Zend_Db, щоб отримати всі доступні записи про продукти з таблиці і повернути їх у вигляді масиву, в той час як метод getProduct () приймає ідентифікатор продукту та повертає тільки зазначену запис. Потім SOAP-сервер перетворює значення, повернуті методом, в пакет відповіді SOAP і поверне його запитувачу клієнту. В лістингу 8 наведено приклад такого відповідного пакета.

Якщо вам цікаво, де инициализирован Zend_Db, то це робиться в засобі завантаження додатка, в $ PROJECT / application / Bootstrap.php. Файл Bootstrap.php містить функцію _initDatabase (), яка встановлює адаптер Zend_Db і реєструє його в реєстрі додатки. Код наведено в лістингу 5 .

Лістинг 5. Ініціалізація адаптера бази даних
<? Php class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {protected function _initDatabase () {$ db = new Zend_Db_Adapter_Pdo_Mysql (array ( 'host' => 'localhost', 'username' => 'user', 'password' => 'pass', ' dbname '=>' example ')); Zend_Registry :: set ( 'Zend_Db', $ db); }}

Щоб побачити це в дії, создалім SOAP-клієнт ( лістинг 6 ) І використовуємо його для підключення до SOAP-службі і виклику методу getProducts ().

Лістинг 6. Приклад SOAP-клієнта
<? Php // завантаження бібліотек Zend require_once 'Zend / Loader.php'; Zend_Loader :: loadClass ( 'Zend_Soap_Client'); // ініціалізація SOAP-клієнта $ options = array ( 'location' => 'http: //example.localhost/index/soap', 'uri' => 'http: //example.localhost/index/soap'); try {$ client = new Zend_Soap_Client (null, $ options); $ Result = $ client-> getProducts (); print_r ($ result); } Catch (SoapFault $ s) {die ( 'ERROR: ['. $ S-> faultcode. ']'. $ S-> faultstring); } Catch (Exception $ e) {die ( 'ERROR:'. $ E-> getMessage ()); }?>

SOAP-клієнт згенерує пакет запиту ( лістинг 7 ).

Лістинг 7. Приклад запиту методу SOAP getProducts ()
<? Xml version = "1.0&quot; encoding = "UTF-8"?> <Env: Envelope xmlns: env = "http://www.w3.org/2003/05/soap-envelope" xmlns: ns1 = "http : //example.localhost/index/soap "xmlns: xsd =" http://www.w3.org/2001/XMLSchema "xmlns: enc =" http://www.w3.org/2003/05/soap -encoding "> <env: Body> <ns1: getProducts env: encodingStyle =" http://www.w3.org/2003/05/soap-encoding "/> </ env: Body> </ env: Envelope>

Сервер відповість на це в коді SOAP ( лістинг 8 ).

Лістинг 8. Приклад відповіді SOAP на метод getProducts ()
<? Xml version = "1.0&quot; encoding = "UTF-8"?> <Env: Envelope xmlns: env = "http://www.w3.org/2003/05/soap-envelope" xmlns: ns1 = "http : //example.localhost/index/soap "xmlns: ns2 =" http://xml.apache.org/xml-soap "xmlns: enc =" http://www.w3.org/2003/05/soap -encoding "xmlns: xsi =" http://www.w3.org/2001/XMLSchema-instance "xmlns: xsd =" http://www.w3.org/2001/XMLSchema "> <env: Body xmlns: rpc = "http://www.w3.org/2003/05/soap-rpc"> <ns1: getProductsResponse env: encodingStyle = "http://www.w3.org/2003/05/soap-encoding"> <rpc: result> return </ rpc: result> <return enc: itemType = "ns2: Map" enc: arraySize = "2" xsi: type = "enc: Array"> <item xsi: type = "ns2: Map "> <item> <key xsi: type =" xsd: string "> id </ key> <value xsi: type =" xsd: string "> 1 </ value> </ item> <item> <key xsi: type = "xsd: string"> title </ key> <value xsi: type = "xsd: string"> Ride Along Fire Engine </ value> </ item> <item> <key xsi: type = "xsd: string "> shortdesc </ key> <value xsi: type =" xsd: string "> This red fire engine is ideal for toddlers who want to travel independently. Comes with flashing lights and beeping horn. </ Value> </ item> <item> <key xsi: type = "xsd: string"> price </ key> <value xsi: type = "xsd: string"> 69.99 < / value> </ item> <item> <key xsi: type = "xsd: string"> quantity </ key> <value xsi: type = "xsd: string"> 11 </ value> </ item> </ item> ... </ return> </ ns1: getProductsResponse> </ env: Body> </ env: Envelope>

Потім SOAP-клієнт перетворює цю відповідь назад в стандартний масив PHP, який можна обробляти або досліджувати далі, як показано на малюнку 2 .

Малюнок 2. Результат запиту SOAP, перетворений в стандартний масив PHP

Додавання, видалення та зміна даних

Все це відноситься до отримання даних за допомогою SOAP. А як щодо додавання і видалення даних?

У класі Example_Manager досить легко реалізувати метод addProduct (). Це демонструється в лістингу 9 .

Лістинг 9. Об'єкт SOAP-сервісу з певним методом addProduct ()
<? Php class Example_Manager {/ ** * додає новий продукт в базу даних * * @param array $ data array of data values ​​with keys -> table fields * @return integer id of inserted product * / public function addProduct ($ data) {$ db = Zend_Registry :: get ( 'Zend_Db'); $ Db-> insert ( 'products', $ data); return $ db-> lastInsertId (); }}

Метод addProduct () в лістингу 9 приймає новий запис про продукт як масив пар ключ-значення, а потім використовує метод insert () об'єкта Zend_Db для занесення цього запису в таблицю бази даних. Він повертає ідентифікатор вставленої записи.

Видалення продуктів здійснюється настільки ж просто: досить додати метод deleteProduct (), який приймає ідентифікатор продукту в якості вхідних даних, а потім використовує метод Zend_Db delete () для видалення запису з бази даних. Цей метод ілюструється в лістингу 10 .

Лістинг 10. Об'єкт SOAP-сервісу з певним методом deleteProduct ()
<? Php class Example_Manager {/ ** * видаляє певний продукт з бази даних * * @param integer $ id * @return integer number of products deleted * / public function deleteProduct ($ id) {$ db = Zend_Registry :: get ( ' Zend_Db '); $ Count = $ db-> delete ( 'products', 'id ='. $ Db-> quote ($ id)); return $ count; }}

В лістингу 10 другий аргумент, який передається методу delete (), вказує обмеження або фільтр, який буде використовуватися при виконанні операції видалення. Цей аргумент важливий, так як без нього Zend_Db видалить з таблиці всі записи.

нарешті, лістинг 11 ілюструє метод updateProduct (), який можна використовувати для зміни запису про продукт. Цей метод приймає два вхідних аргументу - ідентифікатор продукту та масив, що містить змінену запис, і використовує метод Zend_Db update () для виконання запиту UPDATE до таблиці бази даних.

Лістинг 11. Об'єкт SOAP-сервісу з певним методом updateProduct ()
<? Php class Example_Manager {/ ** * Змінює продукт в базі даних * * @param integer $ id * @param array $ data * @return integer number of products updated * / public function updateProduct ($ id, $ data) {$ db = Zend_Registry :: get ( 'Zend_Db'); $ Count = $ db-> update ( 'products', $ data, 'id ='. $ Db-> quote ($ id)); return $ count; }}

Можна спробувати зробити все це з SOAP-клієнтом, як показано в лістингу 12 .

Лістінг 12. Приклад SOAP-клієнта
<? Php // завантаження бібліотек Zend require_once 'Zend / Loader.php'; Zend_Loader :: loadClass ( 'Zend_Soap_Client'); // ініціалізація SOAP-клієнта $ options = array ( 'location' => 'http: //example.localhost/index/soap', 'uri' => 'http: //example.localhost/index/soap'); try {// Додавання нового продукту // Отримання и відображення ідентіфікатора продукту $ p = array ( 'title' => 'Spinning Top', 'shortdesc' => 'Hours of fun await with this colorful spinning top. Includes flashing colored lights. ',' price '=>' 3.99 ',' quantity '=> 57); $ Client = new Zend_Soap_Client (null, $ options); $ Id = $ client-> addProduct ($ p); echo 'Added product with ID:'. $ Result; // зміна існуючого продукту $ p = array ( 'title' => 'Box-With-Me Croc', 'shortdesc' => 'Have fun boxing with this inflatable crocodile, made of tough, washable rubber.', 'Price' => '12 .99 ',' quantity '=> 25); $ Client-> updateProduct ($ id, $ p); echo 'Updated product with ID:'. $ Id; // відалення існуючого продукту $ client-> deleteProduct ($ id); echo 'Deleted product with ID:'. $ Id; } Catch (SoapFault $ s) {die ( 'ERROR: ['. $ S-> faultcode. ']'. $ S-> faultstring); } Catch (Exception $ e) {die ( 'ERROR:'. $ E-> getMessage ()); }?>

ПОВІДОМЛЕННЯ про помилки SOAP

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

На щастя, Zend Framework містить компонент Zend_Validate, який надає вбудовані засоби перевірки даних для найбільш поширених сценаріїв. Цю функцію можна об'єднати з методом Zend_Soap_Server registerFaultException () для перевірки даних в запиті клієнта і відправлення повідомлення про помилку SOAP при різних сценаріях.

Щоб побачити, як це працює, почнемо з створення спеціального класу винятків, що розширює Zend_Exception, як показано в лістингу 13 .

Лістинг 13. Спеціальний клас винятків
<? Php class Example_Exception extends Zend_Exception {}

Збережемо цей клас в $ PROJECT / library / Example / Exception.php.

Потім додамо в різні методи класу сервісу перевірку вхідних даних і видачу спеціального повідомлення про помилку, якщо вхідні дані некоректні або відсутні. лістинг 14 ілюструє відредагований клас Example_Manager.

Лістинг 14. Переглянутий об'єкт SOAP-сервісу з перевіркою вхідних даних і видачею повідомлень
<? Php class Example_Manager {// визначення фільтрів і валідаторів вхідних даних private $ _filters = array ( 'title' => array ( 'HtmlEntities', 'StripTags', 'StringTrim'), 'shortdesc' => array ( 'HtmlEntities' , 'StripTags', 'StringTrim'), 'price' => array ( 'HtmlEntities', 'StripTags', 'StringTrim'), 'quantity' => array ( 'HtmlEntities', 'StripTags', 'StringTrim')) ; private $ _validators = array ( 'title' => array (), 'shortdesc' => array (), 'price' => array ( 'Float'), 'quantity' => array ( 'Int')); / ** * Повертає список всіх продуктів в базі даних * * @return array * / public function getProducts () {$ db = Zend_Registry :: get ( 'Zend_Db'); $ Sql ​​= "SELECT * FROM products"; return $ db-> fetchAll ($ sql); } / ** * Повертає вказаний продукт з бази даних * * @param integer $ id * @return array | Example_Exception * / public function getProduct ($ id) {if (! Zend_Validate :: is ($ id, 'Int')) {throw new Example_Exception ( 'Invalid input'); } $ Db = Zend_Registry :: get ( 'Zend_Db'); $ Sql ​​= "SELECT * FROM products WHERE id = '$ id'"; $ Result = $ db-> fetchAll ($ sql); if (count ($ result)! = 1) {throw new Example_Exception ( 'Invalid product ID:'. $ id); } Return $ result; } / ** * додає новий продукт в базу даних * * @param array $ data array of data values ​​with keys -> table fields * @return integer id of inserted product * / public function addProduct ($ data) {$ input = new Zend_Filter_Input ($ this -> _ filters, $ this -> _ validators, $ data); if (! $ input-> isValid ()) {throw new Example_Exception ( 'Invalid input'); } $ Values ​​= $ input-> getEscaped (); $ Db = Zend_Registry :: get ( 'Zend_Db'); $ Db-> insert ( 'products', $ values); return $ db-> lastInsertId (); } / ** * видаляє певний продукт з бази даних * * @param integer $ id * @return integer number of products deleted * / public function deleteProduct ($ id) {if (! Zend_Validate :: is ($ id, 'Int' )) {throw new Example_Exception ( 'Invalid input'); } $ Db = Zend_Registry :: get ( 'Zend_Db'); $ Count = $ db-> delete ( 'products', 'id ='. $ Db-> quote ($ id)); return $ count; } / ** * Змінює продукт в базі даних * * @param integer $ id * @param array $ data * @return integer number of products updated * / public function updateProduct ($ id, $ data) {$ input = new Zend_Filter_Input ( $ this -> _ filters, $ this -> _ validators, $ data); if (! Zend_Validate :: is ($ id, 'Int') ||! $ input-> isValid ()) {throw new Example_Exception ( 'Invalid input'); } $ Values ​​= $ input-> getEscaped (); $ Db = Zend_Registry :: get ( 'Zend_Db'); $ Count = $ db-> update ( 'products', $ values, 'id ='. $ Db-> quote ($ id)); return $ count; }}

В лістингу 14 API сервісу доповнений перевіркою всіх вхідних параметрів. Для більшості методів API статичний метод Zend_Validate :: is () забезпечує зручний спосіб перевірки вхідних аргументів, а в деяких випадках для перевірки і фільтрації введення використовується додаткова ланцюжок фільтрів Zend_Filter_Input. Будь-які помилки, що виникають в процесі перевірки введення, представляються як екземпляри класу Example_Exception.

Останній крок - змусити сервер SOAP автоматично перетворювати викликані Example_Exceptions в повідомлення про помилки SOAP. Це робиться шляхом реєстрації класу винятків в сервері SOAP з використанням методу registerFaultException (), як у відредагованому IndexController :: soapAction в лістингу 15 .

Лістинг 15. Відредагована визначення SOAPAction () з підтримкою виклику спеціальних винятків як повідомлень про помилки
<? Php class IndexController extends Zend_Controller_Action {public function soapAction () {// відключення макетів і візуалізації $ this-> getHelper ( 'viewRenderer') -> setNoRender (true); // ініціалізація сервера і завдання URI $ server = new Zend_Soap_Server (null, array ( 'uri' => 'http: //example.localhost/index/soap')); // завдання класу служби SOAP $ server-> setClass ( 'Example_Manager'); // регістрірація винятків, які генерують повідомлення про помилки SOAP $ server-> registerFaultException (array ( 'Example_Exception')); // обробка запиту $ server-> handle (); }}

Щоб побачити це в дії, відправивши запит SOAP для методу getProduct () і передати йому недійсний ID. лістинг 16 містить приклад такого запиту SOAP.

Лістинг 16. SOAP-запит з недійсними вхідними аргументами
<? Xml version = "1.0&quot; encoding = "UTF-8"?> <Env: Envelope xmlns: env = "http://www.w3.org/2003/05/soap-envelope" xmlns: ns1 = "http : //example.localhost/index/soap "xmlns: xsd =" http://www.w3.org/2001/XMLSchema "xmlns: xsi =" http://www.w3.org/2001/XMLSchema-instance "xmlns: enc =" http://www.w3.org/2003/05/soap-encoding "> <env: Body> <ns1: getProduct env: encodingStyle =" http://www.w3.org/2003 / 05 / soap-encoding "> <param0 xsi: type =" xsd: string "> nosuchproduct </ param0> </ ns1: getProduct> </ env: Body> </ env: Envelope>

Сервер перевірить вхідні дані і, знайшовши їх недійсними, викличе виключення Example_Exception, яке буде перетворено в повідомлення про помилку SOAP, що повертається клієнтові. лістинг 17 ілюструє відповідь пакет.

Лістинг 17. Створення повідомлення про помилку SOAP
<? Xml version = "1.0&quot; encoding = "UTF-8"?> <SOAP-ENV: Envelope xmlns: SOAP-ENV = "http://schemas.xmlsoap.org/soap/envelope/> <SOAP-ENV: Body> <SOAP-ENV: Fault> <faultcode> Receiver </ faultcode> <faultstring> Invalid input </ faultstring> </ SOAP-ENV: Fault> </ SOAP-ENV: Body> </ SOAP-ENV: Envelope>

З точки зору клієнта SOAP хороша ідея - обгорнути виклик SOAP в блок Try-Catch, щоб помилки SOAP на кшталт тієї, що описана вище, можна було легко перехоплювати і обробляти. Якщо ви повернетеся наприклад клієнта SOAP з лістингу 12 , То побачите, як це можна зробити.

Додавання підтримки WSDL

Один з недоліків стандартного розширення SOAP в PHP - це те, що воно не включає підтримку автоматичної генерації WSDL-файлів для SOAP-сервісу. WSDL-файли корисні тим, що містять інформацію про доступні методи API SOAP і можуть використовуватися шляхом підключення клієнтів до "автоматичного виявлення" API SOAP.

Однак Zend Framework містить компонент Zend_Soap_AutoDiscover, який можна використовувати в цілях автоматичного створення WSDL-файлу для SOAP-сервісу. Він робить це шляхом читання коментарів PHPDoc в класі SOAP-сервісу і перетворення цих коментарів в документ WSDL. Якщо ви переглянете попередні листинги з цієї статті, то побачите, що кожен метод супроводжується коментарем PHPDoc; це зроблено навмисно, щоб спростити автоматичне створення WSDL.

лістинг 18 показує, як налаштувати автоматичне створення WSDL за допомогою компонента Zend_Soap_AutoDiscover.

Лістинг 18. Визначення wsdlAction ()
<? Php class IndexController extends Zend_Controller_Action {public function soapAction () {// відключення макетів і візуалізації $ this-> getHelper ( 'viewRenderer') -> setNoRender (true); // ініціалізація сервера і завдання розташування файлу WSDL $ server = new Zend_Soap_Server ( 'http: //example.localhost/index/wsdl'); // завдання класу служби SOAP $ server-> setClass ( 'Example_Manager'); // регістрірація винятків, які генерують повідомлення про помилки SOAP $ server-> registerFaultException (array ( 'Example_Exception')); // обробка запиту $ server-> handle (); } Public function wsdlAction () {// відключення макетів і візуалізації $ this-> getHelper ( 'viewRenderer') -> setNoRender (true); // настройка автоматичного виявлення WSDL $ wsdl = new Zend_Soap_AutoDiscover (); // завдання класу SOAP-сервісу $ wsdl-> setClass ( 'Example_Manager'); // настройка дії SOAP URI $ wsdl-> setUri ( 'http: //example.localhost/index/soap'); // обробка запиту $ wsdl-> handle (); }}

В лістингу 18 визначено новий метод wsdlAction (), який ініціалізує екземпляр компонента Zend_Soap_AutoDiscover і вказує йому на клас Example_Manager. Виклик методу handle () цього примірника призводить до читання зазначеного класу, аналізу містяться в ньому коментарів PHPDoc і створення відповідного стандартизованого документа WSDL, який повністю описує об'єкт сервісу.

Щоб побачити результат, відкрийте в браузері посилання http: //example.localhost/index/wsdl, і ви повинні побачити щось схоже на малюнок 3 .

Малюнок 3. Динамічно генерується файл WSDL

Тепер замість того, щоб вручну вказувати параметри uri і location, можна очікувати, що сервер і клієнт SOAP будуть використовувати цей файл WSDL. Це демонструє и лістинг 18 , В якому soapAction () змінено таким чином, щоб передавати URL WSDL в конструктор Zend_Soap_Server, який буде запускатися в режимі WSDL. Під'єднані клієнти SOAP також можуть використовувати цей URL WSDL для автоматичного виявлення API SOAP-сервісу.

Висновок

Zend Framework надає повний набір інструментів для швидкого і ефективного додавання API SOAP в Web-додаток. За допомогою цього набору інструментів можна організувати економічний і ефективний обмін інформацією між Web-додатками з використанням добре відомого стандарту SOAP. Вбудована в Zend Framework підтримка SOAP-клієнтів і серверів, а також автоматичне створення WSDL робить його хорошим способом швидкої реалізації та розгортання SOAP-сервісів. І, нарешті, так як Zend Framework - це MVC-сумістне середовище, API SOAP дуже легко застосувати до існуючих додатків Zend Framework при мінімальному впливі на код (і, відповідно, з меншими переробками).

Посилання на всі приклади з цієї статті, а також на простий клієнт SOAP, який можна використовувати для тестування процедур додавання, редагування, видалення і витягання продуктів, наведені в розділі завантаження . За інструментами, використовуваними в цій статті, звертайтеся до розділу ресурси . Я рекомендую завантажити код, пограти з ним і, можливо, спробувати свої сили в додаванні до нього нових можливостей. Я гарантую вам, що ви нічого не зламаєте, і це, безумовно, буде сприяти вашому навчанню. Успіхів!

Ресурси для скачування

Схожі тими

Підпішіть мене на ПОВІДОМЛЕННЯ до коментарів

Content-Type: application / soap + xml; charset = utf-8 Content-Length: 471 <?
Quot; encoding = "UTF-8"?
Content-Length: 800 Keep -Alive: timeout = 5, max = 100 Connection: Keep-Alive Content-Type: application / soap + xml; charset = utf-8 <?
Quot; encoding = "UTF-8"?
Throw new Exception ( 'Invalid product ID:'. $ id); } Return $ result; }}?
Quot; encoding = "UTF-8"?
Quot; encoding = "UTF-8"?
А як щодо додавання і видалення даних?
Id; } Catch (SoapFault $ s) {die ( 'ERROR: ['. $ S-> faultcode. ']'. $ S-> faultstring); } Catch (Exception $ e) {die ( 'ERROR:'. $ E-> getMessage ()); }?