Skip to content

Commit

Permalink
Added XSD Validation (new class OrderXsdValidator)
Browse files Browse the repository at this point in the history
  • Loading branch information
HorstOeko committed Mar 21, 2024
1 parent 7b4ed00 commit d8957cc
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 4 deletions.
8 changes: 4 additions & 4 deletions examples/OrderBuilderComfortSimple.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
->addDocumentAllowanceCharge(31.00, false, "S", "VAT", 20, null, 10.00, 310.00, null, null, "64", "SPECIAL AGREEMENT")
->addDocumentAllowanceCharge(21.00, true, "S", "VAT", 20, null, 10.00, 210.00, null, null, "FC", "FREIGHT SERVICES")
->setDocumentSummation(310, 360, 21, 31, 300, 60)
->setDocumentReceivableSpecifiedTradeAccountingAccount("BUYER_ACCOUNT_REF", "BUYER_ACCOUNT_REF_TYPE")
->setDocumentReceivableSpecifiedTradeAccountingAccount("4711", "1")

->addDocumentTax("S", "VAT", 300.00, 60.00, 20.00, "ExcReason-1", "ExcReasonCode-1", 300.00, 300.00, null)

Expand Down Expand Up @@ -129,7 +129,7 @@
->addDocumentPositionAllowanceCharge(6.00, false, 10.0, 60.0, "64", "SPECIAL AGREEMENT")
->addDocumentPositionAllowanceCharge(6.00, true, 10.0, 60.0, "FC", "FREIGHT SERVICES")
->setDocumentPositionLineSummation(60.0)
->setDocumentPositionReceivableTradeAccountingAccount("BUYER_ACCOUNTING_REF")
->setDocumentPositionReceivableTradeAccountingAccount("4712")
->setDocumentPositionTax("S", "VAT", 19.0, 0.00, "Reason-1", "RC1")
->setDocumentPositionUltimateCustomerOrderReferencedDocument("ULTCUSTORDEREF-1", "1", $dt)

Expand Down Expand Up @@ -160,7 +160,7 @@
->addDocumentPositionAllowanceCharge(1.00, false, 1.0, 100.0, "64", "SPECIAL AGREEMENT")
->addDocumentPositionAllowanceCharge(1.00, true, 1.0, 100.0, "FC", "FREIGHT SERVICES")
->setDocumentPositionLineSummation(100.0)
->setDocumentPositionReceivableTradeAccountingAccount("BUYER_ACCOUNTING_REF")
->setDocumentPositionReceivableTradeAccountingAccount("4713")
->setDocumentPositionUltimateCustomerOrderReferencedDocument("ULTCUSTORDEREF-1", "2", $dt)

->addNewPosition("3")
Expand Down Expand Up @@ -190,7 +190,7 @@
->addDocumentPositionAllowanceCharge(15.00, false, 10.0, 150.0, "64", "SPECIAL AGREEMENT")
->addDocumentPositionAllowanceCharge(15.00, true, 10.0, 150.0, "FC", "FREIGHT SERVICES")
->setDocumentPositionLineSummation(150.0)
->setDocumentPositionReceivableTradeAccountingAccount("BUYER_ACCOUNTING_REF")
->setDocumentPositionReceivableTradeAccountingAccount("0816")
->setDocumentPositionUltimateCustomerOrderReferencedDocument("ULTCUSTORDEREF-1", "3", $dt)

->writeFile(getcwd() . "/order-x.xml");
Expand Down
29 changes: 29 additions & 0 deletions examples/OrderXsdValidation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

use horstoeko\orderx\OrderXsdValidator;
use horstoeko\orderx\OrderDocumentReader;

require dirname(__FILE__) . "/../vendor/autoload.php";

function showValidationResult($xsdValidator)
{
if ($xsdValidator->validationFailed()) {
echo "\033[01;31mValidation failed\e[0m\n";
foreach ($xsdValidator->validationErrors() as $validationError) {
echo $validationError . PHP_EOL;
}
} else {
echo "\033[01;32mValidation passed\e[0m\n";
}
}

/**
* Valid XML
*/

$document = OrderDocumentReader::readAndGuessFromFile(dirname(__FILE__) . "/order-x.xml");

$xsdValidator = new OrderXsdValidator($document);
$xsdValidator->validate();

showValidationResult($xsdValidator);
10 changes: 10 additions & 0 deletions src/OrderSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,16 @@ public static function getValidationDirectory(): string
return PathUtils::combineAllPaths(static::getSourceDirectory(), "validation");
}

/**
* Get the directory where all the schema (XSD) files are located
*
* @return string
*/
public static function getSchemaDirectory(): string
{
return PathUtils::combineAllPaths(static::getSourceDirectory(), "schema");
}

/**
* Get the full filename of the ICC profile to use
*
Expand Down
210 changes: 210 additions & 0 deletions src/OrderXsdValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
<?php

/**
* This file is a part of horstoeko/orderx.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace horstoeko\orderx;

use Exception;
use DOMDocument;
use LibXMLError;
use horstoeko\stringmanagement\PathUtils;
use horstoeko\orderx\OrderDocument;
use horstoeko\orderx\OrderSettings;

/**
* Class representing the validator against XSD for documents
*
* @category Order-X
* @package Order-X
* @author D. Erling <horstoeko@erling.com.de>
* @license https://opensource.org/licenses/MIT MIT
* @link https://github.com/horstoeko/orderx
*/
class OrderXsdValidator
{
/**
* The order document reference
*
* @var OrderDocument
*/
private $document;

/**
* Internal error bag
*
* @var array
*/
private $errorBag = [];

/**
* Constructor
*
* @codeCoverageIgnore
* @param OrderDocument $document
*/
public function __construct(OrderDocument $document)
{
$this->document = $document;
}

/**
* Perform validation of document
*
* @return OrderXsdValidator
*/
public function validate(): OrderXsdValidator
{
$this->clearErrorBag();
$this->initLibXml();

try {
if (!$this->getDocumentContentAsDomDocument()->schemaValidate($this->getDocumentXsdFilename())) {
$this->pushLibXmlErrorsToErrorBag();
}
} catch (Exception $exception) {
$this->addToErrorBag($exception);
} finally {
$this->finalizeLibXml();
}

return $this;
}

/**
* Returns true if validation passed otherwise false
*
* @return boolean
*/
public function validationPased(): bool
{
return empty($this->errorBag);
}

/**
* Returns true if validation failed otherwise false
*
* @return boolean
*/
public function validationFailed(): bool
{
return !$this->validationPased();
}

/**
* Returns an array of all validation errors
*
* @return array
*/
public function validationErrors(): array
{
return $this->errorBag;
}

/**
* Initialize LibXML
*
* @return void
*/
private function initLibXml(): void
{
libxml_use_internal_errors(true);
}

/**
* Finalize LibXML
*
* @return void
*/
private function finalizeLibXml(): void
{
libxml_clear_errors();
libxml_use_internal_errors(false);
}

/**
* Get the content of the document
*
* @return string
*/
private function getDocumentContent(): string
{
return $this->document->serializeAsXml();
}

/**
* Get the content of the document as a DOMDocument
*
* @return DOMDocument
*/
private function getDocumentContentAsDomDocument(): DOMDocument
{
$doc = new DOMDocument();
$doc->loadXML($this->getDocumentContent());

return $doc;
}

/**
* Get the XSD file (schema definition) for the document
*
* @return string
*/
private function getDocumentXsdFilename(): string
{
$xsdFilename = PathUtils::combineAllPaths(
OrderSettings::getSchemaDirectory(),
$this->document->getProfileDefinitionParameter('name'),
$this->document->getProfileDefinitionParameter('xsdfilename')
);

if (!file_exists($xsdFilename)) {
throw new Exception(sprintf("XSD file '%s' not found", $xsdFilename));
}

return $xsdFilename;
}

/**
* Clear the internal error bag
*
* @return void
*/
private function clearErrorBag(): void
{
$this->errorBag = [];
}

/**
* Add message to error bag
*
* @param string|Exception|LibXMLError $error
* @return void
*/
private function addToErrorBag($error): void
{
if (is_string($error)) {
$this->errorBag[] = $error;
} elseif ($error instanceof Exception) {
$this->errorBag[] = $error->getMessage();
} elseif ($error instanceof LibXMLError) {
$this->errorBag[] = sprintf('[line %d] %s : %s', $error->line, $error->code, $error->message);
}
}

/**
* Pushes validation errors to error bag
*
* @return void
*/
private function pushLibXmlErrorsToErrorBag(): void
{
foreach (libxml_get_errors() as $xmlError) {
$this->addToErrorBag($xmlError);
}
}
}

0 comments on commit d8957cc

Please sign in to comment.