Skip to content

Commit

Permalink
[ENH] ZugferdKositValidator can optionally call a running Validator i…
Browse files Browse the repository at this point in the history
…n Daemon-Mode via HTTP

[ENH] ZugferdKositValidator can optionally call a running Validator in Daemon-Mode via HTTP
  • Loading branch information
horstoeko authored Dec 15, 2024
2 parents 9d441ca + 8a63f49 commit c8b40a1
Show file tree
Hide file tree
Showing 2 changed files with 291 additions and 2 deletions.
20 changes: 20 additions & 0 deletions examples/KositValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,23 @@ function showValidationResult(ZugferdKositValidator $kositValidator)
$kositValidator->setDocument($document)->disableCleanup()->validate();

showValidationResult($kositValidator);

/* ----------------------------------------------------------------------------------
- Validation of a document read by ZugferdDocumentPdfReader (Remote, using running daemon)
---------------------------------------------------------------------------------- */

$document = ZugferdDocumentPdfReader::readAndGuessFromFile(dirname(__FILE__) . "/invoice_1.pdf");
$kositValidator->setDocument($document)->enableRemoteMode()->setRemoteModeHost("127.0.0.1")->setRemoteModePort(8081)->validate();

showValidationResult($kositValidator);

/* ----------------------------------------------------------------------------------
- Validation of a document read by ZugferdDocumentReader (Remote, using running daemon)
---------------------------------------------------------------------------------- */

$document = ZugferdDocumentReader::readAndGuessFromFile(dirname(__FILE__) . "/../tests/assets/xml_en16931_5.xml");

$kositValidator = new ZugferdKositValidator($document);
$kositValidator->setDocument($document)->enableRemoteMode()->setRemoteModeHost("127.0.0.1")->setRemoteModePort(8081)->validate();

showValidationResult($kositValidator);
273 changes: 271 additions & 2 deletions src/ZugferdKositValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,27 @@ class ZugferdKositValidator
*/
private $cleanupBaseDirectoryIsDisabled = false;

/**
* Use remote validation (JAVA application is running in daemon mode on a remote host)
*
* @var boolean
*/
private $remoteModeEnabled = false;

/**
* The remote hostname or -ip
*
* @var string
*/
private $remoteModeHost = "";

/**
* The remote host port
*
* @var integer
*/
private $remoteModePort = 0;

/**
* Message Type "Internal Error"
*/
Expand Down Expand Up @@ -291,6 +312,76 @@ public function enableCleanup(): ZugferdKositValidator
return $this;
}

/**
* Disable the usage of a remote host validation
*
* @return ZugferdKositValidator
*/
public function disableRemoteMode(): ZugferdKositValidator
{
$this->remoteModeEnabled = false;

return $this;
}

/**
* Enable the usage of a remote host validation
*
* @return ZugferdKositValidator
*/
public function enableRemoteMode(): ZugferdKositValidator
{
$this->remoteModeEnabled = true;

return $this;
}

/**
* Set the hostname or the ip of the remote host where the validation application
* is running in daemon mode
*
* @param string $remoteModeHost
* @return ZugferdKositValidator
*/
public function setRemoteModeHost(string $remoteModeHost): ZugferdKositValidator
{
if (StringUtils::stringIsNullOrEmpty($remoteModeHost)) {
return $this;
}

$this->remoteModeHost = $remoteModeHost;

return $this;
}

/**
* Set the port of the remote host where the validation application
* is running in daemon mode
*
* @param integer $remoteModePort
* @return ZugferdKositValidator
*/
public function setRemoteModePort(int $remoteModePort): ZugferdKositValidator
{
if ($remoteModePort <= 0) {
return $this;
}

$this->remoteModePort = $remoteModePort;

return $this;
}

/**
* Returns the full remote mode URL
*
* @return string
*/
public function getRemoteModeUrl(): string
{
return sprintf("http://%s:%s", $this->remoteModeHost, $this->remoteModePort);
}

/**
* Perform validation
*
Expand Down Expand Up @@ -574,6 +665,24 @@ public function getProcessOutput(): array
*/
private function checkRequirements(): bool
{
if ($this->remoteModeEnabled === true) {
return $this->checkRequirementsRemote();
}

return $this->checkRequirementsLocal();
}

/**
* CHeck requirements for usage on a local installation
*
* @return boolean
*/
private function checkRequirementsLocal(): bool
{
if ($this->remoteModeEnabled === true) {
return true;
}

if (is_null($this->document)) {
$this->addToMessageBag("You must specify an instance of the ZugferdDocument class");
return false;
Expand All @@ -594,13 +703,68 @@ private function checkRequirements(): bool
return true;
}

/**
* CHeck requirements for usage on a remote host which is running the application
* in daemon mode
*
* @return boolean
*/
private function checkRequirementsRemote(): bool
{
if ($this->remoteModeEnabled !== true) {
return true;
}

if (!function_exists('curl_init') || !function_exists('curl_setopt') || !function_exists('curl_exec') || !function_exists('curl_getinfo') || !function_exists('curl_close')) {
$this->addToMessageBag("PHP-Curl not installed or activated");
return false;
}

if (StringUtils::stringIsNullOrEmpty($this->remoteModeHost)) {
$this->addToMessageBag("You must specify the hostname or it's IP where the Validator is running in daemon mode");
return false;
}

if ($this->remoteModePort <= 0) {
$this->addToMessageBag("You must specify the port of the host where the Validator is running in daemon mode");
return false;
}

try {
$httpConnection = curl_init($this->getRemoteModeUrl());
curl_setopt($httpConnection, CURLOPT_RETURNTRANSFER, true);
curl_setopt($httpConnection, CURLOPT_HEADER, true);
curl_setopt($httpConnection, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($httpConnection, CURLOPT_ENCODING, '');
curl_setopt($httpConnection, CURLOPT_AUTOREFERER, true);
curl_setopt($httpConnection, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($httpConnection, CURLOPT_TIMEOUT, 10);
curl_exec($httpConnection);
$retcode = curl_getinfo($httpConnection, CURLINFO_HTTP_CODE);
curl_close($httpConnection);
if ($retcode != 200) {
$this->addToMessageBag("Failed to connect to the host where the Validator is running in daemon mode");
return false;
}
} catch (Throwable $e) {
$this->addToMessageBag($e);
return false;
}

return true;
}

/**
* Download required files
*
* @return boolean
*/
private function downloadRequiredFiles(): bool
{
if ($this->remoteModeEnabled === true) {
return true;
}

if (!$this->runFileDownload($this->validatorDownloadUrl, $this->resolveAppZipFilename())) {
$this->addToMessageBag(sprintf("Unable to download from %s containing the JAVA-Application", $this->validatorDownloadUrl));
return false;
Expand All @@ -621,6 +785,10 @@ private function downloadRequiredFiles(): bool
*/
private function unpackRequiredFiles(): bool
{
if ($this->remoteModeEnabled === true) {
return true;
}

$validatorAppFile = $this->resolveAppZipFilename();
$validatorScenarioFile = $this->resolveScenatioZipFilename();

Expand All @@ -645,6 +813,10 @@ private function unpackRequiredFiles(): bool
*/
private function unpackRequiredFile(string $filename): bool
{
if ($this->remoteModeEnabled === true) {
return true;
}

$zip = new ZipArchive();

if ($zip->open($filename) !== true) {
Expand Down Expand Up @@ -684,6 +856,24 @@ private function unpackRequiredFile(string $filename): bool
*/
private function performValidation(): bool
{
if ($this->remoteModeEnabled === true) {
return $this->performValidationRemote();
}

return $this->performValidationLocal();
}

/**
* Runs the validator java application locally
*
* @return boolean
*/
private function performValidationLocal(): bool
{
if ($this->remoteModeEnabled === true) {
return true;
}

if (file_put_contents($this->resolveFileToValidateFilename(), $this->document->serializeAsXml()) === false) {
$this->addToMessageBag("Cannot create temporary file which contains the XML to validate");
return false;
Expand All @@ -701,7 +891,47 @@ private function performValidation(): bool
];

if (!$this->runValidationApplication($applicationOptions, $this->resolveBaseDirectory())) {
$this->parseValidatorXmlReport();
$this->parseValidatorXmlReportByFile();
return false;
}

return true;
}

/**
* Runs the validator java application on the remote host
*
* @return boolean
*/
private function performValidationRemote(): bool
{
if ($this->remoteModeEnabled !== true) {
return true;
}

try {
$httpConnection = curl_init($this->getRemoteModeUrl());
curl_setopt($httpConnection, CURLOPT_RETURNTRANSFER, true);
curl_setopt($httpConnection, CURLOPT_HEADER, true);
curl_setopt($httpConnection, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($httpConnection, CURLOPT_ENCODING, '');
curl_setopt($httpConnection, CURLOPT_AUTOREFERER, true);
curl_setopt($httpConnection, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($httpConnection, CURLOPT_TIMEOUT, 120);
curl_setopt($httpConnection, CURLOPT_POST, true);
curl_setopt($httpConnection, CURLOPT_POSTFIELDS, $this->document->serializeAsXml());
curl_setopt($httpConnection, CURLOPT_HTTPHEADER, ["Content-Type: application/xml"]);
$responseXml = curl_exec($httpConnection);
$retcode = curl_getinfo($httpConnection, CURLINFO_HTTP_CODE);
curl_close($httpConnection);
if ($retcode != 200) {
if (preg_match('/<\?xml.*?\?>.*<\/.+>/s', $responseXml, $matches)) {
$this->parseValidatorXmlReportByContent($matches[0]);
}
return false;
}
} catch (Throwable $e) {
$this->addToMessageBag($e);
return false;
}

Expand All @@ -714,7 +944,7 @@ private function performValidation(): bool
*
* @return void
*/
private function parseValidatorXmlReport(): void
private function parseValidatorXmlReportByFile(): void
{
$reportFilename =
PathUtils::combinePathWithFile(
Expand All @@ -729,6 +959,37 @@ private function parseValidatorXmlReport(): void
$domDocument = new DOMDocument();
$domDocument->load($reportFilename);

$this->parseValidatorXmlReportByDomDocument($domDocument);
}

/**
* Parses the XML content string containing the response from the validation app (JAVA application) and put errors
* to messagebag
*
* @param string $xmlContent
* @return void
*/
private function parseValidatorXmlReportByContent(string $xmlContent): void
{
if (StringUtils::stringIsNullOrEmpty($xmlContent)) {
return;
}

$domDocument = new DOMDocument();
$domDocument->loadXML($xmlContent);

$this->parseValidatorXmlReportByDomDocument($domDocument);
}

/**
* Parses the XML DOMDocument containing the response from the validation app (JAVA application) and put errors
* to messagebag
*
* @param DOMDocument $domDocument
* @return void
*/
private function parseValidatorXmlReportByDomDocument(DOMDocument $domDocument): void
{
$domXPath = new DOMXPath($domDocument);

$messageTypeMaps = [
Expand Down Expand Up @@ -762,6 +1023,10 @@ private function parseValidatorXmlReport(): void
*/
private function cleanupBaseDirectory(): void
{
if ($this->remoteModeEnabled === true) {
return;
}

if ($this->cleanupBaseDirectoryIsDisabled === true) {
return;
}
Expand All @@ -781,6 +1046,10 @@ private function cleanupBaseDirectory(): void
*/
private function cleanupBaseDirectoryInternal(string $directoryToRemove): void
{
if ($this->remoteModeEnabled === true) {
return;
}

if (!is_dir($directoryToRemove)) {
return;
}
Expand Down

0 comments on commit c8b40a1

Please sign in to comment.