Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: PHP. Собственная реализация сервера XML-RPC.  (Прочитано 15013 раз)
0 Пользователей и 1 Гость смотрят эту тему.
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« : 03-05-2010 05:38 » 

Штатная реализация XML-RPC в PHP сделана с множеством ошибок, особенно в плане работы с кодировками. После написания ряда функций для понижения энтропии, понял, что штатная поддержка совершенно не нужна в таком виде. Т.к. она уже много лет пребывает в статусе экспериментальной и ошибки не исправляют, то имеет смысл написать собственную реализацию. Для работы ей требуется штатный пакет php-xml.

Выкладываю с целью поделить кодом и обсудить на предмет ошибок, недоработок и т.п. Код рассчитан на PHP 5.2.0 и выше (из-за применения функции strptime).

Код:
<?php

/*

    (c) 2010 RXL (rxl@mail.ru)

    Моя реализация сервера XML-RPC.

    Основной сервис:
        string $classesPath - Префикс пути для поиска файла класса.
        string $classPrefix - Префикс имени класса.
        string $funcPrefix - Префикс имени функции класса.
        string $charset - Кодировка для ответа. Пока это условность - перекодировка не выполняется.
        &array call(string &$xml) - Декодирует и выполняет запрос и возвращает закодированный
            ответ.

    Дополнительный сервис:
        &array parseCall(string &$xml, &$method) - Разбирает запрос,  устанавливает значение
            вызываемого метода и возвращает структуру запроса.
        &string responseXML(array &$data) - Закодировать структуру $data в ответ XML-RPC.

    Имя метода состоит из трех частей, разделенных точками. Например: "test.me.getName". Первая
        часть означает имя файла. Вторая - имя класса (к нему добавляется префикс). Третья - имя
        статического метода класса (к нему тоже добавляется префикс).
    
    Особенность работы с параметрами ответа (метод responseXML):
        Если на первом уровне присутствует элемент "faultCode" и/или "faultString", то
            создается ответ "fault", в противном случае создается "callResponse".
        Имена элементов первого уровня в результат не попадают (не предусмотрено спецификацией).
        Тип массива - массив или структура - определяется по индексам: в массиве индексы числовые
            и идут от ноля, строго в порядке возрастания.
        Принято следующее соглашение по именам элементов:
            *) Имя элемента начинается с символа "*": элемент будет закодирован в base64.
            *) Имя элемента начинается с символа "^": элемент будет закодирован как дата ISO8601,
                согласно формату strftime() "%Y-%m-%dT%H:%M%:S%z".
            *) Действие префикса в имени массива распространяется на все элементы массива (т.к. они
                не имеют своего имени).

*/

class XMLRPCServer
{
    public 
$classesPath = &#39;rpc_classes/&#39;;
    
public $classPrefix = &#39;rpc__&#39;;
    
public $funcPrefix = &#39;rpc__&#39;;
    
public $charset = &#39;utf-8&#39;;

    
private $xpath;

    public function &
responseToXML(&$resp)
    {
        if (!
is_array($resp))
            throw new 
Exception("invalid response format! Must be array.");

        
$xml = &#39;<&#39; . &#39;?xml version="1.0" encoding="&#39; . $this->charset . &#39;"?&#39; . &#39;>&#39;;

        
if (!empty($resp[&#39;faultCode&#39;]) || !empty($resp[&#39;faultString&#39;]))
        
{
            
$xml .= &#39;<methodResponse><fault><value>&#39; .
                
$this->dataToXML(
                    array(
                        &
#39;faultCode&#39; => empty($resp[&#39;faultCode&#39;]) ? -1 : $resp[&#39;faultCode&#39;],
                        
&#39;faultString&#39; => empty($resp[&#39;faultString&#39;]) ? &#39;Unknown error.&#39; : $resp[&#39;faultString&#39;],
                    
)
                ) .
                &
#39;</value></fault></methodResponse>&#39;;
        
}
        else
        {
            
$xml .= &#39;<methodResponse><params>&#39;;

            
foreach ($resp as $k => $v)
            {
                switch (
substr($k01))
                {
                    case &
#39;*&#39;:
                        
$type = &#39;base64&#39;;
                        
break;
                    case &
#39;^&#39;:
                        
$type = &#39;date&#39;;
                        
break;
                    default:
                        
$type = &#39;auto&#39;;
                        
break;
                }

                
$xml .= &#39;<param><value>&#39; . $this->dataToXML($v, $type) . &#39;</value></param>&#39;;
            
}

            
$xml .= &#39;</params><methodResponse>&#39;;
        
}

        return 
$xml;
    }

    private function &
dataToXML(&$data$type = &#39;auto&#39;)
    
{
        if (
is_array($data))
        {
            
$is_assoc false;
            
$n 0;

            foreach (
$data as $k => $v)
            {
                if (!
is_integer($k) || "$k!= "$n")
                {
                    
$is_assoc true;
                    break;
                }

                
$n++;
            }

            if (
$is_assoc)
            {
                
$xml = &#39;<struct>&#39;;

                
foreach ($data as $k => $v)
                {
                    
$name substr($k1);

                    switch (
substr($k01))
                    {
                        case &
#39;*&#39;:
                            
$type = &#39;base64&#39;;
                            
break;
                        case &
#39;^&#39;:
                            
$type = &#39;date&#39;;
                            
break;
                        default:
                            
$type = &#39;auto&#39;;
                            
$name $k;
                            break;
                    }

                    
$xml .= &#39;<member><name>&#39; . htmlspecialchars($name) . &#39;</name><value>&#39; .
                        
$this->dataToXML($v$type) . &#39;</value></member>&#39;;
                
}

                
$xml .= &#39;</struct>&#39;;
            
}
            else
            {
                
$xml = &#39;<array><data>&#39;;

                
foreach ($data as $v)
                    
$xml .= &#39;<value>&#39; . $this->dataToXML($v, $type) . &#39;</value>&#39;;

                
$xml .= &#39;</data></array>&#39;;
            
}
        }
        else
        {
            if (
$type == &#39;date&#39;)
                
$xml = &#39;<dateTime.iso8601>&#39; . strftime(&#39;%Y-%m-%dT%H:%M%:S%z&#39;, $data) . &#39;</dateTime.iso8601>&#39;;
            
elseif ($type == &#39;base64&#39;)
                
$xml = &#39;<base64>&#39; . base64_encode($data) . &#39;</base64>&#39;;
            
elseif (is_float($data))
                
$xml = &#39;<double>&#39; . htmlspecialchars($data) . &#39;</double>&#39;;
            
elseif (is_integer($data))
                
$xml = &#39;<i4>&#39; . htmlspecialchars($data) . &#39;</i4>&#39;;
            
else
                
$xml = &#39;<string>&#39; . htmlspecialchars($data) . &#39;</string>&#39;;
        
}

        return 
$xml;
    }

    public function &
parseCall(&$rawData, &$method)
    {
        
$doc DOMDocument::loadXML($rawDataLIBXML_NOBLANKS LIBXML_NOENT LIBXML_NONET);

        if (!
$doc)
            throw new 
Exception("XML parse error.");

        
$this->xpath = new DOMXPath($doc);

        
$method $this->xpath->query(&#39;/methodCall/methodName&#39;)->item(0)->nodeValue;

        
if (!$method)
            throw new 
Exception("Method parse error.");

        
$nodes $this->xpath->query(&#39;/methodCall/params/param/value/*&#39;);
        
$argv = array();

        for (
$i 0$i $nodes->length$i++)
        {
            if (
$nodes->item($i)->nodeType == XML_ELEMENT_NODE)
                
$argv[] = $this->parseData($nodes->item($i));
        }

        
$this->xpath null;
        return 
$argv;
    }

    private function &
parseData(&$node)
    {
        switch (
$node->nodeName)
        {
            case &
#39;string&#39;:
                
$value $node->nodeValue;
                break;
            case &
#39;boolean&#39;:
            
case &#39;i4&#39;:
            
case &#39;int&#39;:
                
$value = (int)$node->nodeValue;
                break;
            case &
#39;double&#39;:
                
$value = (double)$node->nodeValue;
                break;
            case &
#39;base64&#39;:
                
$value base64_decode($node->nodeValue);
                break;
            case &
#39;dateTime.iso8601&#39;:
                
$value strptime($node->nodeValue, &#39;%Y-%m-%dT%H:%M%:S%z&#39;);
                
break;
            case &
#39;array&#39;:
                
$nodes $this->xpath->query(&#39;data/value/*&#39;, $node);
                
$value = array();

                for (
$i 0$i $nodes->length$i++)
                    if (
$nodes->item($i)->nodeType == XML_ELEMENT_NODE)
                        
$value[] = $this->parseData($nodes->item($i));

                break;
            case &
#39;struct&#39;:
                
$nodes $this->xpath->query(&#39;member&#39;, $node);
                
$value = array();

                for (
$i 0$i $nodes->length$i++)
                    if (
$nodes->item($i)->nodeType == XML_ELEMENT_NODE)
                    {
                        
$name $this->xpath->query(&#39;name&#39;, $nodes->item($i))->item(0)->nodeValue;
                        
$value[$name] = $this->parseData($this->xpath->query(&#39;value/*&#39;, $nodes->item($i))->item(0));
                    
}

                break;
            default:
                throw new 
Exception(&#39;Unknow data type.&#39;);
        
}

        return 
$value;
    }

    public function &
call(&$rawData)
    {
        try
        {
            
$argv $this->parseCall($rawData$method);
            
$method_parts explode(&#39;.&#39;, $method);

            
foreach ($method_parts as $part)
                if (!
preg_match(&#39;/^\w+$/&#39;, $part))
                    
throw new Exception(&#39;Wrong method name.&#39;);

            
if (count($method_parts) != 3)
                throw new 
Exception(&#39;Wrong method name.&#39;);

            
$file $this->classesPath $method_parts[0] . &#39;.php&#39;;
            
$class $this->classPrefix $method_parts[1];
            
$func $this->funcPrefix $method_parts[2];

            if (!
file_exists($file))
                throw new 
Exception(&#39;Class file not found.&#39;);

            
eval("include_once(&#39;$file&#39;);");

            if (!
class_exists($class))
                throw new 
Exception(&#39;Class not found.&#39;);

            
$extra = array();
            
$resp call_user_func(array($class$func), $method$argv$extra);
        }
        catch (
Exception $e)
        {
            
$resp = array(
                &
#39;faultCode&#39; => $e->getCode(),
                
&#39;faultString&#39; => $e->getMessage()
            
);
        }

        return 
$this->responseToXML($resp);
    }
}

?>


Пример метода. Файл rpc_classes/test.php:

Код:
<?php

class rpc__me
{
    public static function 
rpc__getName($methodName$argv$extra)
    {
        
$id $argv[0][&#39;id&#39;];
        
return array(&#39;user_&#39; . $id);
    
}
}

?>


Код интерфейсного файла:

Код:
<?php

define 
(&#39;CHARSET&#39;, &#39;utf-8&#39;);
define (&#39;LF&#39;, "\n");

require_once(&#39;classes/xmlrpc.php&#39;);

try
{
    
$rpc = new XMLRPCServer();
    
$rpc->charset CHARSET;
    
$resp $rpc->call($HTTP_RAW_POST_DATA);

    
header (&#39;Content-Type: text/xml; charset=&#39; . CHARSET);
    
echo $respLF;
}
catch (
Exception $e)
{
    echo &
#39;Error: &#39; . $e->getMessage() . &#39; (&#39; . $e->getCode() . &#39;)&#39;, LF;
}

?>

« Последнее редактирование: 03-05-2010 20:15 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #1 : 03-05-2010 19:28 » 

RXL, спасибо! Думаю, пригодится в будущем.. Если попробую - прокомментирую..
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #2 : 03-05-2010 19:45 » 

Нашел ошибку в responseToXML: корневой элемент должен быть methodResponse, а fault вкладывается в него. Надо завтра уточнить и поправить.

И еще у меня сомнение в выборе формата даты (ISO 8601 подразумевает много вариаций) без разделителей - могут быть трудности с совместимостью. Полагаю правильным формат: "%Y-%m-%dT%H:%M%:S%z"


Поправил оба момента.
Интересный вопрос: не нашел, чем пропарсить дату, кроме как strptime(). Все таки лимит на 5.2.0 - хотелось бы снизить до 5.1.6.

И еще вопрос для обсуждения: стоит ли поддерживать расширение спецификации - <nil/>?

Собираюсь еще написать клиентскую часть на VB6.
« Последнее редактирование: 03-05-2010 20:09 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #3 : 04-05-2010 06:21 » 

RXL, клиентскую часть попробуй сделать все же на .NET: VB.NET или C#. Не знаю как в VB6, но в .NET все прокси генерятся автоматически...
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #4 : 04-05-2010 13:19 » 

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

Offtopic:
Этот VB меня доведет до ручки...  Ненавижу! Я зол!
Поставлю в угол.


А PHP часть нужно еще проверить - соответствует ли спецификации. Например, нашел такое: в "<params>" вкладывается только один "<param>" и в него один "<value>". Т.е. без применения массивов и структур - это единственное значение.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #5 : 04-05-2010 19:23 » 

Обновленный код сервера.

Код:
<?php

/*

    (c) 2010 RXL (rxl@mail.ru)

    Моя реализация сервера XML-RPC.

    Основной сервис:
        string $classesPath - Префикс пути для поиска файла класса.
        string $classPrefix - Префикс имени класса.
        string $funcPrefix - Префикс имени функции класса.
        string $charset - Кодировка для ответа. Пока это условность - перекодировка не выполняется.
        &array call(string &$xml) - Декодирует и выполняет запрос и возвращает закодированный
            ответ.

    Дополнительный сервис:
        &array parseCall(string &$xml, &$method) - Разбирает запрос,  устанавливает значение
            вызываемого метода и возвращает структуру запроса.
        &string responseXML(mixed &$data) - Закодировать структуру $data в ответ XML-RPC.

    Имя метода состоит из трех частей, разделенных точками. Например: "test.me.getName". Первая
        часть означает имя файла. Вторая - имя класса (к нему добавляется префикс). Третья - имя
        статического метода класса (к нему тоже добавляется префикс).
    Данный метод имеет следующую спецификауию:
        mixed &rpc__method(string $methodName, mixed &$args, XMLRPCServer &$xmlrpc);
  
    Особенность работы с параметрами запроса и ответа:
        Тип массива - массив или структура - определяется по индексам: в массиве индексы числовые
            и идут от ноля, строго в порядке возрастания.
        Для даты используется тип DateTime (PHP 5.2.0+). Для двоичных данных - собственный класс BinaryData.
        Если результат ответа - массив и в нем присутствует элемент "faultCode" или "faultString", то
            создается ответ "fault".

*/

class XMLRPCServer
{
    public 
$classesPath = &#39;rpc_classes/&#39;;
    
public $classPrefix = &#39;rpc__&#39;;
    
public $funcPrefix = &#39;rpc__&#39;;
    
public $charset = &#39;utf-8&#39;;

    
private $xpath;

    public function &
responseToXML(&$resp)
    {
        if (
is_array($resp) && (!empty($resp[&#39;faultCode&#39;]) || !empty($resp[&#39;faultString&#39;])))
        
{
            
$fault = array(
                &
#39;faultCode&#39; => empty($resp[&#39;faultCode&#39;]) ? -1 : $resp[&#39;faultCode&#39;],
                
&#39;faultString&#39; => empty($resp[&#39;faultString&#39;]) ? &#39;Unknown error.&#39; : $resp[&#39;faultString&#39;],
            
);
            
$xml = &#39;<fault><value>&#39; .
                
$this->dataToXML($fault) .
                &
#39;</value></fault>&#39;;
        
}
        else
        {
            
$xml = &#39;<params><param><value>&#39; .
                
$this->dataToXML($resp) .
                &
#39;</value></param></params>&#39;;
        
}

        
$xml = &#39;<&#39; . &#39;?xml version="1.0" encoding="&#39; . $this->charset . &#39;"?&#39; . &#39;>&#39; .
            
&#39;<methodResponse>&#39; . $xml . &#39;</methodResponse>&#39;;
        
return $xml;
    }

    private function &
dataToXML(&$data)
    {
        if (
is_array($data))
        {
            
$is_assoc false;
            
$n 0;

            foreach (
$data as $k => $v)
            {
                if (!
is_integer($k) || "$k!= "$n")
                {
                    
$is_assoc true;
                    break;
                }

                
$n++;
            }

            if (
$is_assoc)
            {
                
$xml = &#39;<struct>&#39;;

                
foreach ($data as $k => $v)
                    
$xml .= &#39;<member><name>&#39; . htmlspecialchars($k) . &#39;</name><value>&#39; .
                        
$this->dataToXML($v) . &#39;</value></member>&#39;;

                
$xml .= &#39;</struct>&#39;;
            
}
            else
            {
                
$xml = &#39;<array><data>&#39;;

                
foreach ($data as $v)
                    
$xml .= &#39;<value>&#39; . $this->dataToXML($v) . &#39;</value>&#39;;

                
$xml .= &#39;</data></array>&#39;;
            
}
        }
        elseif (
is_object($data))
        {
            switch (
get_class($data))
            {
                case &
#39;DateTime&#39;:
                    
$xml = &#39;<dateTime.iso8601>&#39; .
                        
htmlspecialchars($data->format(DATE_ATOM)) .
                        &
#39;</dateTime.iso8601>&#39;;
                    
break;
                case &
#39;BinaryData&#39;:
                    
$xml = &#39;<base64>&#39; . $data->getBase64() . &#39;</base64>&#39;;
                    
break;
                default:
                    throw new 
Exception("Unknown object type.");
            }
        }
        else
        {
            if (
is_float($data))
                
$xml = &#39;<double>&#39; . htmlspecialchars($data) . &#39;</double>&#39;;
            
elseif (is_integer($data))
                
$xml = &#39;<i4>&#39; . htmlspecialchars($data) . &#39;</i4>&#39;;
            
else
                
$xml = &#39;<string>&#39; . htmlspecialchars($data) . &#39;</string>&#39;;
        
}

        return 
$xml;
    }

    public function &
parseCall(&$rawData, &$method)
    {
        
$doc DOMDocument::loadXML($rawDataLIBXML_NOBLANKS LIBXML_NOENT LIBXML_NONET);

        if (!
$doc)
            throw new 
Exception("XML parse error.");

        
$this->xpath = new DOMXPath($doc);

        
$method $this->xpath->query(&#39;/methodCall/methodName&#39;)->item(0)->nodeValue;

        
if (!$method)
            throw new 
Exception("Method parse error.");

        
$argv $this->parseData($this->xpath->query(&#39;/methodCall/params/param/value/*&#39;)->item(0));

        
$this->xpath null;
        return 
$argv;
    }

    private function &
parseData(&$node)
    {
        switch (
$node->nodeName)
        {
            case &
#39;string&#39;:
                
$value $node->nodeValue;
                break;
            case &
#39;boolean&#39;:
            
case &#39;i4&#39;:
            
case &#39;int&#39;:
                
$value = (int)$node->nodeValue;
                break;
            case &
#39;double&#39;:
                
$value = (double)$node->nodeValue;
                break;
            case &
#39;base64&#39;:
                
$value = new BinaryData($node->nodeValue, &#39;base64&#39;);
                
break;
            case &
#39;dateTime.iso8601&#39;:
                
$value = new DateTime($node->nodeValue);
                break;
            case &
#39;array&#39;:
                
$nodes $this->xpath->query(&#39;data/value/*&#39;, $node);
                
$value = array();

                for (
$i 0$i $nodes->length$i++)
                    if (
$nodes->item($i)->nodeType == XML_ELEMENT_NODE)
                        
$value[] = $this->parseData($nodes->item($i));

                break;
            case &
#39;struct&#39;:
                
$nodes $this->xpath->query(&#39;member&#39;, $node);
                
$value = array();

                for (
$i 0$i $nodes->length$i++)
                    if (
$nodes->item($i)->nodeType == XML_ELEMENT_NODE)
                    {
                        
$name $this->xpath->query(&#39;name&#39;, $nodes->item($i))->item(0)->nodeValue;
                        
$value[$name] = $this->parseData($this->xpath->query(&#39;value/*&#39;, $nodes->item($i))->item(0));
                    
}

                break;
            default:
                throw new 
Exception(&#39;Unknow data type.&#39;);
        
}

        return 
$value;
    }

    public function &
call(&$rawData)
    {
        try
        {
            
$argv $this->parseCall($rawData$method);
            
$method_parts explode(&#39;.&#39;, $method);

            
foreach ($method_parts as $part)
                if (!
preg_match(&#39;/^\w+$/&#39;, $part))
                    
throw new Exception(&#39;Wrong method name.&#39;);

            
if (count($method_parts) != 3)
                throw new 
Exception(&#39;Wrong method name.&#39;);

            
$file $this->classesPath $method_parts[0] . &#39;.php&#39;;
            
$class $this->classPrefix $method_parts[1];
            
$func $this->funcPrefix $method_parts[2];

            if (!
file_exists($file))
                throw new 
Exception(&#39;Class file not found.&#39;);

            
eval("include_once(&#39;$file&#39;);");

            if (!
class_exists($class))
                throw new 
Exception(&#39;Class not found.&#39;);

            
$resp call_user_func(array($class$func), $method$argv$this);
        }
        catch (
Exception $e)
        {
            
$resp = array(
                &
#39;faultCode&#39; => $e->getCode(),
                
&#39;faultString&#39; => $e->getMessage()
            
);
        }

        return 
$this->responseToXML($resp);
    }
}

class 
BinaryData
{
    private 
$data;

    public function 
__construct(&$data$type = &#39;binary&#39;)
    
{
        switch (
$type)
        {
            case &
#39;binary&#39;:
                
$this->setBinary($data);
                break;
            case &
#39;base64&#39;:
                
$this->setBase64($data);
                break;
            default:
                throw new 
Exception(&#39;Unknown binary format.&#39;);
        
}
    }

    public function 
setBinary(&$bin)
    {
        
$this->data $bin;
    }

    public function 
setBase64(&$b64)
    {
        
$this->data base64_decode($b64);
    }

    public function 
getBinary()
    {
        return 
$data;
    }

    public function 
getBase64()
    {
        return 
base64_encode($data);
    }
}

?>


С датами справился используя класс DateTime, появившийся в PHP 5.2.0. Ранее пробовал strftime/strptime/strtotime, но это было сплошное мучение.

Код клиента на VB6 тут: https://forum.shelek.ru/index.php/topic,24339.msg234522.html#msg234522
Отлаживался с ним и с telnet.
« Последнее редактирование: 04-05-2010 19:50 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #6 : 05-05-2010 03:49 » 

Оба кода (PHP и VB) тестировал на XML-коде без форматирующих пробелов. Надо протестить с ними - вероятно трабла вылезет при разборе XML, после "<params><param><value>" и "<fault><value>" - возможно, что споткнется на текстовом элементе.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #7 : 05-05-2010 05:53 » 

Серверная часть тест на паразитные пробелы прошла успешно.

Не хватает перехвата ошибок в коде, контроля отсутствия вызываемого метода и т.п. мелочей.
« Последнее редактирование: 05-05-2010 06:57 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #8 : 05-05-2010 20:00 » new

Создал SVN-репозиторий: http://svn.shelek.su/public/XML-RPC/PHP-server/trunk/

Критика по прежнему нужна.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines