yakoffka.ru
    грабли, костыли и велосипеды php, css, html, js и прочего

    PHP 2016. Уровень 3.4: PHP + XML Web servises

    ошибки phpСервисно-ориентированная разработка программного обеспечения. конспект-памятка видеокурса 'Программирование на PHP 2016'. Лектор Борисов Игорь Олегович. Учебный Центр «Специалист» при МГТУ им. Н.Э.Баумана.

    конспектировал Капустин Яков

    оглавление

    XML Web services - программы, доступ к которым осуществляется по протоколу HTTP, а обмен данными происходит в формате XML.

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

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

    В отличие от традиционного web-приложения, у web-сервиса нет пользовательского интерфейса. Вместо этого у него есть программный интерфейс, то есть web-сервис предоставляет функции (web-методы), которые могут быть вызваны удаленно (например, по сети Internet). Web-сервис не предназначен для обслуживания конечных пользователей. Его задача - предоставление услуг другим приложениям, будь то web-приложения, приложения с графическим пользовательским интерфейсом или консольные приложения.

    Remote Procedure Call- подход, позволяющий программе вызывать процедуры из другого адресного пространства.

    Реализации RPC:

    • XML-RPC - текстовый протокол на базе HTTP (RFC-3529);
    • SOAP - текстовый протокол на базе HTTP (RFC-4227);
    • JSON-RPC - текстовый протокол на базе HTTP (RFC-4627);
    • .NET Remoting - бинарный протокол на базе TCP, UDP, HTTP
    • DCOM - MSRPC Microsoft Remote Procedure Call;
    • Java RMI.

    SOAP (от англ. Simple Object Access Protocol — простой протокол доступа к объектам) — протокол обмена структурированными сообщениями в распределённой вычислительной среде. Первоначально SOAP предназначался в основном для реализации удалённого вызова процедур (RPC). Сейчас протокол используется для обмена произвольными сообщениями в формате XML, а не только для вызова процедур. Официальная спецификация последней версии 1.2 протокола никак не расшифровывает название SOAP. SOAP является расширением протокола XML-RPC.

    SOAP может использоваться с любым протоколом прикладного уровня: SMTP, FTP, HTTP, HTTPS и др. Однако его взаимодействие с каждым из этих протоколов имеет свои особенности, которые должны быть определены отдельно. Чаще всего SOAP используется поверх HTTP.

    SOAP является одним из стандартов, на которых базируются технологии веб-служб.

    Использование SOAP для передачи сообщений увеличивает их объём и снижает скорость обработки. В системах, где скорость важна, чаще используется пересылка XML-документов через HTTP напрямую, где параметры запроса передаются как обычные HTTP-параметры.

    Для работы необходим подключенный модуль php_soap.dll. Основные SOAP классы: SoapServer, SoapClient.

    https://proselyte.net/tutorials/soap-tutorial/element-fault/.

    PHP Code:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31

      
    // клиент
      // Создание SOAP клиента
      
    $client=new SoapClient("stock.wsdl");  // Web Services Description Language

      // Вызов метода SOAP сервера
      
    $result=$client->getStock("3");
      echo 
    "Товаров на полке: " $result;

      
    // Получение информации о методах сервера
      
    var_dump($client -> __getFunctions());


      
    // сервер
      // Создание сервера
      
    $server = new SoapServer("stock.wsdl");

      
    // Добавление функции, которая будет видна клиенту
      
    $server->addFunction("getStock");

      
    // Обработка SOAP-запроса (запуск сервера)
      
    $server->handle();

      
    // Если функций больше, чем одна, то
      
    $funcs = ["getStock""setStock"];
      
    $server->addFunction($funcs);

      
    // Если служба является классом, то
      
    $server->setClass("StockService");

      

    Напишем небольшой пример создания SOAP-службы, используя процедурный стиль.

    Создание SOAP-сервера.

    Создание файла '_php34/_classes/server.php':
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    <?php
      
    // Описание функции web-сервиса
      
    function getStock($id){
        
    $stock=array(
          
    "1"=>100,
          
    "2"=>200,
          
    "3"=>300,
          
    "4"=>400,
          
    "5"=>500,
        );

        if(isset(
    $stock[$id])){
          
    $quantity=$stock[$id];
          return 
    $quantity;
        }else{
          throw new 
    SoapFault("Server","Несуществующий id товара");
          
    //return 0;
        
    }
      }

      
    // echo getStock(5)."\n<br>";
      // echo getStock(8)."\n<br>";


      // Отключение кэширования WSDL-документа на время разработки
      
    ini_set("soap.wsdl_cache_enabled","0");
      
    // Создание SOAP-сервера
      
    $server=new SoapServer("https://yakoffka.ru/src/conspects/_php34/_classes/stock.wsdl");
      
    // Добавление функции, которая будет видна клиенту
      
    $server->addFunction("getStock");
      
    // Запуск сервера
      
    $server->handle();

      
    ?>
      

    Создание файла stock.wsdl. Передираю весь код не особо заморачиваясь.

    Создание файла '_php34/_classes/stock.wsdl':
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    <?xml version ='1.0' encoding ='UTF-8' ?>
    <definitions name='Stock'
        targetNamespace='https://yakoffka.ru/soap'
        xmlns:tns='https://yakoffka.ru/soap'
        xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/'
        xmlns:xsd='http://www.w3.org/2001/XMLSchema'
        xmlns:soapenc='http://schemas.xmlsoap.org/soap/encoding/'
        xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'
        xmlns='http://schemas.xmlsoap.org/wsdl/'>

      <message name='getStockRequest'>
        <part name='id' type='xsd:string'/>
      </message>
      <message name='getStockResponse'>
        <part name='Result' type='xsd:integer'/>
      </message>

      <portType name='StockPortType'>
        <operation name='getStock'>
          <input message='tns:getStockRequest'/>
          <output message='tns:getStockResponse'/>
        </operation>
      </portType>

      <binding name='StockBinding' type='tns:StockPortType'>
        <soap:binding style='rpc' transport='http://schemas.xmlsoap.org/soap/http'/>
        <operation name='getStock' />
      </binding>

      <service name='StockService'>
        <port name='StockPort' binding='StockBinding'>
          <!--soap:address location='http://mysite.local/demo/soap/server.php'/-->
          <soap:address location='https://yakoffka.ru/src/conspects/_php34/_classes/server.php'/>
        </port>
      </service>
    </definitions>
      

    Создание SOAP-клиента.

    PHP Code:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15

      
    try{
        
    // Создание SOAP-клиента
        
    $client=new SoapClient("https://yakoffka.ru/src/conspects/_php34/_classes/stock.wsdl");
        
    // теперь операции, указанные в файле stock.wsdl становятся методами объекта $client

        // Посылка SOAP-запроса с получением результата
        
    $result=$client->getStock("3");
        echo 
    "в наличии $result шт.";
      }catch(
    SoapFault $exception){
        echo 
    $exception->getMessage();
      }

      unset(
    $server,$client,$result);
      
    Result:
    в наличии 300 шт.

    Проверка работы php-класса SoapFault.

    PHP Code:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15

      
    try{
        
    // Создание SOAP-клиента
        
    $client=new SoapClient("https://yakoffka.ru/src/conspects/_php34/_classes/stock.wsdl");
        
    // теперь операции, указанные в файле stock.wsdl становятся методами объекта $client

        // Посылка SOAP-запроса с получением результата
        
    $result=$client->getStock("7");
        echo 
    "в наличии $result шт.";
      }catch(
    SoapFault $exception){
        echo 
    $exception->getMessage();
      }

      unset(
    $server,$client,$result);
      
    Result:
    Несуществующий id товара

    Напишем небольшой пример создания SOAP-службы, используя объектный стиль. На сервере изменяется только одна строка: '$server->addFunction("getStock");' заменяется на '$server->setClass("StockService");'.

    Создание SOAP-сервера.

    Создание файла '_php34/_classes/server_o.php':
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    <?php
      
    class StockService_o{
        
    // Описание метода web-сервиса
        
    function getStock($id){
          
    $stock=array(
            
    "1"=>100,
            
    "2"=>200,
            
    "3"=>300,
            
    "4"=>400,
            
    "5"=>500,
          );

          if(isset(
    $stock[$id])){
            
    $quantity=$stock[$id];
            return 
    $quantity;
          }else{
            throw new 
    SoapFault("Server","Несуществующий id товара");
            
    //return 0;
          
    }
        }
      }

      
    // Отключение кэширования WSDL-документа на время разработки
      
    ini_set("soap.wsdl_cache_enabled","0");
      
    // Создание SOAP-сервера
      
    $server=new SoapServer("https://yakoffka.ru/src/conspects/_php34/_classes/stock_o.wsdl");

      
    // // Добавление функции, которая будет видна клиенту
      // $server->addFunction("getStock");
      // Добавление класса к SOAP-серверу
      // $server->setClass("WrongClass");// намеренная ошибка!!!!
      
    $server->setClass("StockService_o");

      
    // Запуск сервера
      
    $server->handle();

      
    ?>
      

    Создание файла stock_o.wsdl. Меняем ссылку на SOAP-сервер.

    Создание файла '_php34/_classes/stock_o.wsdl':
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <?xml version ='1.0' encoding ='UTF-8' ?>
    <definitions name='Stock'
        targetNamespace='https://yakoffka.ru/soap'
        xmlns:tns='https://yakoffka.ru/soap'
        xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/'
        xmlns:xsd='http://www.w3.org/2001/XMLSchema'
        xmlns:soapenc='http://schemas.xmlsoap.org/soap/encoding/'
        xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'
        xmlns='http://schemas.xmlsoap.org/wsdl/'>

      <message name='getStockRequest'>
        <part name='id' type='xsd:string'/>
      </message>
      <message name='getStockResponse'>
        <part name='Result' type='xsd:integer'/>
      </message>

      <portType name='StockPortType'>
        <operation name='getStock'>
          <input message='tns:getStockRequest'/>
          <output message='tns:getStockResponse'/>
        </operation>
      </portType>

      <binding name='StockBinding' type='tns:StockPortType'>
        <soap:binding style='rpc' transport='http://schemas.xmlsoap.org/soap/http'/>
        <operation name='getStock' />
      </binding>

      <service name='StockService_o'>
        <port name='StockPort' binding='StockBinding'>
          <soap:address location='https://yakoffka.ru/src/conspects/_php34/_classes/server_o.php'/><!-- меняем ссылку на SOAP-сервер -->
        </port>
      </service>
    </definitions>
      

    Создание SOAP-клиента.

    PHP Code:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15

      
    try{
        
    // Создание SOAP-клиента
        
    $client=new SoapClient("https://yakoffka.ru/src/conspects/_php34/_classes/stock_o.wsdl");
        
    // теперь операции, указанные в файле stock_o.wsdl становятся методами объекта $client

        // Посылка SOAP-запроса с получением результата
        
    $result=$client->getStock("3");
        echo 
    "в наличии $result шт.";
      }catch(
    SoapFault $exception){
        echo 
    $exception->getMessage();
      }

      unset(
    $server,$client,$result);
      
    Result:
    в наличии 300 шт.

    Получение информации о методах сервера и проверка работы php-класса SoapFault.

    PHP Code:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

      
    try{
        
    // Создание SOAP-клиента
        
    $client=new SoapClient("https://yakoffka.ru/src/conspects/_php34/_classes/stock_o.wsdl");
        
    // теперь операции, указанные в файле stock_o.wsdl становятся методами объекта $client

        // Получение информации о методах сервера
        
    $res=print_r($client -> __getFunctions(),true);
        echo 
    "<pre>$res</pre>";

        
    // Посылка SOAP-запроса с получением результата
        
    $result=$client->getStock("7");
        echo 
    "в наличии $result шт.";
      }catch(
    SoapFault $exception){
        echo 
    $exception->getMessage();
      }

      unset(
    $server,$client,$result);
      
    Result:
    Array
    (
        [0] => integer getStock(string $id)
    )
    
    Несуществующий id товара
    Упражнение 1: Создание SOAP-сервера
    • В текстовом редакторе откройте файл soap\soap-server.php
    • Ознакомьтесь с содержимым службы NewsService
    • В нижней части файла после описания класса введите следующий текст:
        // Отключение кеширования wsdl-документа
        ini_set("soap.wsdl_cache_enabled", "0");
        // Создание SOAP-сервера
        $server = new SoapServer("http://mysite.local/soap/news.wsdl");
        // Регистрация класса
        $server->setClass("NewsService");
        // Запуск сервера
        $server->handle();
      		
    • Сохраните файл soap\soap-server.php
    Упражнение 2: Создание SOAP-клиента
    • В текстовом редакторе откройте файл soap\soap-client.php
    • Пересохраните этот файл как C:\Users\Public\OpenServer\domains\localhost\soap-client.php
    • В файле введите следующий текст:
        $client = new SoapClient("http://mysite.local/soap/news.wsdl");
        try{
          // Сколько новостей в категории Политика?
          $result = $client->getNewsCountByCat(1);
          echo "<p>Всего новостей в категории Политика:
          // Сколько новостей всего?<br>
          $result = $client->getNewsCount();
          echo "<p>Всего новостей: $result</p>";
          $result</p>";
          // Покажем конкретную новость
          $result = $client->getNewsById(1);
          $news = unserialize(base64_decode($result));
          var_dump($news);
        }catch(SoapFault $e){
          echo 'Операция '.$e->faultcode.' вернула ошибку: '.$e->getMessage();
        }
      		
    • Сохраните файл soap-client.php
    Упражнение 3: Тестирование сервиса
  • Запустите браузер
  • Наберите в адресной строке браузера http://localhost/soap-client.php
  • Убедитесь, что данные выводятся корректно
  • Если есть ошибки, найдите их и исправьте
  • Попробуйте допустить намеренную ошибку в файле soap\soap-server.php, например, укажите в SQL-запросе несуществующую таблицу. Какие данные при этом будут выведены в браузер?
  • Подготовительные операции: копирование файлов новостной ленты, не требующих изменения и бездумное копирование news.wsdl - заменяем лишь адрес сервера.

    копирование файлов новостной ленты, не требующих изменения:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

      $res
    ="";

      
    $arr_cf=array(
        
    "/_classes/INewsDB.class.php",
        
    "/_classes/NewsDB.class.php",
      );

      
    // проверка существования директории "/full/path/to/_php34_classes/"
      
    $pd="/full/path/to/_php34/_classes";
      if(!
    is_dir($pd)){if(!mkdir($pd,0777,true)){$res.="Не удалось создать директорию $pd.<br>\n";}}

      foreach(
    $arr_cf as $nf){
        
    $source="/full/path/to/_php32$nf";
        
    $dest="/full/path/to/_php34$nf";
        if(
    is_file($source)){
          if(
    copy($source,$dest)){
            
    $res.="Файл $nf скопирован успешно.<br>\n";
          }else{
    $res.="Не удалось скопировать файл $nf.<br>\n";}
        }else{
    $res.="Файл $nf не найден.<br>\n";}
      }

      echo 
    $res;
      
    Result:
    Файл /_classes/INewsDB.class.php скопирован успешно.
    Файл /_classes/NewsDB.class.php скопирован успешно.
    Создание файла '_php34/_classes/news.wsdl' (Бездумное копирование. Заменяем лишь адрес сервера.):
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    <?xml version ="1.0" encoding ="UTF-8" ?>
    <definitions name="News"
        targetNamespace='https://yakoffka.ru/news'
        xmlns:tns='https://yakoffka.ru/news'
        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
        xmlns="http://schemas.xmlsoap.org/wsdl/">

      <message name="getNewsByIdRequest">
        <part name="id" type="xsd:integer"/>
      </message>
      <message name="getNewsByIdResponse">
        <part name="item" type="xsd:base64Binary"/>
      </message>

      <message name="getNewsCountResponse">
        <part name="count" type="xsd:integer"/>
      </message>

      <message name="getNewsCountByCatRequest">
        <part name="cat_id" type="xsd:integer"/>
      </message>
      <message name="getNewsCountByCatResponse">
        <part name="count" type="xsd:integer"/>
      </message>


      <portType name="NewsPortType">
        <operation name="getNewsById">
          <input message="tns:getNewsByIdRequest"/>
          <output message="tns:getNewsByIdResponse"/>
        </operation>
        <operation name="getNewsCount">
          <output message="tns:getNewsCountResponse"/>
        </operation>
        <operation name="getNewsCountByCat">
          <input message="tns:getNewsCountByCatRequest"/>
          <output message="tns:getNewsCountByCatResponse"/>
        </operation>
      </portType>

      <binding name="NewsBinding" type="tns:NewsPortType">
        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="getNewsById" />
        <operation name="getNewsCount" />
        <operation name="getNewsCountByCat" />
      </binding>

      <service name="NewsService">
        <port name="NewsPort" binding="NewsBinding">
          <soap:address location='https://yakoffka.ru/src/conspects/_php34/_classes/soap-server.php'/>
        </port>
      </service>
    </definitions>
      

    Создание SOAP-сервера: файл soap-server.php.

    Создание файла '_php34/_classes/soap-server.php':
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    <?php
      
    // реализуем метод автозагрузки
      
    spl_autoload_register(function($class){
        include(
    "/full/path/to/_php34/_classes/$class.class.php");
      });
      
    // require "/full/path/to/_php34/_classes/NewsDB.class.php";

      
    class NewsService extends NewsDB{
        
    /* Метод возвращает новость по её идентификатору */
        
    function getNewsById($id){
          try{
            
    $sql "SELECT id, title,
                (SELECT name FROM category WHERE category.id=msgs.category) as category, description, source, datetime
                FROM msgs
                WHERE id = 
    $id";
            
    $result $this->_db->query($sql);
            if (!
    is_object($result))
              throw new 
    Exception($this->_db->lastErrorMsg());
            return 
    base64_encode(serialize($this->db2arr($result)));
          }catch(
    Exception $e){
            throw new 
    SoapFault('getNewsById'$e->getMessage());
          }
        }
        
    /* Метод считает количество всех новостей */
        
    function getNewsCount(){
          try{
            
    $sql "SELECT count(*) FROM msgs";
            
    $result $this->_db->querySingle($sql);
            if (!
    $result)
              throw new 
    Exception($this->_db->lastErrorMsg());
            return 
    $result;
          }catch(
    Exception $e){
            throw new 
    SoapFault('getNewsCount'$e->getMessage());
          }
        }
        
    /* Метод считает количество новостей в указанной категории */
        
    function getNewsCountByCat($cat_id){
          try{
            
    $sql "SELECT count(*) FROM msgs WHERE category=$cat_id";
            
    $result $this->_db->querySingle($sql);
            if (!
    $result)
              throw new 
    Exception($this->_db->lastErrorMsg());
            return 
    $result;
          }catch(
    Exception $e){
            throw new 
    SoapFault('getNewsCountByCat'$e->getMessage());
          }
        }
      }

      
    // Отключение кеширования wsdl-документа
      
    ini_set("soap.wsdl_cache_enabled""0");

      
    // Создание SOAP-сервера
      
    $server = new SoapServer("https://yakoffka.ru/src/conspects/_php34/_classes/news.wsdl");

      
    // Регистрация класса
      
    $server->setClass("NewsService");
      
    // Запуск сервера
      
    $server->handle();
    ?>
      

    Создание SOAP-клиента: файл laboratory_3.4.1.php.

    Содержимое файла 'laboratory_3.4.1.php' '/full/path/to/laboratory_3.4.1.php':
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50

      
    // // реализуем метод автозагрузки
      // spl_autoload_register(function($class){
      //   include "/full/path/to/_php34/_classes/$class.class.php");
      // });

      
    $errMsg="";
      
    $res="";

      
    $client = new SoapClient("https://yakoffka.ru/src/conspects/_php34/_classes/news.wsdl");

      try{
        
    // Получение информации о методах сервера
        
    $print_r=print_r($client -> __getFunctions(),true);
        
    $res.="<pre>$print_r</pre>";

        
    // // Вызов несуществующего метода<br>
        // $result = $client->wrongMethod();
        // $res.="<p>Как тебе такое, Илон?</p>";
        // // Операция Client вернула ошибку: Function ("wrongMethod") is not a valid method for this service

        // Сколько новостей всего?<br>
        
    $result $client->getNewsCount();
        
    $res.="<p>Всего новостей: $result</p>";

        
    // Сколько новостей в категории Политика?
        
    $result $client->getNewsCountByCat(1);
        
    $res.="<p>Всего новостей в категории Политика: $result</p>";

        
    // Покажем конкретную новость
        
    $result $client->getNewsById(48);
        
    $news unserialize(base64_decode($result));
        
    $res.=print_r($news,true);

      }catch(
    SoapFault $e){
        
    $errMsg.='Операция '.$e->faultcode.' вернула ошибку: '.$e->getMessage().';';
      }

      unset(
    $server,$client,$result);
      
        
    $mc.="
          <?php
            // Вывод результата
            echo \"<p class='err_mess'>\$errMsg</p>\";
            // Вывод результата
            echo \$res;
            // phpinfo();
          ?>
        "
    ;
      

    laboratory_3.4.1.php.

    XML-RPC запрос:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

      
    <methodCall>
        <
    methodName>getStock</methodName>
        <
    params>
          <
    param>
            <
    value><i4>3</i4></value>
          </
    param>
        </
    params>
      </
    methodCall>
      
    XML-RPC ответ:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12

      
    <methodResponse>
        <
    params>
          <
    param>
            <
    value><i4>300</i4></value>
          </
    param>
          <
    param>
            <
    value><string>Sony Vayo</string></value>
          </
    param>
        </
    params>
      </
    methodResponse>
      
    Создание XML-RPC сервера:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

      
    // Описание службы
      
    $stock = [
        
    "a"=>100,
        
    "b"=>200,
        
    "c"=>300,
        
    "d"=>400,
        
    "e"=>500
      
    ];

      function 
    get_stock($methodName$arguments$extra){
        global 
    $stock;
        
    $code $arguments[0];
        if(
    is_set($stock[$code])){
          return 
    $stock[$code];
        }else{
          return [
    "faultCode" => 1"faultString" => "Нет такой полки"];
        }
      }

      
    // Создание сервера
      
    $server xmlrpc_server_create();

      
    // Добавление функции, которая будет видна клиенту
      
    xmlrpc_server_register_method($server"getStock""get_stock");

      
    // Приём запроса
      
    $request file_get_contents("php://input");

      
    // Обработка запроса
      
    echo xmlrpc_server_call_method($server$requestnull);
      
    Создание XML-RPC клиента:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15

      
    // Создание запроса
      
    $server xmlrpc_encode_request("getStock""b");

      
    // XML-RPC запрос и получение ответа
      
    $response запрос_любым_способом_методом_POST("URL");

      
    // Декодирование ответа
      
    $result xmlrpc_decode("URL");
      if(
    xmlrpc_is_fault($result)){
        echo 
    $result["faultString"];
      }else{
        echo 
    $result;
      }
      

    Контекст - это набор параметров и зависящих от контекста опций, который изменяет или расширяет поведение потока. Контексты создаются при помощи функции stream_context_create() и могут быть переданы большинству функций файловой системы, связанных с созданием потоков (например, fopen(), file(), file_get_contents(), и т. д...).

    Контекст потока:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

      
    // Формирование необходимых данных
      
    $options = [
        
    "http"=> [  // указание протокола
          
    "method" => "GET",
          
    "header" => "User-Agent: PHPBot\r\n".
          
    "Cookie: user=John\r\n"
        
    ]
      ];

      
    // Создание контекста потока
      
    $context stream_context_create($options);

      
    // Запрос с использованием созданного контекста потока
      
    echo file_get_contents("http://mysite.local/xml-rpc/server.php"false$context);

      
    // Получение ответа при использовании fopen()
      
    $f fopen("http://mysite.local/xml-rpc/server.php""r"false$context);
      echo 
    stream_get_contents($f);

      
    // Получение заголовков ответа
      
    print_r(stream_get_meta_data($f));
      
    Упражнение 1: Создание XML-RPC сервера
    • В текстовом редакторе откройте файл xml-rpc\xml-rpc-server.php
    • Ознакомьтесь с содержимым службы NewsService
    • В нижней части файла после описания класса введите следующий текст:
        /* Читаем запрос */
        $request = file_get_contents("php://input");
        /* Создаем XML-RPC сервер */
        $server = xmlrpc_server_create();
        /* Регистрируем метод класса */
        xmlrpc_server_register_method($server, "getNewsById", [new
        NewsService, "xmlRpcGetNewsById"]);
        /*Отдаем правильный заголовок*/
        header('Content-Type: text/xml;charset=utf-8');
        /* Отдаем результат */
        print xmlrpc_server_call_method($server, $request, null);
      		
    • Сохраните файл xml-rpc\xml-rpc-server.php
    Упражнение 2: Создание XML-RPC-клиента
    • В текстовом редакторе откройте файл xml-rpc\xml-rpc-client.php
    • Пересохраните этот файл как C:\Users\Public\OpenServer\domains\localhost\xml-rpc-client.php
    • В файле введите следующий текст:
        header('Content-Type: text/html;charset=utf-8');
        /* Сюда приходят данные с сервера */
        $output = [];
        /* Основная функция */
        function make_request($xml, &$output){
          /* НАЧАЛО ЗАПРОСА */
          $options = [
            'http'=>[
              'method' => "POST",
              'header' => "User-Agent: PHPRPC/1.0\r\n" .
              "Content-Type: text/xml\r\n" .
              "Content-length: " . strlen($xml) . "\r\n",
              'content' => "$xml"
            ]
          ];
          $context = stream_context_create($options);
          $retval = file_get_contents('http://mysite.local/xml-rpc/xml-rpc-server.php', false, $context);
          /* КОНЕЦ ЗАПРОСА */
      
          $data = xmlrpc_decode($retval);
          if (is_array($data) && xmlrpc_is_fault($data)){
          $output = $data;
          }else{
          $output = unserialize(base64_decode($data));
          }
        }
      
        /* Идентификатор статьи */
        $id = 1;
        $request_xml = xmlrpc_encode_request('getNewsById',array($id));make_request($request_xml, $output);
      
        /* Вывод результата */
        var_dump($output);
      		
    • Сохраните файл xml-rpc-client.php
    Упражнение 3: Тестирование сервиса
  • Запустите браузер
  • Наберите в адресной строке браузера http://localhost/xml-rpc-client.php
  • Убедитесь, что данные выводятся корректно
  • Если есть ошибки, найдите их и исправьте
  • Попробуйте допустить намеренную ошибку в файле xml-rpc\xml-rpc-server.php, например, укажите в SQL-запросе несуществующую таблицу. Какие данные при этом будут выведены в браузер?
  • Создание файла '_php34/_classes/xml-rpc-server.php':
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    <?php
      error_reporting
    (0);

      
    // реализуем метод автозагрузки
      
    spl_autoload_register(function($class){
        include(
    "/full/path/to/_php34/_classes/$class.class.php");
      });

      class 
    NewsServiceRPC extends NewsDB{
        
    /* Метод возвращает новость по её идентификатору */
        
    function getNewsById($id){
          try{
            
    $sql "SELECT id, title,
                (SELECT name FROM category WHERE category.id=msgs.category) as category, description, source, datetime
                FROM msgs
                WHERE id = 
    $id";
            
    $result $this->_db->query($sql);
            
    //var_dump($result);
            
    if (!is_object($result))
              throw new 
    Exception($this->_db->lastErrorMsg());
            return 
    $this->db2arr($result);
          }catch(
    Exception $e){
            return 
    $e->getMessage();
          }
        }

        function 
    xmlRpcGetNewsById($method_name$args$extra) {
          if (!
    is_array($args) || count($args) <> 1){
            return array(
    'faultCode'=>-3'faultString'=>'Неверное количество параметров!');
          }
          
    $id $args[0];
          
    $result $this->getNewsById($id);
          if(!
    is_array($result)){
            return array(
    'faultCode'=>-2'faultString'=>"Ошибка: $result!");
          }elseif(empty(
    $result)){
            return array(
    'faultCode'=>-1'faultString'=>"Новость с идентификатором $id отсутствует!");
          }else{
            return 
    base64_encode(serialize($result));
          }
        }
      }


      
    /* Читаем запрос */
      
    $request file_get_contents("php://input");

      
    /* Создаем XML-RPC сервер */
      
    $server xmlrpc_server_create();

      
    /* Регистрируем метод класса */
      
    xmlrpc_server_register_method($server"getNewsById", [new NewsServiceRPC"xmlRpcGetNewsById"]);
      
    /*Отдаем правильный заголовок*/
      
    header('Content-Type: text/xml;charset=utf-8');

      
    /* Отдаем результат */
      
    print xmlrpc_server_call_method($server$requestnull);


    ?>
      
    Вызов XML-RPC-клиента:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35

      
    /* Сюда приходят данные с сервера */
      
    $output = [];
      
    /* Основная функция */
      
    function make_request($xml, &$output){
        
    /* НАЧАЛО ЗАПРОСА */
        
    $options = [
          
    'http'=>[
            
    'method' => "POST",
            
    'header' => "User-Agent: PHPRPC/1.0\r\n" .
            
    "Content-Type: text/xml\r\n" .
            
    "Content-length: " strlen($xml) . "\r\n",
            
    'content' => "$xml"
          
    ]
        ];
        
    $context stream_context_create($options);
        
    $retval file_get_contents('https://yakoffka.ru/src/conspects/_php34/_classes/xml-rpc-server.php'false$context);
        
    /* КОНЕЦ ЗАПРОСА */

        
    $data xmlrpc_decode($retval);
        if (
    is_array($data) && xmlrpc_is_fault($data)){
        
    $output $data;
        }else{
        
    $output unserialize(base64_decode($data));
        }
      }

      
    /* Идентификатор статьи */
      
    $id 30;
      
    $request_xml xmlrpc_encode_request('getNewsById',array($id));make_request($request_xml$output);

      
    /* Вывод результата */
      
    echo "Идентификатор статьи id = $id;<br>";
      
    var_dump($output);
      
    Result:
    Идентификатор статьи id = 30;
    array(2) { ["faultCode"]=> int(-1) ["faultString"]=> string(75) "Новость с идентификатором 30 отсутствует!" }

    Создание XML-RPC-клиента: файл laboratory_3.4.2.php.

    Содержимое файла 'laboratory_3.4.2.php' '/full/path/to/laboratory_3.4.2.php':
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41

      header
    ('Content-Type: text/html;charset=utf-8');
      
    /* Сюда приходят данные с сервера */
      
    $output = [];
      
    /* Основная функция */
      
    function make_request($xml, &$output){
        
    /* НАЧАЛО ЗАПРОСА */
        
    $options = [
          
    'http'=>[
            
    'method' => "POST",
            
    'header' => "User-Agent: PHPRPC/1.0\r\n" .
            
    "Content-Type: text/xml\r\n" .
            
    "Content-length: " strlen($xml) . "\r\n",
            
    'content' => "$xml"
          
    ]
        ];
        
    $context stream_context_create($options);
        
    $retval file_get_contents('https://yakoffka.ru/src/conspects/_php34/_classes/xml-rpc-server.php'false$context);
        
    /* КОНЕЦ ЗАПРОСА */

        
    $data xmlrpc_decode($retval);
        if (
    is_array($data) && xmlrpc_is_fault($data)){
        
    $output $data;
        }else{
        
    $output unserialize(base64_decode($data));
        }
      }
      
        
    $mc.="
          <?php
            /* Идентификатор статьи */
            \$id = 30;
            \$request_xml = xmlrpc_encode_request('getNewsById',array(\$id));make_request(\$request_xml, \$output);

            /* Вывод результата */
            echo \"Идентификатор статьи id = \$id;<br>\";
            var_dump(\$output);

          ?>
        "
    ;
      

    laboratory_3.4.2.php.