diff --git a/README.md b/README.md index 8dad80c..c472075 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,27 @@ array(3) { [`TextNodeProcessorInterface`]: src/NodeProcessor/TextNodeProcessorInterface.php +## Passing custom data to `processFile` + +`XmlProcessor::processFile()` accepts an optional associative array of custom data. +It is stored on the [`XmlProcessorContext`], together with the filename currently being processed, +and is available to every node processor via `$context->getXmlProcessorContext()`. + +```php +$processor->processFile('file.xml', ['tenant' => 'acme']); +``` + +```php +public function openElement(OpenContext $context) +{ + $xmlProcessorContext = $context->getXmlProcessorContext(); + $filename = $xmlProcessorContext->getFilename(); + $tenant = $xmlProcessorContext->get('tenant'); +} +``` + +[`XmlProcessorContext`]: src/XmlProcessorContext.php + [`NodeProcessorContext`]: src/NodeProcessor/Context/NodeProcessorContext.php [`OpenContext]: src/NodeProcessor/Context/OpenContext.php diff --git a/src/XmlProcessor.php b/src/XmlProcessor.php index d17bc60..953a27e 100644 --- a/src/XmlProcessor.php +++ b/src/XmlProcessor.php @@ -100,8 +100,13 @@ function getProcessor(string $processorName): ?NodeProcessorInterface return $this->context->getProcessor($processorName); } - public function processFile(string $filename): void + /** + * @param array $data + */ + public function processFile(string $filename, array $data = []): void { + $this->context->setFilename($filename); + $this->context->setData($data); $this->xml->open($filename); foreach ($this->parserProperties as $parserProperty => $value) { $this->xml->setParserProperty($parserProperty, $value); diff --git a/src/XmlProcessorContext.php b/src/XmlProcessorContext.php index 4d00d70..029fe6e 100644 --- a/src/XmlProcessorContext.php +++ b/src/XmlProcessorContext.php @@ -18,6 +18,13 @@ class XmlProcessorContext private Closure $skipNode; + private string $filename = ''; + + /** + * @var array + */ + private array $data = []; + public function __construct(XMLReader $xml, iterable $processors, Closure $skipNode) { $this->xml = $xml; @@ -45,4 +52,52 @@ public function getXMLReader(): XMLReader { return $this->xml; } + + public function setFilename(string $filename): void + { + $this->filename = $filename; + } + + public function getFilename(): string + { + return $this->filename; + } + + /** + * @param array $data + */ + public function setData(array $data): void + { + $this->data = $data; + } + + /** + * @return array + */ + public function getData(): array + { + return $this->data; + } + + /** + * @param mixed $default + * @return mixed + */ + public function get(string $key, $default = null) + { + return $this->data[$key] ?? $default; + } + + /** + * @param mixed $value + */ + public function set(string $key, $value): void + { + $this->data[$key] = $value; + } + + public function has(string $key): bool + { + return \array_key_exists($key, $this->data); + } } diff --git a/tests/Unit/XmlProcessorContextTest.php b/tests/Unit/XmlProcessorContextTest.php index 65e4be6..80cdb20 100644 --- a/tests/Unit/XmlProcessorContextTest.php +++ b/tests/Unit/XmlProcessorContextTest.php @@ -67,6 +67,51 @@ function skipCurrentNodeDataProvider(): iterable yield [false]; } + function testGetFilename(): void + { + $context = new XmlProcessorContext($this->getXMLReaderMock(), [], fn() => true); + self::assertSame('', $context->getFilename()); + + $context->setFilename('foo.xml'); + self::assertSame('foo.xml', $context->getFilename()); + } + + function testGetData(): void + { + $context = new XmlProcessorContext($this->getXMLReaderMock(), [], fn() => true); + self::assertSame([], $context->getData()); + + $context->setData(['foo' => 'bar']); + self::assertSame(['foo' => 'bar'], $context->getData()); + } + + function testGet(): void + { + $context = new XmlProcessorContext($this->getXMLReaderMock(), [], fn() => true); + self::assertNull($context->get('foo')); + self::assertSame('default', $context->get('foo', 'default')); + + $context->setData(['foo' => 'bar']); + self::assertSame('bar', $context->get('foo')); + } + + function testSet(): void + { + $context = new XmlProcessorContext($this->getXMLReaderMock(), [], fn() => true); + $context->set('foo', 'bar'); + self::assertSame('bar', $context->get('foo')); + self::assertSame(['foo' => 'bar'], $context->getData()); + } + + function testHas(): void + { + $context = new XmlProcessorContext($this->getXMLReaderMock(), [], fn() => true); + self::assertFalse($context->has('foo')); + + $context->set('foo', 'bar'); + self::assertTrue($context->has('foo')); + } + private function getXMLReaderMock(): XMLReader { return $this->getMockBuilder(XMLReader::class)->getMock(); diff --git a/tests/Unit/XmlProcessorTest.php b/tests/Unit/XmlProcessorTest.php index bb01908..699358b 100644 --- a/tests/Unit/XmlProcessorTest.php +++ b/tests/Unit/XmlProcessorTest.php @@ -8,6 +8,7 @@ use Generator; use Netlogix\XmlProcessor\NodeProcessor\CloseNodeProcessorInterface; use Netlogix\XmlProcessor\NodeProcessor\Context\CloseContext; +use Netlogix\XmlProcessor\NodeProcessor\Context\NodeProcessorContext; use Netlogix\XmlProcessor\NodeProcessor\Context\OpenContext; use Netlogix\XmlProcessor\NodeProcessor\Context\TextContext; use Netlogix\XmlProcessor\NodeProcessor\NodeProcessorInterface; @@ -82,6 +83,28 @@ public function testProcessFile() $xmlProcessor->processFile(__DIR__ . '/../Fixtures/XmlProcessorTest/test.xml'); } + public function testProcessFile_fillsContextWithFilenameAndData(): void + { + $filename = __DIR__ . '/../Fixtures/XmlProcessorTest/test.xml'; + $nodeProcessor = $this->getMockForAbstractClass(NodeProcessorInterface::class); + + $nodeProcessor + ->method('getSubscribedEvents') + ->will($this->returnCallback( + fn() => yield from [ + XmlProcessor::EVENT_OPEN_FILE => function (NodeProcessorContext $context) use ($filename): void { + $xmlProcessorContext = $context->getXmlProcessorContext(); + self::assertSame($filename, $xmlProcessorContext->getFilename()); + self::assertSame(['foo' => 'bar'], $xmlProcessorContext->getData()); + self::assertSame('bar', $xmlProcessorContext->get('foo')); + } + ] + )); + + $xmlProcessor = new XmlProcessor([$nodeProcessor]); + $xmlProcessor->processFile($filename, ['foo' => 'bar']); + } + public function testProcessFile_skipCurrentNode() { $nodeProcessor = $this->getMockForAbstractClass(NodeProcessorInterface::class);