The Writer component can be used to write a lot of XML in a memory-safe and readable way. It uses builders to specify how the XML should be structured.
use VeeWee\Xml\Writer\Writer;
use function VeeWee\Xml\Writer\Builder\document;
use function VeeWee\Xml\Writer\Builder\attribute;
use function VeeWee\Xml\Writer\Builder\children;
use function VeeWee\Xml\Writer\Builder\element;
use function VeeWee\Xml\Writer\Configurator\indentation;
$writer = Writer::forFile($someFile, indentation(' '));
$writer->write(
document('1.0', 'UTF-8',
element('hello', children([
attribute('default', 'world'),
element('item', value('Jos')),
element('item', value('Bos')),
element('item', value('Mos')),
]))
)
);
<?xml version="1.0" encoding="UTF-8"?>
<hello default="world">
<item>Jos</item>
<item>Bos</item>
<item>Mos</item>
</hello>
The Writer consists out of following composable blocks:
- Builders: Lets you build XML by using a declarative API.
- Configurators: Configure how the Writer behaves.
- Mappers: Map the XMLWriter to something else.
- Applicatives: Apply an action on the open writer.
- Openers: Specify where you want to write to.
Lets you build XML by using a declarative API.
use VeeWee\Xml\Writer\Writer;
use function VeeWee\Xml\Writer\Builder\document;
use function VeeWee\Xml\Writer\Builder\children;
use function VeeWee\Xml\Writer\Builder\element;
use function VeeWee\Xml\Writer\Builder\namespace_attribute;
use function VeeWee\Xml\Writer\Builder\prefixed_attribute;
use function VeeWee\Xml\Writer\Configurator\indentation;
$writer = Writer::forFile($someFile, indentation(' '));
$writer->write(
document('1.0', 'UTF-8',
element('hello',
namespace_attribute('http://helloworld.com', 'hello'),
children([
element('item',
prefixed_attribute('hello', 'default', 'true'),
value('Jos')
),
element('item', value('Bos')),
element('item', value('Mos')),
]),
children($provideYourOwnElementGenerators)
)
)
);
<?xml version="1.0" encoding="UTF-8"?>
<hello xmlns:hello="http://helloworld.com">
<item hello:default="true">Jos</item>
<item>Bos</item>
<item>Mos</item>
<!-- The tags provided by $provideYourOwnElementGenerators -->
</hello>
Writes a single attribute to an element:
use function VeeWee\Xml\Writer\Builder\attribute;
use function VeeWee\Xml\Writer\Builder\element;
element('foo',
attribute('bar', 'baz')
);
<foo bar="baz" />
Write multiple attributes to an element:
use function VeeWee\Xml\Writer\Builder\attributes;
use function VeeWee\Xml\Writer\Builder\element;
element('foo',
attributes([
'hello' => 'world',
'bar' => 'baz',
])
);
<foo hello="world" bar="baz" />
Writes a CDATA section:
use function VeeWee\Xml\Dom\Builder\value;
use function VeeWee\Xml\Writer\Builder\cdata;
use function VeeWee\Xml\Writer\Builder\element;
element('foo',
cdata(value('some cdata'))
);
<foo><![CDATA[some cdata]]></foo>
Inserts multiple nodes at current position in the writer.
use function VeeWee\Xml\Writer\Builder\element;
use function VeeWee\Xml\Writer\Builder\children;
element('hello',
children([
element('world'),
element('you')
])
);
<hello>
<world />
<you />
</hello>
It takes any iterable
as input, meaning that you could use this function as an entry-point to couple your own memory-safe data into the builder.
For example:
use function MyCustom\Generator\db_data;
use function VeeWee\Xml\Writer\Builder\element;
use function VeeWee\Xml\Writer\Builder\children;
function myOwnDataBuilder(): Generator
{
foreach (db_data() as $record) {
yield element('item', value($record['name']));
}
}
children(myOwnDataBuilder());
Writes a comment section:
use function VeeWee\Xml\Dom\Builder\value;
use function VeeWee\Xml\Writer\Builder\comment;
use function VeeWee\Xml\Writer\Builder\element;
element('foo',
comment(value('some comment'))
);
<foo><!--some comment--></foo>
This builder can be used to specify an XML version and charset. However, it is not required to wrap a document. You could as well start out with a root element instead!
use function VeeWee\Xml\Writer\Builder\element;
use function VeeWee\Xml\Writer\Builder\document;
document('1.0', 'UTF-8',
element('root')
);
<?xml version="1.0" encoding="UTF-8"?>
<root />
Builds a regular XML element. It can contain a set of configurators that can be used to specify the attributes, children, value, ... of the element.
use function VeeWee\Xml\Writer\Builder\element;
element('hello', ...$configurators);
<hello />
Can be used to add an xlmns
attribute to an element.
You can choose to only add the xmlns
attributes to a parent attribute.
This way you don't end up with having the namespace URI all over the place.
use function VeeWee\Xml\Writer\Builder\element;
use function VeeWee\Xml\Writer\Builder\namespace_attribute;
element('foo',
namespace_attribute('https://acme.com'),
namespace_attribute('https://helloworld.com', 'hello')
);
<foo xmlns="https://acme.com" xmlns:hello="https://helloworld.com" />
Can be used to add a namespaced attribute with or without prefix. This function will also add the xmlns attribute.
use function VeeWee\Xml\Writer\Builder\element;
use function VeeWee\Xml\Writer\Builder\namespaced_attribute;
element('foo',
namespaced_attribute('https://acme.com', 'acme', 'hello', world)
);
<foo xmlns:acme="https://acme.com" acme:hello="world" />
Can be used to add multiple namespaced attributes with or without prefix at once. This function will add the xmlns attribute.
use function VeeWee\Xml\Writer\Builder\element;
use function VeeWee\Xml\Writer\Builder\namespaced_attributes;
element('foo',
namespaced_attributes('https://acme.com', [
'acme:hello' => 'world',
'acme:foo' => 'bar',
])
);
<foo xmlns:acme="https://acme.com" acme:hello="world" acme:foo="bar" />
Build a namespaced element. It can contain a set of configurators that can be used to specify the attributes, children, value, ... of the element with or without prefix. This function will add the xmlns attribute.
use function VeeWee\Xml\Writer\Builder\namespace_attribute;
use function VeeWee\Xml\Writer\Builder\prefixed_element;
namespaced_element('http://acme.com', 'acme', 'hello',
...$configurators
);
<acme:hello xmlns:acme="http://acme.com" />
Can be used to add a namespaced prefixed attribute.
This function won't add the xmlns attribute. You need to manually specify it with the namespace_attribute
function.
use function VeeWee\Xml\Writer\Builder\element;
use function VeeWee\Xml\Writer\Builder\namespace_attribute;
use function VeeWee\Xml\Writer\Builder\prefixed_attribute;
element('foo',
namespace_attribute('https://acme.com', 'acme'),
prefixed_attribute('acme', 'hello', 'world'),
);
<foo xmlns:acme="https://acme.com" acme:hello="world" />
Can be used to add multiple prefixed attributes at once.
This function won't add the xmlns attribute. You need to manually specify it with the namespace_attribute
function.
use function VeeWee\Xml\Writer\Builder\element;
use function VeeWee\Xml\Writer\Builder\namespace_attribute;
use function VeeWee\Xml\Writer\Builder\prefixed_attributes;
element('foo',
namespace_attribute('https://acme.com', 'acme'),
prefixed_attributes([
'acme:hello' => 'world',
'acme:foo' => 'bar',
])
);
<foo xmlns:acme="https://acme.com" acme:hello="world" acme:foo="bar" />
Build a namespace prefixed element.
It can contain a set of configurators that can be used to specify the attributes, children, value, ... of the element.
This function won't add the xmlns attribute. You need to manually specify it with the namespace_attribute
function.
use function VeeWee\Xml\Writer\Builder\namespace_attribute;
use function VeeWee\Xml\Writer\Builder\prefixed_element;
prefixed_element('acme', 'hello',
namespace_attribute('http://acme.com', 'acme'),
...$configurators
);
<acme:hello xmlns:acme="http://acme.com" />
Can be used to insert raw strings into the XML. Be careful: these strings won't be validated or escaped and are just appended to the XML without any sort of validation.
use function VeeWee\Xml\Writer\Builder\raw;
raw('<hello>world</hello>');
<hello>world</hello>
Can set a value to any XML element.
use function VeeWee\Xml\Writer\Builder\element;
use function VeeWee\Xml\Writer\Builder\value;
element('hello', value('world'));
<hello>world</hello>
A builder can be any callable
that takes a XMLWriter
and yields write actions on it:
namespace VeeWee\Xml\Writer\Builder;
use Generator;
use XMLWriter;
interface Builder
{
/**
* @returns \Generator<bool>
*/
public function __invoke(XMLWriter $writer): Generator;
}
For example:
use VeeWee\Xml\Writer\Builder\Builder;
class MyAttribute implements Builder
{
public function __invoke(XMLWriter $writer) : Generator
{
yield $writer->startAttribute('my-attrib');
yield $writer->text('my-value');
yield $writer->endAttribute();
}
}
Specify how you want to configure the XML writer.
By default, the writer does not indent. With this configurator, you can specify how you want to indent the XML file:
use VeeWee\Xml\Writer\Writer;
use function VeeWee\Xml\Writer\Configurator\indentation;
Writer::forFile(
'some.xml',
indentation(' ')
);
Now, every XML level will be indented with the specified indentation string!
A configurator can be any callable
that takes a XMLWriter
and configures it:
namespace VeeWee\Xml\Writer\Configurator;
use XMLWriter;
interface Configurator
{
public function __invoke(XMLWriter $writer): XMLWriter;
}
You can apply the configurator as followed:
use VeeWee\Xml\Writer\Writer;
$document = Writer::configure(...$configurators);
If you are using an in-memory writer, you can use the memory_output()
mapper to get the written content as a string.
use VeeWee\Xml\Writer\Writer;
use function VeeWee\Xml\Writer\Mapper\memory_output;
$doc = Writer::inMemory()
->write($yourXml)
->map(memory_output());
Apply an action on the open writer.
Flushes the writer to the output.
use VeeWee\Xml\Writer\Writer;
use function VeeWee\Xml\Writer\Applicative\flush;
$doc = Writer::forStream($stream)
->write($yourXml)
->apply(flush());
namespace VeeWee\Xml\Writer\Applicative;
interface Applicative
{
/**
* @return mixed
*/
public function __invoke(\XMLWriter $writer): mixed;
}
You can apply the applicative as followed:
namespace VeeWee\Xml\Writer\Writer;
$writer = Writer::configure($opener)->apply($applicative);
Starts a writer that stores the written XML in-memory. You can use the memory_output mapper to retrieve the written XML.
use VeeWee\Xml\Writer\Writer;
use function VeeWee\Xml\Writer\Mapper\memory_output;
$doc = Writer::inMemory(...$configurators)
->write($yourXml)
->map(memory_output());
Writes an XML document to a file.
When the file or folder does not exists, the code will attempt to create it.
If it is not possible to create a target to write to, a RuntimException
will be thrown.
use VeeWee\Xml\Writer\Writer;
$doc = Writer::forFile('some-xml.xml', ...$configurators);
Loads an XML document into an open resource stream.
use VeeWee\Xml\Writer\Writer;
use function VeeWee\Xml\Writer\Applicative\flush;
$doc = Writer::forStream($stream, ...$configurators)
->write($yourXml)
->apply(flush())
namespace VeeWee\Xml\Writer\Opener;
use XMLWriter;
interface Opener
{
public function __invoke(): XMLWriter;
}
You can apply the loader as followed:
namespace VeeWee\Xml\Writer\Writer;
$writer = Writer::configure($opener, ...$configurators);