Skip to content

Skraeda/xmlary

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

55 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Xmlary

CircleCI Codecov Version Badge License Badge

This package is a set of XML utilities for PHP.

Writer examples

Examples of how the writer converts arrays to XML.

Use toString to get the XML body string or toDomDocument to get a PHP DOMDocument object.

Basic elements

use Skraeda\Xmlary\XmlWriter;

$writer = new XmlWriter;

$array = [
    'People' => [
        // String value should become <Marie>Unknown</Marie>
        'Marie' => 'Unknown',

        // Null value should become <Lao/>
        'Lao' => null,

        // Boolean value should become <Peter>1</Peter>
        'Peter' => true,

        // Nested value should become <John><Age>20</Age></John>
        'John' => [
            'Age' => 20,
        ]
    ]
];

echo $writer->toString($array);

outputs

<People>
    <Marie>Unknown</Marie>
    <Lao/>
    <Peter>1</Peter>
    <John>
        <Age>20</Age>
    </John>
</People>

Array elements

$array = [
    'Root' => [
        'ArrayOfValues' => [
            // Should create duplicate <Values> elements
            'Values' => [
                'String1',
                2,
                true,
                null
            ]
        ],
        'ArrayOfNestedValues' => [
            // Should create duplicate <Values> elements
            'Values' => [
                [
                    'StringValue' => 'String1'
                ],
                [
                    'BoolValue' => true
                ],
                [
                    'NullValue' => null
                ],
                [
                    'NumericValue' => 123
                ],
                [
                    'Nested' => [1, 2]
                ]
            ]
        ]
    ]
];

echo $writer->toString($array);

outputs

<Root>
    <ArrayOfValues>
        <Values>String1</Values>
        <Values>2</Values>
        <Values>1</Values>
        <Values/>
    </ArrayOfValues>
    <ArrayOfNestedValues>
        <Values>
            <StringValue>String1</StringValue>
        </Values>
        <Values>
            <BoolValue>1</BoolValue>
        </Values>
        <Values>
            <NullValue/>
        </Values>
        <Values>
            <NumericValue>123</NumericValue>
        </Values>
        <Values>
            <Nested>1</Nested>
            <Nested>2</Nested>
        </Values>
    </ArrayOfNestedValues>
</Root>

Writer configuration

This section describes some ways you can customize the XmlWriter. You can configure the XmlWriter through interfaces, configure the inner DOMDocument object before or after generation or add keywords to handle individual elements differently.

Interfaces

You can set a custom configuration object, validator or value converter via the XmlWriter constructor or through setter methods.

Example

use Skraeda\Xmlary\Contracts\XmlValueConverterContract;

/**
 * Custom value converter to change boolean values to true / false strings instead of 1 / 0
 */
class CustomXmlValueConverter implements XmlValueConverterContract
{
    public function convert($value)
    {
        if (is_bool($value)) {
            return $value ? 'true' : 'false';
        }
        return $value;
    }
}

// Outputs <Root>false</Root>
echo (new XmlWriter)
    ->setConverter(new CustomXmlValueConverter)
    ->toString(['Root' => false]);

Middleware

You can add middleware before or after DOMDocument creation.

Example

// Add namespaced attribute to root element.
$writer->pushAfterMiddleware(function ($doc) {
    $namespace = $doc->createAttributeNS('http://example.com', 'example:attr');
    $namespace->value = 'foo';
    $doc->firstChild->appendChild($namespace);
});

// Outputs <foo:Root xmlns:example="http://example.com" example:attr="foo">value</foo:Root>
$writer->toString(['foo:Root' => 'value']);

Keywords

Extend the XmlWriter with Keywords to add some custom functionality on the element level.

Example

use Skraeda\Xmlary\Contracts\XmlKeyword;

class CDataKeyword implements XmlKeyword
{
    public function handle(DOMDocument $doc, DOMNode $parent, $value): void
    {
        $parent->appendChild($doc->createCDATASection($value));
    }
}

echo $writer->extend('cdata', new CDataKeyword)->toString([
    'Root' => [
        'Element' => [
            '@cdata' => 'bar',
        ]
    ]
]);

outputs

<Root><Element><![CDATA[bar]]></Element></Root>

Bootstrap

You can use the bootstrap method on the writer to configure it with default keywords.

Keywords

  • attributes: Set element attributes
  • value: Set element value (Useful if using other keywords in the same node)
  • cdata: Wraps a value in CDATA block
  • comment: Wraps a value in comment block
  • handler: Add a customer function handler for a node block

Example

$writer = (new XmlWriter)->bootstrap();

$arr = [
    'Root' => [
        '@attributes' => [
            'xmlns:example' => 'http://example.com',
            'example:attr' => 'foo'
        ],
        'Element' => [
            '@attributes' => [
                'foo' => 'bar'
            ],
            '@value' => 'Value'
        ],
        'CData' => [
            '@cdata' => 'Wrapped',
            '@value' => 'NotWrapped'
        ],
        'Comment' => [
            '@value' => 'Value',
            '@comment' => 'Comment'
        ],
        'Handler' => [
            '@handler' => function (DOMDocument $doc, DOMNode $parent) {
                $parent->appendChild($doc->createElement('Name', 'Value'));
            }
        ]
    ]
];

echo $writer->toString($arr);

Reader examples

Examples of how the reader converts XML to arrays.

Example 1

$xml = <<<XML
<Root>
    <Element id="1">Value</Element>
</Root>
XML;

(new XmlReader)->parse($xml));

outputs

[
    'Root' => [
        'Element' => [
            '@attributes' => [
                'id' => '1'
            ],
            '@value' => Value
        ]
    ]
]

Example 2

$xml = <<<XML
<Order>
    <Items>
        <Item id="1">
            <Price>2.00</Price>
            <Type>Drink</Type>
            <Supplier>
                <Organization>
                    <OrganizationName />
                </Organization>
            </Supplier>
        </Item>
        <Item id="3">
            <Price><![CDATA[1.00]]></Price>
            <Type>Misc</Type>
            <Supplier />
        </Item>
    </Items>
</Order>
XML;

outputs

[
    'Order' => [
        'Items' => [
            'Item' => [
                [
                    '@attributes' => [
                        'id' => 1
                    ],
                    'Price' => [
                        '@value' => '2.00'
                    ],
                    'Supplier' => [
                        'Organization' => [
                            'OrganizationName' => [
                                '@value' => null
                            ]
                        ]
                    ]
                ],
                [
                    '@attributes' => [
                        'id' => 3
                    ],
                    'Price' => [
                        '@value' => '1.00'
                    ]
                    'Type' => [
                        '@value' => 'Misc'
                    ],
                    'Supplier' => [
                        '@value' => null
                    ]
                ]
            ]
        ]
    ]
]

Example 3

You can provide a configuration mapping for the nodes for some basic changes to the generated array.

use Skraeda\Xmlary\XmlReaderNodeConfiguration as Config;

$xml = <<<XML
<Root>
    <Element>1</Element>
    <Inner>&lt;Sheet&gt;&lt;Data&gt;2&lt;/Data&gt;&lt;/Sheet&gt;</Inner>
</Root>
XML;

$reader = new XmlReader;

$config = [
    'Root' => [
        // Provide config to rename Root element
        '@config' => new Config('NewRoot'),
        'Element' => [
            // Provide config to ensure Element is always an array element and add custom value converter to cast to int and add +2 to the value.
            '@config' => new Config(null, true, function ($oldValue) {
                return ((int) $oldValue) + 2;
            })
        ],
        'Inner' => [
            '@config' => new Config(null, false, function ($oldValue) use ($reader) {
                return $reader->parse(html_entity_decode($oldValue));
            }, function (XmlReaderNode $node) {
                // Callback handler to execute when the ReaderNode is created..
            })
        ]
    ]
];

$reader->parse($xml, $config);

outputs

[
    'NewRoot' => [
        'Element' => [
            [
                '@value' => 3
            ]
        ],
        'Inner' => [
            '@value' => [
                'Sheet' => [
                    'Data' => [
                        '@value' => '2'
                    ]
                ]
            ]
        ]
    ]
]

You can create custom config classes to reduce repitition if you need.

Either extend the reader node configuration class or implement the interface.

Reader configuration

This section describes how you can customize the XmlReader.

Interfaces

You can set a custom configuration object through the constructor or setter methods.

Utilities

XmlSerializable

Interface you can define on a model so it can be formatted as XML by an XmlWriter.

You need to define xmlSerialize on your model which should return an array similar to the above examples.

XmlSerialize

Trait you can add on a model to give it a default xmlSerialize handler using reflection.

XmlMessage

Abstract base class you can extend to give your object a default xmlSerialize handler using reflection.

Example

use Skraeda\Xmlary\XmlMessage;

class MyMessage extends XmlMessage
{
    public $Public = 'PublicValue';

    protected $Protected = 'ProtectedValue';

    private $Private = 'PrivateValue';

    public $Array;

    public $Inner;

    public $Changer = 'WillBeChanged';

    public $Attrs = null;

    public function __construct(?MyMessage $Inner = null)
    {
        $this->Array = ['Field' => 'Value'];
        $this->Inner = $Inner;
    }

    protected function ChangerMutator()
    {
        return 'ChangedValue';
    }

    // Uses AttributeKeyword and ValueKeyword
    protected function AttrsAttributes()
    {
        return [
            'value' => 'AttributeValue'
        ];
    }

    protected function AttrsTag()
    {
        return 'AttributeTag';
    }

    protected function attributes()
    {
        return [
            'foo' => 'bar'
        ];
    }
}

echo $writer->toString(new MyMessage(new MyMessage));

outputs

<MyMessage foo="bar">
    <Public>PublicValue</Public>
    <Protected>ProtectedValue</Protected>
    <Private>PrivateValue</Private>
    <Array>
        <Field>Value</Field>
    </Array>
    <Inner>
        <MyMessage foo="bar">
            <Public>PublicValue</Public>
            <Protected>ProtectedValue</Protected>
            <Private>PrivateValue</Private>
            <Array>
                <Field>Value</Field>
            </Array>
            <Inner/>
            <Changer>ChangedValue</Changer>
            <AttributeTag name="AttributeValue"/>
        </MyMessage>
    </Inner>
    <Changer>ChangedValue</Changer>
    <AttributeTag name="AttributeValue">
</MyMessage>

Development

PHP7.2 CLI dockerfile included, use it to test any new functionality.

Start

docker-compose up

Test

docker-compose exec app vendor/bin/phpunit

About

Collection of PHP utilities for working with XML

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages