inspirum / xml
简单的XML写入器和高效的XML读取器,具有强大的xml-to-array转换功能
v3.1.0
2023-11-05 11:56 UTC
Requires
- php: ^8.2
- ext-dom: *
- ext-json: *
- ext-xmlreader: *
- inspirum/arrayable: ^1.2
Requires (Dev)
- inspirum/coding-standard: ^1.3
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.1
- squizlabs/php_codesniffer: ^3.7
README
简单的XML流畅写入器和高效的XML读取器。
使用示例
此处显示的所有代码片段均已修改以供清晰阅读,因此它们可能无法执行。
XML写入器
写入Google Merchant XML馈文件
/** @var Inspirum\XML\Builder\DocumentFactory $factory */ $locale = 'cs'; $currencyCode = 'CZK'; $xml = $factory->create(); $rss = $xml->addElement('rss', [ 'version' => '2.0', 'xmlns:g' => 'http://base.google.com/ns/1.0', ]); $channel = $rss->addElement('channel'); $channel->addTextElement('title', 'Google Merchant'); $channel->addTextElement('link', 'https://www.example.com'); $channel->addTextElement('description', 'Google Merchant products feed'); $channel->addTextElement('language', $locale); $channel->addTextElement('lastBuildDate', (new \DateTime())->format('D, d M y H:i:s O')); $channel->addTextElement('generator', 'Eshop'); foreach ($products as $product) { $item = $xml->createElement('item'); $item->addTextElement('g:id', $product->getId()); $item->addTextElement('title', $product->getName($locale)); $item->addTextElement('link', $product->getUrl()); $item->addTextElement('description', \strip_tags($product->getDescription($locale))); $item->addTextElement('g:image_link', $product->getImageUrl()); foreach ($product->getAdditionalImageUrls() as $imageUrl) { $item->addTextElement('g:additional_image_link', $imageUrl); } $price = $product->getPrice($currencyCode); $item->addTextElement('g:price', $price->getOriginalPriceWithVat() . ' ' . $currencyCode); if ($price->inDiscount()) { $item->addTextElement('g:sale_price', $price->getPriceWithVat() . ' ' . $currencyCode); } if ($product->hasEAN()) { $item->addTextElement('g:gtin', $product->getEAN()); } else { $item->addTextElement('g:identifier_exists', 'no'); } $item->addTextElement('g:condition', 'new'); if ($product->inStock()) { $item->addTextElement('g:availability', 'in stock'); } elseif ($product->hasPreorder()) { $item->addTextElement('g:availability', 'preorder'); $item->addTextElement('g:availability_date', $product->getDeliveryDate()); } else { $item->addTextElement('g:availability', 'out of stock'); } $item->addTextElement('g:brand', $product->getBrand()); $item->addTextElement('g:size', $product->getParameterValue('size', $locale)); $item->addTextElement('g:color', $product->getParameterValue('color', $locale)); $item->addTextElement('g:material', $product->getParameterValue('material', $locale)); if ($product->isVariant()) { $item->addTextElement('g:item_group_id', $product->getParentProductId()()); } if ($product->getCustomAttribute('google_category') !== null) { $item->addTextElement('g:google_product_category', $product->getCustomAttribute('google_category')); } elseif ($product->getMainCategory() !== null) { $item->addTextElement('g:product_type', $product->getMainCategory()->getFullname($locale)); } } $xml->validate('/google_feed.xsd'); $xml->save('/output/feeds/google.xml'); /** var_dump($xml->toString(true)); <?xml version="1.0" encoding="UTF-8"?> <rss version="2.0" xmlns:g="http://base.google.com/ns/1.0"> <channel> <title>Google Merchant</title> <link>https://www.example.com</link> <description>Google Merchant products feed</description> <language>cs</language> <lastBuildDate>Sat, 14 Nov 20 08:00:00 +0200</lastBuildDate> <generator>Eshop</generator> <item> <g:id>0001</g:id> <title><![CDATA[Sample products #1 A&B]]></title> <link>http://localhost/produkt/sample-product-1-a-b</link> <description>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</description> <g:image_link>http://localhost/images/no_image.webp</g:image_link> <g:price>19.99 CZK</g:price> <g:gtin>7220110003812</g:gtin> <g:condition>new</g:condition> <g:availability>in stock</g:availability> <g:brand>Co.</g:brand> </item> ... </channel> </rss> */
XML读取器
从Google Merchant XML馈读取数据
/** @var \Inspirum\XML\Reader\ReaderFactory $factory */ $reader = $factory->create('/output/feeds/google.xml'); $title = $reader->nextNode('title')->getTextContent(); /** var_dump($title); 'Google Merchant' */ $lastBuildDate = $reader->nextNode('lastBuildDate')->getTextContent(); /** var_dump($lastBuildDate); '2020-08-25T13:53:38+00:00' */ $price = 0.0; foreach ($reader->iterateNode('item') as $item) { $data = $item->toArray(); $price += (float) $data['g:price']; } /** var_dump($price); 501.98 */
通过xpath(具有有效命名空间)分割数据到XML片段
/** @var \Inspirum\XML\Reader\ReaderFactory $factory */ $reader = $factory->create('/output/feeds/google.xml'); foreach ($reader->iterateNode('/rss/channel/item', true) as $item) { $data = $item->toString(); $id = ($item->xpath('/item/g:id')[0] ?? null)?->getTextContent() // ... }
系统需求
安装
运行composer require命令
$ composer require inspirum/xml
或在您的composer.json
中添加需求
"inspirum/xml": "^3.0"
使用
可用的框架集成
但您也可以在不使用任何框架实现的情况下使用它
use Inspirum\XML\Builder\DefaultDocumentFactory; use Inspirum\XML\Builder\DefaultDOMDocumentFactory; use Inspirum\XML\Reader\DefaultReaderFactory; use Inspirum\XML\Reader\DefaultXMLReaderFactory; $documentFactory = new DefaultDocumentFactory(new DefaultDOMDocumentFactory()); $document = $documentFactory->create(); // ... $readerFactory = new DefaultReaderFactory(new DefaultXMLReaderFactory(), $documentFactory); $reader = $readerFactory->create('/path/to/file.xml'); // ...
XML写入器
可选地,您可以为XML版本和编码(默认为UTF-8)指定。
use Inspirum\XML\Builder\DefaultDocumentFactory; $factory = new DefaultDocumentFactory() $xml = $factory->create('1.0', 'WINDOWS-1250'); /** <?xml version="1.0" encoding="WINDOWS-1250"?> */ $xml = $factory->create(); /** <?xml version="1.0" encoding="UTF-8"?> */
嵌套元素
$a = $xml->addElement('a'); $a->addTextElement('b', 'BB', ['id' => 1]); $b = $a->addElement('b', ['id' => 2]); $b->addTextElement('c', 'CC'); /** <?xml version="1.0" encoding="UTF-8"?> <a> <b id="1">BB</a> <b id="2"> <c>CC</c> </b> </a> */
作为流畅构建器使用
$xml->addElement('root')->addElement('a')->addElement('b', ['id' => 1])->addTextElement('c', 'CC'); /** <?xml version="1.0" encoding="UTF-8"?> <root> <a> <b id="2"> <c>CC</c> </b> </a> </root> */
自动CDATA转义
$a = $xml->addElement('a'); $a->addTextElement('b', 'me & you'); $a->addTextElement('b', '30 km'); /** <?xml version="1.0" encoding="UTF-8"?> <a> <b> <![CDATA[me & you]]> </b> <b> <![CDATA[30 km]]> </b> </a> */
强制CDATA转义
$a = $xml->addElement('a'); $a->addTextElement('b', 'me'); $a->addTextElement('b', 'you', forcedEscape: true); /** <?xml version="1.0" encoding="UTF-8"?> <a> <b>me</b> <b> <![CDATA[you]]> </b> </a> */
添加XML片段
$a = $xml->addElement('a'); $a->addXMLData('<b><c>CC</c></b><b>0</b>'); $a->addTextElement('b', '1'); /** <?xml version="1.0" encoding="UTF-8"?> <a> <b> <c>CC</c> </b> <b>0</b> <b>1</b> </a> */
要使用自动命名空间,您只需在(通常是)根元素上设置xmlns:{prefix}
属性。
元素(或/和属性)使用给定的前缀作为{prefix}:{localName}
,它将使用createElementNS
或createAttributeNS
方法创建。
$root = $xml->addElement('g:root', ['xmlns:g' =>'stock.xsd', 'g:version' => '2.0']); $items = $root->addElement('g:items'); $items->addTextElement('g:item', 1); $items->addTextElement('g:item', 2); $items->addTextElement('g:item', 3); /** <?xml version="1.0" encoding="UTF-8"?> <g:root xmlns:g="stock.xsd" g:version="2.0"> <g:items> <g:item>1</g:item> <g:item>2</g:item> <g:item>3</g:item> </a> </root> */
命名空间支持对于使用XSD模式进行XML验证是必要的
try { $xml->validate('/sample.xsd'); // valid XML } catch (\DOMException $exception) { // invalid XML }
XML读取器
/sample.xml
<?xml version="1.0" encoding="utf-8"?> <g:feed xmlns:g="stock.xsd" g:version="2.0"> <g:updated>2020-08-25T13:53:38+00:00</g:updated> <title></title> <g:items> <g:item active="true" price="99.9"> <g:id>1</g:id> <g:name>Test 1</g:name> </g:item> <item active="true" price="19.9"> <g:id>2</g:id> <g:name>Test 2</g:name> </item> <g:item active="false" price="0"> <g:id>3</g:id> <g:name>Test 3</g:name> </g:item> </g:items> </g:feed>
将XML文件读取到Node实例中
使用给定名称读取下一个节点
$node = $reader->nextNode('g:updated'); $node->getTextContent(); /** '2020-08-25T13:53:38+00:00' */ $node->toString(); /** <g:updated>2020-08-25T13:53:38+00:00</g:updated> */
强大的转换为数组的方法
$data = $reader->nextNode('g:items')->toArray(); /** var_dump($ids); [ 'g:item' => [ 0 => [ 'g:id' => '1' 'g:name' => 'Test 1' '@attributes' => [ 'active' => 'true' 'price' => '99.9' ] ] 1 => [ 'g:id' => '3' 'g:name' => 'Test 3' '@attributes' => [ 'active' => 'false' 'price' => '0' ] ] ] 'item' => [ 0 => [ 'g:id' => '2' 'g:name' => 'Test 2' '@attributes' => [ 'active' => 'true' 'price' => '19.9' ] ] ] ] */
支持为toArray
方法提供可选配置
use Inspirum\XML\Builder\DefaultDocumentFactory; use Inspirum\XML\Formatter\FullResponseConfig; $factory = new DefaultDocumentFactory() $config = new FullResponseConfig( attributesName: '@attr', valueName: '@val', autoCast: true, ); $data = $factory->createForFile('/sample.xml')->toArray($config); /** var_dump($ids); [ '@attr' => [] '@val' => null '@nodes' => [ 'g:feed' => [ 0 => [ '@attr' => [ 'g:version' => 2.0 ] '@val' => null '@nodes' => [ 'g:updated' => [ 0 => [ '@attr' => [] '@val' => '2020-08-25T13:53:38+00:00' '@nodes' => [] ] ] 'title' => [ 0 => [ '@attr' => [] '@val' => null '@nodes' => [] ] ] 'g:items' => [ 0 => [ '@attr' => [] '@val' => null '@nodes' => [ 'g:item' => [ 0 => [ '@attr' => [ 'active' => true 'price' => 99.9 ] '@val' => null '@nodes' => [ 'g:id' => [ 0 => [ '@attr' => [] '@val' => 1 '@nodes' => [] ] ] 'g:name' => [ 0 => [ '@attr' => [] '@val' => 'Test 1' '@nodes' => [] ] ] ] ] 1 => [ '@attr' => [ 'active' => false 'price' => 0 ] '@val' => null '@nodes' => [ 'g:id' => [ 0 => [ '@attr' => [] '@val' => 3 '@nodes' => [] ] ] 'g:name' => [ 0 => [ '@attr' => [] '@val' => 'Test 3' '@nodes' => [] ] ] ] ] ] 'item' => [ 0 => [ '@attr' => [ 'active' => true 'price' => 19.9 ] '@val' => null '@nodes' => [ 'g:id' => [ 0 => [ '@attr' => [] '@val' => 2 '@nodes' => [] ] ] 'g:name' => [ 0 => [ '@attr' => [] '@val' => 'Test 2' '@nodes' => [] ] ] ] ] ] ] ] ] ] ] ] ] ] */
迭代给定名称的所有节点
$ids = []; foreach ($reader->iterateNode('item') as $item) { $ids[] = $item->toArray()['id']; } /** var_dump($ids); [ 0 => '1' 1 => '3' ] */
通过xpath(具有有效命名空间)分割数据到XML片段
$items = []; foreach ($reader->iterateNode('g:item', true) as $item) { $items[] = $item->toString(); } /** var_dump($items); [ 0 => '<g:item xmlns:g="stock.xsd" active="true" price="99.9"><g:id>1</g:id><g:name>Test 1</g:name></g:item>' 1 => '<g:item xmlns:g="stock.xsd" active="false" price="0"><g:id>3</g:id><g:name>Test 3</g:name></g:item>' ] */
所有可用方法
Inspirum\XML\Builder\DocumentFactory
Inspirum\XML\Builder\Document
Inspirum\XML\Builder\Node
Inspirum\XML\Reader\ReaderFactory
Inspirum\XML\Reader\Reader
测试
要运行单元测试,请运行
$ composer test:test
要显示覆盖率,请运行
$ composer test:coverage
贡献
有关详细信息,请参阅CONTRIBUTING和CODE_OF_CONDUCT。
安全性
如果您发现任何与安全性相关的问题,请通过电子邮件tomas.novotny@inspirum.cz而不是使用问题跟踪器来报告。
鸣谢
许可
MIT许可(MIT)。有关更多信息,请参阅许可文件。