diff --git a/src/CoreShop/Bundle/AddressBundle/Resources/config/services.yml b/src/CoreShop/Bundle/AddressBundle/Resources/config/services.yml index 225fc2331e..f740072659 100755 --- a/src/CoreShop/Bundle/AddressBundle/Resources/config/services.yml +++ b/src/CoreShop/Bundle/AddressBundle/Resources/config/services.yml @@ -38,9 +38,17 @@ services: public: false arguments: - '@coreshop.repository.country' + - '@pimcore.cache.core.handler' tags: - { name: coreshop.context.country.request_based.resolver, priority: 10 } + coreshop.context.country.cached: '@CoreShop\Component\Address\Context\RequestBased\CachedCountryContext' + CoreShop\Component\Address\Context\RequestBased\CachedCountryContext: + decorates: coreshop.context.country.request_based.resolver.composite + decoration_priority: 255 + arguments: + - '@CoreShop\Component\Address\Context\RequestBased\CachedCountryContext.inner' + coreshop.address.formatter: '@CoreShop\Component\Address\Formatter\AddressFormatter' CoreShop\Component\Address\Formatter\AddressFormatterInterface: '@CoreShop\Component\Address\Formatter\AddressFormatter' CoreShop\Component\Address\Formatter\AddressFormatter: diff --git a/src/CoreShop/Component/Address/Context/RequestBased/CachedCountryContext.php b/src/CoreShop/Component/Address/Context/RequestBased/CachedCountryContext.php new file mode 100644 index 0000000000..bc341680c7 --- /dev/null +++ b/src/CoreShop/Component/Address/Context/RequestBased/CachedCountryContext.php @@ -0,0 +1,50 @@ +inner = $inner; + } + + /** + * {@inheritdoc} + */ + public function findCountry(Request $request) + { + if (null === $this->country) { + $this->country = $this->inner->findCountry($request); + } + + return $this->country; + } +} diff --git a/src/CoreShop/Component/Address/Context/RequestBased/GeoLiteBasedRequestResolver.php b/src/CoreShop/Component/Address/Context/RequestBased/GeoLiteBasedRequestResolver.php index 533024cc28..7fdbbcc580 100644 --- a/src/CoreShop/Component/Address/Context/RequestBased/GeoLiteBasedRequestResolver.php +++ b/src/CoreShop/Component/Address/Context/RequestBased/GeoLiteBasedRequestResolver.php @@ -16,6 +16,7 @@ use CoreShop\Component\Address\Model\CountryInterface; use CoreShop\Component\Address\Repository\CountryRepositoryInterface; use GeoIp2\Database\Reader; +use Pimcore\Cache\Core\CoreHandlerInterface; use Symfony\Component\HttpFoundation\Request; final class GeoLiteBasedRequestResolver implements RequestResolverInterface @@ -25,12 +26,25 @@ final class GeoLiteBasedRequestResolver implements RequestResolverInterface */ private $countryRepository; + /** + * @var CoreHandlerInterface + */ + private $cache; + + /** + * @var string + */ + private $geoDbFile; + /** * @param CountryRepositoryInterface $countryRepository + * @param CoreHandlerInterface $cache */ - public function __construct(CountryRepositoryInterface $countryRepository) + public function __construct(CountryRepositoryInterface $countryRepository, CoreHandlerInterface $cache) { $this->countryRepository = $countryRepository; + $this->cache = $cache; + $this->geoDbFile = PIMCORE_CONFIGURATION_DIRECTORY . '/GeoLite2-City.mmdb'; } /** @@ -38,30 +52,61 @@ public function __construct(CountryRepositoryInterface $countryRepository) */ public function findCountry(Request $request) { - $geoDbFile = PIMCORE_CONFIGURATION_DIRECTORY . '/GeoLite2-City.mmdb'; - $record = null; + if (!file_exists($this->geoDbFile)) { + throw new CountryNotFoundException(); + } - if (file_exists($geoDbFile)) { - try { - $reader = new Reader($geoDbFile); + $record = null; + $isoCode = null; + $clientIp = $request->getClientIp(); - $clientIp = $request->getClientIp(); + if ($this->checkIfIpIsPrivate($clientIp)) { + throw new CountryNotFoundException(); + } - if (!$this->checkIfIpIsPrivate($clientIp)) { - $record = $reader->city($clientIp); + $cacheKey = sprintf('geo_lite_ip_%s', md5($clientIp)); - $country = $this->countryRepository->findByCode($record->country->isoCode); + if ($countryIsoCode = $this->cache->getItem($cacheKey)) { + $country = $this->countryRepository->findByCode($countryIsoCode); - if ($country instanceof CountryInterface) { - return $country; - } - } - } catch (\Exception $e) { - //If something goes wrong, ignore the exception and throw a CountryNotFoundException + if ($country instanceof CountryInterface) { + return $country; } } - throw new CountryNotFoundException(); + $countryIsoCode = $this->guessCountryByGeoLite($clientIp); + + if ($countryIsoCode === null) { + throw new CountryNotFoundException(); + } + + $country = $this->countryRepository->findByCode($countryIsoCode); + + if (!$country instanceof CountryInterface) { + throw new CountryNotFoundException(); + } + + $this->cache->save($cacheKey, $countryIsoCode, [], 24*60*60); + + return $country; + } + + /** + * @param string $clientIp + * + * @return string|null + */ + private function guessCountryByGeoLite($clientIp) + { + try { + $reader = new Reader($this->geoDbFile); + $record = $reader->city($clientIp); + return $record->country->isoCode; + } catch (\Exception $e) { + //If something goes wrong, ignore the exception and throw a CountryNotFoundException + } + + return null; } /** @@ -73,7 +118,7 @@ public function findCountry(Request $request) */ private function checkIfIpIsPrivate($clientIp) { - $priAddrs = [ + $privateAddresses = [ '10.0.0.0|10.255.255.255', // single class A network '172.16.0.0|172.31.255.255', // 16 contiguous class B network '192.168.0.0|192.168.255.255', // 256 contiguous class C network @@ -82,8 +127,8 @@ private function checkIfIpIsPrivate($clientIp) ]; $longIp = ip2long($clientIp); - if ($longIp != -1) { - foreach ($priAddrs as $priAddr) { + if ($longIp !== -1) { + foreach ($privateAddresses as $priAddr) { list($start, $end) = explode('|', $priAddr); // IF IS PRIVATE