MidТеория6 min

JSON и XML

json_encode/decode, SimpleXML, DOM, парсинг

JSON и XML в PHP

JSON (JavaScript Object Notation)

json_encode — кодирование в JSON

<?php
declare(strict_types=1);

// Basic encoding
$data = ['name' => 'Alice', 'age' => 25, 'active' => true];
$json = json_encode($data);
echo $json;  // {"name":"Alice","age":25,"active":true}

// Pretty print
$json = json_encode($data, JSON_PRETTY_PRINT);
/*
{
    "name": "Alice",
    "age": 25,
    "active": true
}
*/

// Common flags
$json = json_encode($data,
    JSON_PRETTY_PRINT |        // Human-readable formatting
    JSON_UNESCAPED_UNICODE |   // Don't escape Unicode characters
    JSON_UNESCAPED_SLASHES |   // Don't escape forward slashes
    JSON_THROW_ON_ERROR        // Throw exception instead of returning false
);

// Encoding various types
echo json_encode(42);            // 42
echo json_encode('hello');       // "hello"
echo json_encode(true);          // true
echo json_encode(null);          // null
echo json_encode([1, 2, 3]);    // [1,2,3]

Тонкости json_encode

<?php
declare(strict_types=1);

// Sequential array => JSON array
$seq = [0 => 'a', 1 => 'b', 2 => 'c'];
echo json_encode($seq);  // ["a","b","c"]

// Non-sequential array => JSON object!
$nonSeq = [0 => 'a', 2 => 'b', 5 => 'c'];
echo json_encode($nonSeq);  // {"0":"a","2":"b","5":"c"}

// Force array to be JSON object
$seq = ['a', 'b', 'c'];
echo json_encode($seq, JSON_FORCE_OBJECT);  // {"0":"a","1":"b","2":"c"}

// Empty array vs empty object
echo json_encode([]);                    // []
echo json_encode(new stdClass());        // {}
echo json_encode([], JSON_FORCE_OBJECT); // {}

// NAN and INF
echo json_encode(NAN);   // false (encoding fails!)
echo json_encode(INF);   // false (encoding fails!)

// json_encode returns false on failure
$result = json_encode("\xB1\x31");  // Invalid UTF-8
var_dump($result);                   // false
echo json_last_error_msg();          // Malformed UTF-8 characters

Ловушка экзамена: Если массив имеет НЕпоследовательные числовые ключи (например, после array_filter), json_encode() превратит его в JSON-объект, а не массив! Используйте array_values() для переиндексации.

json_decode — декодирование JSON

<?php
declare(strict_types=1);

$json = '{"name":"Alice","age":25,"active":true}';

// Default: returns stdClass object
$obj = json_decode($json);
echo $obj->name;   // Alice
echo $obj->age;    // 25

// As associative array (second parameter = true)
$arr = json_decode($json, true);
echo $arr['name'];  // Alice
echo $arr['age'];   // 25

// Depth limit (third parameter)
$deep = '{"a":{"b":{"c":{"d":"value"}}}}';
$result = json_decode($deep, true, 3);  // Max depth 3
var_dump($result);  // null (too deep!)

// Flags (fourth parameter)
$json = '{"number": 12345678901234567890}';
$data = json_decode($json, true, 512, JSON_BIGINT_AS_STRING);
echo $data['number'];  // "12345678901234567890" (as string, not float)

// JSON_THROW_ON_ERROR (PHP 7.3+)
try {
    $data = json_decode('invalid json', true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    echo $e->getMessage();  // Syntax error
}

Обработка ошибок

<?php
declare(strict_types=1);

// Old way: check json_last_error()
$data = json_decode('invalid');
if (json_last_error() !== JSON_ERROR_NONE) {
    echo json_last_error_msg();  // Syntax error
}

// Error constants
// JSON_ERROR_NONE          — No error
// JSON_ERROR_DEPTH         — Maximum stack depth exceeded
// JSON_ERROR_STATE_MISMATCH — Invalid or malformed JSON
// JSON_ERROR_CTRL_CHAR     — Control character error
// JSON_ERROR_SYNTAX        — Syntax error
// JSON_ERROR_UTF8          — Malformed UTF-8 characters
// JSON_ERROR_INF_OR_NAN    — Inf and NaN cannot be encoded

// New way (PHP 7.3+): use JSON_THROW_ON_ERROR
try {
    $encoded = json_encode($data, JSON_THROW_ON_ERROR);
    $decoded = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    echo "JSON Error: " . $e->getMessage();
    echo "Error code: " . $e->getCode();
}

Запомни: Всегда используйте JSON_THROW_ON_ERROR (PHP 7.3+) вместо проверки json_last_error(). Это современный и безопасный подход. Без этого флага json_decode просто вернёт null, и вы можете спутать это с валидным JSON "null".

json_validate (PHP 8.3+)

<?php
declare(strict_types=1);

// PHP 8.3+: Validate JSON without decoding
var_dump(json_validate('{"name":"Alice"}'));  // true
var_dump(json_validate('invalid'));            // false
var_dump(json_validate(''));                    // false
var_dump(json_validate('null'));               // true

// More efficient than json_decode for validation only
// (doesn't allocate memory for decoded data)

SimpleXML

Чтение XML

<?php
declare(strict_types=1);

$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
    <book category="fiction">
        <title lang="en">The Great Gatsby</title>
        <author>F. Scott Fitzgerald</author>
        <year>1925</year>
        <price>10.99</price>
    </book>
    <book category="science">
        <title lang="en">A Brief History of Time</title>
        <author>Stephen Hawking</author>
        <year>1988</year>
        <price>15.99</price>
    </book>
</bookstore>
XML;

// Parse XML string
$books = new SimpleXMLElement($xml);

// Access elements
echo $books->book[0]->title;    // The Great Gatsby
echo $books->book[0]->author;   // F. Scott Fitzgerald

// Access attributes
echo $books->book[0]['category'];           // fiction
echo $books->book[0]->title['lang'];        // en

// Iterate over elements
foreach ($books->book as $book) {
    echo $book->title . ': $' . $book->price . "\n";
}

// Load from file
$books = simplexml_load_file('books.xml');

// Load from string
$books = simplexml_load_string($xml);

Поиск с XPath

<?php
declare(strict_types=1);

$xml = simplexml_load_string($xmlString);

// XPath queries
$results = $xml->xpath('//book[@category="fiction"]');
foreach ($results as $book) {
    echo $book->title . "\n";
}

// Find by attribute value
$results = $xml->xpath('//title[@lang="en"]');

// Find by element value
$results = $xml->xpath('//book[year>1950]');

// Count elements
$count = count($xml->xpath('//book'));

Создание и модификация XML

<?php
declare(strict_types=1);

// Create new XML
$xml = new SimpleXMLElement('<bookstore/>');

// Add child elements
$book = $xml->addChild('book');
$book->addAttribute('category', 'fiction');
$book->addChild('title', 'New Book');
$book->addChild('author', 'Author Name');
$book->addChild('price', '19.99');

// Output XML
echo $xml->asXML();

// Save to file
$xml->asXML('/tmp/books.xml');

// Convert to array (trick)
$array = json_decode(json_encode($xml), true);

Ловушка экзамена: SimpleXML конвертирует элементы в строки автоматически при использовании в строковом контексте. Но при присваивании переменной вы получаете объект SimpleXMLElement, не строку! Для надёжного получения строки используйте (string)$element.

<?php
declare(strict_types=1);

$xml = new SimpleXMLElement('<root><name>Alice</name></root>');

$name = $xml->name;
var_dump($name);          // object(SimpleXMLElement)
var_dump((string)$name);  // string(5) "Alice"

// Comparison trap
var_dump($xml->name == 'Alice');   // true (auto-cast to string)
var_dump($xml->name === 'Alice');  // false (object !== string)

DOM (Document Object Model)

Чтение и парсинг

<?php
declare(strict_types=1);

$xmlString = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<users>
    <user id="1">
        <name>Alice</name>
        <email>[email protected]</email>
    </user>
    <user id="2">
        <name>Bob</name>
        <email>[email protected]</email>
    </user>
</users>
XML;

// Load XML
$dom = new DOMDocument();
$dom->loadXML($xmlString);

// Access elements
$users = $dom->getElementsByTagName('user');
echo $users->length;  // 2

foreach ($users as $user) {
    $name = $user->getElementsByTagName('name')->item(0)->nodeValue;
    $email = $user->getElementsByTagName('email')->item(0)->nodeValue;
    $id = $user->getAttribute('id');
    echo "[$id] $name: $email\n";
}

// XPath with DOM
$xpath = new DOMXPath($dom);
$results = $xpath->query('//user[@id="1"]/name');
echo $results->item(0)->nodeValue;  // Alice

Создание XML с DOM

<?php
declare(strict_types=1);

$dom = new DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = true;

// Create root element
$root = $dom->createElement('catalog');
$dom->appendChild($root);

// Add product element
$product = $dom->createElement('product');
$product->setAttribute('id', '1');
$root->appendChild($product);

// Add child elements
$name = $dom->createElement('name', 'Widget');
$product->appendChild($name);

$price = $dom->createElement('price', '29.99');
$product->appendChild($price);

// Output
echo $dom->saveXML();

// Save to file
$dom->save('/tmp/catalog.xml');

// Load HTML (tolerant of malformed markup)
$dom = new DOMDocument();
@$dom->loadHTML('<h1>Title</h1><p>Text');  // No closing </p> is OK

SimpleXML vs DOM

Особенность SimpleXML DOM
Простота использования Высокая Средняя
Объём API Маленький Большой
Поддержка XPath Да Да
Создание XML Ограничено Полное
Работа с HTML Нет Да
Namespaces Базовая Полная
Производительность Быстрее для чтения Гибче для модификации

Конвертация между SimpleXML и DOM

<?php
declare(strict_types=1);

// SimpleXML to DOM
$simple = simplexml_load_string('<root><item>test</item></root>');
$domElement = dom_import_simplexml($simple);
$dom = $domElement->ownerDocument;

// DOM to SimpleXML
$dom = new DOMDocument();
$dom->loadXML('<root><item>test</item></root>');
$simple = simplexml_import_dom($dom);
echo $simple->item;  // test

Парсинг XML с XMLReader (потоковый)

<?php
declare(strict_types=1);

// XMLReader — memory-efficient for large files
$reader = new XMLReader();
$reader->open('/tmp/large-file.xml');

while ($reader->read()) {
    if ($reader->nodeType === XMLReader::ELEMENT && $reader->name === 'product') {
        $node = new SimpleXMLElement($reader->readOuterXml());
        echo $node->name . ': ' . $node->price . "\n";
    }
}

$reader->close();

Запомни: Для больших XML-файлов используйте XMLReader (потоковый парсинг, низкое потребление памяти) вместо SimpleXML или DOM (загружают весь документ в память). Для маленьких файлов SimpleXML — самый удобный вариант.


Вопросы с экзамена ZCE

Проверь себя

5 из 7

Какая функция используется для декодирования JSON-строки/массива?

Какая функция появилась в PHP 8.3 для проверки JSON?

Какая функция используется для кодирования строки/массива в JSON-строку?

Что произойдёт при `json_encode(NAN)`?

Что вернёт `json_encode([0 => 'a', 2 => 'b'])`?