From 5812a417e56c226d1d238aefa89d73ff65709fac Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Wed, 18 Sep 2024 17:07:47 +0200 Subject: [PATCH] Introduce breaking reader/writer PHP 8.4 changes and new stream functions --- docs/reader.md | 23 ++ docs/writer.md | 75 ++++- src/Xml/Reader/Loader/xml_file_loader.php | 6 +- src/Xml/Reader/Loader/xml_stream_loader.php | 26 ++ src/Xml/Reader/Loader/xml_string_loader.php | 6 +- src/Xml/Reader/Reader.php | 10 + src/Xml/Writer/Applicative/Applicative.php | 10 + src/Xml/Writer/Applicative/flush.php | 20 ++ src/Xml/Writer/Configurator/open.php | 28 -- src/Xml/Writer/Opener/Opener.php | 2 +- src/Xml/Writer/Opener/memory_opener.php | 6 +- src/Xml/Writer/Opener/xml_file_opener.php | 6 +- src/Xml/Writer/Opener/xml_stream_opener.php | 20 ++ src/Xml/Writer/Writer.php | 23 +- src/bootstrap.php | 4 +- stubs/XMLReader.phpstub | 243 ++++++++++++-- stubs/XMLWriter.phpstub | 353 +++++++++++++++++++- 17 files changed, 768 insertions(+), 93 deletions(-) create mode 100644 src/Xml/Reader/Loader/xml_stream_loader.php create mode 100644 src/Xml/Writer/Applicative/Applicative.php create mode 100644 src/Xml/Writer/Applicative/flush.php delete mode 100644 src/Xml/Writer/Configurator/open.php create mode 100644 src/Xml/Writer/Opener/xml_stream_opener.php diff --git a/docs/reader.md b/docs/reader.md index 32635e8..c4b6dee 100644 --- a/docs/reader.md +++ b/docs/reader.md @@ -175,16 +175,39 @@ $reader = Reader::configure($yourLoader, ...$configurators); ```php use VeeWee\Xml\Reader\Reader; +use VeeWee\Xml\Reader\Loader\xml_file_loader; $reader = Reader::fromXmlFile('some-file.xml', ...$configurators); + +// OR + +$reader = Reader::configure(xml_file_loader('some-file.xml', encoding: 'UTF-8', flags: LIBXML_NOBLANKS), ...$configurators); +``` + +#### xml_stream_loader + +```php +use VeeWee\Xml\Reader\Reader; +use function VeeWee\Xml\Reader\Loader\xml_stream_loader; + +$reader = Reader::fromXmlStream($stream, ...$configurators); + +// OR + +$reader = Reader::configure(xml_stream_loader($stream, encoding: 'UTF-8', flags: LIBXML_NOBLANKS), ...$configurators); ``` #### xml_string_loader ```php use VeeWee\Xml\Reader\Reader; +use function VeeWee\Xml\Reader\Loader\xml_string_loader; $reader = Reader::fromXmlString('', ...$configurators); + +// OR + +$reader = Reader::configure(xml_string_loader('', encoding: 'UTF-8', flags: LIBXML_NOBLANKS), ...$configurators); ``` #### Writing your own loader diff --git a/docs/writer.md b/docs/writer.md index 6713beb..6fa9bc8 100644 --- a/docs/writer.md +++ b/docs/writer.md @@ -43,6 +43,7 @@ The Writer consists out of following composable blocks: - [Builders](#builders): Lets you build XML by using a declarative API. - [Configurators](#configurators): Configure how the Writer behaves. - [Mappers](#mappers): Map the XMLWriter to something else. +- [Applicatives](#applicatives): Apply an action on the open writer. - [Openers](#openers): Specify where you want to write to. ## Builders @@ -446,20 +447,6 @@ class MyAttribute implements Builder Specify how you want to configure the XML writer. -#### open - -The opener configurator takes an [opener](#openers) to specify the target of the writer. - -```php -use VeeWee\Xml\Writer\Writer; -use function VeeWee\Xml\Writer\Configurator\open; -use function VeeWee\Xml\Writer\Opener\xml_file_opener; - -Writer::configure( - open(xml_file_opener('somefile.xml')) -); -``` - #### indentation By default, the writer does not indent. @@ -515,6 +502,47 @@ $doc = Writer::inMemory() ->map(memory_output()); ``` +## Applicatives + +Apply an action on the open writer. + + +#### flush + +Flushes the writer to the output. + +```php +use VeeWee\Xml\Writer\Writer; +use function VeeWee\Xml\Writer\Applicative\flush; + +$doc = Writer::forStream($stream) + ->write($yourXml) + ->apply(flush()); +``` + +#### Writing your own applicatives + +```php +namespace VeeWee\Xml\Writer\Applicative; + +interface Applicative +{ + /** + * @return mixed + */ + public function __invoke(\XMLWriter $writer): mixed; +} + +``` + +You can apply the applicative as followed: + +```php +namespace VeeWee\Xml\Writer\Writer; + +$writer = Writer::configure($opener)->apply($applicative); +``` + ## Openers #### memory_opener @@ -533,7 +561,7 @@ $doc = Writer::inMemory(...$configurators) #### xml_file_opener -Loads an XML document from a file. +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. @@ -543,6 +571,19 @@ use VeeWee\Xml\Writer\Writer; $doc = Writer::forFile('some-xml.xml', ...$configurators); ``` +#### xml_stream_opener + +Loads an XML document into an open resource stream. + +```php +use VeeWee\Xml\Writer\Writer; +use function VeeWee\Xml\Writer\Applicative\flush; + +$doc = Writer::forStream($stream, ...$configurators) + ->write($yourXml) + ->apply(flush()) +``` + #### Writing your own opener ```php @@ -552,7 +593,7 @@ use XMLWriter; interface Opener { - public function __invoke(XMLWriter $writer): bool; + public function __invoke(): XMLWriter; } ``` @@ -561,5 +602,5 @@ You can apply the loader as followed: ```php namespace VeeWee\Xml\Writer\Writer; -$writer = Writer::configure($loader, ...$configurators); +$writer = Writer::configure($opener, ...$configurators); ``` diff --git a/src/Xml/Reader/Loader/xml_file_loader.php b/src/Xml/Reader/Loader/xml_file_loader.php index 214ebd4..b684bf0 100644 --- a/src/Xml/Reader/Loader/xml_file_loader.php +++ b/src/Xml/Reader/Loader/xml_file_loader.php @@ -13,13 +13,13 @@ /** * @return Closure(): XMLReader */ -function xml_file_loader(string $file): Closure +function xml_file_loader(string $file, ?string $encoding = null, int $flags = 0): Closure { return static fn (): XMLReader => disallow_issues( - static function () use ($file): XMLReader { + static function () use ($file, $encoding, $flags): XMLReader { Assert::fileExists($file); return disallow_libxml_false_returns( - XMLReader::open($file), + XMLReader::fromUri($file, $encoding, $flags), 'Could not open the provided XML file!' ); } diff --git a/src/Xml/Reader/Loader/xml_stream_loader.php b/src/Xml/Reader/Loader/xml_stream_loader.php new file mode 100644 index 0000000..f2a3b96 --- /dev/null +++ b/src/Xml/Reader/Loader/xml_stream_loader.php @@ -0,0 +1,26 @@ + disallow_issues( + static function () use ($stream, $encoding, $flags, $documentUri): XMLReader { + return disallow_libxml_false_returns( + XMLReader::fromStream($stream, $encoding, $flags, $documentUri), + 'Could not read the provided XML stream!' + ); + } + ); +} diff --git a/src/Xml/Reader/Loader/xml_string_loader.php b/src/Xml/Reader/Loader/xml_string_loader.php index 2b78128..bdc480d 100644 --- a/src/Xml/Reader/Loader/xml_string_loader.php +++ b/src/Xml/Reader/Loader/xml_string_loader.php @@ -13,14 +13,14 @@ /** * @return Closure(): XMLReader */ -function xml_string_loader(string $xml): Closure +function xml_string_loader(string $xml, ?string $encoding = null, int $flags = 0): Closure { return static fn (): XMLReader => disallow_issues( - static function () use ($xml): XMLReader { + static function () use ($xml, $encoding, $flags): XMLReader { Assert::notEmpty($xml, 'The provided XML can not be empty!'); return disallow_libxml_false_returns( - XMLReader::XML($xml), + XMLReader::fromString($xml, $encoding, $flags), 'Could not read the provided XML!' ); } diff --git a/src/Xml/Reader/Reader.php b/src/Xml/Reader/Reader.php index 2bb3927..3759f2e 100644 --- a/src/Xml/Reader/Reader.php +++ b/src/Xml/Reader/Reader.php @@ -14,6 +14,7 @@ use function VeeWee\Xml\ErrorHandling\stop_on_first_issue; use function VeeWee\Xml\Internal\configure; use function VeeWee\Xml\Reader\Loader\xml_file_loader; +use function VeeWee\Xml\Reader\Loader\xml_stream_loader; use function VeeWee\Xml\Reader\Loader\xml_string_loader; final class Reader @@ -57,6 +58,15 @@ public static function fromXmlString(string $xml, callable ... $configurators): return self::configure(xml_string_loader($xml), ...$configurators); } + /** + * @param resource $stream + * @param list $configurators + */ + public static function fromXmlStream(mixed $stream, callable ... $configurators): self + { + return self::configure(xml_stream_loader($stream), ...$configurators); + } + /** * @param callable(NodeSequence): bool $matcher * diff --git a/src/Xml/Writer/Applicative/Applicative.php b/src/Xml/Writer/Applicative/Applicative.php new file mode 100644 index 0000000..e3bbe86 --- /dev/null +++ b/src/Xml/Writer/Applicative/Applicative.php @@ -0,0 +1,10 @@ +flush($empty); + }; +} diff --git a/src/Xml/Writer/Configurator/open.php b/src/Xml/Writer/Configurator/open.php deleted file mode 100644 index dc9d040..0000000 --- a/src/Xml/Writer/Configurator/open.php +++ /dev/null @@ -1,28 +0,0 @@ - disallow_issues( - static function () use ($writer, $opener): XMLWriter { - disallow_libxml_false_returns( - $opener($writer), - 'Could not open the writer stream.' - ); - - return $writer; - } - ); -} diff --git a/src/Xml/Writer/Opener/Opener.php b/src/Xml/Writer/Opener/Opener.php index ea90f5c..8559620 100644 --- a/src/Xml/Writer/Opener/Opener.php +++ b/src/Xml/Writer/Opener/Opener.php @@ -8,5 +8,5 @@ interface Opener { - public function __invoke(XMLWriter $writer): bool; + public function __invoke(): XMLWriter; } diff --git a/src/Xml/Writer/Opener/memory_opener.php b/src/Xml/Writer/Opener/memory_opener.php index c964ec8..0f96126 100644 --- a/src/Xml/Writer/Opener/memory_opener.php +++ b/src/Xml/Writer/Opener/memory_opener.php @@ -8,11 +8,9 @@ use XMLWriter; /** - * @return Closure(XMLWriter): bool XMLWriter + * @return Closure(): XMLWriter */ function memory_opener(): Closure { - return static function (XMLWriter $writer): bool { - return $writer->openMemory(); - }; + return static fn () => XMLWriter::toMemory(); } diff --git a/src/Xml/Writer/Opener/xml_file_opener.php b/src/Xml/Writer/Opener/xml_file_opener.php index 066e264..9d0400e 100644 --- a/src/Xml/Writer/Opener/xml_file_opener.php +++ b/src/Xml/Writer/Opener/xml_file_opener.php @@ -12,16 +12,16 @@ /** * @param non-empty-string $file * - * @return Closure(XMLWriter): bool XMLWriter + * @return Closure(): XMLWriter */ function xml_file_opener(string $file): Closure { - return static function (XMLWriter $writer) use ($file) : bool { + return static function () use ($file) : XMLWriter { // Try to create the file first. // If the file exists, it will truncated. (Default behaviour of XMLWriter as well) // If it cannot be created, it will throw exceptions. write($file, '', WriteMode::Truncate); - return $writer->openUri($file); + return XMLWriter::toUri($file); }; } diff --git a/src/Xml/Writer/Opener/xml_stream_opener.php b/src/Xml/Writer/Opener/xml_stream_opener.php new file mode 100644 index 0000000..73bb548 --- /dev/null +++ b/src/Xml/Writer/Opener/xml_stream_opener.php @@ -0,0 +1,20 @@ + $configurators */ - public static function configure(callable ... $configurators): self + public static function configure(callable $opener, callable ... $configurators): self { - return self::fromUnsafeWriter(new XMLWriter(), ...$configurators); + return self::fromUnsafeWriter($opener(), ...$configurators); } /** @@ -46,7 +47,19 @@ public static function fromUnsafeWriter(XMLWriter $writer, callable ... $configu public static function forFile(string $file, callable ... $configurators): self { return self::configure( - open(xml_file_opener($file)), + xml_file_opener($file), + ...$configurators + ); + } + + /** + * @param resource $stream + * @param list<(callable(XMLWriter): XMLWriter)> $configurators + */ + public static function forStream(mixed $stream, callable ... $configurators): self + { + return self::configure( + xml_stream_opener($stream), ...$configurators ); } @@ -57,7 +70,7 @@ public static function forFile(string $file, callable ... $configurators): self public static function inMemory(callable ... $configurators): self { return self::configure( - open(memory_opener()), + memory_opener(), ...$configurators ); } diff --git a/src/bootstrap.php b/src/bootstrap.php index f44991d..392fd8e 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -125,6 +125,7 @@ 'Xml\Reader\Configurator\substitute_entities' => __DIR__.'/Xml/Reader/Configurator/substitute_entities.php', 'Xml\Reader\Configurator\xsd_schema' => __DIR__.'/Xml/Reader/Configurator/xsd_schema.php', 'Xml\Reader\Loader\xml_file_loader' => __DIR__.'/Xml/Reader/Loader/xml_file_loader.php', + 'Xml\Reader\Loader\xml_stream_loader' => __DIR__.'/Xml/Reader/Loader/xml_stream_loader.php', 'Xml\Reader\Loader\xml_string_loader' => __DIR__.'/Xml/Reader/Loader/xml_string_loader.php', 'Xml\Reader\Matcher\all' => __DIR__.'/Xml/Reader/Matcher/all.php', 'Xml\Reader\Matcher\any' => __DIR__.'/Xml/Reader/Matcher/any.php', @@ -142,6 +143,7 @@ 'Xml\Reader\Matcher\nested' => __DIR__.'/Xml/Reader/Matcher/nested.php', 'Xml\Reader\Matcher\not' => __DIR__.'/Xml/Reader/Matcher/not.php', 'Xml\Reader\Matcher\sequence' => __DIR__.'/Xml/Reader/Matcher/sequence.php', + 'Xml\Writer\Applicative\flush' => __DIR__.'/Xml/Writer/Applicative/flush.php', 'Xml\Writer\Builder\attribute' => __DIR__.'/Xml/Writer/Builder/attribute.php', 'Xml\Writer\Builder\attributes' => __DIR__.'/Xml/Writer/Builder/attributes.php', 'Xml\Writer\Builder\cdata' => __DIR__.'/Xml/Writer/Builder/cdata.php', @@ -159,10 +161,10 @@ 'Xml\Writer\Builder\raw' => __DIR__.'/Xml/Writer/Builder/raw.php', 'Xml\Writer\Builder\value' => __DIR__.'/Xml/Writer/Builder/value.php', 'Xml\Writer\Configurator\indentation' => __DIR__.'/Xml/Writer/Configurator/indentation.php', - 'Xml\Writer\Configurator\open' => __DIR__.'/Xml/Writer/Configurator/open.php', 'Xml\Writer\Mapper\memory_output' => __DIR__.'/Xml/Writer/Mapper/memory_output.php', 'Xml\Writer\Opener\memory_opener' => __DIR__.'/Xml/Writer/Opener/memory_opener.php', 'Xml\Writer\Opener\xml_file_opener' => __DIR__.'/Xml/Writer/Opener/xml_file_opener.php', + 'Xml\Writer\Opener\xml_stream_opener' => __DIR__.'/Xml/Writer/Opener/xml_stream_opener.php', 'Xml\Xsd\Schema\Manipulator\base_path' => __DIR__.'/Xml/Xsd/Schema/Manipulator/base_path.php', 'Xml\Xsd\Schema\Manipulator\overwrite_with_local_files' => __DIR__.'/Xml/Xsd/Schema/Manipulator/overwrite_with_local_files.php', 'Xml\Xslt\Configurator\all_functions' => __DIR__.'/Xml/Xslt/Configurator/all_functions.php', diff --git a/stubs/XMLReader.phpstub b/stubs/XMLReader.phpstub index 7e008bc..2c742f1 100644 --- a/stubs/XMLReader.phpstub +++ b/stubs/XMLReader.phpstub @@ -1,26 +1,225 @@