[Из песочницы] Конструктор XML-строки из PHP-массива

habr.png

Как часто приходится работать с XML PHP-разработчикам? Не так часто, на самом деле. Обычно потребность возникает при интеграции со сторонним сервисом, т.к. BetaPRO, OnTime или CDEK. И вот тут обычно возникает такая ситуация, когда ваш код становится похожим на


$date = '2016-09-25T12:45:10';
$account = 'f62dcb094cc91617def72d9c260b4483';
$secure = '81ad561784277fa864bf644d755fb164';
$count = 1;
$copy = 4;
$dispatchNumber = '1033229706';
$orderDate = '2016-09-25T12:45:10';

$request = << 

    

XML;


и это еще не все! Нужно позаботится о том, чтобы значения атрибутов и содержимое заключенное в теги не содержало спецсимволов присущие XML. Если для конкретно этого запроса можно быть уверенным, что ничего из спецсимволов сюда не попадет, то контролировать каждый запрос вовсе бы не хотелось. Поэтому через «фильтр» пропускается все. Отсюда следует, что нужно еще «загнаться» с htmlspecialchars или с CDATA, или с XMLWriter, и знать, как это применить и не раз еще «свернуть себе кровь». Как вы видите, времени стоит «убить» достаточно, а результат-то хочется уже сейчас. Эх… А как хотелось бы, чтобы XML можно было бы создавать так же быстро, как JSON: отдал массив, а тебе XML-строку, и никаких заморочек. Опечалившись сложившейся ситуацией я в далеком 2015ом году я решил сделать такой конструктор.


Вашему вниманию представляю xml-constructor для PHP начиная с версии 5.4 и до 7.2 на момент публикации данной статьи.


Использование


Для начала использования установим данный пакет через Composer:


$ composer require bupy7/xml-constructor


Его так же можно просто скопировать вручную куда вы хотите, т.к. пакет не имеет никаких доп.зависимостей, кроме, как наличия libxml в самом PHP.


Теперь создадим XML-строку используя PHP-массив:


$date = '2016-09-25T12:45:10';
$account = 'f62dcb094cc91617def72d9c260b4483';
$secure = '81ad561784277fa864bf644d755fb164';
$count = 1;
$copy = 4;
$dispatchNumber = '1033229706';
$orderDate = '2016-09-25T12:45:10';

$in = [
    [
        'tag' => 'OrdersPrint',
        'attributes' => [
            'Date' => $date,
            'Account' => $account,
            'Secure' => $secure,
            'OrderCount' => $count,
            'CopyCount' => $copy,
        ],
        'elements' => [
            [
                'tag' => 'Order',
                'attributes' => [
                    'DispathNumber' => $dispatchNumber,
                    'Date' => $orderDate,
                ],
            ],
        ],
    ],
];
$request = (new \bupy7\xml\constructor\XmlConstructor())->fromArray($in)->toOutput();


Результат:




    


Вот и вся работа! Об остальном позаботится xml-constructor.


И давайте попробуем передать что-то «запрещенное» в значения и посмотрим, как будет вести себя xml-constructor:


$date = '2016-09-25T12:45:10';
$secure = '81ad561784277fa864bf644d755fb164';
$count = 1;
$copy = 4;
$dispatchNumber = '1033229706';
$orderDate = '2016-09-25T12:45:10';

// ACHTUNG !!!
$account = '';
$orderContent = '"chars"';

$in = [
    [
        'tag' => 'OrdersPrint',
        'attributes' => [
            'Date' => $date,
            'Account' => $account,
            'Secure' => $secure,
            'OrderCount' => $count,
            'CopyCount' => $copy,
        ],
        'elements' => [
            [
                'tag' => 'Order',
                'attributes' => [
                    'DispathNumber' => $dispatchNumber,
                    'Date' => $orderDate,
                ],
                'content' => $orderContent,
            ],
        ],
    ],   
];

$request = (new \bupy7\xml\constructor\XmlConstructor())->fromArray($in)->toOutput();


Результат:




    <special>"chars"


Требования


Создание XML-строки сводится к тому, что нужно передать PHP-массив с нужными ключами и в правильной структуре. Ключей всего четыре:


  • tag — строка с названием тега;
  • content — содержимое заключенное между тегом;
  • attributes — массив ключ-значение, где ключ — это, название атрибута (строка), а значение — его значение (строка);
  • elements — новая вложенность тегов внутри которого этот ключ был указан. Также будет содержать в себе все перечисленные выше элементы. Вложенность неограниченная.


Каждый элемент массива должен содержать массив с одним ключом tag, как минимум. Ключи attributes, content и elements необязательные.


Первый уровень вложенности есть ничто иное, как корни XML-документа, т.е.:


$in = [
    [
        'tag' => 'FirstRoot',
    ],
    [
        'tag' => 'SecondRoot',
        'content' => 'Content of SecondRoot',
    ],
];

$request = (new \bupy7\xml\constructor\XmlConstructor())->fromArray($in)->toOutput();


Результат:




Content of SecondRoot


Конфигурация


Из конфигурации все только самое необходимое.


  • indentString — произвольная строка для отступов. По умолчанию 4 пробела. Если не хотите использовать отступы вообще — передайте false.
  • startDocument — массив ключ-значение с атрибутами XML декларации документа. По умолчанию это . Если вам не нужна декларация — передайте false.


Для применения конфигурации нужно передать массив ключ-значение в конструктор первым аргументом:


$date = '2016-09-25T12:45:10';
$account = 'f62dcb094cc91617def72d9c260b4483';
$secure = '81ad561784277fa864bf644d755fb164';
$count = 1;
$copy = 4;
$dispatchNumber = '1033229706';
$orderDate = '2016-09-25T12:45:10';

$in = [
    [
        'tag' => 'OrdersPrint',
        'attributes' => [
            'Date' => $date,
            'Account' => $account,
            'Secure' => $secure,
            'OrderCount' => $count,
            'CopyCount' => $copy,
        ],
        'elements' => [
            [
                'tag' => 'Order',
                'attributes' => [
                    'DispathNumber' => $dispatchNumber,
                    'Date' => $orderDate,
                ],
            ],
        ],
    ],
];
$request = (new \bupy7\xml\constructor\XmlConstructor([
        'indentString' => '****',
        'startDocument' => false,
    ]))
    ->fromArray($in)
    ->toOutput();


Результат:



Заключение


Расширение очень простое и привносит массу удобств во время интеграции с сервисами использующих XML для своего API. Стоит ли использовать xml-constructor — решать только вам.


Спасибо за потраченное время на прочтение!


Ссылки


  • xml-constructor
  • Composer
  • libxml
  • Мой Github

© Habrahabr.ru