-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d071629
commit eb5e1c1
Showing
6 changed files
with
317 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# General entries | ||
|
||
# IntelliJ project files | ||
.idea | ||
*.iml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
# SimpleXMlLoader | ||
A simple wrapper around SimpleXML's interface for loading XML resources or string data. | ||
|
||
Main goals: | ||
- provide a transparent OOP interface to SimpleXML's `simplexml_load_file()` and `simplexml_load_string()` functions | ||
- handle SimpleXML's internal errors | ||
- follow LibXMl's functional API | ||
- throw catchable Exceptions (XmlLoaderException) | ||
|
||
This wrapper will: | ||
- try to load your XML resource into a \SimpleXMLElement object | ||
- handle any LibXML errors that occur during loading | ||
|
||
This wrapper will not: | ||
- validate your XML | ||
- do any operation over the \SimpleXMLElement | ||
|
||
# Basic usage | ||
There are two main functions in the SimpleXMlLoader: | ||
``` | ||
public function loadFile($filename, $class_name = "SimpleXMLElement", $options = 0, $ns = "", $is_prefix = false); | ||
public function loadString($data, $class_name = "SimpleXMLElement", $options = 0, $ns = "", $is_prefix = false); | ||
``` | ||
**NOTE:** Notice that these functions follow the signature of `simplexml_load_file()` and `simplexml_load_string()`. You should check the docs of [simplexml_load_file()](http://php.net/manual/en/function.simplexml-load-file.php) and [simplexml_load_string()](http://php.net/manual/en/function.simplexml-load-string.php) for details. And NO the author does not like the function parameters' names :). | ||
|
||
The loader throws XmlLoaderException on any error that occurs you should always wrap it in a try/catch block and handle any errors yourself: | ||
``` | ||
use BorislavSabev\SimpleXmlLoader\XmlLoader; | ||
use BorislavSabev\SimpleXmlLoader\Exception\XmlLoaderException; | ||
$xmlLoader = new XmlLoader; | ||
try { | ||
/** @var \SimpleXMLElement $simpleXmlElement */ | ||
$simpleXmlElement = $xmlLoader->loadFile($filename); | ||
//Now do something with the loaded \SimpleXMLElement... | ||
} catch (XmlLoaderException $e) { | ||
/** @var array $xmlErrors */ | ||
$xmlErrors = $xmlLoader->getXmlPayload() | ||
->getXmlErrors(); | ||
} | ||
``` | ||
|
||
The XmlLoader instance is meant to be reused thus: | ||
- Each call to `XmlLoader->loadFile()` or `XmlLoader->loadString()` will clear any LibXml errors stored | ||
- Each call to `XmlLoader->loadFile()` or `XmlLoader->loadString()` will replace it's internal XmlPayload object | ||
|
||
## Using it in a loop | ||
As each consecutive call to a loader method resets the state of LibXML and the payload you must extract all the data that you need between consecutive calls. | ||
|
||
``` | ||
use BorislavSabev\SimpleXmlLoader\XmlLoader; | ||
use BorislavSabev\SimpleXmlLoader\Exception\XmlLoaderException; | ||
$xmlLoader = new XmlLoader; | ||
foreach ($aBunchOfXmlStrings as $xmlString { | ||
try { | ||
/** @var \SimpleXMLElement $simpleXmlElement */ | ||
$simpleXmlElement = $xmlLoader->loadString($xmlString); | ||
} catch (XmlLoaderException $e) { | ||
/** @var array $xmlErrors */ | ||
$xmlErrors = $xmlLoader->getXmlPayload() | ||
->getXmlErrors(); | ||
} | ||
//Any data within LibXML or our XmlPayload will be lost after this iteration of the loop | ||
} | ||
``` | ||
|
||
# Reusing XmlPayload | ||
You can also reuse the XmlPayload in your application if you which. Say for example you use this wrapper to parse different XML file but then want to pass the result to different Services in your code that will handle any business logic. You could just get the XmlPayload and pass it along to a specific service (or whatever really): | ||
|
||
``` | ||
use BorislavSabev\SimpleXmlLoader\XmlLoader; | ||
use BorislavSabev\SimpleXmlLoader\Exception\XmlLoaderException; | ||
$xmlLoader = new XmlLoader; | ||
try { | ||
$xmlLoader->loadFile($filename); | ||
/** @var XmlPayload $xmlPayload */ | ||
$xmlPayload = $xmlLoader->getXmlPayload(); | ||
//Generic example: | ||
$serviceBroker->pass( | ||
MyCoolService::class, | ||
$xmlPayload | ||
); | ||
//Now do something with the loaded \SimpleXMLElement... | ||
} catch (XmlLoaderException $e) { | ||
/** @var array $xmlErrors */ | ||
$xmlErrors = $xmlLoader->getXmlPayload() | ||
->getXmlErrors(); | ||
} | ||
``` | ||
**NOTE:** Generally you should have you own payload objects to pass data around in your Domain. The idea of XmlPayload is to be internal for XmlLoader thus it cannot contain any logic outside of that task. | ||
|
||
# The XmlLoaderException and LibXML's libXMLError | ||
XmlLoaderException's codes are specific to this wrapper. | ||
LibXML's libXMLError objects are just returned in an array and as received from SimpleXML/LibXML. | ||
|
||
# Personal Opinion on the SimpleXML PHP Extension | ||
Generally SimpleXML is not a solid PHP extension and, in my mind, it should be used rarely when you need to do something simple fast. Any serious work should be done via DomDocument. | ||
The previous two sentences are the author's personal opinion which as with any opinion should be taken with a grain of salt. | ||
|
||
# Contributing | ||
Please do! PR's are very welcome. The author is far from thinking that this wrapper library is perfect. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"name": "borislavsabev/simple-xml-loader", | ||
"description": "A simple wrapper library around SimpleXml's simplexml_load_file() and simplexml_load_string() functions. Provides LibXMLError handling.", | ||
"type": "library", | ||
"require": { | ||
"ext-SimpleXML": "^0.1.0" | ||
}, | ||
"require-dev": { | ||
"phpunit/phpunit": "^6.2" | ||
}, | ||
"authors": [ | ||
{ | ||
"name": "Borislav Sabev", | ||
"email": "[email protected]" | ||
} | ||
], | ||
"minimum-stability": "dev" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
|
||
namespace BorislavSabev\SimpleXmlLoader\Exception; | ||
|
||
/** | ||
* Class XmlLoaderException | ||
* | ||
* @project SimpleXmlLoader | ||
* @package BorislavSabev\SimpleXmlLoader | ||
* @author Borislav Sabev <[email protected]> | ||
*/ | ||
class XmlLoaderException extends \RuntimeException | ||
{ | ||
const ERROR_CODE_FILE = 1; | ||
const ERROR_CODE_STRING = 2; | ||
const ERROR_CODE_XML_ERRORS = 3; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
<?php | ||
|
||
namespace BorislavSabev\SimpleXmlLoader; | ||
|
||
use BorislavSabev\SimpleXmlLoader\XmlPayload; | ||
use BorislavSabev\SimpleXmlLoader\Exception\XmlLoaderException; | ||
|
||
|
||
/** | ||
* Class XmlLoader | ||
* | ||
* @project SimpleXmlLoader | ||
* @package BorislavSabev\SimpleXmlLoader | ||
* @author Borislav Sabev <[email protected]> | ||
*/ | ||
class XmlLoader | ||
{ | ||
/** @var \BorislavSabev\SimpleXmlLoader\XmlPayload|null The XmlPayload object */ | ||
private $xmlPayload = null; | ||
|
||
/** | ||
* XmlLoader constructor. | ||
*/ | ||
public function __construct() | ||
{ | ||
libxml_use_internal_errors(true); | ||
} | ||
|
||
/** | ||
* Interprets an XML file into an object | ||
* Follows simplexml_load_file()'s method signature | ||
* | ||
* @param $filename | ||
* @param string $class_name | ||
* @param int $options | ||
* @param string $ns | ||
* @param bool $is_prefix | ||
* @return \SimpleXMLElement | ||
*/ | ||
public function loadFile($filename, $class_name = "SimpleXMLElement", $options = 0, $ns = "", $is_prefix = false) | ||
{ | ||
if (!file_exists($filename)) { | ||
throw new XmlLoaderException("(File does not exist: {$filename}", XmlLoaderException::ERROR_CODE_FILE); | ||
} | ||
|
||
libxml_clear_errors(); | ||
$this->xmlPayload = new XmlPayload(); | ||
$this->xmlPayload->xmlFilename = basename($filename); | ||
$this->xmlPayload->xmlElement = simplexml_load_file($filename, $class_name, $options, $ns, $is_prefix); | ||
|
||
if ($this->xmlPayload->xmlElement === false) { | ||
$this->handleLibXmlErrors(); | ||
} | ||
|
||
return $this->xmlPayload->getXmlElement(); | ||
} | ||
|
||
/** | ||
* Interprets a string of XML into an object | ||
* Follows simplexml_load_string()'s method signature | ||
* @param $data | ||
* @param string $class_name | ||
* @param int $options | ||
* @param string $ns | ||
* @param bool $is_prefix | ||
* @throws XmlLoaderException | ||
* @return \SimpleXMLElement | ||
*/ | ||
public function loadString($data, $class_name = "SimpleXMLElement", $options = 0, $ns = "", $is_prefix = false) | ||
{ | ||
libxml_clear_errors(); | ||
$this->xmlPayload = new XmlPayload(); | ||
$this->xmlPayload->xmlElement = simplexml_load_string($data, $class_name, $options, $ns, $is_prefix); | ||
if ($this->xmlPayload->xmlElement === false) { | ||
$this->handleLibXmlErrors(); | ||
} | ||
|
||
return $this->xmlPayload->getXmlElement(); | ||
} | ||
|
||
/** | ||
* Get the XmlPayload object or null if you did not call any of the loaders | ||
* @return \BorislavSabev\SimpleXmlLoader\XmlPayload|null | ||
*/ | ||
public function getXmlPayload() | ||
{ | ||
return $this->xmlPayload; | ||
} | ||
|
||
/** | ||
* Handle LibXml errors internally | ||
* @throws XmlLoaderException | ||
*/ | ||
private function handleLibXmlErrors() | ||
{ | ||
$this->xmlPayload->loadErrors(libxml_get_errors()); | ||
throw new XmlLoaderException('Encountered LibXML errors', XmlLoaderException::ERROR_CODE_XML_ERRORS); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
<?php | ||
namespace BorislavSabev\SimpleXmlLoader; | ||
|
||
/** | ||
* Class XmlPayload | ||
* | ||
* A payload for LibXML's stuff | ||
* | ||
* @project SimpleXmlLoader | ||
* @package BorislavSabev\SimpleXmlLoader | ||
* @author Borislav Sabev <[email protected]> | ||
*/ | ||
class XmlPayload | ||
{ | ||
/** @var null|string XML file's filename if any */ | ||
public $xmlFilename = null; | ||
/** @var null|\SimpleXMLElement The resulting XML element as returned by SimpleXml */ | ||
public $xmlElement = null; | ||
/** @var bool Bit showing if there are errors on load */ | ||
public $xmlHasErrors = false; | ||
/** @var array An array of LibXMLError objects if there are any */ | ||
public $xmlErrors = []; | ||
|
||
/** | ||
* Get the loaded \SimpleXMLElement | ||
* @return null|\SimpleXMLElement | ||
*/ | ||
public function getXmlElement() | ||
{ | ||
return $this->xmlElement; | ||
} | ||
|
||
/** | ||
* Get an array with LibXMLError objects if there are any; False otherwise | ||
* @return array|bool | ||
*/ | ||
public function getXmlErrors() | ||
{ | ||
if (empty($this->xmlErrors)) { | ||
return false; | ||
} | ||
|
||
return $this->xmlErrors; | ||
} | ||
|
||
/** | ||
* Does the payload currently contain errors? | ||
* @return bool | ||
*/ | ||
public function hasErrors() | ||
{ | ||
return $this->xmlHasErrors; | ||
} | ||
|
||
/** | ||
* Load errors into the payload | ||
* @param $xmlErrors | ||
*/ | ||
public function loadErrors($xmlErrors) | ||
{ | ||
if (!empty($xmlErrors)) { | ||
$this->xmlHasErrors = true; | ||
foreach ($xmlErrors as $xmlError) { | ||
$this->xmlErrors[] = $xmlError; | ||
} | ||
} | ||
} | ||
} |