visitService = $visitService; $this->ipLocationResolver = $ipLocationResolver; $this->dbUpdater = $dbUpdater; } protected function configure(): void { $this ->setName(self::NAME) ->setAliases(self::ALIASES) ->setDescription('Resolves visits origin locations.'); } protected function lockedExecute(InputInterface $input, OutputInterface $output): int { $this->io = new SymfonyStyle($input, $output); try { $this->checkDbUpdate(); $this->visitService->locateUnlocatedVisits( [$this, 'getGeolocationDataForVisit'], static function (VisitLocation $location) use ($output) { if (!$location->isEmpty()) { $output->writeln( sprintf(' [Address located at "%s"]', $location->getCountryName()) ); } } ); $this->io->success('Finished processing all IPs'); return ExitCodes::EXIT_SUCCESS; } catch (Throwable $e) { $this->io->error($e->getMessage()); if ($e instanceof Exception && $this->io->isVerbose()) { $this->getApplication()->renderThrowable($e, $this->io); } return ExitCodes::EXIT_FAILURE; } } public function getGeolocationDataForVisit(Visit $visit): Location { if (! $visit->hasRemoteAddr()) { $this->io->writeln( 'Ignored visit with no IP address', OutputInterface::VERBOSITY_VERBOSE ); throw IpCannotBeLocatedException::forEmptyAddress(); } $ipAddr = $visit->getRemoteAddr(); $this->io->write(sprintf('Processing IP %s', $ipAddr)); if ($ipAddr === IpAddress::LOCALHOST) { $this->io->writeln(' [Ignored localhost address]'); throw IpCannotBeLocatedException::forLocalhost(); } try { return $this->ipLocationResolver->resolveIpLocation($ipAddr); } catch (WrongIpException $e) { $this->io->writeln(' [An error occurred while locating IP. Skipped]'); if ($this->io->isVerbose()) { $this->getApplication()->renderThrowable($e, $this->io); } throw IpCannotBeLocatedException::forError($e); } } private function checkDbUpdate(): void { try { $this->dbUpdater->checkDbUpdate(function (bool $olderDbExists) { $this->io->writeln( sprintf('%s GeoLite2 database...', $olderDbExists ? 'Updating' : 'Downloading') ); $this->progressBar = new ProgressBar($this->io); }, function (int $total, int $downloaded) { $this->progressBar->setMaxSteps($total); $this->progressBar->setProgress($downloaded); }); if ($this->progressBar !== null) { $this->progressBar->finish(); $this->io->newLine(); } } catch (GeolocationDbUpdateFailedException $e) { if (! $e->olderDbExists()) { $this->io->error('GeoLite2 database download failed. It is not possible to locate visits.'); throw $e; } $this->io->newLine(); $this->io->writeln( '[Warning] GeoLite2 database update failed. Proceeding with old version.' ); } } protected function getLockConfig(): LockedCommandConfig { return new LockedCommandConfig($this->getName()); } }