diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index a781440..fc5f29a 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -12,18 +12,50 @@ on: jobs: Build: runs-on: 'ubuntu-latest' - container: 'byjg/php:${{ matrix.php-version }}-cli' + container: + image: 'byjg/php:${{ matrix.php-version }}-cli' + options: --user root --privileged strategy: matrix: php-version: + - "8.5" + - "8.4" - "8.3" - - "8.2" - - "8.1" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - run: composer install - - run: ./vendor/bin/phpunit + - run: composer test + + Psalm: + name: Psalm Static Analyzer + runs-on: ubuntu-latest + permissions: + # for github/codeql-action/upload-sarif to upload SARIF results + security-events: write + container: + image: byjg/php:8.4-cli + options: --user root --privileged + + steps: + - name: Git checkout + uses: actions/checkout@v4 + + - name: Composer + run: composer install + + - name: Psalm + # Note: Ignoring error code 2, which just signals that some + # flaws were found, not that Psalm itself failed to run. + run: ./vendor/bin/psalm + --show-info=true + --report=psalm-results.sarif || [ $? = 2 ] + + - name: Upload Analysis results to GitHub + uses: github/codeql-action/upload-sarif@v4 + if: github.ref == 'refs/heads/master' + with: + sarif_file: psalm-results.sarif Documentation: if: github.ref == 'refs/heads/master' @@ -32,5 +64,6 @@ jobs: with: folder: php project: ${{ github.event.repository.name }} - secrets: inherit + secrets: + DOC_TOKEN: ${{ secrets.DOC_TOKEN }} diff --git a/.run/PHPUnit.run.xml b/.run/PHPUnit.run.xml new file mode 100644 index 0000000..f81c245 --- /dev/null +++ b/.run/PHPUnit.run.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.run/psalm.run.xml b/.run/psalm.run.xml new file mode 100644 index 0000000..d9c1b61 --- /dev/null +++ b/.run/psalm.run.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..a8c1b2a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug current Script in Console", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 9003, + "runtimeArgs": [ + "-dxdebug.start_with_request=yes" + ], + "env": { + "XDEBUG_MODE": "debug,develop", + "XDEBUG_CONFIG": "client_port=${port}" + } + }, + { + "name": "PHPUnit Debug", + "type": "php", + "request": "launch", + "program": "${workspaceFolder}/vendor/bin/phpunit", + "cwd": "${workspaceFolder}", + "port": 9003, + "runtimeArgs": [ + "-dxdebug.start_with_request=yes" + ], + "env": { + "XDEBUG_MODE": "debug,develop", + "XDEBUG_CONFIG": "client_port=${port}" + } + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 652d019..654a123 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,44 @@ # AnyDataset-Xml -[![Build Status](https://github.com/byjg/php-anydataset-xml/actions/workflows/phpunit.yml/badge.svg?branch=master)](https://github.com/byjg/php-anydataset-xml/actions/workflows/phpunit.yml) -[![Opensource ByJG](https://img.shields.io/badge/opensource-byjg-success.svg)](http://opensource.byjg.com) -[![GitHub source](https://img.shields.io/badge/Github-source-informational?logo=github)](https://github.com/byjg/php-anydataset-xml/) -[![GitHub license](https://img.shields.io/github/license/byjg/php-anydataset-xml.svg)](https://opensource.byjg.com/opensource/licensing.html) +[![Sponsor](https://img.shields.io/badge/Sponsor-%23ea4aaa?logo=githubsponsors&logoColor=white&labelColor=0d1117)](https://github.com/sponsors/byjg) +[![Build Status](https://github.com/byjg/php-anydataset-xml/actions/workflows/phpunit.yml/badge.svg?branch=master)](https://github.com/byjg/php-anydataset-xml/actions/workflows/phpunit.yml) +[![Opensource ByJG](https://img.shields.io/badge/opensource-byjg-success.svg)](http://opensource.byjg.com) +[![GitHub source](https://img.shields.io/badge/Github-source-informational?logo=github)](https://github.com/byjg/php-anydataset-xml/) +[![GitHub license](https://img.shields.io/github/license/byjg/php-anydataset-xml.svg)](https://opensource.byjg.com/opensource/licensing.html) [![GitHub release](https://img.shields.io/github/release/byjg/php-anydataset-xml.svg)](https://github.com/byjg/php-anydataset-xml/releases/) -XML abstraction dataset. Anydataset is an agnostic data source abstraction layer in PHP. - -See more about Anydataset [here](https://opensource.byjg.com/php/anydataset). - -## Examples - -### Simple Manipulation - -example1.xml -```xml - - - - Everyday Italian - Giada De Laurentiis - 2005 - 30.00 - - - Harry Potter - J K. Rowling - 2005 - 29.99 - - - Learning XML - Erik T. Ray - 2003 - 39.95 - - +XML abstraction dataset for the AnyDataset library. AnyDataset is an agnostic data source abstraction layer in PHP. + +## Features + +- **Simple XML Processing**: Process XML data with a clean, consistent API +- **XPath Support**: Use XPath expressions to select nodes and attributes +- **Namespace Support**: Full support for XML namespaces +- **Repeated Node Handling**: Automatically handles repeated nodes by returning arrays +- **Custom Field Processing**: Define custom field mappings with closures/callbacks +- **Flexible Input**: Accept XML as string, DOMDocument, XmlNode, or File object +- **AnyDataset Integration**: Compatible with the AnyDataset abstraction layer + +## Documentation + +- [XmlDataset](docs/XmlDataset.md) - Main class for working with XML data +- [XPath Expressions](docs/XPath-Expressions.md) - Guide to using XPath in the library + +## Installation + +```bash +composer require "byjg/anydataset-xml" ``` -example1.php +## Quick Example + ```php "@category", "title" => "title", @@ -60,99 +52,25 @@ $dataset = new \ByJG\AnyDataset\Xml\XmlDataset( $iterator = $dataset->getIterator(); foreach ($iterator as $row) { echo $row->get('category'); // Print COOKING, CHILDREN, WEB - echo $row->get('title'); // Print Everyday Italian, Harry Potter, Learning Xml + echo $row->get('title'); // Print Everyday Italian, Harry Potter, Learning XML echo $row->get('lang'); // Print en-US, de-DE, pt-BR echo $row->get('lang2'); // Print en, de, pt } ``` -### Xml with namespaces - -example2.xml -```xml - - - myId - 2014-09-15T19:35:55.795Z - - Title - - - - - - - - My Name - My Email - - Contacts - 2107 - 1 - 20 - - http://www.google.com/m8/feeds/contacts/my%40gmail.com/base/0 - 2013-10-05T22:16:03.564Z - - Person 1 - - - - - - - - http://www.google.com/m8/feeds/contacts/my%40gmail.com/base/1 - 2012-07-12T17:19:17.546Z - - Person 2 - - - - - - -``` - -example2.php -```php - "http://www.w3.org/2005/Atom", - "gd" => "http://schemas.google.com/g/2005" -); - -$rootNode = 'fake:entry'; -$colNode = array("id" => "fake:id", "updated" => "fake:updated", "name" => "fake:title", "email" => "gd:email/@address"); -$xmlDataset = new \ByJG\AnyDataset\Xml\XmlDataset( - $xml, - $rootNode, - $colNode, - $namespace -); -$xmlIterator = $xmlDataset->getIterator(); -``` - -## Install +## Dependencies -``` -composer require "byjg/anydataset-xml" +```mermaid +flowchart TD + byjg/anydataset-xml --> byjg/anydataset + byjg/anydataset-xml --> ext-dom ``` -## Running the Unit tests +## Running the Unit Tests ```bash vendor/bin/phpunit ``` -## Dependencies - -```mermaid -flowchart TD - byjg/anydataset-xml --> byjg/anydataset - byjg/anydataset-xml --> ext-dom -``` ---- [Open source ByJG](http://opensource.byjg.com) diff --git a/composer.json b/composer.json index c2e41ab..734c410 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "byjg/anydataset-xml", - "description": "Anydataset Xml abstraction. Anydataset is an agnostic data source abstraction layer in PHP.", + "description": "XML abstraction dataset for the AnyDataset library. AnyDataset is an agnostic data source abstraction layer in PHP.", "autoload": { "psr-4": { "ByJG\\AnyDataset\\Xml\\": "src/" @@ -14,15 +14,20 @@ "prefer-stable": true, "minimum-stability": "dev", "require": { - "php": ">=8.1 <8.4", + "php": ">=8.3 <8.6", "ext-dom": "*", - "byjg/anydataset": "^5.0" + "byjg/anydataset": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^9.6" + "phpunit/phpunit": "^10.5|^11.5", + "vimeo/psalm": "^5.9|^6.13" }, "provide": { "byjg/anydataset-implementation": "1.0" }, + "scripts": { + "test": "vendor/bin/phpunit", + "psalm": "vendor/bin/psalm" + }, "license": "MIT" } diff --git a/docs/XPath-Expressions.md b/docs/XPath-Expressions.md new file mode 100644 index 0000000..14bd02d --- /dev/null +++ b/docs/XPath-Expressions.md @@ -0,0 +1,128 @@ +--- +sidebar_position: 2 +title: XPath Expressions +--- + +# XPath Expressions in AnyDataset-Xml + +This library uses XPath expressions to select nodes and attributes from XML documents. Understanding XPath is essential for effectively using the AnyDataset-Xml library. + +## Basic XPath Syntax + +Here are some common XPath expressions used in the library: + +| Expression | Description | Example | +|----------------------|------------------------------------------------------------------|----------------------------------------------------------------------------------------| +| `element` | Selects all elements with the given name | `"title"` selects all `` elements | +| `@attribute` | Selects the attribute with the given name | `"@category"` selects the category attribute | +| `element/@attribute` | Selects an attribute of an element | `"title/@lang"` selects the lang attribute of title elements | +| `parent/child` | Selects all child elements of the parent | `"book/author"` selects all author elements that are children of book elements | +| `//element` | Selects all elements with the given name, regardless of position | `"//title"` selects all title elements anywhere in the document | +| `*` | Selects all elements | `"book/*"` selects all child elements of book | +| `element[n]` | Selects the nth element | `"author[1]"` selects the first author element | +| `element[condition]` | Selects elements that satisfy the condition | `"book[@category='WEB']"` selects book elements with category attribute equal to 'WEB' | + +## Implementation Details + +:::warning Key Points +- All field names in the resulting data are converted to lowercase +- If an XPath expression doesn't match any nodes, an empty string is returned for that field +- If an XPath expression matches multiple nodes, all values are collected automatically in an array +::: + +## Examples in AnyDataset-Xml + +### Basic Element Selection + +```php +$colNodes = [ + "title" => "title", // Selects the <title> element + "author" => "author", // Selects the <author> element + "year" => "year", // Selects the <year> element + "price" => "price" // Selects the <price> element +]; +``` + +### Attribute Selection + +```php +$colNodes = [ + "category" => "@category", // Selects the category attribute of the current node + "lang" => "title/@lang" // Selects the lang attribute of the title element +]; +``` + +### Using Namespaces + +When working with namespaced XML, you need to register the namespaces and use them in your XPath expressions: + +```php +$namespace = [ + "atom" => "http://www.w3.org/2005/Atom", + "gd" => "http://schemas.google.com/g/2005" +]; + +$colNodes = [ + "id" => "atom:id", // Selects the id element in the atom namespace + "email" => "gd:email/@address" // Selects the address attribute of the email element in the gd namespace +]; + +$dataset = new \ByJG\AnyDataset\Xml\XmlDataset( + $xml, + "atom:entry", // Selects entry elements in the atom namespace + $colNodes, + $namespace +); +``` + +## Advanced Usage + +### Custom Field Processing + +You can use callback functions to process field values: + +```php +$colNodes = [ + "title" => "title", + "lang" => "title/@lang", + "shortLang" => function ($row) { + return substr($row->get('lang'), 0, 2); + } +]; +``` + +### Handling Repeated Nodes + +When an XPath expression matches multiple nodes, the values are automatically collected in an array: + +```php +// For XML like: +// <book> +// <author>Author 1</author> +// <author>Author 2</author> +// </book> + +$colNodes = [ + "authors" => "author" // Will automatically return an array of all author values: ["Author 1", "Author 2"] +]; + +// Access as: +$authorArray = $row->get('authors'); +``` + +### Accessing Field Values + +:::caution Case Sensitivity +All field names are converted to lowercase when accessed through the iterator. +::: + +```php +$colNodes = [ + "Title" => "title", + "AUTHOR" => "author" +]; + +// Access using lowercase: +$title = $row->get('title'); // Not $row->get('Title') +$author = $row->get('author'); // Not $row->get('AUTHOR') +``` \ No newline at end of file diff --git a/docs/XmlDataset.md b/docs/XmlDataset.md new file mode 100644 index 0000000..bb80ac5 --- /dev/null +++ b/docs/XmlDataset.md @@ -0,0 +1,135 @@ +--- +sidebar_position: 1 +title: XmlDataset +--- + +# XmlDataset + +The `XmlDataset` class is the main entry point for working with XML data in the AnyDataset library. + +## Basic Usage + +```php +$dataset = new \ByJG\AnyDataset\Xml\XmlDataset( + $xml, // The XML string, can also be an XmlNode, DOMDocument, or File object + "book", // The node that represents a row + [ + "category" => "@category", + "title" => "title", + "lang" => "title/@lang", + ] // Mapping columns +); + +$iterator = $dataset->getIterator(); +foreach ($iterator as $row) { + echo $row->get('category'); + echo $row->get('title'); + echo $row->get('lang'); +} +``` + +## Constructor Parameters + +```php +public function __construct( + XmlNode|DOMDocument|string|File $xml, + string $rowNode, + array $colNode, + array $registerNS = [] +) +``` + +- **$xml**: The XML source. Can be: + - A string containing XML + - An XmlNode object + - A DOMDocument object + - A File object + +- **$rowNode**: XPath expression that identifies the nodes to be treated as rows + +- **$colNode**: Associative array mapping field names to XPath expressions + - Keys: The field names that will be accessible in the iterator (will be converted to lowercase) + - Values: XPath expressions relative to the row node, or callback functions + +- **$registerNS**: Optional array of namespace prefixes and URIs + +## Methods + +### getIterator() + +```php +public function getIterator(): GenericIterator +``` + +Returns an `XmlIterator` instance that can be used to iterate through the XML data. + +## Field Values Handling + +:::info Important Behavior +- When an XPath expression matches multiple nodes, the values are automatically collected in an array +- All field names are converted to lowercase when accessed through the iterator +- If no nodes match an XPath expression, an empty string is returned for that field +::: + +## Examples + +### Basic Example + +```php +$xml = file_get_contents('example.xml'); + +$dataset = new \ByJG\AnyDataset\Xml\XmlDataset( + $xml, + "book", + [ + "category" => "@category", + "title" => "title", + "lang" => "title/@lang" + ] +); + +$iterator = $dataset->getIterator(); +foreach ($iterator as $row) { + echo $row->get('category'); // Print attribute values + echo $row->get('title'); // Print element values + echo $row->get('lang'); // Print attribute of an element +} +``` + +### With Custom Field Processing + +```php +$dataset = new \ByJG\AnyDataset\Xml\XmlDataset( + $xml, + "book", + [ + "category" => "@category", + "title" => "title", + "lang" => "title/@lang", + "lang2" => function ($row) { + return substr($row->get('lang'), 0, 2); + } + ] +); +``` + +### With Namespaces + +```php +$namespace = [ + "atom" => "http://www.w3.org/2005/Atom", + "gd" => "http://schemas.google.com/g/2005" +]; + +$dataset = new \ByJG\AnyDataset\Xml\XmlDataset( + $xml, + "atom:entry", + [ + "id" => "atom:id", + "updated" => "atom:updated", + "name" => "atom:title", + "email" => "gd:email/@address" + ], + $namespace +); +``` \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f01d221..711a9b0 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -4,33 +4,33 @@ To change this license header, choose License Headers in Project Properties. To change this template file, choose Tools | Templates and open the template in the editor. --> - <!-- see http://www.phpunit.de/wiki/Documentation --> -<phpunit bootstrap="./vendor/autoload.php" +<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + bootstrap="./vendor/autoload.php" colors="true" testdox="true" - convertErrorsToExceptions="true" - convertNoticesToExceptions="true" - convertWarningsToExceptions="true" - convertDeprecationsToExceptions="true" - stopOnFailure="false"> + displayDetailsOnTestsThatTriggerDeprecations="true" + displayDetailsOnTestsThatTriggerErrors="true" + displayDetailsOnTestsThatTriggerNotices="true" + displayDetailsOnTestsThatTriggerWarnings="true" + displayDetailsOnPhpunitDeprecations="true" + stopOnFailure="false" + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"> + <php> + <ini name="display_errors" value="On"/> + <ini name="display_startup_errors" value="On"/> + <ini name="error_reporting" value="E_ALL"/> + </php> - <php> - <ini name="display_errors" value="On" /> - <ini name="display_startup_errors" value="On" /> - <ini name="error_reporting" value="E_ALL" /> - </php> + <source> + <include> + <directory>./src/</directory> + </include> + </source> - <filter> - <whitelist> - <directory>./src</directory> - </whitelist> - </filter> - - <testsuites> - <testsuite name="Test Suite"> - <directory>./tests</directory> - </testsuite> - </testsuites> - + <testsuites> + <testsuite name="Test Suite"> + <directory>./tests/</directory> + </testsuite> + </testsuites> </phpunit> diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..037de8b --- /dev/null +++ b/psalm.xml @@ -0,0 +1,18 @@ +<?xml version="1.0"?> +<psalm + errorLevel="3" + resolveFromConfigFile="true" + findUnusedBaselineEntry="true" + findUnusedCode="false" + cacheDirectory="/tmp/psalm" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="https://getpsalm.org/schema/config" + xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" +> + <projectFiles> + <directory name="src" /> + <ignoreFiles> + <directory name="vendor" /> + </ignoreFiles> + </projectFiles> +</psalm> \ No newline at end of file diff --git a/src/XmlDataset.php b/src/XmlDataset.php index c80d6df..858d465 100644 --- a/src/XmlDataset.php +++ b/src/XmlDataset.php @@ -3,13 +3,12 @@ namespace ByJG\AnyDataset\Xml; use ByJG\AnyDataset\Core\GenericIterator; -use ByJG\AnyDataset\Core\Exception\DatasetException; +use ByJG\XmlUtil\Exception\FileException; use ByJG\XmlUtil\Exception\XmlUtilException; use ByJG\XmlUtil\File; use ByJG\XmlUtil\XmlDocument; use ByJG\XmlUtil\XmlNode; use DOMDocument; -use InvalidArgumentException; class XmlDataset { @@ -26,7 +25,7 @@ class XmlDataset * * @var string[] */ - private ?array $colNodes; + private array $colNodes; /** * @var XmlDocument @@ -42,9 +41,10 @@ class XmlDataset /** * @param XmlNode|DOMDocument|string|File $xml * @param string $rowNode - * @param string[] $colNode + * @param array $colNode * @param array $registerNS * @throws XmlUtilException + * @throws FileException */ public function __construct(XmlNode|DOMDocument|string|File $xml, string $rowNode, array $colNode, array $registerNS = []) { diff --git a/src/XmlIterator.php b/src/XmlIterator.php index 6cd697b..4b5f24e 100644 --- a/src/XmlIterator.php +++ b/src/XmlIterator.php @@ -3,12 +3,14 @@ namespace ByJG\AnyDataset\Xml; use ByJG\AnyDataset\Core\GenericIterator; -use ByJG\AnyDataset\Core\Exception\IteratorException; use ByJG\AnyDataset\Core\Row; +use ByJG\AnyDataset\Core\RowArray; +use ByJG\AnyDataset\Core\RowInterface; use ByJG\XmlUtil\Exception\XmlUtilException; use ByJG\XmlUtil\XmlNode; use DOMNodeList; -use InvalidArgumentException; +use Override; +use ReturnTypeWillChange; class XmlIterator extends GenericIterator { @@ -16,94 +18,108 @@ class XmlIterator extends GenericIterator /** * Enter description here... * - * @var DOMNodeList|null + * @var DOMNodeList */ - private ?DOMNodeList $nodeList = null; + private DOMNodeList $nodeList; /** * Enter description here... * * @var string[] */ - private ?array $colNodes = null; + private array $colNodes; + + private ?RowInterface $currentRow = null; + private int $currentIndex = 0; - /** - * Enter description here... - * - * @var int - */ - private int $current = 0; protected array $registerNS; - public function __construct(DOMNodeList $nodeList, array $colNodes, array $registerNS = null) + public function __construct(DOMNodeList $nodeList, array $colNodes, ?array $registerNS = null) { - $this->registerNS = $registerNS; + $this->registerNS = $registerNS ?? []; $this->nodeList = $nodeList; $this->colNodes = $colNodes; - - $this->current = 0; - } - - public function count(): int - { - return $this->nodeList->length; } /** - * @access public - * @return bool + * @throws XmlUtilException */ - public function hasNext(): bool + protected function parseXmlNode(): ?RowInterface { - if ($this->current < $this->count()) { - return true; + if (!$this->valid()) { + return null; } - return false; - } + $rowNumber = $this->currentIndex; + $node = $this->nodeList->item($rowNumber); - /** - * @access public - * @return Row|null - * @throws IteratorException - * @throws XmlUtilException - */ - public function moveNext(): ?Row - { - if (!$this->hasNext()) { + if ($node === null) { return null; } - $node = $this->nodeList->item($this->current++); - - $row = new Row(); + $row = new RowArray(); $callables = []; - foreach ($this->colNodes as $key => $colxpath) { - if (is_callable($colxpath)) { - $callables[$key] = $colxpath; + $xmlNode = XmlNode::instance($node); + $lowercaseKeys = array_map('strtolower', array_keys($this->colNodes)); + $this->colNodes = array_combine($lowercaseKeys, array_values($this->colNodes)); + foreach ($this->colNodes as $key => $colXpath) { + if (is_callable($colXpath)) { + $callables[$key] = $colXpath; continue; } - $nodeCol = XmlNode::instance($node)->selectNodes($colxpath, $this->registerNS); + $nodeCol = $xmlNode->selectNodes($colXpath, $this->registerNS); if ($nodeCol->count() == 0) { - $row->addField(strtolower($key), ""); + $row->set($key, ""); } else { foreach ($nodeCol as $col) { - $row->addField(strtolower($key), $col->nodeValue); + $row->set($key, $col->nodeValue, append: true); } } } foreach ($callables as $key => $callable) { - $row->addField(strtolower($key), $callable($row)); + $row->set($key, $callable($row), append: true); } + $this->currentRow = $row; + return $row; } + #[ReturnTypeWillChange] + #[Override] public function key(): int { - return $this->current; + return $this->currentIndex; + } + + /** + * @return RowInterface|null + */ + #[ReturnTypeWillChange] + #[Override] + public function current(): ?RowInterface + { + if ($this->currentRow === null) { + $this->parseXmlNode(); + } + return $this->currentRow; + } + + #[ReturnTypeWillChange] + #[Override] + public function next(): void + { + $this->currentIndex++; + $this->currentRow = null; + } + + #[ReturnTypeWillChange] + #[Override] + public function valid(): bool + { + return ($this->currentIndex < count($this->nodeList)); } } diff --git a/tests/XmlDatasetTest.php b/tests/XmlDatasetTest.php index 96e4318..d33e0dd 100644 --- a/tests/XmlDatasetTest.php +++ b/tests/XmlDatasetTest.php @@ -4,8 +4,10 @@ use ByJG\AnyDataset\Core\IteratorInterface; use ByJG\AnyDataset\Core\Row; +use ByJG\AnyDataset\Core\RowInterface; use ByJG\AnyDataset\Xml\XmlDataset; use ByJG\XmlUtil\Exception\XmlUtilException; +use Override; use PHPUnit\Framework\TestCase; class XmlDatasetTest extends TestCase @@ -40,6 +42,7 @@ class XmlDatasetTest extends TestCase protected $arrTest2 = array(); // Run before each test case + #[Override] public function setUp(): void { $this->arrTest = array(); @@ -58,9 +61,8 @@ public function testcreateXMLDataset() $xmlDataset = new XmlDataset(XmlDatasetTest::XML_OK, $this->rootNode, $this->arrColumn); $xmlIterator = $xmlDataset->getIterator(); - $this->assertTrue($xmlIterator instanceof IteratorInterface); - $this->assertTrue($xmlIterator->hasNext()); - $this->assertEquals($xmlIterator->Count(), 3); + $this->assertTrue($xmlIterator->valid()); + $this->assertCount(3, $xmlIterator->toArray()); } public function testnavigateXMLIterator() @@ -69,8 +71,9 @@ public function testnavigateXMLIterator() $xmlIterator = $xmlDataset->getIterator(); $count = 0; - while ($xmlIterator->hasNext()) { - $this->assertSingleRow($xmlIterator->moveNext(), $count++); + while ($xmlIterator->valid()) { + $this->assertSingleRow($xmlIterator->current(), $count++); + $xmlIterator->next(); } $this->assertEquals($count, 3); @@ -100,7 +103,7 @@ public function testwrongNodeRoot() $xmlDataset = new XmlDataset(XmlDatasetTest::XML_OK, "wrong", $this->arrColumn); $xmlIterator = $xmlDataset->getIterator(); - $this->assertEquals($xmlIterator->count(), 0); + $this->assertCount(0, $xmlIterator->toArray()); } public function testwrongColumn() @@ -108,7 +111,7 @@ public function testwrongColumn() $xmlDataset = new XmlDataset(XmlDatasetTest::XML_OK, $this->rootNode, array("title" => "aaaa")); $xmlIterator = $xmlDataset->getIterator(); - $this->assertEquals($xmlIterator->count(), 3); + $this->assertCount(3, $xmlIterator->toArray()); } public function testrepeatedNodes() @@ -123,22 +126,20 @@ public function testrepeatedNodes() <price>30.00</price> </book></bookstore>'; - $xmlDataset = new XmlDataset($xml, $this->rootNode, array("author" => "author")); + $xmlDataset = new XmlDataset($xml, $this->rootNode, array("AUTHOR" => "author")); // It will be converted to author $xmlIterator = $xmlDataset->getIterator(); - $this->assertTrue($xmlIterator instanceof IteratorInterface); - $this->assertTrue($xmlIterator->hasNext()); - $this->assertEquals(1, $xmlIterator->Count()); + $this->assertTrue($xmlIterator->valid()); - $sr = $xmlIterator->moveNext(); - $authors = $sr->getAsArray('author'); + $sr = $xmlIterator->current(); + $authors = $sr->get('author'); $this->assertEquals(2, count($authors)); $this->assertEquals('Giada De Laurentiis', $authors[0]); $this->assertEquals('Another Author', $authors[1]); } - public function testatomXml() + public function testAtomXml() { $xml = '<feed xmlns="http://www.w3.org/2005/Atom" xmlns:batch="http://schemas.google.com/gdata/batch" xmlns:gContact="http://schemas.google.com/contact/2008" xmlns:gd="http://schemas.google.com/g/2005" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/"> <id>myId</id> @@ -152,7 +153,7 @@ public function testatomXml() <link rel="self" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/my%40gmail.com/full?max-results=20"/> <link rel="next" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/my%40gmail.com/full?max-results=20&start-index=21"/> <author> - <name>My Name</name> + <n>My Name</n> <email>My Email</email> </author> <generator version="1.0" uri="http://www.google.com/m8/feeds">Contacts</generator> @@ -187,22 +188,30 @@ public function testatomXml() "gd" => "http://schemas.google.com/g/2005" ); $rootNode = 'fake:entry'; - $colNode = array("id" => "fake:id", "updated" => "fake:updated", "name" => "fake:title", "email" => "gd:email/@address", "item" => function($row) { return $row->get("name") . " - " . $row->get("email"); }); + $colNode = [ + "id" => "fake:id", + "updated" => "fake:updated", + "name" => "fake:title", + "email" => "gd:email/@address", + "item" => function($row) { return $row->get("name") . " - " . $row->get("email"); } + ]; $xmlDataset = new XmlDataset($xml, $rootNode, $colNode, $namespace); $xmlIterator = $xmlDataset->getIterator(); - $this->assertTrue($xmlIterator instanceof IteratorInterface); - $this->assertTrue($xmlIterator->hasNext()); - $this->assertEquals(2, $xmlIterator->Count()); + $this->assertTrue($xmlIterator->valid()); - $row = $xmlIterator->moveNext(); + $rowCur = $xmlIterator->current(); + + $row = $rowCur; $this->assertEquals("http://www.google.com/m8/feeds/contacts/my%40gmail.com/base/0", $row->get("id")); $this->assertEquals("2013-10-05T22:16:03.564Z", $row->get("updated")); $this->assertEquals("Person 1", $row->get("name")); $this->assertEquals("p1@gmail.com", $row->get("email")); $this->assertEquals("Person 1 - p1@gmail.com", $row->get("item")); + $this->assertEquals($row, $rowCur); - $row = $xmlIterator->moveNext(); + $xmlIterator->next(); + $row = $xmlIterator->current(); $this->assertEquals("http://www.google.com/m8/feeds/contacts/my%40gmail.com/base/1", $row->get("id")); $this->assertEquals("2012-07-12T17:19:17.546Z", $row->get("updated")); $this->assertEquals("Person 2", $row->get("name")); @@ -212,13 +221,13 @@ public function testatomXml() /** - * @param Row $sr + * @param RowInterface $sr */ public function assertSingleRow($sr, $count) { - $this->assertEquals($sr->get("category"), $this->arrTest[$count]["category"]); - $this->assertEquals($sr->get("title"), $this->arrTest[$count]["title"]); - $this->assertEquals($sr->get("lang"), $this->arrTest[$count]["lang"]); + $this->assertEquals($this->arrTest[$count]["category"], $sr->get("category"), "Row $count"); + $this->assertEquals($this->arrTest[$count]["title"], $sr->get("title"), "Row $count"); + $this->assertEquals($this->arrTest[$count]["lang"], $sr->get("lang"), "Row $count"); } }