Веб-сервіси =========== [Веб-сервіс](http://uk.wikipedia.org/wiki/Веб-служба) — програмна система, розроблена для забезпечення взаємодії між декількома компʼютерами через мережу. У веб-додатку це зазвичай набір API, який можна використовувати через інтернет для виконання дій на віддаленому сервері, обслуговуючому веб-сервіс. Наприклад, клієнт, заснований на [Flex](http://www.adobe.com/products/flex/), може викликати функції, реалізовані на сервері у PHP-додатку. У якості базового рівня протоколу використовується [SOAP](http://uk.wikipedia.org/wiki/SOAP). Для того, щоб спростити завдання створення веб-сервісу, в Yii включені [CWebService] та [CWebServiceAction]. API згруповані по класах, які називаються *провайдерами*. Для кожного класу Yii генерує [WSDL](http://www.w3.org/TR/wsdl), що описує функціонал наданого API і правила його використання клієнтом. При обробці виклику клієнта, Yii створює відповідний йому примірник провайдера, викликає метод API і відповідає на запит. > Note|Примітка: Для роботи [CWebService] потрібно [розширення PHP SOAP](http://php.net/manual/en/ref.soap.php). Переконайтеся, що воно включено, перш, ніж пробувати приклади, описані далі. Створення провайдера -------------------- Як вже було описано, провайдер — це клас, який реалізує методи, які можуть бути викликані віддалено. Для того, щоб визначити, які методи можуть бути викликані віддалено і яке значення повертати, Yii використовує [спеціальні коментарі](http://java.sun.com/j2se/javadoc/writingdoccomments/) та [reflection](http://php.net/manual/en/book.reflection.php). Спробуємо реалізувати простий сервіс, що віддає інформацію про котирування акцій певної компанії. Для цього нам потрібно реалізувати провайдер, як показано нижче. Варто зазначити, що успадковуємо клас провайдера `StockController` від [CController]. Спадкування не є обовʼязковим. ~~~ [php] class StockController extends CController { /** * @param string індекс підприємства * @return float ціна * @soap */ public function getPrice($symbol) { $prices=array('IBM'=>100, 'GOOGLE'=>350); return isset($prices[$symbol])?$prices[$symbol]:0; //…повертаємо ціну для компанії з індексом $symbol } } ~~~ Вище ми описали, що метод `getPrice` є частиною API веб-сервісу, позначивши його у коментарі тегом `@soap`. Там же ми описали типи параметрів і значення, що повертається. Додаткові методи API можуть бути описані точно таким же чином. Реалізація дії веб-сервісу -------------------------- Після створення провайдера необхідно зробити його доступним для клієнтів. Для цього необхідно описати дію контролера [CWebServiceAction]. У нашому прикладі ми використовуємо `StockController`: ~~~ [php] class StockController extends CController { public function actions() { return array( 'quote'=>array( 'class'=>'CWebServiceAction', ), ); } /** * @param string індекс підприємства * @return float ціна * @soap */ public function getPrice($symbol) { //…повертаємо ціну для компанії з індексом $symbol } } ~~~ Це все, що потрібно для створення веб-сервісу. Тепер при зверненні до URL `http://hostname/path/to/index.php?r=stock/quote`, ми отримаємо обʼємний XML, насправді є WSDL описаного нами веб-сервісу. > Tip|Підказка: За замовчуванням, при використанні [CWebServiceAction] мається на увазі, що поточний контролер є провайдером. Саме тому ми визначили метод `getPrice` у класі `StockController`. Використання веб-сервісу ------------------------ Для того, щоб наш приклад був повним, створимо клієнт, який використовує веб-сервіс, який ми тільки що створили. У прикладі клієнт буде написаний на PHP, але для його реалізації можна використовувати і інші мови, такі як `Java`, `C#`, `Flex` і т.д. ~~~ [php] $client=new SoapClient('http://hostname/path/to/index.php?r=stock/quote'); echo $client->getPrice('GOOGLE'); ~~~ Запустивши даний скрипт через браузер або у консолі, ви повинні отримати `350`, що відповідає ціні акцій `GOOGLE`. Типи даних ---------- При описі методів і властивостей класу, які повинні бути доступні через веб-сервіс, нам необхідно визначити типи параметрів і значень. Для цього можуть бути використані наступні типи: - str/string: відповідає `xsd:string`; - int/integer: відповідає `xsd:int`; - float/double: відповідає `xsd:float`; - bool/boolean: відповідає `xsd:boolean`; - date: відповідає `xsd:date`; - time: відповідає `xsd:time`; - datetime: відповідає `xsd:dateTime`; - array: відповідає `xsd:string`; - object: відповідає `xsd:struct`; - mixed: відповідає `xsd:anyType`. Якщо тип не є одним із наведених вище, він сприймається як складений тип, що складається із властивостей. Цей тип відповідає класу, а його властивості — public-змінним класу, зазначених у коментарях `@soap`. Також можна використовувати масиви. Для цього необхідно дописати `[]` у кінець примітивного або складеного типу. Таким чином ми отримаємо масив з елементами заданого типу. Нижче наведено приклад визначення методу API `getPosts`, що повертає масив обʼєктів класу `Post`. ~~~ [php] class PostController extends CController { /** * @return Post[] список записів * @soap */ public function getPosts() { return Post::model()->findAll(); } } class Post extends CActiveRecord { /** * @var integer ID запису * @soap */ public $id; /** * @var string заголовок запису * @soap */ public $title; public static function model($className=__CLASS__) { return parent::model($className); } } ~~~ Зіставлення класів ------------------ Для отримання від клієнта параметрів складеного типу, у додатку повинні бути задані відповідності типів WSDL класам PHP. Для цього необхідно налаштувати властивість [classMap|CWebServiceAction::classMap] класу [CWebServiceAction]. ~~~ [php] class PostController extends CController { public function actions() { return array( 'service'=>array( 'class'=>'CWebServiceAction', 'classMap'=>array( 'Post'=>'Post', // або просто 'Post' ), ), ); } … } ~~~ Перехоплення віддаленого виклику методу --------------------------------------- Реалізуючи інтерфейс [IWebServiceProvider], провайдер може перехоплювати віддалені виклики методів. Використовуючи [IWebServiceProvider::beforeWebMethod], можна отримати поточний екземпляр [CWebService]. Через [CWebService::methodName] — назву метода, що викликається. Якщо метод з якихось причин (наприклад, відсутність прав на його виконання) не повинен бути викликаний, необхідно повернути false.