-
Notifications
You must be signed in to change notification settings - Fork 40
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
HorstOeko
committed
Mar 11, 2024
1 parent
56bc31d
commit 0d15c30
Showing
5 changed files
with
290 additions
and
40 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,30 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:a="urn:un:unece:uncefact:data:standard:QualifiedDataType:100" xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:10" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | ||
<rsm:ExchangedDocumentContext> | ||
<ram:GuidelineSpecifiedDocumentContextParameter> | ||
<ram:ID>urn:cen.eu:en16931:2017</ram:ID> | ||
</ram:GuidelineSpecifiedDocumentContextParameter> | ||
</rsm:ExchangedDocumentContext> | ||
<rsm:ExchangedDocument> | ||
<ram:ID>471102</ram:ID> | ||
<ram:TypeCode>380</ram:TypeCode> | ||
<ram:IssueDateTime> | ||
<udt:DateTimeString format="102">20180305</udt:DateTimeString> | ||
</ram:IssueDateTime> | ||
<ram:IncludedNote> | ||
<ram:Content>Rechnung gemäß Bestellung vom 01.03.2018.</ram:Content> | ||
</ram:IncludedNote> | ||
<ram:IncludedNote> | ||
<ram:Content>Lieferant GmbH | ||
Lieferantenstraße 20 | ||
80333 München | ||
Deutschland | ||
Geschäftsführer: Hans Muster | ||
Handelsregisternummer: H A 123 | ||
</ram:Content> | ||
<ram:SubjectCode>REG</ram:SubjectCode> | ||
</ram:IncludedNote> | ||
</rsm:ExchangedDocument> | ||
<rsm:SupplyChainTradeTransaction> | ||
</rsm:SupplyChainTradeTransaction> | ||
</rsm:CrossIndustryInvoice> |
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,41 @@ | ||
<?php | ||
|
||
use horstoeko\zugferd\ZugferdXsdValidator; | ||
use horstoeko\zugferd\ZugferdDocumentReader; | ||
use horstoeko\zugferd\ZugferdDocumentPdfReader; | ||
|
||
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"; | ||
} | ||
} | ||
|
||
/** | ||
* Invalid XML | ||
*/ | ||
|
||
$document = ZugferdDocumentReader::readAndGuessFromFile(dirname(__FILE__) . "/InvoiceXsdInvalid.xml"); | ||
|
||
$xsdValidator = new ZugferdXsdValidator($document); | ||
$xsdValidator->validate(); | ||
|
||
showValidationResult($xsdValidator); | ||
|
||
/** | ||
* Valid XML | ||
*/ | ||
|
||
$document = ZugferdDocumentPdfReader::readAndGuessFromFile(dirname(__FILE__) . "/invoice_1.pdf"); | ||
|
||
$xsdValidator = new ZugferdXsdValidator($document); | ||
$xsdValidator->validate(); | ||
|
||
showValidationResult($xsdValidator); |
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
This file was deleted.
Oops, something went wrong.
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,209 @@ | ||
<?php | ||
|
||
/** | ||
* This file is a part of horstoeko/zugferd. | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace horstoeko\zugferd; | ||
|
||
use Exception; | ||
use DOMDocument; | ||
use LibXMLError; | ||
use horstoeko\stringmanagement\PathUtils; | ||
use horstoeko\zugferd\ZugferdDocument; | ||
use horstoeko\zugferd\ZugferdSettings; | ||
|
||
/** | ||
* Class representing the validator against XSD for documents | ||
* | ||
* @category Zugferd | ||
* @package Zugferd | ||
* @author D. Erling <horstoeko@erling.com.de> | ||
* @license https://opensource.org/licenses/MIT MIT | ||
* @link https://github.com/horstoeko/zugferd | ||
*/ | ||
class ZugferdXsdValidator | ||
{ | ||
/** | ||
* The invoice document reference | ||
* | ||
* @var ZugferdDocument | ||
*/ | ||
private $document; | ||
|
||
/** | ||
* Internal error bag | ||
* | ||
* @var array | ||
*/ | ||
private $errorBag = []; | ||
|
||
/** | ||
* Constructor | ||
* | ||
* @codeCoverageIgnore | ||
* @param ZugferdDocument $document | ||
*/ | ||
public function __construct(ZugferdDocument $document) | ||
{ | ||
$this->document = $document; | ||
} | ||
|
||
/** | ||
* Perform validation of document | ||
* | ||
* @return ZugferdXsdValidator | ||
*/ | ||
public function validate(): ZugferdXsdValidator | ||
{ | ||
$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( | ||
ZugferdSettings::getSchemaDirectory(), | ||
$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); | ||
} | ||
} | ||
} |