src/EventSubscriber/SitemapSubscriber.php line 88

Open in your IDE?
  1. <?php
  2. /**
  3.  * Created by simpson <simpsonwork@gmail.com>
  4.  * Date: 2019-04-18
  5.  * Time: 11:17
  6.  */
  7. namespace App\EventSubscriber;
  8. use App\Entity\EnumTrait;
  9. use App\Entity\Location\City;
  10. use App\Entity\Profile\Profile;
  11. use App\Entity\Service;
  12. use App\Entity\Saloon\Saloon;
  13. use App\Repository\CityRepository;
  14. use App\Repository\ProfileRepository;
  15. use App\Repository\SaloonRepository;
  16. use App\Repository\ServiceRepository;
  17. use App\Routing\DynamicRouter;
  18. use App\Service\Features;
  19. use App\Service\ProfileNameLandingProvider;
  20. use Carbon\Carbon;
  21. use Carbon\CarbonImmutable;
  22. use Doctrine\Persistence\ManagerRegistry;
  23. use GuzzleHttp\ClientInterface;
  24. use Presta\SitemapBundle\Event\SitemapPopulateEvent;
  25. use Presta\SitemapBundle\Service\UrlContainerInterface;
  26. use Presta\SitemapBundle\Sitemap\Url\UrlConcrete;
  27. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  28. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  29. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  30. use Symfony\Component\Routing\RouterInterface;
  31. use Symfony\Component\Yaml\Yaml;
  32. class SitemapSubscriber implements EventSubscriberInterface
  33. {
  34.     public const ROUTE_SITEMAP_OPTION 'sitemap.custom';
  35.     protected CityRepository $cityRepository;
  36.     protected ProfileRepository $profileRepository;
  37.     protected SaloonRepository $saloonRepository;
  38.     protected ServiceRepository $serviceRepository;
  39.     protected string $defaultCity;
  40.     protected ClientInterface $httpClient;
  41.     private array $sitemapConfig;
  42.     private ?array $routeLocales;
  43.     public function __construct(
  44.         protected RouterInterface $router,
  45.         private Features          $features,
  46.         ManagerRegistry           $registry,
  47.         ParameterBagInterface     $parameterBag,
  48.         ClientInterface           $apiDomainTimelineClient,
  49.         protected string          $sitemapConfigPath,
  50.         private ProfileNameLandingProvider $profileNameLandingProvider,
  51.     )
  52.     {
  53.         $this->defaultCity $parameterBag->get('default_city');
  54.         $this->cityRepository $registry->getManagerForClass(City::class)->getRepository(City::class);
  55.         $this->profileRepository $registry->getManagerForClass(Profile::class)->getRepository(Profile::class);
  56.         $this->saloonRepository $registry->getManagerForClass(Saloon::class)->getRepository(Saloon::class);
  57.         $this->serviceRepository $registry->getManagerForClass(Service::class)->getRepository(Service::class);
  58.         $this->httpClient $apiDomainTimelineClient;
  59.         if ($this->features->has_translations()) {
  60.             $this->routeLocales $this->features->sitemap_multiple_locales()
  61.                 ? ['ru''en']
  62.                 : ['ru'];
  63.         } else {
  64.             $this->routeLocales null;
  65.         }
  66.     }
  67.     /**
  68.      * @inheritDoc
  69.      */
  70.     public static function getSubscribedEvents()
  71.     {
  72.         return [
  73.             SitemapPopulateEvent::ON_SITEMAP_POPULATE => 'populate',
  74.         ];
  75.     }
  76.     public function populate(SitemapPopulateEvent $event): void
  77.     {
  78.         $this->prepareConfig();
  79.         $urlContainer $event->getUrlContainer();
  80.         $this->registerHomepage($urlContainer);
  81.         $this->registerCityUrls($urlContainer);
  82.         $this->registerProfileUrls($urlContainer);
  83.         if ($this->features->has_saloons()) {
  84.             $this->registerSaloonUrls($urlContainer);
  85.         }
  86.     }
  87.     private function dateMutable(?\DateTimeImmutable $dateImmutable): ?\DateTime
  88.     {
  89.         if (null === $dateImmutable) {
  90.             return Carbon::now();
  91.         }
  92.         return Carbon::createFromTimestampUTC($dateImmutable->getTimestamp());
  93.     }
  94.     private function normalizeUriIdentity(string $value): string
  95.     {
  96.         $normalized strtolower(str_replace('_''-'$value));
  97.         return $normalized;
  98.     }
  99.     private function generateLocalizedUrls(string $canonicalRoute, array $routeParameters): iterable
  100.     {
  101.         if (null === $this->routeLocales) {
  102.             yield $this->router->generate($canonicalRoute$routeParametersUrlGeneratorInterface::ABSOLUTE_URL);
  103.         } else {
  104.             foreach ($this->routeLocales as $routeLocale) {
  105.                 yield $this->router->generate("$canonicalRoute.$routeLocale"$routeParametersUrlGeneratorInterface::ABSOLUTE_URL);
  106.             }
  107.         }
  108.     }
  109.     protected function registerHomepage(UrlContainerInterface $urlContainer): void
  110.     {
  111.         $lastModified Carbon::now();
  112.         foreach ($this->generateLocalizedUrls('homepage', []) as $url) {
  113.             $urlContainer->addUrl(new UrlConcrete(
  114.                 $url$lastModified
  115.             ), $this->getSitemapSectionName('geo'));
  116.         }
  117.     }
  118.     protected function registerCityUrls(UrlContainerInterface $urlContainer): void
  119.     {
  120.         $lastModified $this->getSectionLastModified('geo');
  121.         $homepageAsCityList $this->features->homepage_as_city_list();
  122.         foreach ($this->cityRepository->iterateAll() as $city) {
  123.             /** @var City $city */
  124.             // Если включена фича вывода списка городов на главной странице, добавляем в sitemap для всех городов (в том числе и для дефолтного) страницу фильтра по городу;
  125.             // Если фича выключена, то для дефолтного города не добавляем страницу фильтра анкет по городу - она уже будет добавлена как роут "homepage".
  126.             if ($homepageAsCityList || !$city->equals($this->defaultCity)) {
  127.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_city', ['city' => $city->getUriIdentity()]) as $url) {
  128.                     $urlContainer->addUrl(new UrlConcrete(
  129.                         $url$lastModified
  130.                     ), $this->getSitemapSectionName('geo'));
  131.                 }
  132.             }
  133.             $this->registerCityLocationUrls($urlContainer$city);
  134.             $this->registerCityStaticUrls($urlContainer$city);
  135.             $this->registerCityNameUrls($urlContainer$city);
  136.         }
  137.     }
  138.     protected function registerCityNameUrls(UrlContainerInterface $urlContainerCity $city): void
  139.     {
  140.         if (!$this->features->list_names()) {
  141.             return;
  142.         }
  143.         $lastModified $this->getSectionLastModified('profiles');
  144.         foreach ($this->generateLocalizedUrls('profile_list.list_names', ['city' => $city->getUriIdentity()]) as $url) {
  145.             $urlContainer->addUrl(new UrlConcrete($url$lastModified), $this->getSitemapSectionName('profiles'));
  146.         }
  147.         foreach ($this->profileNameLandingProvider->namesByCity($city) as $name) {
  148.             foreach ($this->generateLocalizedUrls('profile_list.list_by_name', [
  149.                 'city' => $city->getUriIdentity(),
  150.                 'name' => $name['uriIdentity'],
  151.             ]) as $url) {
  152.                 $urlContainer->addUrl(new UrlConcrete($url$lastModified), $this->getSitemapSectionName('profiles'));
  153.             }
  154.         }
  155.     }
  156.     protected function registerCityLocationUrls(UrlContainerInterface $urlContainerCity $city): void
  157.     {
  158.         $lastModified $this->getSectionLastModified('geo');
  159.         $categoriesLastModified $this->getSectionLastModified('categories');
  160.         foreach ($city->getCounties() as $county) {
  161.             foreach ($this->generateLocalizedUrls('profile_list.list_by_county', ['city' => $city->getUriIdentity(), 'county' => $county->getUriIdentity()]) as $url) {
  162.                 $urlContainer->addUrl(new UrlConcrete(
  163.                     $url$lastModified
  164.                 ), $this->getSitemapSectionName('geo'));
  165.             }
  166.         }
  167.         foreach ($city->getDistricts() as $district) {
  168.             foreach ($this->generateLocalizedUrls('profile_list.list_by_district', ['city' => $city->getUriIdentity(), 'district' => $district->getUriIdentity()]) as $url) {
  169.                 $urlContainer->addUrl(new UrlConcrete(
  170.                     $url$lastModified
  171.                 ), $this->getSitemapSectionName('geo'));
  172.             }
  173.         }
  174.         foreach ($city->getStations() as $station) {
  175.             foreach ($this->generateLocalizedUrls('profile_list.list_by_station', ['city' => $city->getUriIdentity(), 'station' => $station->getUriIdentity()]) as $url) {
  176.                 $urlContainer->addUrl(new UrlConcrete(
  177.                     $url$lastModified
  178.                 ), $this->getSitemapSectionName('geo'));
  179.             }
  180.         }
  181.         if ($this->features->extra_category_eromassage()) {
  182.             foreach ($city->getCounties() as $county) {
  183.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_county_eromassage', ['city' => $city->getUriIdentity(), 'county' => $county->getUriIdentity()]) as $url) {
  184.                     $urlContainer->addUrl(new UrlConcrete(
  185.                         $url$categoriesLastModified
  186.                     ), $this->getSitemapSectionName('categories'));
  187.                 }
  188.             }
  189.             foreach ($city->getDistricts() as $district) {
  190.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_district_eromassage', ['city' => $city->getUriIdentity(), 'district' => $district->getUriIdentity()]) as $url) {
  191.                     $urlContainer->addUrl(new UrlConcrete(
  192.                         $url$categoriesLastModified
  193.                     ), $this->getSitemapSectionName('categories'));
  194.                 }
  195.             }
  196.             foreach ($city->getStations() as $station) {
  197.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_station_eromassage', ['city' => $city->getUriIdentity(), 'station' => $station->getUriIdentity()]) as $url) {
  198.                     $urlContainer->addUrl(new UrlConcrete(
  199.                         $url$categoriesLastModified
  200.                     ), $this->getSitemapSectionName('categories'));
  201.                 }
  202.             }
  203.         }
  204.         if ($this->features->extra_category_verified()) {
  205.             foreach ($city->getCounties() as $county) {
  206.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_county_verified', ['city' => $city->getUriIdentity(), 'county' => $county->getUriIdentity()]) as $url) {
  207.                     $urlContainer->addUrl(new UrlConcrete(
  208.                         $url$categoriesLastModified
  209.                     ), $this->getSitemapSectionName('categories'));
  210.                 }
  211.             }
  212.             foreach ($city->getDistricts() as $district) {
  213.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_district_verified', ['city' => $city->getUriIdentity(), 'district' => $district->getUriIdentity()]) as $url) {
  214.                     $urlContainer->addUrl(new UrlConcrete(
  215.                         $url$categoriesLastModified
  216.                     ), $this->getSitemapSectionName('categories'));
  217.                 }
  218.             }
  219.             foreach ($city->getStations() as $station) {
  220.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_station_verified', ['city' => $city->getUriIdentity(), 'station' => $station->getUriIdentity()]) as $url) {
  221.                     $urlContainer->addUrl(new UrlConcrete(
  222.                         $url$categoriesLastModified
  223.                     ), $this->getSitemapSectionName('categories'));
  224.                 }
  225.             }
  226.         }
  227.         if ($this->features->extra_category_cheap()) {
  228.             foreach ($city->getCounties() as $county) {
  229.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_county_cheap', ['city' => $city->getUriIdentity(), 'county' => $county->getUriIdentity()]) as $url) {
  230.                     $urlContainer->addUrl(new UrlConcrete(
  231.                         $url$categoriesLastModified
  232.                     ), $this->getSitemapSectionName('categories'));
  233.                 }
  234.             }
  235.             foreach ($city->getDistricts() as $district) {
  236.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_district_cheap', ['city' => $city->getUriIdentity(), 'district' => $district->getUriIdentity()]) as $url) {
  237.                     $urlContainer->addUrl(new UrlConcrete(
  238.                         $url$categoriesLastModified
  239.                     ), $this->getSitemapSectionName('categories'));
  240.                 }
  241.             }
  242.             foreach ($city->getStations() as $station) {
  243.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_station_cheap', ['city' => $city->getUriIdentity(), 'station' => $station->getUriIdentity()]) as $url) {
  244.                     $urlContainer->addUrl(new UrlConcrete(
  245.                         $url$categoriesLastModified
  246.                     ), $this->getSitemapSectionName('categories'));
  247.                 }
  248.             }
  249.         }
  250.         if ($this->features->extra_category_mature()) {
  251.             foreach ($city->getCounties() as $county) {
  252.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_county_mature', ['city' => $city->getUriIdentity(), 'county' => $county->getUriIdentity()]) as $url) {
  253.                     $urlContainer->addUrl(new UrlConcrete(
  254.                         $url$categoriesLastModified
  255.                     ), $this->getSitemapSectionName('categories'));
  256.                 }
  257.             }
  258.             foreach ($city->getDistricts() as $district) {
  259.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_district_mature', ['city' => $city->getUriIdentity(), 'district' => $district->getUriIdentity()]) as $url) {
  260.                     $urlContainer->addUrl(new UrlConcrete(
  261.                         $url$categoriesLastModified
  262.                     ), $this->getSitemapSectionName('categories'));
  263.                 }
  264.             }
  265.             foreach ($city->getStations() as $station) {
  266.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_station_mature', ['city' => $city->getUriIdentity(), 'station' => $station->getUriIdentity()]) as $url) {
  267.                     $urlContainer->addUrl(new UrlConcrete(
  268.                         $url$categoriesLastModified
  269.                     ), $this->getSitemapSectionName('categories'));
  270.                 }
  271.             }
  272.         }
  273.         if ($this->features->extra_category_uzbek()) {
  274.             foreach ($city->getCounties() as $county) {
  275.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_county_uzbek', ['city' => $city->getUriIdentity(), 'county' => $county->getUriIdentity()]) as $url) {
  276.                     $urlContainer->addUrl(new UrlConcrete(
  277.                         $url$categoriesLastModified
  278.                     ), $this->getSitemapSectionName('categories'));
  279.                 }
  280.             }
  281.             foreach ($city->getDistricts() as $district) {
  282.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_district_uzbek', ['city' => $city->getUriIdentity(), 'district' => $district->getUriIdentity()]) as $url) {
  283.                     $urlContainer->addUrl(new UrlConcrete(
  284.                         $url$categoriesLastModified
  285.                     ), $this->getSitemapSectionName('categories'));
  286.                 }
  287.             }
  288.             foreach ($city->getStations() as $station) {
  289.                 foreach ($this->generateLocalizedUrls('profile_list.list_by_station_uzbek', ['city' => $city->getUriIdentity(), 'station' => $station->getUriIdentity()]) as $url) {
  290.                     $urlContainer->addUrl(new UrlConcrete(
  291.                         $url$categoriesLastModified
  292.                     ), $this->getSitemapSectionName('categories'));
  293.                 }
  294.             }
  295.         }
  296.         foreach ($this->generateLocalizedUrls('map.page', ['city' => $city->getUriIdentity()]) as $url) {
  297.             $urlContainer->addUrl(new UrlConcrete(
  298.                 $url$lastModified
  299.             ), $this->getSitemapSectionName('geo'));
  300.         }
  301.     }
  302.     protected function registerCityStaticUrls(UrlContainerInterface $urlContainerCity $city): void
  303.     {
  304.         $lastModified $this->getSectionLastModified('categories');
  305.         if ($this->features->has_masseurs()) {
  306.             foreach ($this->generateLocalizedUrls('masseur_list.page', ['city' => $city->getUriIdentity()]) as $url) {
  307.                 $urlContainer->addUrl(new UrlConcrete(
  308.                     $url$lastModified
  309.                 ), $this->getSitemapSectionName('categories'));
  310.             }
  311.         }
  312.         if ($this->features->has_saloons()) {
  313.             foreach ($this->generateLocalizedUrls('saloon_list.list_by_city', ['city' => $city->getUriIdentity()]) as $url) {
  314.                 $urlContainer->addUrl(new UrlConcrete(
  315.                     $url$lastModified
  316.                 ), $this->getSitemapSectionName('categories'));
  317.             }
  318.         }
  319.         /*
  320.         if ($this->features->has_archive_page()) {
  321.             foreach ($this->generateLocalizedUrls('profile_list.list_archived', ['city' => $city->getUriIdentity()]) as $url) {
  322.                 $urlContainer->addUrl(new UrlConcrete(
  323.                     $url, $lastModified
  324.                 ), $this->getSitemapSectionName('categories'));
  325.             }
  326.         }
  327.         */
  328.         foreach ($this->serviceRepository->iterateAll() as $service) {
  329.             /** @var \App\Entity\Service $service */
  330.             foreach ($this->generateLocalizedUrls('profile_list.list_by_provided_service', ['city' => $city->getUriIdentity(), 'service' => $service->getUriIdentity()]) as $url) {
  331.                 $urlContainer->addUrl(new UrlConcrete(
  332.                     $url$lastModified
  333.                 ), $this->getSitemapSectionName('categories'));
  334.             }
  335.         }
  336.         foreach ($this->findSitemapRoutesBySection('categories') as $route => $parameters) {
  337.             $parameters['city'] = $city->getUriIdentity();
  338.             foreach ($this->generateLocalizedUrls($route$parameters) as $url) {
  339.                 $urlContainer->addUrl(new UrlConcrete(
  340.                     $url$lastModified
  341.                 ), $this->getSitemapSectionName('categories'));
  342.             }
  343.         }
  344.     }
  345.     protected function registerProfileUrls(UrlContainerInterface $urlContainer): void
  346.     {
  347.         foreach ($this->profileRepository->sitemapItemsIterator() as $profile) {
  348.             foreach ($this->generateLocalizedUrls('profile_preview.page', ['city' => $profile['city_uri'], 'profile' => $profile['uri']]) as $url) {
  349.                 $urlContainer->addUrl(new UrlConcrete(
  350.                     $url$this->dateMutable($profile['updatedAt'])
  351.                 ), $this->getSitemapSectionName('profiles'));
  352.             }
  353.         }
  354.     }
  355.     protected function registerSaloonUrls(UrlContainerInterface $urlContainer): void
  356.     {
  357.         foreach ($this->saloonRepository->sitemapItemsIterator() as $saloon) {
  358.             foreach ($this->generateLocalizedUrls('saloon_preview.page', ['city' => $saloon['city_uri'], 'saloon' => $saloon['uri']]) as $url) {
  359.                 $urlContainer->addUrl(new UrlConcrete(
  360.                     $url$this->dateMutable($saloon['updatedAt'])
  361.                 ), $this->getSitemapSectionName('saloons'));
  362.             }
  363.         }
  364.     }
  365.     /**
  366.      * Return overridden section name for sitemap file
  367.      */
  368.     protected function getSitemapSectionName(string $name): string
  369.     {
  370.         return $this->sitemapConfig['sections'][$name] ?? $name;
  371.     }
  372.     protected function findSitemapRoutesBySection(string $section): iterable
  373.     {
  374.         $processedRoutes = [];
  375.         foreach ($this->router->getRouteCollection() as $name => $route) {
  376.             if (true === $route->getDefault('_route_disabled')) {
  377.                 continue;
  378.             }
  379.             if (str_starts_with($nameDynamicRouter::DEFAULT_CITY_ROUTE_PREFIX)
  380.                 || str_starts_with($nameDynamicRouter::OVERRIDDEN_ROUTE_PREFIX)
  381.                 || str_ends_with($nameDynamicRouter::PAGINATION_ROUTE_POSTFIX)) {
  382.                 continue;
  383.             }
  384.             $config $route->getOption(self::ROUTE_SITEMAP_OPTION);
  385.             if (empty($config) || $section !== ($config['section'] ?? null)) {
  386.                 continue;
  387.             }
  388.             if (null !== $this->features) {
  389.                 $routeFeature $route->getDefault('_feature');
  390.                 if (null !== $routeFeature && !$this->features->isActive($routeFeature)) {
  391.                     continue;
  392.                 }
  393.             }
  394.             $canonical $route->getDefault('_canonical_route');
  395.             if (null !== $canonical) {
  396.                 $name $canonical;
  397.             }
  398.             if (array_key_exists($name$processedRoutes)) {
  399.                 continue;
  400.             }
  401.             $processedRoutes[$name] = true;
  402.             $controller $route->getDefault('_controller');
  403.             if (is_array($controller)) {
  404.                 $controller "$controller[0]::$controller[1]";
  405.             }
  406.             if (null === $controller || !str_contains($controller'::')) {
  407.                 continue;
  408.             }
  409.             [$class, ] = explode('::'$controller2);
  410.             if (!class_exists($class)) {
  411.                 continue;
  412.             }
  413.             if (!empty($config['data'])) {
  414.                 foreach ($this->resolveRouteEnumParameters($config['data']) as $parameters) {
  415.                     yield $name => $parameters;
  416.                 }
  417.             } else {
  418.                 yield $name => [];
  419.             }
  420.         }
  421.     }
  422.     private function prepareConfig(): void
  423.     {
  424.         $this->sitemapConfig = [];
  425.         if (!file_exists($this->sitemapConfigPath)) {
  426.             return;
  427.         }
  428.         try {
  429.             $this->sitemapConfig Yaml::parseFile($this->sitemapConfigPath);
  430.         } catch (\Exception $e) {
  431.             trigger_error($e->getMessage(), E_USER_WARNING);
  432.         }
  433.     }
  434.     private function resolveRouteEnumParameters(array $data): iterable
  435.     {
  436.         $hasEnum false;
  437.         $firstRow $data[0];
  438.         foreach ($firstRow as $parameterName => $enumClass) {
  439.             if (is_string($enumClass) && in_array(EnumTrait::class, class_uses($enumClass), true)) {
  440.                 $hasEnum true;
  441.                 foreach ($enumClass::getUriLocations() as $uri) {
  442.                     yield [$parameterName => $uri];
  443.                 }
  444.                 break;
  445.             }
  446.         }
  447.         if (!$hasEnum) {
  448.             return $data;
  449.         }
  450.     }
  451.     private function getSectionLastModified(string $section): \DateTime
  452.     {
  453.         // Дефолтные дни месяца для LastModified секций
  454.         $defaults = [
  455.             'geo' => 5,
  456.             'categories' => 20,
  457.         ];
  458.         if (!isset($defaults[$section])) {
  459.             throw new \InvalidArgumentException("Unknown section: $section");
  460.         }
  461.         $defaultLastModified Carbon::create(nullnull$defaults[$section]);
  462.         if ($defaultLastModified->isFuture()) {
  463.             $defaultLastModified->subMonth();
  464.         }
  465.         $lastSwitch $this->getLastDomainSwitch();
  466.         if (null !== $lastSwitch && $lastSwitch $defaultLastModified) {
  467.             return $this->dateMutable($lastSwitch);
  468.         }
  469.         return $defaultLastModified;
  470.     }
  471.     private function getLastDomainSwitch(): ?\DateTimeImmutable
  472.     {
  473.         static $lastSwitch null;
  474.         static $calledPreviously false;
  475.         if (!$calledPreviously) {
  476.             try {
  477.                 $calledPreviously true;
  478.                 $response $this->httpClient->request('GET''');
  479.                 $data json_decode($response->getBody()->getContents(), true512JSON_THROW_ON_ERROR);
  480.                 if (null === $data) { // empty timeline, no switches history
  481.                     $lastSwitch null;
  482.                 } else {
  483.                     $lastSwitch CarbonImmutable::parse($data['switchedAt']);
  484.                 }
  485.             } catch (\Exception $ex) {
  486.                 trigger_error('Failed to get last domain switch date. '.$ex->getMessage(), E_USER_WARNING);
  487.                 $lastSwitch null;
  488.             }
  489.         }
  490.         return $lastSwitch;
  491.     }
  492. }