visitService = $visitService; $this->ipLocationResolver = $ipLocationResolver; $this->locker = $locker; } protected function configure(): void { $this ->setName(self::NAME) ->setDescription('Processes visits where location is not set yet'); } protected function execute(InputInterface $input, OutputInterface $output): ?int { $this->output = $output; $io = new SymfonyStyle($input, $output); $lock = $this->locker->createLock(self::NAME); if (! $lock->acquire()) { $io->warning(sprintf('There is already an instance of the "%s" command in execution', self::NAME)); return ExitCodes::EXIT_WARNING; } try { $this->visitService->locateUnlocatedVisits( [$this, 'getGeolocationDataForVisit'], function (VisitLocation $location) use ($output) { if (! $location->isEmpty()) { $output->writeln( sprintf(' [Address located at "%s"]', $location->getCountryName()) ); } } ); $io->success('Finished processing all IPs'); } finally { $lock->release(); return ExitCodes::EXIT_SUCCESS; } } public function getGeolocationDataForVisit(Visit $visit): Location { if (! $visit->hasRemoteAddr()) { $this->output->writeln( 'Ignored visit with no IP address', OutputInterface::VERBOSITY_VERBOSE ); throw IpCannotBeLocatedException::forEmptyAddress(); } $ipAddr = $visit->getRemoteAddr(); $this->output->write(sprintf('Processing IP %s', $ipAddr)); if ($ipAddr === IpAddress::LOCALHOST) { $this->output->writeln(' [Ignored localhost address]'); throw IpCannotBeLocatedException::forLocalhost(); } try { return $this->ipLocationResolver->resolveIpLocation($ipAddr); } catch (WrongIpException $e) { $this->output->writeln(' [An error occurred while locating IP. Skipped]'); if ($this->output->isVerbose()) { $this->getApplication()->renderException($e, $this->output); } throw IpCannotBeLocatedException::forError($e); } } }