├── .gitignore ├── .scrutinizer.yml ├── src ├── Justice │ ├── SubjectNotFoundException.php │ └── JusticeRecord.php ├── Ares │ ├── AresException.php │ ├── TaxRecord.php │ ├── AresRecords.php │ └── AresRecord.php ├── Parser │ ├── Helper │ │ └── StringHelper.php │ ├── DateTimeParser.php │ └── PersonParser.php ├── ValueObject │ └── Person.php ├── Justice.php └── Ares.php ├── phpunit.xml ├── examples ├── ares.php └── external.php ├── .github └── workflows │ └── travis.yaml ├── .travis.yml ├── composer.json ├── LICENSE ├── tests ├── JusticeTest.php └── AresTest.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /vendor 3 | composer.lock 4 | .phpunit.result.cache 5 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | tools: 2 | external_code_coverage: true 3 | php_analyzer: true 4 | sensiolabs_security_checker: true 5 | 6 | checks: 7 | php: 8 | code_rating: true 9 | -------------------------------------------------------------------------------- /src/Justice/SubjectNotFoundException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class AresException extends \Exception 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tests 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Parser/Helper/StringHelper.php: -------------------------------------------------------------------------------- 1 | findByIdentificationNumber(73263753); 10 | 11 | echo sprintf( 12 | '%s, %s, %s, %s, IČ %s, DIČ %s', 13 | $record->getCompanyName(), 14 | $record->getStreet(), 15 | $record->getZip(), 16 | $record->getTown(), 17 | $record->getCompanyId(), 18 | $record->getTaxId() 19 | ); 20 | -------------------------------------------------------------------------------- /examples/external.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class TaxRecord 11 | { 12 | /** 13 | * @var string 14 | */ 15 | private $taxId = null; 16 | 17 | /** 18 | * TaxRecord constructor. 19 | * 20 | * @param string $taxId 21 | */ 22 | public function __construct($taxId) 23 | { 24 | $this->taxId = !empty($taxId) ? $taxId : null; 25 | } 26 | 27 | /** 28 | * @return string 29 | */ 30 | public function getTaxId() 31 | { 32 | return $this->taxId; 33 | } 34 | 35 | /** 36 | * @return string 37 | */ 38 | public function __toString() 39 | { 40 | return strval($this->taxId); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Parser/DateTimeParser.php: -------------------------------------------------------------------------------- 1 | 'January', 15 | 'února' => 'February', 16 | 'března' => 'March', 17 | 'dubna' => 'April', 18 | 'května' => 'May', 19 | 'června' => 'June', 20 | 'července' => 'July', 21 | 'srpna' => 'August', 22 | 'září' => 'September', 23 | 'října' => 'October', 24 | 'listopadu' => 'November', 25 | 'prosince' => 'December', 26 | ]; 27 | 28 | /** 29 | * @param string $czechDate 30 | * 31 | * @return DateTimeInterface 32 | */ 33 | public static function parseFromCzechDateString($czechDate) 34 | { 35 | $czechDate = trim($czechDate); 36 | $englishDate = strtr($czechDate, self::$czechToEnglishMonths); 37 | 38 | return new DateTime($englishDate); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dfridrich/ares", 3 | "type": "library", 4 | "description": "Communication with ARES (Czech business register)", 5 | "keywords": ["php", "ares"], 6 | "homepage": "https://github.com/dfridrich/DefrAresBundle", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Dennis Fridrich", 11 | "email": "fridrich.dennis@gmail.com" 12 | } 13 | ], 14 | "require": { 15 | "php": "^7.4|^8.0", 16 | "beberlei/assert": "^2.4|^3.0", 17 | "dfridrich/library": "^1.37.14", 18 | "ext-simplexml": "*", 19 | "fabpot/goutte": "^4.0", 20 | "symfony/string": "^4|^5|^6" 21 | }, 22 | "require-dev": { 23 | "symplify/coding-standard": "^8", 24 | "phpunit/phpunit": "^9" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "Defr\\": "src/" 29 | } 30 | }, 31 | "config": { 32 | "bin-dir": "bin", 33 | "allow-plugins": { 34 | "dealerdirect/phpcodesniffer-composer-installer": true 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Dennis Fridrich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/ValueObject/Person.php: -------------------------------------------------------------------------------- 1 | name = $name; 30 | $this->birthday = $birthday; 31 | $this->address = $address; 32 | $this->registered = $registered; 33 | $this->deleted = $deleted; 34 | $this->type = $type; 35 | } 36 | 37 | public function getName(): string 38 | { 39 | return $this->name; 40 | } 41 | 42 | public function getBirthday(): ?DateTimeInterface 43 | { 44 | return $this->birthday; 45 | } 46 | 47 | public function getAddress(): ?string 48 | { 49 | return $this->address; 50 | } 51 | 52 | public function getRegistered(): DateTimeInterface 53 | { 54 | return $this->registered; 55 | } 56 | 57 | public function getDeleted(): ?DateTimeInterface 58 | { 59 | return $this->deleted; 60 | } 61 | 62 | public function getType(): ?string 63 | { 64 | return $this->type; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/Justice/JusticeRecord.php: -------------------------------------------------------------------------------- 1 | people = $people; 27 | } 28 | 29 | /** 30 | * @return array|Person[] 31 | */ 32 | public function getPeople(bool $onlyActive = true) 33 | { 34 | if($onlyActive) { 35 | return array_values(array_filter($this->people, fn (Person $person) => $person->getDeleted() === null)); 36 | } 37 | 38 | return $this->people; 39 | } 40 | 41 | /** 42 | * @return bool 43 | */ 44 | public function isInsolvencyRecord() 45 | { 46 | return $this->insolvencyRecord; 47 | } 48 | 49 | /** 50 | * @param bool $insolvencyRecord 51 | */ 52 | public function setInsolvencyRecord($insolvencyRecord) 53 | { 54 | $this->insolvencyRecord = $insolvencyRecord; 55 | } 56 | 57 | /** 58 | * @return bool 59 | */ 60 | public function isExecutionRecord() 61 | { 62 | return $this->executionRecord; 63 | } 64 | 65 | /** 66 | * @param bool $executionRecord 67 | */ 68 | public function setExecutionRecord($executionRecord) 69 | { 70 | $this->executionRecord = $executionRecord; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/Ares/AresRecords.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | final class AresRecords implements \ArrayAccess, \IteratorAggregate, \Countable 13 | { 14 | /** 15 | * @var AresRecord[] 16 | */ 17 | private $array = []; 18 | 19 | /** 20 | * @param mixed $offset 21 | * 22 | * @return bool 23 | */ 24 | public function offsetExists($offset) 25 | { 26 | if (isset($this->array[$offset])) { 27 | return true; 28 | } else { 29 | return false; 30 | } 31 | } 32 | 33 | /** 34 | * @param mixed $offset 35 | * 36 | * @return bool|AresRecord 37 | */ 38 | public function offsetGet($offset) 39 | { 40 | if ($this->offsetExists($offset)) { 41 | return $this->array[$offset]; 42 | } else { 43 | return false; 44 | } 45 | } 46 | 47 | /** 48 | * @param mixed $offset 49 | * @param AresRecord $value 50 | */ 51 | public function offsetSet($offset, $value) 52 | { 53 | if ($offset) { 54 | $this->array[$offset] = $value; 55 | } else { 56 | $this->array[] = $value; 57 | } 58 | } 59 | 60 | /** 61 | * @param mixed $offset 62 | */ 63 | public function offsetUnset($offset) 64 | { 65 | unset($this->array[$offset]); 66 | } 67 | 68 | /** 69 | * @return ArrayIterator 70 | */ 71 | public function getIterator() 72 | { 73 | return new ArrayIterator($this->array); 74 | } 75 | 76 | /** 77 | * {@inheritdoc} 78 | */ 79 | public function count() 80 | { 81 | return count($this->array); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /tests/JusticeTest.php: -------------------------------------------------------------------------------- 1 | justice = new Justice(new Client()); 19 | } 20 | 21 | public function testFindById(): void 22 | { 23 | $justiceRecord = $this->justice->findById(27791394); 24 | $this->assertInstanceOf('Defr\Justice\JusticeRecord', $justiceRecord); 25 | 26 | /** @var Person[] $people */ 27 | $people = $justiceRecord->getPeople(); 28 | $this->assertCount(3, $people); 29 | 30 | $this->assertSame($people[0]->getName(), 'DENNIS FRIDRICH'); 31 | $this->assertEquals($people[0]->getBirthday(), new DateTimeImmutable('1985-08-09')); 32 | $this->assertEquals($people[0]->getRegistered(), new DateTimeImmutable('2017-07-03')); 33 | $this->assertSame($people[0]->getAddress(), 'Obděnice 15, 262 55 Petrovice'); 34 | $this->assertNull($people[0]->getDeleted()); 35 | $this->assertSame($people[0]->getType(), PersonParser::EXECUTIVE); 36 | 37 | $this->assertSame($people[1]->getName(), 'DENNIS FRIDRICH'); 38 | $this->assertSame($people[2]->getName(), 'Mgr. ROBERT RUNTÁK'); 39 | 40 | $this->assertFalse($justiceRecord->isInsolvencyRecord()); 41 | $this->assertFalse($justiceRecord->isExecutionRecord()); 42 | 43 | $justiceRecord = $this->justice->findById(26823357); 44 | $this->assertTrue($justiceRecord->isInsolvencyRecord()); 45 | $this->assertFalse($justiceRecord->isExecutionRecord()); 46 | } 47 | 48 | public function testNotFoundFindId(): void 49 | { 50 | $justiceRecord = $this->justice->findById(123456); 51 | $this->assertFalse($justiceRecord); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/Parser/PersonParser.php: -------------------------------------------------------------------------------- 1 | filter('.div-cell div div:nth-child(1) span:nth-child(1)')->text(); 20 | 21 | try { 22 | $birthday = DateTimeParser::parseFromCzechDateString( 23 | $crawler->filter('.div-cell div div:nth-child(1) span:nth-child(3)')->text() 24 | ); 25 | } catch (Throwable $exception) { 26 | $birthday = null; 27 | } 28 | 29 | $address = $crawler->filter('.div-cell div div:nth-child(2) span:nth-child(1)')->text(); 30 | 31 | try { 32 | $registered = DateTimeParser::parseFromCzechDateString( 33 | s($crawler->text())->split(' zapsáno')[1]->trim()->split(' vymazáno')[0]->trim() 34 | ); 35 | } catch (Throwable $exception) { 36 | $registered = null; 37 | } 38 | 39 | 40 | try { 41 | $deleted = DateTimeParser::parseFromCzechDateString( 42 | s($crawler->text())->split(' vymazáno')[1]->trim() 43 | ); 44 | } catch (Throwable $exception) { 45 | $deleted = null; 46 | } 47 | 48 | $typeString = $crawler->filter('.vr-hlavicka')->text(); 49 | $type = null; 50 | 51 | if (mb_strpos($typeString, 'ednatel') !== false) { 52 | $type = self::EXECUTIVE; 53 | } 54 | 55 | if (mb_strpos($typeString, 'polečník') !== false) { 56 | $type = self::PARTNER; 57 | } 58 | 59 | return new Person( 60 | $name, 61 | $birthday, 62 | $address, 63 | $registered, 64 | $deleted, 65 | $type, 66 | ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ARES 2 | 3 | [![Build Status](https://img.shields.io/travis/dfridrich/Ares.svg?style=flat-square)](https://travis-ci.org/dfridrich/Ares) 4 | [![Quality Score](https://img.shields.io/scrutinizer/g/dfridrich/Ares.svg?style=flat-square)](https://scrutinizer-ci.com/g/dfridrich/Ares) 5 | [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/dfridrich/Ares.svg?style=flat-square)](https://scrutinizer-ci.com/g/dfridrich/Ares) 6 | [![Downloads](https://img.shields.io/packagist/dt/dfridrich/ares.svg?style=flat-square)](https://packagist.org/packages/dfridrich/ares) 7 | [![Latest stable](https://img.shields.io/packagist/v/dfridrich/ares.svg?style=flat-square)](https://packagist.org/packages/dfridrich/ares) 8 | 9 | 10 | Communication with ARES & Justice (Czech business registers). 11 | 12 | ## Installation 13 | 14 | ```sh 15 | composer require dfridrich/ares 16 | ``` 17 | 18 | ## Usage 19 | 20 | ```php 21 | findByIdentificationNumber(73263753); // instance of AresRecord 29 | 30 | $people = $record->getCompanyPeople(); // array of Person 31 | ``` 32 | 33 | ## ARES Balancer 34 | 35 | You can use simple balance script to spread the traffic among more IP addresses. See script `examples/external.php`. 36 | 37 | ### Usage 38 | 39 | ```php 40 | $ares = new Ares(); 41 | $ares->setBalancer('http://some.loadbalancer.domain'); 42 | ``` 43 | 44 | ## Develop 45 | 46 | ### Running tests suite in local docker environment 47 | ```php 48 | docker run -v `pwd`:/app -i -t php:7.2-fpm /bin/bash -c "/app/vendor/phpunit/phpunit/phpunit --colors --configuration /app/phpunit.xml /app/tests/" 49 | ``` 50 | 51 | 52 | ## Coding standard 53 | 54 | ### Check 55 | 56 | ``` 57 | vendor/bin/symplify-cs check src tests 58 | ``` 59 | 60 | ### Fix 61 | 62 | ``` 63 | vendor/bin/symplify-cs fix src tests 64 | ``` 65 | 66 | ## Contributors 67 | 68 | The list of people who contributed to this library. 69 | 70 | - @dfridrich 71 | - @TomasVotruba 72 | - @filipmelik 73 | - @Zemistr 74 | - @jkuchar 75 | - @petrparolek 76 | - @tlapi 77 | -------------------------------------------------------------------------------- /src/Justice.php: -------------------------------------------------------------------------------- 1 | client = $client; 37 | } 38 | 39 | /** 40 | * @param int $id 41 | * 42 | * @throws SubjectNotFoundException 43 | * @throws \Assert\AssertionFailedException 44 | * 45 | * @return JusticeRecord|false 46 | */ 47 | public function findById($id) 48 | { 49 | 50 | $id = is_numeric($id) ? (int)$id : $id; 51 | 52 | Assertion::integer((int)$id); 53 | 54 | $crawler = $this->client->request('GET', sprintf(self::URL_SUBJECTS, $id)); 55 | $detailUrl = $this->extractDetailUrlFromCrawler($crawler); 56 | 57 | if (false === $detailUrl) { 58 | return false; 59 | } 60 | 61 | $people = []; 62 | 63 | $justice = new JusticeRecord(); 64 | 65 | $crawler = $this->client->request('GET', $detailUrl); 66 | $crawler->filter('.aunp-content .div-table')->each(function (Crawler $table) use (&$people, &$justice) { 67 | $title = trim($table->filter('.vr-hlavicka')->text()); 68 | 69 | try { 70 | if (in_array($title, ['jednatel:', 'Jednatel:', 'Společník:'])) { 71 | $person = PersonParser::parseFromDomCrawler($table); 72 | if ($person !== null && !isset($people[$person->getName()])) { 73 | $people[] = $person; 74 | } 75 | } 76 | 77 | if ($justice->isInsolvencyRecord() !== true && $title === 'Údaje o insolvencích:') { 78 | $justice->setInsolvencyRecord(true); 79 | } 80 | if ($justice->isExecutionRecord() !== true && $title === 'Údaje o exekucích:') { 81 | $justice->setExecutionRecord(true); 82 | } 83 | } catch (\Exception $e) { 84 | throw $e; 85 | } 86 | }); 87 | 88 | $justice->setPeople($people); 89 | 90 | return $justice; 91 | } 92 | 93 | /** 94 | * @param Crawler $crawler 95 | * 96 | * @return false|string 97 | */ 98 | private function extractDetailUrlFromCrawler(Crawler $crawler) 99 | { 100 | $linksFound = $crawler->filter('.result-links > li > a'); 101 | if (!$linksFound) { 102 | return false; 103 | } 104 | 105 | $href = $linksFound->extract(['href']); 106 | if (!isset($href[1])) { 107 | return false; 108 | } 109 | 110 | return self::URL_BASE.$href[1]; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /tests/AresTest.php: -------------------------------------------------------------------------------- 1 | ares = new Ares(null, true); 18 | } 19 | 20 | /** 21 | * @param $companyId 22 | * @param $expectedException 23 | * @param $expectedExceptionMessage 24 | * @param $expected 25 | * 26 | * @dataProvider providerTestFindByIdentificationNumber 27 | * 28 | * @throws AresException 29 | */ 30 | public function testFindByIdentificationNumber( 31 | $companyId, 32 | $expectedException, 33 | $expectedExceptionMessage, 34 | $expected 35 | ): void { 36 | // setup 37 | if ($expectedException !== null) { 38 | $this->expectException($expectedException); 39 | } 40 | if ($expectedExceptionMessage !== null) { 41 | $this->expectExceptionMessage($expectedExceptionMessage); 42 | } 43 | 44 | // when 45 | $actual = $this->ares->findByIdentificationNumber($companyId); 46 | 47 | // then 48 | $this->assertEquals($expected, $actual); 49 | } 50 | 51 | /** 52 | * @return array 53 | */ 54 | public function providerTestFindByIdentificationNumber(): array 55 | { 56 | return [ 57 | [ 58 | // integer ID number 59 | 'companyId' => 48136450, 60 | 'expectedException' => null, 61 | 'expectedExceptionMessage' => null, 62 | 'expected' => new AresRecord( 63 | '48136450', 64 | 'CZ48136450', 65 | 'ČESKÁ NÁRODNÍ BANKA', 66 | 'Na příkopě', 67 | '864', 68 | '28', 69 | 'Praha 1', 70 | 'Nové Město', 71 | '11000', 72 | '19', 73 | 'N' 74 | ), 75 | ], 76 | [ 77 | // string ID number 78 | 'companyId' => '48136450', 79 | 'expectedException' => null, 80 | 'expectedExceptionMessage' => null, 81 | 'expected' => new AresRecord( 82 | '48136450', 83 | 'CZ48136450', 84 | 'ČESKÁ NÁRODNÍ BANKA', 85 | 'Na příkopě', 86 | '864', 87 | '28', 88 | 'Praha 1', 89 | 'Nové Město', 90 | '11000', 91 | '19', 92 | 'N' 93 | ), 94 | ], 95 | [ 96 | // string ID number with leading zeros 97 | 'companyId' => '00006947', 98 | 'expectedException' => null, 99 | 'expectedExceptionMessage' => null, 100 | 'expected' => new AresRecord( 101 | '00006947', 102 | 'CZ00006947', 103 | 'Ministerstvo financí', 104 | 'Letenská', 105 | '525', 106 | '15', 107 | 'Praha 1', 108 | 'Malá Strana', 109 | '11800', 110 | '19', 111 | 'N' 112 | ), 113 | ], 114 | [ 115 | // company in insolvency 116 | 'companyId' => '45275301', 117 | 'expectedException' => null, 118 | 'expectedExceptionMessage' => null, 119 | 'expected' => new AresRecord( 120 | '45275301', 121 | 'CZ45275301', 122 | 'MTH Praha a.s.', 123 | 'Kandertova', 124 | '1131', 125 | '1a', 126 | 'Praha 8', 127 | 'Libeň', 128 | '18000', 129 | '19', 130 | 'E' 131 | ), 132 | ], 133 | [ 134 | // nonsense string ID number with some charaters in it 135 | 'companyId' => 'ABC1234', 136 | 'expectedException' => InvalidArgumentException::class, 137 | 'expectedExceptionMessage' => 'IČ firmy musí být číslo.', 138 | 'expected' => null, 139 | ], 140 | [ 141 | // empty string ID number 142 | 'companyId' => '', 143 | 'expectedException' => InvalidArgumentException::class, 144 | 'expectedExceptionMessage' => 'IČ firmy musí být číslo.', 145 | 'expected' => null, 146 | ], 147 | [ 148 | // non-existent ID number 149 | 'companyId' => '12345678912345', 150 | 'expectedException' => AresException::class, 151 | 'expectedExceptionMessage' => 'IČ firmy nebylo nalezeno.', 152 | 'expected' => null, 153 | ], 154 | ]; 155 | } 156 | 157 | public function testFindByName(): void 158 | { 159 | $results = $this->ares->findByName('sever'); 160 | 161 | $this->assertGreaterThan(0, count($results)); 162 | } 163 | 164 | public function testFindByNameNonExistentName(): void 165 | { 166 | self::expectException(AresException::class); 167 | self::expectExceptionMessage('Nic nebylo nalezeno.'); 168 | $this->ares->findByName('some non-existent company name'); 169 | } 170 | 171 | public function testGetCompanyPeople() 172 | { 173 | $record = $this->ares->findByIdentificationNumber(27791394); 174 | $companyPeople = $record->getCompanyPeople(); 175 | 176 | $this->assertCount(3, $companyPeople); 177 | } 178 | 179 | public function testBalancer(): void 180 | { 181 | $ares = new Ares(); 182 | $ares->setBalancer('http://some.loadbalancer.domain'); 183 | self::expectExceptionMessageMatches('/php_network_getaddresses/'); 184 | try { 185 | $ares->findByIdentificationNumber(26168685); 186 | } catch (AresException $e) { 187 | throw $e; 188 | } 189 | $this->assertEquals( 190 | 'http://some.loadbalancer.domain' 191 | .'?url=http%3A%2F%2Fwwwinfo.mfcr.cz%2Fcgi-bin%2Fares%2Fdarv_bas.cgi%3Fico%3D26168685', 192 | $ares->getLastUrl() 193 | ); 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /src/Ares/AresRecord.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class AresRecord 14 | { 15 | /** 16 | * @var int 17 | */ 18 | private $companyId; 19 | 20 | /** 21 | * @var string 22 | */ 23 | private $taxId; 24 | 25 | /** 26 | * @var string 27 | */ 28 | private $companyName; 29 | 30 | /** 31 | * @var string 32 | */ 33 | private $street; 34 | 35 | /** 36 | * @var string 37 | */ 38 | private $streetHouseNumber; 39 | 40 | /** 41 | * @var string 42 | */ 43 | private $streetOrientationNumber; 44 | 45 | /** 46 | * @var string 47 | */ 48 | private $town; 49 | 50 | /** 51 | * @var string 52 | */ 53 | private $area; 54 | 55 | /** 56 | * @var string 57 | */ 58 | private $zip; 59 | 60 | /** 61 | * @var integer 62 | */ 63 | private $stateCode; 64 | 65 | /** 66 | * @var string 67 | */ 68 | private $insolvencyRegister; 69 | 70 | /** 71 | * @var Justice 72 | */ 73 | private $justiceRecord; 74 | 75 | /** 76 | * @var null|GouteClient 77 | */ 78 | protected $client; 79 | 80 | public function __construct( 81 | $companyId = null, 82 | $taxId = null, 83 | $companyName = null, 84 | $street = null, 85 | $streetHouseNumber = null, 86 | $streetOrientationNumber = null, 87 | $town = null, 88 | $area = null, 89 | $zip = null, 90 | $stateCode = null, 91 | $insolvencyRegister = null 92 | ) { 93 | $this->companyId = $companyId; 94 | $this->taxId = !empty($taxId) ? $taxId : null; 95 | $this->companyName = $companyName; 96 | $this->street = $street; 97 | $this->streetHouseNumber = !empty($streetHouseNumber) ? $streetHouseNumber : null; 98 | $this->streetOrientationNumber = !empty($streetOrientationNumber) ? $streetOrientationNumber : null; 99 | $this->town = $town; 100 | $this->area = $area; 101 | $this->zip = $zip; 102 | $this->stateCode = $stateCode; 103 | $this->insolvencyRegister = $insolvencyRegister; 104 | } 105 | 106 | public function getStreetWithNumbers() 107 | { 108 | return $this->street.' ' 109 | .($this->streetOrientationNumber 110 | ? 111 | $this->streetHouseNumber.'/'.$this->streetOrientationNumber 112 | : 113 | $this->streetHouseNumber); 114 | } 115 | 116 | public function __toString() 117 | { 118 | return $this->companyName; 119 | } 120 | 121 | public function getCompanyId() 122 | { 123 | return $this->companyId; 124 | } 125 | 126 | public function getTaxId() 127 | { 128 | return $this->taxId; 129 | } 130 | 131 | public function getCompanyName() 132 | { 133 | return $this->companyName; 134 | } 135 | 136 | public function getStreet() 137 | { 138 | return $this->street; 139 | } 140 | 141 | public function getStreetHouseNumber() 142 | { 143 | return $this->streetHouseNumber; 144 | } 145 | 146 | public function getStreetOrientationNumber() 147 | { 148 | return $this->streetOrientationNumber; 149 | } 150 | 151 | public function getTown() 152 | { 153 | return $this->town; 154 | } 155 | 156 | public function getArea() 157 | { 158 | return $this->area; 159 | } 160 | 161 | public function getZip() 162 | { 163 | return $this->zip; 164 | } 165 | 166 | public function setClient(GouteClient $client) 167 | { 168 | $this->client = $client; 169 | 170 | return $this; 171 | } 172 | 173 | public function getClient() 174 | { 175 | if (!$this->client) { 176 | $this->client = new GouteClient(); 177 | } 178 | 179 | return $this->client; 180 | } 181 | 182 | public function getJusticeRecord() 183 | { 184 | if ($this->justiceRecord !== null) { 185 | return $this->justiceRecord; 186 | } 187 | $client = $this->getClient(); 188 | $justice = new Justice($client); 189 | $this->justiceRecord = $justice->findById($this->companyId); 190 | return $this->justiceRecord; 191 | } 192 | 193 | public function getCompanyPeople($onlyActive = true) 194 | { 195 | $justiceRecord = $this->getJusticeRecord(); 196 | if ($justiceRecord) { 197 | return $justiceRecord->getPeople($onlyActive); 198 | } 199 | 200 | return []; 201 | } 202 | 203 | public function setCompanyId($companyId) 204 | { 205 | $this->companyId = $companyId; 206 | } 207 | 208 | public function setTaxId($taxId) 209 | { 210 | $this->taxId = $taxId; 211 | } 212 | 213 | public function setCompanyName($companyName) 214 | { 215 | $this->companyName = $companyName; 216 | } 217 | 218 | public function setStreet($street) 219 | { 220 | $this->street = $street; 221 | } 222 | 223 | public function setStreetHouseNumber($streetHouseNumber) 224 | { 225 | $this->streetHouseNumber = $streetHouseNumber; 226 | } 227 | 228 | public function setStreetOrientationNumber($streetOrientationNumber) 229 | { 230 | $this->streetOrientationNumber = $streetOrientationNumber; 231 | } 232 | 233 | public function setTown($town) 234 | { 235 | $this->town = $town; 236 | } 237 | 238 | public function setArea($area) 239 | { 240 | $this->area = $area; 241 | } 242 | 243 | public function setZip($zip) 244 | { 245 | $this->zip = $zip; 246 | } 247 | 248 | public function getStateCode() 249 | { 250 | return $this->stateCode; 251 | } 252 | 253 | public function setStateCode($stateCode) 254 | { 255 | $this->stateCode = $stateCode; 256 | } 257 | 258 | /** 259 | * @param string $registers 260 | * 261 | * N (nebo jiný znak) - není v evidenci 262 | * A - platná registrace 263 | * Z - zaniklá registrace 264 | * E - v pozici č. 22 označuje, že existuje záznam v Insolvenčním rejstříku. Nutno prověřit stav řízení! 265 | */ 266 | public function setRegisters($registers) 267 | { 268 | if (isset($registers[21])) { 269 | $this->insolvencyRegister = $registers[21]; 270 | } 271 | } 272 | 273 | public function getInsolvencyRegister() 274 | { 275 | return $this->insolvencyRegister; 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/Ares.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class Ares 18 | { 19 | const URL_BAS = 'https://wwwinfo.mfcr.cz/cgi-bin/ares/darv_bas.cgi?ico=%s'; 20 | const URL_RES = 'https://wwwinfo.mfcr.cz/cgi-bin/ares/darv_res.cgi?ICO=%s'; 21 | const URL_TAX = 'https://wwwinfo.mfcr.cz/cgi-bin/ares/ares_es.cgi?ico=%s&filtr=0'; 22 | const URL_FIND = 'https://wwwinfo.mfcr.cz/cgi-bin/ares/ares_es.cgi?obch_jm=%s&obec=%s&filtr=0'; 23 | 24 | /** 25 | * @var string 26 | */ 27 | private $cacheStrategy = 'YW'; 28 | 29 | /** 30 | * Path to directory that serves as an local cache for Ares service responses. 31 | * 32 | * @var string 33 | */ 34 | private $cacheDir = null; 35 | 36 | /** 37 | * Whether we are running in debug/development environment. 38 | * 39 | * @var bool 40 | */ 41 | private $debug; 42 | 43 | /** 44 | * Load balancing domain URL. 45 | * 46 | * @var string 47 | */ 48 | private $balancer = null; 49 | 50 | /** 51 | * Stream context options. 52 | * 53 | * @var array 54 | */ 55 | private $contextOptions = [ 56 | 'ssl' => [ 57 | 'verify_peer' => false, 58 | 'verify_peer_name' => false, 59 | ], 60 | ]; 61 | 62 | /** 63 | * Last called URL. 64 | * 65 | * @var string 66 | */ 67 | private $lastUrl; 68 | 69 | /** 70 | * @param string|null $cacheDir 71 | * @param bool $debug 72 | * @param string|null $balancer 73 | */ 74 | public function __construct($cacheDir = null, $debug = false, $balancer = null) 75 | { 76 | if (null === $cacheDir) { 77 | $cacheDir = sys_get_temp_dir(); 78 | } 79 | 80 | if (null !== $balancer) { 81 | $this->balancer = $balancer; 82 | } 83 | 84 | $this->cacheDir = $cacheDir.'/defr/ares'; 85 | $this->debug = $debug; 86 | 87 | if (!is_dir($this->cacheDir)) { 88 | mkdir($this->cacheDir, 0777, true); 89 | } 90 | } 91 | 92 | /** 93 | * @param $id 94 | * 95 | * @throws InvalidArgumentException 96 | * @throws Ares\AresException 97 | * 98 | * @return AresRecord 99 | */ 100 | public function findByIdentificationNumber($id) 101 | { 102 | $this->checkCompanyId($id); 103 | 104 | if (empty($id)) { 105 | throw new AresException('IČ firmy musí být zadáno.'); 106 | } 107 | 108 | $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; 109 | $cachedFile = $this->cacheDir.'/bas_'.$cachedFileName; 110 | $cachedRawFile = $this->cacheDir.'/bas_raw_'.$cachedFileName; 111 | 112 | if (is_file($cachedFile)) { 113 | return unserialize(file_get_contents($cachedFile)); 114 | } 115 | 116 | $url = $this->composeUrl(sprintf(self::URL_BAS, $id)); 117 | 118 | try { 119 | $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); 120 | if ($this->debug) { 121 | file_put_contents($cachedRawFile, $aresRequest); 122 | } 123 | $aresResponse = simplexml_load_string($aresRequest); 124 | 125 | if ($aresResponse) { 126 | $ns = $aresResponse->getDocNamespaces(); 127 | $data = $aresResponse->children($ns['are']); 128 | $elements = $data->children($ns['D'])->VBAS; 129 | 130 | $ico = $elements->ICO; 131 | if ((string) $ico !== (string) $id) { 132 | throw new AresException('IČ firmy nebylo nalezeno.'); 133 | } 134 | 135 | $record = new AresRecord(); 136 | 137 | $record->setCompanyId(strval($elements->ICO)); 138 | $record->setTaxId(strval($elements->DIC)); 139 | $record->setCompanyName(strval($elements->OF)); 140 | if (strval($elements->AA->NU) !== '') { 141 | $record->setStreet(strval($elements->AA->NU)); 142 | } else { 143 | if (strval($elements->AA->NCO) !== '') { 144 | $record->setStreet(strval($elements->AA->NCO)); 145 | } 146 | } 147 | 148 | if (strval($elements->AA->CO)) { 149 | $record->setStreetHouseNumber(strval($elements->AA->CD)); 150 | $record->setStreetOrientationNumber(strval($elements->AA->CO)); 151 | } else { 152 | $record->setStreetHouseNumber(strval($elements->AA->CD)); 153 | } 154 | 155 | if (strval($elements->AA->N) === 'Praha') { 156 | $record->setTown(strval($elements->AA->NMC)); 157 | $record->setArea(strval($elements->AA->NCO)); 158 | } elseif (strval($elements->AA->NCO) !== strval($elements->AA->N)) { 159 | $record->setTown(strval($elements->AA->N)); 160 | $record->setArea(strval($elements->AA->NCO)); 161 | } else { 162 | $record->setTown(strval($elements->AA->N)); 163 | } 164 | 165 | $record->setZip(strval($elements->AA->PSC)); 166 | 167 | $record->setRegisters(strval($elements->PSU)); 168 | 169 | $stateInfo = $elements->AA->AU->children($ns['U']); 170 | $record->setStateCode(strval($stateInfo->KK)); 171 | } else { 172 | throw new AresException('Databáze ARES není dostupná.'); 173 | } 174 | } catch (\Exception $e) { 175 | throw new AresException($e->getMessage()); 176 | } 177 | 178 | file_put_contents($cachedFile, serialize($record)); 179 | 180 | return $record; 181 | } 182 | 183 | /** 184 | * Find subject in RES (Registr ekonomických subjektů) 185 | * 186 | * @param $id 187 | * 188 | * @throws InvalidArgumentException 189 | * @throws Ares\AresException 190 | * 191 | * @return AresRecord 192 | */ 193 | public function findInResById($id) 194 | { 195 | $this->checkCompanyId($id); 196 | 197 | $url = $this->composeUrl(sprintf(self::URL_RES, $id)); 198 | 199 | $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; 200 | $cachedFile = $this->cacheDir.'/res_'.$cachedFileName; 201 | $cachedRawFile = $this->cacheDir.'/res_raw_'.$cachedFileName; 202 | 203 | if (is_file($cachedFile)) { 204 | return unserialize(file_get_contents($cachedFile)); 205 | } 206 | 207 | try { 208 | $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); 209 | if ($this->debug) { 210 | file_put_contents($cachedRawFile, $aresRequest); 211 | } 212 | $aresResponse = simplexml_load_string($aresRequest); 213 | 214 | if ($aresResponse) { 215 | $ns = $aresResponse->getDocNamespaces(); 216 | $data = $aresResponse->children($ns['are']); 217 | $elements = $data->children($ns['D'])->Vypis_RES; 218 | 219 | if (strval($elements->ZAU->ICO) === $id) { 220 | $record = new AresRecord(); 221 | $record->setCompanyId(strval($id)); 222 | $record->setTaxId($this->findVatById($id)); 223 | $record->setCompanyName(strval($elements->ZAU->OF)); 224 | $record->setStreet(strval($elements->SI->NU)); 225 | $record->setStreetHouseNumber(strval($elements->SI->CD)); 226 | $record->setStreetOrientationNumber(strval($elements->SI->CO)); 227 | $record->setTown(strval($elements->SI->N)); 228 | $record->setZip(strval($elements->SI->PSC)); 229 | } else { 230 | throw new AresException('IČ firmy nebylo nalezeno.'); 231 | } 232 | } else { 233 | throw new AresException('Databáze ARES není dostupná.'); 234 | } 235 | } catch (\Exception $e) { 236 | throw new AresException($e->getMessage()); 237 | } 238 | file_put_contents($cachedFile, serialize($record)); 239 | 240 | return $record; 241 | } 242 | 243 | /** 244 | * Find subject's tax ID in OR (Obchodní rejstřík). 245 | 246 | * @param $id 247 | * 248 | * @throws InvalidArgumentException 249 | * @throws \Exception 250 | * 251 | * @return string 252 | */ 253 | public function findVatById($id) 254 | { 255 | $this->checkCompanyId($id); 256 | 257 | $url = $this->composeUrl(sprintf(self::URL_TAX, $id)); 258 | 259 | $cachedFileName = $id.'_'.date($this->cacheStrategy).'.php'; 260 | $cachedFile = $this->cacheDir.'/tax_'.$cachedFileName; 261 | $cachedRawFile = $this->cacheDir.'/tax_raw_'.$cachedFileName; 262 | 263 | if (is_file($cachedFile)) { 264 | return unserialize(file_get_contents($cachedFile)); 265 | } 266 | 267 | try { 268 | $vatRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); 269 | if ($this->debug) { 270 | file_put_contents($cachedRawFile, $vatRequest); 271 | } 272 | $vatResponse = simplexml_load_string($vatRequest); 273 | 274 | if ($vatResponse) { 275 | $record = new TaxRecord(); 276 | $ns = $vatResponse->getDocNamespaces(); 277 | $data = $vatResponse->children($ns['are']); 278 | $elements = $data->children($ns['dtt'])->V->S; 279 | 280 | if (strval($elements->ico) === $id) { 281 | $record->setTaxId(str_replace('dic=', 'CZ', strval($elements->p_dph))); 282 | } else { 283 | throw new AresException('DIČ firmy nebylo nalezeno.'); 284 | } 285 | } else { 286 | throw new AresException('Databáze MFČR není dostupná.'); 287 | } 288 | } catch (\Exception $e) { 289 | throw new \Exception($e->getMessage()); 290 | } 291 | file_put_contents($cachedFile, serialize($record)); 292 | 293 | return $record; 294 | } 295 | 296 | /** 297 | * Find subject by its name in OR (Obchodní rejstřík). 298 | * 299 | * @param string $name 300 | * @param string|null $city 301 | * 302 | * @throws InvalidArgumentException 303 | * @throws AresException 304 | * 305 | * @return array|AresRecord[]|AresRecords 306 | */ 307 | public function findByName($name, $city = null) 308 | { 309 | if (strlen($name) < 3) { 310 | throw new InvalidArgumentException('Zadejte minimálně 3 znaky pro hledání.'); 311 | } 312 | 313 | $url = $this->composeUrl(sprintf( 314 | self::URL_FIND, 315 | urlencode(Lib::stripDiacritics($name)), 316 | urlencode(Lib::stripDiacritics($city)) 317 | )); 318 | 319 | $cachedFileName = date($this->cacheStrategy).'_'.md5($name.$city).'.php'; 320 | $cachedFile = $this->cacheDir.'/find_'.$cachedFileName; 321 | $cachedRawFile = $this->cacheDir.'/find_raw_'.$cachedFileName; 322 | 323 | if (is_file($cachedFile)) { 324 | return unserialize(file_get_contents($cachedFile)); 325 | } 326 | 327 | $aresRequest = file_get_contents($url, false, stream_context_create($this->contextOptions)); 328 | if ($this->debug) { 329 | file_put_contents($cachedRawFile, $aresRequest); 330 | } 331 | $aresResponse = simplexml_load_string($aresRequest); 332 | if (!$aresResponse) { 333 | throw new AresException('Databáze ARES není dostupná.'); 334 | } 335 | 336 | $ns = $aresResponse->getDocNamespaces(); 337 | $data = $aresResponse->children($ns['are']); 338 | $elements = $data->children($ns['dtt'])->V->S; 339 | 340 | if (empty($elements)) { 341 | throw new AresException('Nic nebylo nalezeno.'); 342 | } 343 | 344 | $records = new AresRecords(); 345 | foreach ($elements as $element) { 346 | $record = new AresRecord(); 347 | $record->setCompanyId(strval($element->ico)); 348 | $record->setTaxId( 349 | ($element->dph ? str_replace('dic=', 'CZ', strval($element->p_dph)) : '') 350 | ); 351 | $record->setCompanyName(strval($element->ojm)); 352 | $records[] = $record; 353 | } 354 | file_put_contents($cachedFile, serialize($records)); 355 | 356 | return $records; 357 | } 358 | 359 | /** 360 | * @param string $cacheStrategy 361 | */ 362 | public function setCacheStrategy($cacheStrategy) 363 | { 364 | $this->cacheStrategy = $cacheStrategy; 365 | } 366 | 367 | /** 368 | * @param bool $debug 369 | */ 370 | public function setDebug($debug) 371 | { 372 | $this->debug = $debug; 373 | } 374 | 375 | /** 376 | * @param string $balancer 377 | * 378 | * @return $this 379 | */ 380 | public function setBalancer($balancer) 381 | { 382 | $this->balancer = $balancer; 383 | 384 | return $this; 385 | } 386 | 387 | /** 388 | * @return string 389 | */ 390 | public function getLastUrl() 391 | { 392 | return $this->lastUrl; 393 | } 394 | 395 | /** 396 | * @param int $id 397 | */ 398 | private function checkCompanyId($id) 399 | { 400 | if (!is_numeric($id) || !preg_match('/^\d+$/', $id)) { 401 | throw new InvalidArgumentException('IČ firmy musí být číslo.'); 402 | } 403 | } 404 | 405 | /** 406 | * @param string $url 407 | * 408 | * @return string 409 | */ 410 | private function composeUrl($url) 411 | { 412 | if ($this->balancer) { 413 | $url = sprintf('%s?url=%s', $this->balancer, urlencode($url)); 414 | } 415 | 416 | $this->lastUrl = $url; 417 | 418 | return $url; 419 | } 420 | 421 | } 422 | --------------------------------------------------------------------------------