From 8e2a9fdb451921cac78aa12346e0d5ec36f12562 Mon Sep 17 00:00:00 2001 From: Daniel Kurowski Date: Mon, 18 Sep 2023 13:53:36 +0200 Subject: [PATCH 1/7] ares: decouple api client from public facade --- src/Ares.php | 298 ++------------------------- src/Ares/ApiClient/ApiClient.php | 342 +++++++++++++++++++++++++++++++ 2 files changed, 360 insertions(+), 280 deletions(-) create mode 100644 src/Ares/ApiClient/ApiClient.php diff --git a/src/Ares.php b/src/Ares.php index fe29e68..8560070 100644 --- a/src/Ares.php +++ b/src/Ares.php @@ -2,10 +2,10 @@ namespace Defr; +use Defr\Ares\ApiClient\ApiClient; use Defr\Ares\AresException; use Defr\Ares\AresRecord; use Defr\Ares\AresRecords; -use Defr\Ares\TaxRecord; use InvalidArgumentException; /** @@ -16,55 +16,16 @@ */ class Ares { + /** @deprecated no need to be public, will be removed in next major version */ const URL_BAS = 'https://wwwinfo.mfcr.cz/cgi-bin/ares/darv_bas.cgi?ico=%s'; + /** @deprecated no need to be public, will be removed in next major version */ const URL_RES = 'https://wwwinfo.mfcr.cz/cgi-bin/ares/darv_res.cgi?ICO=%s'; + /** @deprecated no need to be public, will be removed in next major version */ const URL_TAX = 'https://wwwinfo.mfcr.cz/cgi-bin/ares/ares_es.cgi?ico=%s&filtr=0'; + /** @deprecated no need to be public, will be removed in next major version */ const URL_FIND = 'https://wwwinfo.mfcr.cz/cgi-bin/ares/ares_es.cgi?obch_jm=%s&obec=%s&filtr=0'; - /** - * @var string - */ - private $cacheStrategy = 'YW'; - - /** - * Path to directory that serves as an local cache for Ares service responses. - * - * @var string - */ - private $cacheDir = null; - - /** - * Whether we are running in debug/development environment. - * - * @var bool - */ - private $debug; - - /** - * Load balancing domain URL. - * - * @var string - */ - private $balancer = null; - - /** - * Stream context options. - * - * @var array - */ - private $contextOptions = [ - 'ssl' => [ - 'verify_peer' => false, - 'verify_peer_name' => false, - ], - ]; - - /** - * Last called URL. - * - * @var string - */ - private $lastUrl; + private ApiClient $apiClient; /** * @param string|null $cacheDir @@ -73,24 +34,11 @@ class Ares */ public function __construct($cacheDir = null, $debug = false, $balancer = null) { - if (null === $cacheDir) { - $cacheDir = sys_get_temp_dir(); - } - - if (null !== $balancer) { - $this->balancer = $balancer; - } - - $this->cacheDir = $cacheDir.'/defr/ares'; - $this->debug = $debug; - - if (!is_dir($this->cacheDir)) { - mkdir($this->cacheDir, 0777, true); - } + $this->apiClient = new ApiClient($cacheDir, $debug, $balancer); } /** - * @param $id + * @param mixed $id * * @throws InvalidArgumentException * @throws Ares\AresException @@ -105,79 +53,7 @@ public function findByIdentificationNumber($id) throw new AresException('IČ firmy musí být zadáno.'); } - $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; - $cachedFile = $this->cacheDir.'/bas_'.$cachedFileName; - $cachedRawFile = $this->cacheDir.'/bas_raw_'.$cachedFileName; - - if (is_file($cachedFile)) { - return unserialize(file_get_contents($cachedFile)); - } - - $url = $this->composeUrl(sprintf(self::URL_BAS, $id)); - - try { - $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); - if ($this->debug) { - file_put_contents($cachedRawFile, $aresRequest); - } - $aresResponse = simplexml_load_string($aresRequest); - - if ($aresResponse) { - $ns = $aresResponse->getDocNamespaces(); - $data = $aresResponse->children($ns['are']); - $elements = $data->children($ns['D'])->VBAS; - - $ico = $elements->ICO; - if ((string) $ico !== (string) $id) { - throw new AresException('IČ firmy nebylo nalezeno.'); - } - - $record = new AresRecord(); - - $record->setCompanyId(strval($elements->ICO)); - $record->setTaxId(strval($elements->DIC)); - $record->setCompanyName(strval($elements->OF)); - if (strval($elements->AA->NU) !== '') { - $record->setStreet(strval($elements->AA->NU)); - } else { - if (strval($elements->AA->NCO) !== '') { - $record->setStreet(strval($elements->AA->NCO)); - } - } - - if (strval($elements->AA->CO)) { - $record->setStreetHouseNumber(strval($elements->AA->CD)); - $record->setStreetOrientationNumber(strval($elements->AA->CO)); - } else { - $record->setStreetHouseNumber(strval($elements->AA->CD)); - } - - if (strval($elements->AA->N) === 'Praha') { - $record->setTown(strval($elements->AA->NMC)); - $record->setArea(strval($elements->AA->NCO)); - } elseif (strval($elements->AA->NCO) !== strval($elements->AA->N)) { - $record->setTown(strval($elements->AA->N)); - $record->setArea(strval($elements->AA->NCO)); - } else { - $record->setTown(strval($elements->AA->N)); - } - - $record->setZip(strval($elements->AA->PSC)); - - $record->setRegisters(strval($elements->PSU)); - - $stateInfo = $elements->AA->AU->children($ns['U']); - $record->setStateCode(strval($stateInfo->KK)); - } else { - throw new AresException('Databáze ARES není dostupná.'); - } - } catch (\Exception $e) { - throw new AresException($e->getMessage()); - } - - file_put_contents($cachedFile, serialize($record)); - - return $record; + return $this->apiClient->findByIdentificationNumber($id); } /** @@ -194,50 +70,7 @@ public function findInResById($id) { $this->checkCompanyId($id); - $url = $this->composeUrl(sprintf(self::URL_RES, $id)); - - $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; - $cachedFile = $this->cacheDir.'/res_'.$cachedFileName; - $cachedRawFile = $this->cacheDir.'/res_raw_'.$cachedFileName; - - if (is_file($cachedFile)) { - return unserialize(file_get_contents($cachedFile)); - } - - try { - $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); - if ($this->debug) { - file_put_contents($cachedRawFile, $aresRequest); - } - $aresResponse = simplexml_load_string($aresRequest); - - if ($aresResponse) { - $ns = $aresResponse->getDocNamespaces(); - $data = $aresResponse->children($ns['are']); - $elements = $data->children($ns['D'])->Vypis_RES; - - if (strval($elements->ZAU->ICO) === $id) { - $record = new AresRecord(); - $record->setCompanyId(strval($id)); - $record->setTaxId($this->findVatById($id)); - $record->setCompanyName(strval($elements->ZAU->OF)); - $record->setStreet(strval($elements->SI->NU)); - $record->setStreetHouseNumber(strval($elements->SI->CD)); - $record->setStreetOrientationNumber(strval($elements->SI->CO)); - $record->setTown(strval($elements->SI->N)); - $record->setZip(strval($elements->SI->PSC)); - } else { - throw new AresException('IČ firmy nebylo nalezeno.'); - } - } else { - throw new AresException('Databáze ARES není dostupná.'); - } - } catch (\Exception $e) { - throw new AresException($e->getMessage()); - } - file_put_contents($cachedFile, serialize($record)); - - return $record; + return $this->apiClient->findInResById($id); } /** @@ -248,49 +81,13 @@ public function findInResById($id) * @throws InvalidArgumentException * @throws \Exception * - * @return string + * @return AresRecord */ public function findVatById($id) { $this->checkCompanyId($id); - $url = $this->composeUrl(sprintf(self::URL_TAX, $id)); - - $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; - $cachedFile = $this->cacheDir.'/tax_'.$cachedFileName; - $cachedRawFile = $this->cacheDir.'/tax_raw_'.$cachedFileName; - - if (is_file($cachedFile)) { - return unserialize(file_get_contents($cachedFile)); - } - - try { - $vatRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); - if ($this->debug) { - file_put_contents($cachedRawFile, $vatRequest); - } - $vatResponse = simplexml_load_string($vatRequest); - - if ($vatResponse) { - $record = new TaxRecord(); - $ns = $vatResponse->getDocNamespaces(); - $data = $vatResponse->children($ns['are']); - $elements = $data->children($ns['dtt'])->V->S; - - if (strval($elements->ico) === $id) { - $record->setTaxId(str_replace('dic=', 'CZ', strval($elements->p_dph))); - } else { - throw new AresException('DIČ firmy nebylo nalezeno.'); - } - } else { - throw new AresException('Databáze MFČR není dostupná.'); - } - } catch (\Exception $e) { - throw new \Exception($e->getMessage()); - } - file_put_contents($cachedFile, serialize($record)); - - return $record; + return $this->apiClient->findVatById($id); } /** @@ -310,50 +107,7 @@ public function findByName($name, $city = null) throw new InvalidArgumentException('Zadejte minimálně 3 znaky pro hledání.'); } - $url = $this->composeUrl(sprintf( - self::URL_FIND, - urlencode(Lib::stripDiacritics($name)), - urlencode(Lib::stripDiacritics($city)) - )); - - $cachedFileName = date($this->cacheStrategy).'_'.md5($name.$city).'.php'; - $cachedFile = $this->cacheDir.'/find_'.$cachedFileName; - $cachedRawFile = $this->cacheDir.'/find_raw_'.$cachedFileName; - - if (is_file($cachedFile)) { - return unserialize(file_get_contents($cachedFile)); - } - - $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); - if ($this->debug) { - file_put_contents($cachedRawFile, $aresRequest); - } - $aresResponse = simplexml_load_string($aresRequest); - if (!$aresResponse) { - throw new AresException('Databáze ARES není dostupná.'); - } - - $ns = $aresResponse->getDocNamespaces(); - $data = $aresResponse->children($ns['are']); - $elements = $data->children($ns['dtt'])->V->S; - - if (empty($elements)) { - throw new AresException('Nic nebylo nalezeno.'); - } - - $records = new AresRecords(); - foreach ($elements as $element) { - $record = new AresRecord(); - $record->setCompanyId(strval($element->ico)); - $record->setTaxId( - ($element->dph ? str_replace('dic=', 'CZ', strval($element->p_dph)) : '') - ); - $record->setCompanyName(strval($element->ojm)); - $records[] = $record; - } - file_put_contents($cachedFile, serialize($records)); - - return $records; + return $this->apiClient->findByName($name, $city); } /** @@ -361,7 +115,7 @@ public function findByName($name, $city = null) */ public function setCacheStrategy($cacheStrategy) { - $this->cacheStrategy = $cacheStrategy; + $this->apiClient->setCacheStrategy($cacheStrategy); } /** @@ -369,7 +123,7 @@ public function setCacheStrategy($cacheStrategy) */ public function setDebug($debug) { - $this->debug = $debug; + $this->apiClient->setDebug($debug); } /** @@ -379,7 +133,7 @@ public function setDebug($debug) */ public function setBalancer($balancer) { - $this->balancer = $balancer; + $this->apiClient->setBalancer($balancer); return $this; } @@ -389,11 +143,11 @@ public function setBalancer($balancer) */ public function getLastUrl() { - return $this->lastUrl; + return $this->apiClient->getLastUrl(); } /** - * @param int $id + * @param mixed $id */ private function checkCompanyId($id) { @@ -402,20 +156,4 @@ private function checkCompanyId($id) } } - /** - * @param string $url - * - * @return string - */ - private function composeUrl($url) - { - if ($this->balancer) { - $url = sprintf('%s?url=%s', $this->balancer, urlencode($url)); - } - - $this->lastUrl = $url; - - return $url; - } - } diff --git a/src/Ares/ApiClient/ApiClient.php b/src/Ares/ApiClient/ApiClient.php new file mode 100644 index 0000000..26aeb82 --- /dev/null +++ b/src/Ares/ApiClient/ApiClient.php @@ -0,0 +1,342 @@ + [ + 'verify_peer' => false, + 'verify_peer_name' => false, + ], + ]; + + /** + * Last called URL. + */ + private string $lastUrl; + + public function __construct(?string $cacheDir = null, bool $debug = false, ?string $balancer = null) + { + if (null === $cacheDir) { + $cacheDir = sys_get_temp_dir(); + } + + if (null !== $balancer) { + $this->balancer = $balancer; + } + + $this->cacheDir = $cacheDir.'/defr/ares'; + $this->debug = $debug; + + if (!is_dir($this->cacheDir)) { + mkdir($this->cacheDir, 0777, true); + } + } + + /** + * {@inheritDoc} + */ + public function findByIdentificationNumber($id): AresRecord + { + $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; + $cachedFile = $this->cacheDir.'/bas_'.$cachedFileName; + $cachedRawFile = $this->cacheDir.'/bas_raw_'.$cachedFileName; + + if (is_file($cachedFile)) { + return unserialize(file_get_contents($cachedFile)); + } + + $url = $this->composeUrl(sprintf(self::URL_BAS, $id)); + + try { + $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); + if ($this->debug) { + file_put_contents($cachedRawFile, $aresRequest); + } + $aresResponse = simplexml_load_string($aresRequest); + + if ($aresResponse) { + $ns = $aresResponse->getDocNamespaces(); + $data = $aresResponse->children($ns['are']); + $elements = $data->children($ns['D'])->VBAS; + + $ico = $elements->ICO; + if ((string) $ico !== (string) $id) { + throw new AresException('IČ firmy nebylo nalezeno.'); + } + + $record = new AresRecord(); + + $record->setCompanyId(strval($elements->ICO)); + $record->setTaxId(strval($elements->DIC)); + $record->setCompanyName(strval($elements->OF)); + if (strval($elements->AA->NU) !== '') { + $record->setStreet(strval($elements->AA->NU)); + } else { + if (strval($elements->AA->NCO) !== '') { + $record->setStreet(strval($elements->AA->NCO)); + } + } + + if (strval($elements->AA->CO)) { + $record->setStreetHouseNumber(strval($elements->AA->CD)); + $record->setStreetOrientationNumber(strval($elements->AA->CO)); + } else { + $record->setStreetHouseNumber(strval($elements->AA->CD)); + } + + if (strval($elements->AA->N) === 'Praha') { + $record->setTown(strval($elements->AA->NMC)); + $record->setArea(strval($elements->AA->NCO)); + } elseif (strval($elements->AA->NCO) !== strval($elements->AA->N)) { + $record->setTown(strval($elements->AA->N)); + $record->setArea(strval($elements->AA->NCO)); + } else { + $record->setTown(strval($elements->AA->N)); + } + + $record->setZip(strval($elements->AA->PSC)); + + $record->setRegisters(strval($elements->PSU)); + + $stateInfo = $elements->AA->AU->children($ns['U']); + $record->setStateCode(strval($stateInfo->KK)); + } else { + throw new AresException('Databáze ARES není dostupná.'); + } + } catch (\Exception $e) { + throw new AresException($e->getMessage()); + } + + file_put_contents($cachedFile, serialize($record)); + + return $record; + } + + /** + * {@inheritDoc} + */ + public function findInResById(int $id): AresRecord + { + $url = $this->composeUrl(sprintf(self::URL_RES, $id)); + + $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; + $cachedFile = $this->cacheDir.'/res_'.$cachedFileName; + $cachedRawFile = $this->cacheDir.'/res_raw_'.$cachedFileName; + + if (is_file($cachedFile)) { + return unserialize(file_get_contents($cachedFile)); + } + + try { + $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); + if ($this->debug) { + file_put_contents($cachedRawFile, $aresRequest); + } + $aresResponse = simplexml_load_string($aresRequest); + + if ($aresResponse) { + $ns = $aresResponse->getDocNamespaces(); + $data = $aresResponse->children($ns['are']); + $elements = $data->children($ns['D'])->Vypis_RES; + + if (strval($elements->ZAU->ICO) === $id) { + $record = new AresRecord(); + $record->setCompanyId(strval($id)); + $record->setTaxId($this->findVatById($id)); + $record->setCompanyName(strval($elements->ZAU->OF)); + $record->setStreet(strval($elements->SI->NU)); + $record->setStreetHouseNumber(strval($elements->SI->CD)); + $record->setStreetOrientationNumber(strval($elements->SI->CO)); + $record->setTown(strval($elements->SI->N)); + $record->setZip(strval($elements->SI->PSC)); + } else { + throw new AresException('IČ firmy nebylo nalezeno.'); + } + } else { + throw new AresException('Databáze ARES není dostupná.'); + } + } catch (\Exception $e) { + throw new AresException($e->getMessage()); + } + file_put_contents($cachedFile, serialize($record)); + + return $record; + } + + /** + * {@inheritDoc} + */ + public function findVatById(int $id): AresRecord + { + $url = $this->composeUrl(sprintf(self::URL_TAX, $id)); + + $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; + $cachedFile = $this->cacheDir.'/tax_'.$cachedFileName; + $cachedRawFile = $this->cacheDir.'/tax_raw_'.$cachedFileName; + + if (is_file($cachedFile)) { + return unserialize(file_get_contents($cachedFile)); + } + + try { + $vatRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); + if ($this->debug) { + file_put_contents($cachedRawFile, $vatRequest); + } + $vatResponse = simplexml_load_string($vatRequest); + + if ($vatResponse) { + $record = new TaxRecord(); + $ns = $vatResponse->getDocNamespaces(); + $data = $vatResponse->children($ns['are']); + $elements = $data->children($ns['dtt'])->V->S; + + if (strval($elements->ico) === $id) { + $record->setTaxId(str_replace('dic=', 'CZ', strval($elements->p_dph))); + } else { + throw new AresException('DIČ firmy nebylo nalezeno.'); + } + } else { + throw new AresException('Databáze MFČR není dostupná.'); + } + } catch (\Exception $e) { + throw new \Exception($e->getMessage()); + } + file_put_contents($cachedFile, serialize($record)); + + return $record; + } + + /** + * {@inheritDoc} + */ + public function findByName(string $name, ?string $city = null) + { + $url = $this->composeUrl(sprintf( + self::URL_FIND, + urlencode(Lib::stripDiacritics($name)), + urlencode(Lib::stripDiacritics($city)) + )); + + $cachedFileName = date($this->cacheStrategy).'_'.md5($name.$city).'.php'; + $cachedFile = $this->cacheDir.'/find_'.$cachedFileName; + $cachedRawFile = $this->cacheDir.'/find_raw_'.$cachedFileName; + + if (is_file($cachedFile)) { + return unserialize(file_get_contents($cachedFile)); + } + + $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); + if ($this->debug) { + file_put_contents($cachedRawFile, $aresRequest); + } + $aresResponse = simplexml_load_string($aresRequest); + if (!$aresResponse) { + throw new AresException('Databáze ARES není dostupná.'); + } + + $ns = $aresResponse->getDocNamespaces(); + $data = $aresResponse->children($ns['are']); + $elements = $data->children($ns['dtt'])->V->S; + + if (empty($elements)) { + throw new AresException('Nic nebylo nalezeno.'); + } + + $records = new AresRecords(); + foreach ($elements as $element) { + $record = new AresRecord(); + $record->setCompanyId(strval($element->ico)); + $record->setTaxId( + ($element->dph ? str_replace('dic=', 'CZ', strval($element->p_dph)) : '') + ); + $record->setCompanyName(strval($element->ojm)); + $records[] = $record; + } + file_put_contents($cachedFile, serialize($records)); + + return $records; + } + + /** + * {@inheritDoc} + */ + public function setCacheStrategy(string $cacheStrategy): void + { + $this->cacheStrategy = $cacheStrategy; + } + + /** + * {@inheritDoc} + */ + public function setDebug(bool $debug): void + { + $this->debug = $debug; + } + + /** + * {@inheritDoc} + */ + public function setBalancer(string $balancer) + { + $this->balancer = $balancer; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function getLastUrl(): string + { + return $this->lastUrl; + } + + private function composeUrl(string $url): string + { + if ($this->balancer) { + $url = sprintf('%s?url=%s', $this->balancer, urlencode($url)); + } + + $this->lastUrl = $url; + + return $url; + } + +} From c5aeee44f7672fd34b1a8859289bcd21b4c632de Mon Sep 17 00:00:00 2001 From: Daniel Kurowski Date: Mon, 18 Sep 2023 13:56:31 +0200 Subject: [PATCH 2/7] make interface for api client --- src/Ares.php | 3 +- src/Ares/ApiClient/ApiClient.php | 333 ++---------------------------- src/Ares/ApiClient/AresV1.php | 342 +++++++++++++++++++++++++++++++ 3 files changed, 366 insertions(+), 312 deletions(-) create mode 100644 src/Ares/ApiClient/AresV1.php diff --git a/src/Ares.php b/src/Ares.php index 8560070..4b06e84 100644 --- a/src/Ares.php +++ b/src/Ares.php @@ -3,6 +3,7 @@ namespace Defr; use Defr\Ares\ApiClient\ApiClient; +use Defr\Ares\ApiClient\AresV1; use Defr\Ares\AresException; use Defr\Ares\AresRecord; use Defr\Ares\AresRecords; @@ -34,7 +35,7 @@ class Ares */ public function __construct($cacheDir = null, $debug = false, $balancer = null) { - $this->apiClient = new ApiClient($cacheDir, $debug, $balancer); + $this->apiClient = new AresV1($cacheDir, $debug, $balancer); } /** diff --git a/src/Ares/ApiClient/ApiClient.php b/src/Ares/ApiClient/ApiClient.php index 26aeb82..608c91f 100644 --- a/src/Ares/ApiClient/ApiClient.php +++ b/src/Ares/ApiClient/ApiClient.php @@ -5,338 +5,49 @@ use Defr\Ares\AresException; use Defr\Ares\AresRecord; use Defr\Ares\AresRecords; -use Defr\Ares\TaxRecord; -class ApiClient +interface ApiClient { - private const URL_BAS = 'https://wwwinfo.mfcr.cz/cgi-bin/ares/darv_bas.cgi?ico=%s'; - private const URL_RES = 'https://wwwinfo.mfcr.cz/cgi-bin/ares/darv_res.cgi?ICO=%s'; - private const URL_TAX = 'https://wwwinfo.mfcr.cz/cgi-bin/ares/ares_es.cgi?ico=%s&filtr=0'; - private const URL_FIND = 'https://wwwinfo.mfcr.cz/cgi-bin/ares/ares_es.cgi?obch_jm=%s&obec=%s&filtr=0'; - - private string $cacheStrategy = 'YW'; - - /** - * Path to directory that serves as an local cache for Ares service responses. - */ - private ?string $cacheDir = null; - /** - * Whether we are running in debug/development environment. + * @param mixed $id + * @throws AresException */ - private bool $debug; + public function findByIdentificationNumber($id): AresRecord; /** - * Load balancing domain URL. - */ - private ?string $balancer = null; - - /** - * Stream context options. + * Find subject in RES (Registr ekonomických subjektů) * - * @var array{ssl: array{verify_peer: bool, verify_peer_name: bool}} - */ - private array $contextOptions = [ - 'ssl' => [ - 'verify_peer' => false, - 'verify_peer_name' => false, - ], - ]; - - /** - * Last called URL. - */ - private string $lastUrl; - - public function __construct(?string $cacheDir = null, bool $debug = false, ?string $balancer = null) - { - if (null === $cacheDir) { - $cacheDir = sys_get_temp_dir(); - } - - if (null !== $balancer) { - $this->balancer = $balancer; - } - - $this->cacheDir = $cacheDir.'/defr/ares'; - $this->debug = $debug; - - if (!is_dir($this->cacheDir)) { - mkdir($this->cacheDir, 0777, true); - } - } - - /** - * {@inheritDoc} + * @throws AresException */ - public function findByIdentificationNumber($id): AresRecord - { - $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; - $cachedFile = $this->cacheDir.'/bas_'.$cachedFileName; - $cachedRawFile = $this->cacheDir.'/bas_raw_'.$cachedFileName; - - if (is_file($cachedFile)) { - return unserialize(file_get_contents($cachedFile)); - } - - $url = $this->composeUrl(sprintf(self::URL_BAS, $id)); - - try { - $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); - if ($this->debug) { - file_put_contents($cachedRawFile, $aresRequest); - } - $aresResponse = simplexml_load_string($aresRequest); - - if ($aresResponse) { - $ns = $aresResponse->getDocNamespaces(); - $data = $aresResponse->children($ns['are']); - $elements = $data->children($ns['D'])->VBAS; - - $ico = $elements->ICO; - if ((string) $ico !== (string) $id) { - throw new AresException('IČ firmy nebylo nalezeno.'); - } - - $record = new AresRecord(); - - $record->setCompanyId(strval($elements->ICO)); - $record->setTaxId(strval($elements->DIC)); - $record->setCompanyName(strval($elements->OF)); - if (strval($elements->AA->NU) !== '') { - $record->setStreet(strval($elements->AA->NU)); - } else { - if (strval($elements->AA->NCO) !== '') { - $record->setStreet(strval($elements->AA->NCO)); - } - } - - if (strval($elements->AA->CO)) { - $record->setStreetHouseNumber(strval($elements->AA->CD)); - $record->setStreetOrientationNumber(strval($elements->AA->CO)); - } else { - $record->setStreetHouseNumber(strval($elements->AA->CD)); - } - - if (strval($elements->AA->N) === 'Praha') { - $record->setTown(strval($elements->AA->NMC)); - $record->setArea(strval($elements->AA->NCO)); - } elseif (strval($elements->AA->NCO) !== strval($elements->AA->N)) { - $record->setTown(strval($elements->AA->N)); - $record->setArea(strval($elements->AA->NCO)); - } else { - $record->setTown(strval($elements->AA->N)); - } - - $record->setZip(strval($elements->AA->PSC)); - - $record->setRegisters(strval($elements->PSU)); - - $stateInfo = $elements->AA->AU->children($ns['U']); - $record->setStateCode(strval($stateInfo->KK)); - } else { - throw new AresException('Databáze ARES není dostupná.'); - } - } catch (\Exception $e) { - throw new AresException($e->getMessage()); - } - - file_put_contents($cachedFile, serialize($record)); - - return $record; - } + public function findInResById(int $id): AresRecord; /** - * {@inheritDoc} - */ - public function findInResById(int $id): AresRecord - { - $url = $this->composeUrl(sprintf(self::URL_RES, $id)); - - $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; - $cachedFile = $this->cacheDir.'/res_'.$cachedFileName; - $cachedRawFile = $this->cacheDir.'/res_raw_'.$cachedFileName; - - if (is_file($cachedFile)) { - return unserialize(file_get_contents($cachedFile)); - } - - try { - $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); - if ($this->debug) { - file_put_contents($cachedRawFile, $aresRequest); - } - $aresResponse = simplexml_load_string($aresRequest); - - if ($aresResponse) { - $ns = $aresResponse->getDocNamespaces(); - $data = $aresResponse->children($ns['are']); - $elements = $data->children($ns['D'])->Vypis_RES; - - if (strval($elements->ZAU->ICO) === $id) { - $record = new AresRecord(); - $record->setCompanyId(strval($id)); - $record->setTaxId($this->findVatById($id)); - $record->setCompanyName(strval($elements->ZAU->OF)); - $record->setStreet(strval($elements->SI->NU)); - $record->setStreetHouseNumber(strval($elements->SI->CD)); - $record->setStreetOrientationNumber(strval($elements->SI->CO)); - $record->setTown(strval($elements->SI->N)); - $record->setZip(strval($elements->SI->PSC)); - } else { - throw new AresException('IČ firmy nebylo nalezeno.'); - } - } else { - throw new AresException('Databáze ARES není dostupná.'); - } - } catch (\Exception $e) { - throw new AresException($e->getMessage()); - } - file_put_contents($cachedFile, serialize($record)); - - return $record; - } - - /** - * {@inheritDoc} - */ - public function findVatById(int $id): AresRecord - { - $url = $this->composeUrl(sprintf(self::URL_TAX, $id)); - - $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; - $cachedFile = $this->cacheDir.'/tax_'.$cachedFileName; - $cachedRawFile = $this->cacheDir.'/tax_raw_'.$cachedFileName; - - if (is_file($cachedFile)) { - return unserialize(file_get_contents($cachedFile)); - } - - try { - $vatRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); - if ($this->debug) { - file_put_contents($cachedRawFile, $vatRequest); - } - $vatResponse = simplexml_load_string($vatRequest); - - if ($vatResponse) { - $record = new TaxRecord(); - $ns = $vatResponse->getDocNamespaces(); - $data = $vatResponse->children($ns['are']); - $elements = $data->children($ns['dtt'])->V->S; - - if (strval($elements->ico) === $id) { - $record->setTaxId(str_replace('dic=', 'CZ', strval($elements->p_dph))); - } else { - throw new AresException('DIČ firmy nebylo nalezeno.'); - } - } else { - throw new AresException('Databáze MFČR není dostupná.'); - } - } catch (\Exception $e) { - throw new \Exception($e->getMessage()); - } - file_put_contents($cachedFile, serialize($record)); - - return $record; - } - - /** - * {@inheritDoc} - */ - public function findByName(string $name, ?string $city = null) - { - $url = $this->composeUrl(sprintf( - self::URL_FIND, - urlencode(Lib::stripDiacritics($name)), - urlencode(Lib::stripDiacritics($city)) - )); - - $cachedFileName = date($this->cacheStrategy).'_'.md5($name.$city).'.php'; - $cachedFile = $this->cacheDir.'/find_'.$cachedFileName; - $cachedRawFile = $this->cacheDir.'/find_raw_'.$cachedFileName; - - if (is_file($cachedFile)) { - return unserialize(file_get_contents($cachedFile)); - } - - $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); - if ($this->debug) { - file_put_contents($cachedRawFile, $aresRequest); - } - $aresResponse = simplexml_load_string($aresRequest); - if (!$aresResponse) { - throw new AresException('Databáze ARES není dostupná.'); - } - - $ns = $aresResponse->getDocNamespaces(); - $data = $aresResponse->children($ns['are']); - $elements = $data->children($ns['dtt'])->V->S; - - if (empty($elements)) { - throw new AresException('Nic nebylo nalezeno.'); - } - - $records = new AresRecords(); - foreach ($elements as $element) { - $record = new AresRecord(); - $record->setCompanyId(strval($element->ico)); - $record->setTaxId( - ($element->dph ? str_replace('dic=', 'CZ', strval($element->p_dph)) : '') - ); - $record->setCompanyName(strval($element->ojm)); - $records[] = $record; - } - file_put_contents($cachedFile, serialize($records)); - - return $records; - } - - /** - * {@inheritDoc} + * Find subject's tax ID in OR (Obchodní rejstřík). + * + * @throws \Exception */ - public function setCacheStrategy(string $cacheStrategy): void - { - $this->cacheStrategy = $cacheStrategy; - } + public function findVatById(int $id): AresRecord; /** - * {@inheritDoc} + * Find subject by its name in OR (Obchodní rejstřík). + * + * @throws AresException + * + * @return array|AresRecord[]|AresRecords */ - public function setDebug(bool $debug): void - { - $this->debug = $debug; - } + public function findByName(string $name, ?string $city = null); - /** - * {@inheritDoc} - */ - public function setBalancer(string $balancer) - { - $this->balancer = $balancer; + public function setCacheStrategy(string $cacheStrategy): void; - return $this; - } + public function setDebug(bool $debug): void; /** - * {@inheritDoc} + * @return static */ - public function getLastUrl(): string - { - return $this->lastUrl; - } - - private function composeUrl(string $url): string - { - if ($this->balancer) { - $url = sprintf('%s?url=%s', $this->balancer, urlencode($url)); - } - - $this->lastUrl = $url; + public function setBalancer(string $balancer); - return $url; - } + public function getLastUrl(): string; } diff --git a/src/Ares/ApiClient/AresV1.php b/src/Ares/ApiClient/AresV1.php new file mode 100644 index 0000000..7149459 --- /dev/null +++ b/src/Ares/ApiClient/AresV1.php @@ -0,0 +1,342 @@ + [ + 'verify_peer' => false, + 'verify_peer_name' => false, + ], + ]; + + /** + * Last called URL. + */ + private string $lastUrl; + + public function __construct(?string $cacheDir = null, bool $debug = false, ?string $balancer = null) + { + if (null === $cacheDir) { + $cacheDir = sys_get_temp_dir(); + } + + if (null !== $balancer) { + $this->balancer = $balancer; + } + + $this->cacheDir = $cacheDir.'/defr/ares'; + $this->debug = $debug; + + if (!is_dir($this->cacheDir)) { + mkdir($this->cacheDir, 0777, true); + } + } + + /** + * {@inheritDoc} + */ + public function findByIdentificationNumber($id): AresRecord + { + $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; + $cachedFile = $this->cacheDir.'/bas_'.$cachedFileName; + $cachedRawFile = $this->cacheDir.'/bas_raw_'.$cachedFileName; + + if (is_file($cachedFile)) { + return unserialize(file_get_contents($cachedFile)); + } + + $url = $this->composeUrl(sprintf(self::URL_BAS, $id)); + + try { + $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); + if ($this->debug) { + file_put_contents($cachedRawFile, $aresRequest); + } + $aresResponse = simplexml_load_string($aresRequest); + + if ($aresResponse) { + $ns = $aresResponse->getDocNamespaces(); + $data = $aresResponse->children($ns['are']); + $elements = $data->children($ns['D'])->VBAS; + + $ico = $elements->ICO; + if ((string) $ico !== (string) $id) { + throw new AresException('IČ firmy nebylo nalezeno.'); + } + + $record = new AresRecord(); + + $record->setCompanyId(strval($elements->ICO)); + $record->setTaxId(strval($elements->DIC)); + $record->setCompanyName(strval($elements->OF)); + if (strval($elements->AA->NU) !== '') { + $record->setStreet(strval($elements->AA->NU)); + } else { + if (strval($elements->AA->NCO) !== '') { + $record->setStreet(strval($elements->AA->NCO)); + } + } + + if (strval($elements->AA->CO)) { + $record->setStreetHouseNumber(strval($elements->AA->CD)); + $record->setStreetOrientationNumber(strval($elements->AA->CO)); + } else { + $record->setStreetHouseNumber(strval($elements->AA->CD)); + } + + if (strval($elements->AA->N) === 'Praha') { + $record->setTown(strval($elements->AA->NMC)); + $record->setArea(strval($elements->AA->NCO)); + } elseif (strval($elements->AA->NCO) !== strval($elements->AA->N)) { + $record->setTown(strval($elements->AA->N)); + $record->setArea(strval($elements->AA->NCO)); + } else { + $record->setTown(strval($elements->AA->N)); + } + + $record->setZip(strval($elements->AA->PSC)); + + $record->setRegisters(strval($elements->PSU)); + + $stateInfo = $elements->AA->AU->children($ns['U']); + $record->setStateCode(strval($stateInfo->KK)); + } else { + throw new AresException('Databáze ARES není dostupná.'); + } + } catch (\Exception $e) { + throw new AresException($e->getMessage()); + } + + file_put_contents($cachedFile, serialize($record)); + + return $record; + } + + /** + * {@inheritDoc} + */ + public function findInResById(int $id): AresRecord + { + $url = $this->composeUrl(sprintf(self::URL_RES, $id)); + + $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; + $cachedFile = $this->cacheDir.'/res_'.$cachedFileName; + $cachedRawFile = $this->cacheDir.'/res_raw_'.$cachedFileName; + + if (is_file($cachedFile)) { + return unserialize(file_get_contents($cachedFile)); + } + + try { + $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); + if ($this->debug) { + file_put_contents($cachedRawFile, $aresRequest); + } + $aresResponse = simplexml_load_string($aresRequest); + + if ($aresResponse) { + $ns = $aresResponse->getDocNamespaces(); + $data = $aresResponse->children($ns['are']); + $elements = $data->children($ns['D'])->Vypis_RES; + + if (strval($elements->ZAU->ICO) === $id) { + $record = new AresRecord(); + $record->setCompanyId(strval($id)); + $record->setTaxId($this->findVatById($id)); + $record->setCompanyName(strval($elements->ZAU->OF)); + $record->setStreet(strval($elements->SI->NU)); + $record->setStreetHouseNumber(strval($elements->SI->CD)); + $record->setStreetOrientationNumber(strval($elements->SI->CO)); + $record->setTown(strval($elements->SI->N)); + $record->setZip(strval($elements->SI->PSC)); + } else { + throw new AresException('IČ firmy nebylo nalezeno.'); + } + } else { + throw new AresException('Databáze ARES není dostupná.'); + } + } catch (\Exception $e) { + throw new AresException($e->getMessage()); + } + file_put_contents($cachedFile, serialize($record)); + + return $record; + } + + /** + * {@inheritDoc} + */ + public function findVatById(int $id): AresRecord + { + $url = $this->composeUrl(sprintf(self::URL_TAX, $id)); + + $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; + $cachedFile = $this->cacheDir.'/tax_'.$cachedFileName; + $cachedRawFile = $this->cacheDir.'/tax_raw_'.$cachedFileName; + + if (is_file($cachedFile)) { + return unserialize(file_get_contents($cachedFile)); + } + + try { + $vatRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); + if ($this->debug) { + file_put_contents($cachedRawFile, $vatRequest); + } + $vatResponse = simplexml_load_string($vatRequest); + + if ($vatResponse) { + $record = new TaxRecord(); + $ns = $vatResponse->getDocNamespaces(); + $data = $vatResponse->children($ns['are']); + $elements = $data->children($ns['dtt'])->V->S; + + if (strval($elements->ico) === $id) { + $record->setTaxId(str_replace('dic=', 'CZ', strval($elements->p_dph))); + } else { + throw new AresException('DIČ firmy nebylo nalezeno.'); + } + } else { + throw new AresException('Databáze MFČR není dostupná.'); + } + } catch (\Exception $e) { + throw new \Exception($e->getMessage()); + } + file_put_contents($cachedFile, serialize($record)); + + return $record; + } + + /** + * {@inheritDoc} + */ + public function findByName(string $name, ?string $city = null) + { + $url = $this->composeUrl(sprintf( + self::URL_FIND, + urlencode(Lib::stripDiacritics($name)), + urlencode(Lib::stripDiacritics($city)) + )); + + $cachedFileName = date($this->cacheStrategy).'_'.md5($name.$city).'.php'; + $cachedFile = $this->cacheDir.'/find_'.$cachedFileName; + $cachedRawFile = $this->cacheDir.'/find_raw_'.$cachedFileName; + + if (is_file($cachedFile)) { + return unserialize(file_get_contents($cachedFile)); + } + + $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); + if ($this->debug) { + file_put_contents($cachedRawFile, $aresRequest); + } + $aresResponse = simplexml_load_string($aresRequest); + if (!$aresResponse) { + throw new AresException('Databáze ARES není dostupná.'); + } + + $ns = $aresResponse->getDocNamespaces(); + $data = $aresResponse->children($ns['are']); + $elements = $data->children($ns['dtt'])->V->S; + + if (empty($elements)) { + throw new AresException('Nic nebylo nalezeno.'); + } + + $records = new AresRecords(); + foreach ($elements as $element) { + $record = new AresRecord(); + $record->setCompanyId(strval($element->ico)); + $record->setTaxId( + ($element->dph ? str_replace('dic=', 'CZ', strval($element->p_dph)) : '') + ); + $record->setCompanyName(strval($element->ojm)); + $records[] = $record; + } + file_put_contents($cachedFile, serialize($records)); + + return $records; + } + + /** + * {@inheritDoc} + */ + public function setCacheStrategy(string $cacheStrategy): void + { + $this->cacheStrategy = $cacheStrategy; + } + + /** + * {@inheritDoc} + */ + public function setDebug(bool $debug): void + { + $this->debug = $debug; + } + + /** + * {@inheritDoc} + */ + public function setBalancer(string $balancer) + { + $this->balancer = $balancer; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function getLastUrl(): string + { + return $this->lastUrl; + } + + private function composeUrl(string $url): string + { + if ($this->balancer) { + $url = sprintf('%s?url=%s', $this->balancer, urlencode($url)); + } + + $this->lastUrl = $url; + + return $url; + } + +} From 6ee21d43522ac2718eeb7c5effeacf88f9e5b4e3 Mon Sep 17 00:00:00 2001 From: Daniel Kurowski Date: Mon, 18 Sep 2023 13:57:42 +0200 Subject: [PATCH 3/7] fix import --- src/Ares/ApiClient/AresV1.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ares/ApiClient/AresV1.php b/src/Ares/ApiClient/AresV1.php index 7149459..fa13145 100644 --- a/src/Ares/ApiClient/AresV1.php +++ b/src/Ares/ApiClient/AresV1.php @@ -6,6 +6,7 @@ use Defr\Ares\AresRecord; use Defr\Ares\AresRecords; use Defr\Ares\TaxRecord; +use Defr\Lib; class AresV1 implements ApiClient From c22ee988caa729214a6d2eec158b0085a87cbb5e Mon Sep 17 00:00:00 2001 From: Daniel Kurowski Date: Mon, 18 Sep 2023 14:34:32 +0200 Subject: [PATCH 4/7] decouple caching from api client --- src/Ares.php | 17 ++- src/Ares/ApiClient/ApiClient.php | 2 + src/Ares/ApiClient/AresV1.php | 88 +++----------- src/Ares/ApiClient/ResponseCache.php | 172 +++++++++++++++++++++++++++ tests/AresTest.php | 2 + 5 files changed, 206 insertions(+), 75 deletions(-) create mode 100644 src/Ares/ApiClient/ResponseCache.php diff --git a/src/Ares.php b/src/Ares.php index 4b06e84..abff330 100644 --- a/src/Ares.php +++ b/src/Ares.php @@ -4,6 +4,7 @@ use Defr\Ares\ApiClient\ApiClient; use Defr\Ares\ApiClient\AresV1; +use Defr\Ares\ApiClient\ResponseCache; use Defr\Ares\AresException; use Defr\Ares\AresRecord; use Defr\Ares\AresRecords; @@ -33,9 +34,11 @@ class Ares * @param bool $debug * @param string|null $balancer */ - public function __construct($cacheDir = null, $debug = false, $balancer = null) + public function __construct($cacheDir = null, $debug = false, $balancer = null, ?ApiClient $apiClient = null) { - $this->apiClient = new AresV1($cacheDir, $debug, $balancer); + $this->apiClient = $apiClient !== null + ? $apiClient + : new ResponseCache(new AresV1($balancer), $cacheDir, $debug, $balancer); } /** @@ -139,6 +142,11 @@ public function setBalancer($balancer) return $this; } + public function setApiClient(ApiClient $apiClient): void + { + $this->apiClient = $apiClient; + } + /** * @return string */ @@ -147,6 +155,11 @@ public function getLastUrl() return $this->apiClient->getLastUrl(); } + public function getLastRawResponse(): ?string + { + return $this->apiClient->getLastRawResponse(); + } + /** * @param mixed $id */ diff --git a/src/Ares/ApiClient/ApiClient.php b/src/Ares/ApiClient/ApiClient.php index 608c91f..5611b14 100644 --- a/src/Ares/ApiClient/ApiClient.php +++ b/src/Ares/ApiClient/ApiClient.php @@ -50,4 +50,6 @@ public function setBalancer(string $balancer); public function getLastUrl(): string; + public function getLastRawResponse(): ?string; + } diff --git a/src/Ares/ApiClient/AresV1.php b/src/Ares/ApiClient/AresV1.php index fa13145..63c7358 100644 --- a/src/Ares/ApiClient/AresV1.php +++ b/src/Ares/ApiClient/AresV1.php @@ -17,18 +17,6 @@ class AresV1 implements ApiClient private const URL_TAX = 'https://wwwinfo.mfcr.cz/cgi-bin/ares/ares_es.cgi?ico=%s&filtr=0'; private const URL_FIND = 'https://wwwinfo.mfcr.cz/cgi-bin/ares/ares_es.cgi?obch_jm=%s&obec=%s&filtr=0'; - private string $cacheStrategy = 'YW'; - - /** - * Path to directory that serves as an local cache for Ares service responses. - */ - private ?string $cacheDir = null; - - /** - * Whether we are running in debug/development environment. - */ - private bool $debug; - /** * Load balancing domain URL. */ @@ -51,22 +39,13 @@ class AresV1 implements ApiClient */ private string $lastUrl; - public function __construct(?string $cacheDir = null, bool $debug = false, ?string $balancer = null) - { - if (null === $cacheDir) { - $cacheDir = sys_get_temp_dir(); - } + private ?string $lastRawResponse = null; + public function __construct(?string $balancer = null) + { if (null !== $balancer) { $this->balancer = $balancer; } - - $this->cacheDir = $cacheDir.'/defr/ares'; - $this->debug = $debug; - - if (!is_dir($this->cacheDir)) { - mkdir($this->cacheDir, 0777, true); - } } /** @@ -74,21 +53,11 @@ public function __construct(?string $cacheDir = null, bool $debug = false, ?stri */ public function findByIdentificationNumber($id): AresRecord { - $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; - $cachedFile = $this->cacheDir.'/bas_'.$cachedFileName; - $cachedRawFile = $this->cacheDir.'/bas_raw_'.$cachedFileName; - - if (is_file($cachedFile)) { - return unserialize(file_get_contents($cachedFile)); - } - $url = $this->composeUrl(sprintf(self::URL_BAS, $id)); try { $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); - if ($this->debug) { - file_put_contents($cachedRawFile, $aresRequest); - } + $this->lastRawResponse = $aresRequest; $aresResponse = simplexml_load_string($aresRequest); if ($aresResponse) { @@ -144,8 +113,6 @@ public function findByIdentificationNumber($id): AresRecord throw new AresException($e->getMessage()); } - file_put_contents($cachedFile, serialize($record)); - return $record; } @@ -156,19 +123,9 @@ public function findInResById(int $id): AresRecord { $url = $this->composeUrl(sprintf(self::URL_RES, $id)); - $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; - $cachedFile = $this->cacheDir.'/res_'.$cachedFileName; - $cachedRawFile = $this->cacheDir.'/res_raw_'.$cachedFileName; - - if (is_file($cachedFile)) { - return unserialize(file_get_contents($cachedFile)); - } - try { $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); - if ($this->debug) { - file_put_contents($cachedRawFile, $aresRequest); - } + $this->lastRawResponse = $aresRequest; $aresResponse = simplexml_load_string($aresRequest); if ($aresResponse) { @@ -195,7 +152,6 @@ public function findInResById(int $id): AresRecord } catch (\Exception $e) { throw new AresException($e->getMessage()); } - file_put_contents($cachedFile, serialize($record)); return $record; } @@ -207,19 +163,9 @@ public function findVatById(int $id): AresRecord { $url = $this->composeUrl(sprintf(self::URL_TAX, $id)); - $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; - $cachedFile = $this->cacheDir.'/tax_'.$cachedFileName; - $cachedRawFile = $this->cacheDir.'/tax_raw_'.$cachedFileName; - - if (is_file($cachedFile)) { - return unserialize(file_get_contents($cachedFile)); - } - try { $vatRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); - if ($this->debug) { - file_put_contents($cachedRawFile, $vatRequest); - } + $this->lastRawResponse = $vatRequest; $vatResponse = simplexml_load_string($vatRequest); if ($vatResponse) { @@ -239,7 +185,6 @@ public function findVatById(int $id): AresRecord } catch (\Exception $e) { throw new \Exception($e->getMessage()); } - file_put_contents($cachedFile, serialize($record)); return $record; } @@ -255,18 +200,8 @@ public function findByName(string $name, ?string $city = null) urlencode(Lib::stripDiacritics($city)) )); - $cachedFileName = date($this->cacheStrategy).'_'.md5($name.$city).'.php'; - $cachedFile = $this->cacheDir.'/find_'.$cachedFileName; - $cachedRawFile = $this->cacheDir.'/find_raw_'.$cachedFileName; - - if (is_file($cachedFile)) { - return unserialize(file_get_contents($cachedFile)); - } - $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); - if ($this->debug) { - file_put_contents($cachedRawFile, $aresRequest); - } + $this->lastRawResponse = $aresRequest; $aresResponse = simplexml_load_string($aresRequest); if (!$aresResponse) { throw new AresException('Databáze ARES není dostupná.'); @@ -290,7 +225,6 @@ public function findByName(string $name, ?string $city = null) $record->setCompanyName(strval($element->ojm)); $records[] = $record; } - file_put_contents($cachedFile, serialize($records)); return $records; } @@ -329,6 +263,14 @@ public function getLastUrl(): string return $this->lastUrl; } + /** + * {@inheritDoc} + */ + public function getLastRawResponse(): ?string + { + return $this->lastRawResponse; + } + private function composeUrl(string $url): string { if ($this->balancer) { diff --git a/src/Ares/ApiClient/ResponseCache.php b/src/Ares/ApiClient/ResponseCache.php new file mode 100644 index 0000000..f089a43 --- /dev/null +++ b/src/Ares/ApiClient/ResponseCache.php @@ -0,0 +1,172 @@ +inner = $innerApiClient; + + if (null === $cacheDir) { + $cacheDir = sys_get_temp_dir(); + } + + if (null !== $balancer) { + $this->balancer = $balancer; + } + + $this->cacheDir = $cacheDir.'/defr/ares'; + $this->debug = $debug; + + if (!is_dir($this->cacheDir)) { + mkdir($this->cacheDir, 0777, true); + } + } + + /** + * {@inheritDoc} + */ + public function findByIdentificationNumber($id): AresRecord + { + $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; + $cachedFile = $this->cacheDir.'/bas_'.$cachedFileName; + + if (is_file($cachedFile)) { + return unserialize(file_get_contents($cachedFile)); + } + + $record = $this->inner->findByIdentificationNumber($id); + + file_put_contents($cachedFile, serialize($record)); + + return $record; + } + + /** + * {@inheritDoc} + */ + public function findInResById(int $id): AresRecord + { + $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; + $cachedFile = $this->cacheDir.'/res_'.$cachedFileName; + + if (is_file($cachedFile)) { + return unserialize(file_get_contents($cachedFile)); + } + + $record = $this->inner->findInResById($id); + + file_put_contents($cachedFile, serialize($record)); + + return $record; + } + + /** + * {@inheritDoc} + */ + public function findVatById(int $id): AresRecord + { + $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; + $cachedFile = $this->cacheDir.'/tax_'.$cachedFileName; + + if (is_file($cachedFile)) { + return unserialize(file_get_contents($cachedFile)); + } + + $record = $this->inner->findVatById($id); + + file_put_contents($cachedFile, serialize($record)); + + return $record; + } + + /** + * {@inheritDoc} + */ + public function findByName(string $name, ?string $city = null) + { + $cachedFileName = date($this->cacheStrategy).'_'.md5($name.$city).'.php'; + $cachedFile = $this->cacheDir.'/find_'.$cachedFileName; + + if (is_file($cachedFile)) { + return unserialize(file_get_contents($cachedFile)); + } + + $records = $this->inner->findByName($name, $city); + + file_put_contents($cachedFile, serialize($records)); + + return $records; + } + + /** + * {@inheritDoc} + */ + public function setCacheStrategy(string $cacheStrategy): void + { + $this->cacheStrategy = $cacheStrategy; + } + + /** + * {@inheritDoc} + */ + public function setDebug(bool $debug): void + { + $this->debug = $debug; + } + + /** + * {@inheritDoc} + */ + public function setBalancer(string $balancer) + { + $this->inner->setBalancer($balancer); + + return $this; + } + + /** + * {@inheritDoc} + */ + public function getLastUrl(): string + { + return $this->inner->getLastUrl(); + } + + /** + * {@inheritDoc} + */ + public function getLastRawResponse(): ?string + { + return $this->inner->getLastRawResponse(); + } + +} diff --git a/tests/AresTest.php b/tests/AresTest.php index e221772..c38f976 100644 --- a/tests/AresTest.php +++ b/tests/AresTest.php @@ -15,6 +15,7 @@ final class AresTest extends TestCase protected function setUp(): void { $this->ares = new Ares(null, true); + $this->ares->setApiClient(new Ares\ApiClient\AresV1()); // skip caching } /** @@ -179,6 +180,7 @@ public function testGetCompanyPeople() public function testBalancer(): void { $ares = new Ares(); + $ares->setApiClient(new Ares\ApiClient\AresV1()); // skip caching $ares->setBalancer('http://some.loadbalancer.domain'); self::expectExceptionMessageMatches('/php_network_getaddresses/'); try { From f143c16ad157b02b0a4f5cc68e6eec7d00a72658 Mon Sep 17 00:00:00 2001 From: Daniel Kurowski Date: Mon, 18 Sep 2023 14:39:21 +0200 Subject: [PATCH 5/7] add .idea/ folder to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a694df9..2db9bb2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/.idea /bin /vendor composer.lock From e250c56c77f5c5e5586f2d36a50b1a47e64837f7 Mon Sep 17 00:00:00 2001 From: Daniel Kurowski Date: Tue, 19 Sep 2023 07:36:48 +0200 Subject: [PATCH 6/7] WIP: implement ares v2 API --- src/Ares/ApiClient/AresV2.php | 279 ++++++++++++++++++++++++++++++++++ tests/AresTest.php | 2 +- 2 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 src/Ares/ApiClient/AresV2.php diff --git a/src/Ares/ApiClient/AresV2.php b/src/Ares/ApiClient/AresV2.php new file mode 100644 index 0000000..5675993 --- /dev/null +++ b/src/Ares/ApiClient/AresV2.php @@ -0,0 +1,279 @@ + [ + 'verify_peer' => false, + 'verify_peer_name' => false, + ], + ]; + + /** + * Last called URL. + */ + private string $lastUrl; + + private ?string $lastRawResponse = null; + + public function __construct(?string $balancer = null) + { + if (null !== $balancer) { + $this->balancer = $balancer; + } + } + + /** + * {@inheritDoc} + */ + public function findByIdentificationNumber($id): AresRecord + { + $url = $this->composeUrl(sprintf(self::URL_BAS, $id)); + + try { + $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); + $this->lastRawResponse = $aresRequest; + $aresResponse = json_decode($aresRequest); + + if ($aresResponse) { + $ico = $aresResponse->ico; + if ((string) $ico !== (string) $id) { + throw new AresException('IČ firmy nebylo nalezeno.'); + } + + $record = new AresRecord(); + + $record->setCompanyId(strval($aresResponse->ico)); + $record->setTaxId('CZ'.$aresResponse->dic); + $record->setCompanyName(strval($aresResponse->obchodniJmeno)); + $record->setStreet(strval($aresResponse->sidlo->nazevUlice)); + $record->setStreetHouseNumber(strval($aresResponse->sidlo->cisloDomovni)); + $record->setStreetOrientationNumber($aresResponse->sidlo->cisloOrientacni . ($aresResponse->sidlo->cisloOrientacniPismeno ?? '')); + + if (strval($aresResponse->sidlo->nazevObce) === 'Praha') { + $record->setTown(strval($aresResponse->sidlo->nazevMestskeCastiObvodu)); + $record->setArea(strval($aresResponse->sidlo->nazevCastiObce)); + } elseif (strval($aresResponse->sidlo->nazevCastiObce) !== strval($aresResponse->sidlo->nazevObce)) { + $record->setTown(strval($aresResponse->sidlo->nazevObce)); + $record->setArea(strval($aresResponse->sidlo->nazevCastiObce)); + } else { + $record->setTown($aresResponse->sidlo->nazevObce); + } + + $record->setZip(strval($aresResponse->sidlo->psc)); + + // todo A/N are present, but don't know where E/Z could be taken from + $record->setRegisters(strval($aresResponse->seznamRegistraci->stavZdrojeVr ?? null)); + + // todo this does not match in all cases + $record->setStateCode(strval($aresResponse->sidlo->kodKraje ?? null)); + } else { + throw new AresException('Databáze ARES není dostupná.'); + } + } catch (\Exception $e) { + throw new AresException($e->getMessage()); + } + + return $record; + } + + /** + * {@inheritDoc} + */ + public function findInResById(int $id): AresRecord + { + $url = $this->composeUrl(sprintf(self::URL_RES, $id)); + + try { + $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); + $this->lastRawResponse = $aresRequest; + $aresResponse = json_decode($aresRequest); + + // todo rewrite + + if ($aresResponse) { + $ns = $aresResponse->getDocNamespaces(); + $data = $aresResponse->children($ns['are']); + $elements = $data->children($ns['D'])->Vypis_RES; + + if (strval($elements->ZAU->ICO) === $id) { + $record = new AresRecord(); + $record->setCompanyId(strval($id)); + $record->setTaxId($this->findVatById($id)); + $record->setCompanyName(strval($elements->ZAU->OF)); + $record->setStreet(strval($elements->SI->NU)); + $record->setStreetHouseNumber(strval($elements->SI->CD)); + $record->setStreetOrientationNumber(strval($elements->SI->CO)); + $record->setTown(strval($elements->SI->N)); + $record->setZip(strval($elements->SI->PSC)); + } else { + throw new AresException('IČ firmy nebylo nalezeno.'); + } + } else { + throw new AresException('Databáze ARES není dostupná.'); + } + } catch (\Exception $e) { + throw new AresException($e->getMessage()); + } + + return $record; + } + + /** + * {@inheritDoc} + */ + public function findVatById(int $id): AresRecord + { + $url = $this->composeUrl(sprintf(self::URL_TAX, $id)); + + try { + $vatRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); + $this->lastRawResponse = $vatRequest; + $vatResponse = json_decode($vatRequest); + + // todo rewrite + + if ($vatResponse) { + $record = new TaxRecord(); + $ns = $vatResponse->getDocNamespaces(); + $data = $vatResponse->children($ns['are']); + $elements = $data->children($ns['dtt'])->V->S; + + if (strval($elements->ico) === $id) { + $record->setTaxId(str_replace('dic=', 'CZ', strval($elements->p_dph))); + } else { + throw new AresException('DIČ firmy nebylo nalezeno.'); + } + } else { + throw new AresException('Databáze MFČR není dostupná.'); + } + } catch (\Exception $e) { + throw new \Exception($e->getMessage()); + } + + return $record; + } + + /** + * {@inheritDoc} + */ + public function findByName(string $name, ?string $city = null) + { + $url = $this->composeUrl(sprintf( + self::URL_FIND, + urlencode(Lib::stripDiacritics($name)), + urlencode(Lib::stripDiacritics($city)) + )); + + $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); + $this->lastRawResponse = $aresRequest; + $aresResponse = json_decode($aresRequest); + if (!$aresResponse) { + throw new AresException('Databáze ARES není dostupná.'); + } + + // todo rewrite + + $ns = $aresResponse->getDocNamespaces(); + $data = $aresResponse->children($ns['are']); + $elements = $data->children($ns['dtt'])->V->S; + + if (empty($elements)) { + throw new AresException('Nic nebylo nalezeno.'); + } + + $records = new AresRecords(); + foreach ($elements as $element) { + $record = new AresRecord(); + $record->setCompanyId(strval($element->ico)); + $record->setTaxId( + ($element->dph ? str_replace('dic=', 'CZ', strval($element->p_dph)) : '') + ); + $record->setCompanyName(strval($element->ojm)); + $records[] = $record; + } + + return $records; + } + + /** + * {@inheritDoc} + */ + public function setCacheStrategy(string $cacheStrategy): void + { + $this->cacheStrategy = $cacheStrategy; + } + + /** + * {@inheritDoc} + */ + public function setDebug(bool $debug): void + { + $this->debug = $debug; + } + + /** + * {@inheritDoc} + */ + public function setBalancer(string $balancer) + { + $this->balancer = $balancer; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function getLastUrl(): string + { + return $this->lastUrl; + } + + /** + * {@inheritDoc} + */ + public function getLastRawResponse(): ?string + { + return $this->lastRawResponse; + } + + private function composeUrl(string $url): string + { + if ($this->balancer) { + $url = sprintf('%s?url=%s', $this->balancer, urlencode($url)); + } + + $this->lastUrl = $url; + + return $url; + } + +} diff --git a/tests/AresTest.php b/tests/AresTest.php index c38f976..b76b6b6 100644 --- a/tests/AresTest.php +++ b/tests/AresTest.php @@ -15,7 +15,7 @@ final class AresTest extends TestCase protected function setUp(): void { $this->ares = new Ares(null, true); - $this->ares->setApiClient(new Ares\ApiClient\AresV1()); // skip caching + $this->ares->setApiClient(new Ares\ApiClient\AresV2()); // skip caching } /** From d9b4111d2fc809a81772e4bf3c9b40b9987b8a86 Mon Sep 17 00:00:00 2001 From: Daniel Kurowski Date: Mon, 18 Sep 2023 17:06:27 +0200 Subject: [PATCH 7/7] start using new API on 1st of October --- src/Ares.php | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/Ares.php b/src/Ares.php index abff330..4880f15 100644 --- a/src/Ares.php +++ b/src/Ares.php @@ -2,8 +2,10 @@ namespace Defr; +use DateTimeImmutable; use Defr\Ares\ApiClient\ApiClient; use Defr\Ares\ApiClient\AresV1; +use Defr\Ares\ApiClient\AresV2; use Defr\Ares\ApiClient\ResponseCache; use Defr\Ares\AresException; use Defr\Ares\AresRecord; @@ -30,15 +32,28 @@ class Ares private ApiClient $apiClient; /** - * @param string|null $cacheDir - * @param bool $debug - * @param string|null $balancer - */ - public function __construct($cacheDir = null, $debug = false, $balancer = null, ?ApiClient $apiClient = null) + * @param string|null $cacheDir + * @param bool $debug + * @param string|null $balancer + * @param ApiClient|null $apiClient + * @param bool $newAresFromOctober not reflected if $apiClient passed + * @param bool $forceNewAres not reflected if $apiClient passed + */ + public function __construct($cacheDir = null, $debug = false, $balancer = null, ?ApiClient $apiClient = null, bool $newAresFromOctober = true, bool $forceNewAres = false) { - $this->apiClient = $apiClient !== null - ? $apiClient - : new ResponseCache(new AresV1($balancer), $cacheDir, $debug, $balancer); + if ($apiClient !== null) { + $this->apiClient = $apiClient; + return; + } + + // make auto-switch as of October + $now = DateTimeImmutable::createFromFormat('U', time()); + $startOfOctober = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', '2023-10-01 00:00:00'); + $inner = ($newAresFromOctober && $now >= $startOfOctober) || $forceNewAres + ? new AresV2($balancer) + : new AresV1($balancer); + + $this->apiClient = new ResponseCache($inner, $cacheDir, $debug, $balancer); } /**