vendor/contao/core-bundle/src/Resources/contao/library/Contao/Automator.php line 233

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Contao.
  4.  *
  5.  * (c) Leo Feyer
  6.  *
  7.  * @license LGPL-3.0-or-later
  8.  */
  9. namespace Contao;
  10. use Contao\CoreBundle\OptIn\OptIn;
  11. use FOS\HttpCacheBundle\CacheManager;
  12. use Symfony\Component\Console\Input\ArgvInput;
  13. use Symfony\Component\Console\Output\NullOutput;
  14. /**
  15.  * Provide methods to run automated jobs.
  16.  *
  17.  * @author Leo Feyer <https://github.com/leofeyer>
  18.  */
  19. class Automator extends System
  20. {
  21.     /**
  22.      * Make the constuctor public
  23.      */
  24.     public function __construct()
  25.     {
  26.         parent::__construct();
  27.     }
  28.     /**
  29.      * Purge the search tables
  30.      */
  31.     public function purgeSearchTables()
  32.     {
  33.         $searchIndexer System::getContainer()->get('contao.search.indexer');
  34.         // The search indexer is disabled
  35.         if (null === $searchIndexer)
  36.         {
  37.             return;
  38.         }
  39.         // Clear the index
  40.         $searchIndexer->clear();
  41.         $strCachePath StringUtil::stripRootDir(System::getContainer()->getParameter('kernel.cache_dir'));
  42.         // Purge the cache folder
  43.         $objFolder = new Folder($strCachePath '/contao/search');
  44.         $objFolder->purge();
  45.         // Add a log entry
  46.         $this->log('Purged the search tables'__METHOD__TL_CRON);
  47.     }
  48.     /**
  49.      * Purge the undo table
  50.      */
  51.     public function purgeUndoTable()
  52.     {
  53.         $objDatabase Database::getInstance();
  54.         // Truncate the table
  55.         $objDatabase->execute("TRUNCATE TABLE tl_undo");
  56.         // Add a log entry
  57.         $this->log('Purged the undo table'__METHOD__TL_CRON);
  58.     }
  59.     /**
  60.      * Purge the version table
  61.      */
  62.     public function purgeVersionTable()
  63.     {
  64.         $objDatabase Database::getInstance();
  65.         // Truncate the table
  66.         $objDatabase->execute("TRUNCATE TABLE tl_version");
  67.         // Add a log entry
  68.         $this->log('Purged the version table'__METHOD__TL_CRON);
  69.     }
  70.     /**
  71.      * Purge the system log
  72.      */
  73.     public function purgeSystemLog()
  74.     {
  75.         $objDatabase Database::getInstance();
  76.         // Truncate the table
  77.         $objDatabase->execute("TRUNCATE TABLE tl_log");
  78.         // Add a log entry
  79.         $this->log('Purged the system log'__METHOD__TL_CRON);
  80.     }
  81.     /**
  82.      * Purge the crawl queue
  83.      */
  84.     public function purgeCrawlQueue()
  85.     {
  86.         $objDatabase Database::getInstance();
  87.         // Truncate the table
  88.         $objDatabase->execute("TRUNCATE TABLE tl_crawl_queue");
  89.         // Add a log entry
  90.         $this->log('Purged the crawl queue'__METHOD__TL_CRON);
  91.     }
  92.     /**
  93.      * Purge the image cache
  94.      */
  95.     public function purgeImageCache()
  96.     {
  97.         $container System::getContainer();
  98.         $strTargetPath StringUtil::stripRootDir($container->getParameter('contao.image.target_dir'));
  99.         $strRootDir $container->getParameter('kernel.project_dir');
  100.         // Walk through the subfolders
  101.         foreach (scan($strRootDir '/' $strTargetPath) as $dir)
  102.         {
  103.             if (strncmp($dir'.'1) !== 0)
  104.             {
  105.                 $objFolder = new Folder($strTargetPath '/' $dir);
  106.                 $objFolder->purge();
  107.             }
  108.         }
  109.         // Also empty the shared cache so there are no links to deleted images
  110.         $this->purgePageCache();
  111.         // Add a log entry
  112.         $this->log('Purged the image cache'__METHOD__TL_CRON);
  113.     }
  114.     /**
  115.      * Purge the script cache
  116.      */
  117.     public function purgeScriptCache()
  118.     {
  119.         // assets/js and assets/css
  120.         foreach (array('assets/js''assets/css') as $dir)
  121.         {
  122.             // Purge the folder
  123.             $objFolder = new Folder($dir);
  124.             $objFolder->purge();
  125.         }
  126.         // Recreate the internal style sheets
  127.         $this->import(StyleSheets::class, 'StyleSheets');
  128.         $this->StyleSheets->updateStyleSheets();
  129.         // Also empty the shared cache so there are no links to deleted scripts
  130.         $this->purgePageCache();
  131.         // Add a log entry
  132.         $this->log('Purged the script cache'__METHOD__TL_CRON);
  133.     }
  134.     /**
  135.      * Purge the shared cache
  136.      */
  137.     public function purgePageCache()
  138.     {
  139.         $container System::getContainer();
  140.         if (!$container->has('fos_http_cache.cache_manager'))
  141.         {
  142.             $this->log('Cannot purge the shared cache; invalid reverse proxy configuration'__METHOD__TL_ERROR);
  143.             return;
  144.         }
  145.         /** @var CacheManager $cacheManager */
  146.         $cacheManager $container->get('fos_http_cache.cache_manager');
  147.         if (!$cacheManager->supports(CacheManager::CLEAR))
  148.         {
  149.             $this->log('Cannot purge the shared cache; invalid reverse proxy configuration'__METHOD__TL_ERROR);
  150.             return;
  151.         }
  152.         $cacheManager->clearCache();
  153.         // Add a log entry
  154.         $this->log('Purged the shared cache'__METHOD__TL_CRON);
  155.     }
  156.     /**
  157.      * Purge the search cache
  158.      */
  159.     public function purgeSearchCache()
  160.     {
  161.         $strCacheDir StringUtil::stripRootDir(System::getContainer()->getParameter('kernel.cache_dir'));
  162.         $objFolder = new Folder($strCacheDir '/contao/search');
  163.         $objFolder->purge();
  164.         // Add a log entry
  165.         $this->log('Purged the search cache'__METHOD__TL_CRON);
  166.     }
  167.     /**
  168.      * Purge the internal cache
  169.      */
  170.     public function purgeInternalCache()
  171.     {
  172.         $container System::getContainer();
  173.         $clearer $container->get('contao.cache.clear_internal');
  174.         $clearer->clear($container->getParameter('kernel.cache_dir'));
  175.         // Add a log entry
  176.         $this->log('Purged the internal cache'__METHOD__TL_CRON);
  177.     }
  178.     /**
  179.      * Purge the temp folder
  180.      */
  181.     public function purgeTempFolder()
  182.     {
  183.         // Purge the folder
  184.         $objFolder = new Folder('system/tmp');
  185.         $objFolder->purge();
  186.         // Add a log entry
  187.         $this->log('Purged the temp folder'__METHOD__TL_CRON);
  188.     }
  189.     /**
  190.      * Purge registrations that have not been activated within 24 hours
  191.      */
  192.     public function purgeRegistrations()
  193.     {
  194.         $objMember MemberModel::findExpiredRegistrations();
  195.         if ($objMember === null)
  196.         {
  197.             return;
  198.         }
  199.         while ($objMember->next())
  200.         {
  201.             $objMember->delete();
  202.         }
  203.         // Add a log entry
  204.         $this->log('Purged the unactivated member registrations'__METHOD__TL_CRON);
  205.     }
  206.     /**
  207.      * Purge opt-in tokens
  208.      */
  209.     public function purgeOptInTokens()
  210.     {
  211.         /** @var OptIn $optIn */
  212.         $optIn System::getContainer()->get('contao.opt-in');
  213.         $optIn->purgeTokens();
  214.         // Add a log entry
  215.         $this->log('Purged the expired double opt-in tokens'__METHOD__TL_CRON);
  216.     }
  217.     /**
  218.      * Remove old XML files from the share directory
  219.      *
  220.      * @param boolean $blnReturn If true, only return the finds and don't delete
  221.      *
  222.      * @return array An array of old XML files
  223.      */
  224.     public function purgeXmlFiles($blnReturn=false)
  225.     {
  226.         $arrFeeds = array();
  227.         $objDatabase Database::getInstance();
  228.         // XML sitemaps
  229.         $objFeeds $objDatabase->execute("SELECT sitemapName FROM tl_page WHERE type='root' AND createSitemap=1 AND sitemapName!=''");
  230.         while ($objFeeds->next())
  231.         {
  232.             $arrFeeds[] = $objFeeds->sitemapName;
  233.         }
  234.         // HOOK: preserve third party feeds
  235.         if (isset($GLOBALS['TL_HOOKS']['removeOldFeeds']) && \is_array($GLOBALS['TL_HOOKS']['removeOldFeeds']))
  236.         {
  237.             foreach ($GLOBALS['TL_HOOKS']['removeOldFeeds'] as $callback)
  238.             {
  239.                 $this->import($callback[0]);
  240.                 $arrFeeds array_merge($arrFeeds$this->{$callback[0]}->{$callback[1]}());
  241.             }
  242.         }
  243.         // Delete the old files
  244.         if (!$blnReturn)
  245.         {
  246.             $shareDir System::getContainer()->getParameter('contao.web_dir') . '/share';
  247.             foreach (scan($shareDir) as $file)
  248.             {
  249.                 if (is_dir($shareDir '/' $file))
  250.                 {
  251.                     continue; // see #6652
  252.                 }
  253.                 $objFile = new File(StringUtil::stripRootDir($shareDir) . '/' $file);
  254.                 if ($objFile->extension == 'xml' && !\in_array($objFile->filename$arrFeeds))
  255.                 {
  256.                     $objFile->delete();
  257.                 }
  258.             }
  259.         }
  260.         return $arrFeeds;
  261.     }
  262.     /**
  263.      * Generate the Google XML sitemaps
  264.      *
  265.      * @param integer $intId The root page ID
  266.      */
  267.     public function generateSitemap($intId=0)
  268.     {
  269.         $time Date::floorToMinute();
  270.         $objDatabase Database::getInstance();
  271.         $this->purgeXmlFiles();
  272.         $strQuery "SELECT id, language, sitemapName FROM tl_page WHERE type='root' AND createSitemap='1' AND sitemapName!='' AND published='1' AND (start='' OR start<='$time') AND (stop='' OR stop>'$time')";
  273.         // Get a particular root page
  274.         if ($intId 0)
  275.         {
  276.             $strQuery .= ' AND id=' . (int) $intId;
  277.         }
  278.         $objRoot $objDatabase->execute($strQuery);
  279.         // Return if there are no pages
  280.         if ($objRoot->numRows 1)
  281.         {
  282.             return;
  283.         }
  284.         // Create the XML file
  285.         while ($objRoot->next())
  286.         {
  287.             $objFile = new File(StringUtil::stripRootDir(System::getContainer()->getParameter('contao.web_dir')) . '/share/' $objRoot->sitemapName '.xml');
  288.             $objFile->truncate();
  289.             $objFile->append('<?xml version="1.0" encoding="UTF-8"?>');
  290.             $objFile->append('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">');
  291.             // Find the searchable pages
  292.             $arrPages Backend::findSearchablePages($objRoot->id''true);
  293.             // HOOK: take additional pages
  294.             if (isset($GLOBALS['TL_HOOKS']['getSearchablePages']) && \is_array($GLOBALS['TL_HOOKS']['getSearchablePages']))
  295.             {
  296.                 foreach ($GLOBALS['TL_HOOKS']['getSearchablePages'] as $callback)
  297.                 {
  298.                     $this->import($callback[0]);
  299.                     $arrPages $this->{$callback[0]}->{$callback[1]}($arrPages$objRoot->idtrue$objRoot->language);
  300.                 }
  301.             }
  302.             // Add pages
  303.             foreach ($arrPages as $strUrl)
  304.             {
  305.                 $strUrl explode('/'$strUrl4);
  306.                 if (isset($strUrl[3]))
  307.                 {
  308.                     $strUrl[3] = rawurlencode($strUrl[3]);
  309.                     $strUrl[3] = str_replace(array('%2F''%3F''%3D''%26''%5B''%5D''%25'), array('/''?''=''&''['']''%'), $strUrl[3]);
  310.                 }
  311.                 $strUrl implode('/'$strUrl);
  312.                 $strUrl ampersand($strUrl);
  313.                 $objFile->append('  <url><loc>' $strUrl '</loc></url>');
  314.             }
  315.             $objFile->append('</urlset>');
  316.             $objFile->close();
  317.             // Add a log entry
  318.             $this->log('Generated sitemap "' $objRoot->sitemapName '.xml"'__METHOD__TL_CRON);
  319.         }
  320.     }
  321.     /**
  322.      * Regenerate the XML files
  323.      */
  324.     public function generateXmlFiles()
  325.     {
  326.         // Sitemaps
  327.         $this->generateSitemap();
  328.         // HOOK: add custom jobs
  329.         if (isset($GLOBALS['TL_HOOKS']['generateXmlFiles']) && \is_array($GLOBALS['TL_HOOKS']['generateXmlFiles']))
  330.         {
  331.             foreach ($GLOBALS['TL_HOOKS']['generateXmlFiles'] as $callback)
  332.             {
  333.                 $this->import($callback[0]);
  334.                 $this->{$callback[0]}->{$callback[1]}();
  335.             }
  336.         }
  337.         // Also empty the shared cache so there are no links to deleted files
  338.         $this->purgePageCache();
  339.         // Add a log entry
  340.         $this->log('Regenerated the XML files'__METHOD__TL_CRON);
  341.     }
  342.     /**
  343.      * Generate the symlinks in the web/ folder
  344.      */
  345.     public function generateSymlinks()
  346.     {
  347.         $container System::getContainer();
  348.         $command $container->get('contao.command.symlinks');
  349.         $status $command->run(new ArgvInput(array()), new NullOutput());
  350.         // Add a log entry
  351.         if ($status 0)
  352.         {
  353.             $this->log('The symlinks could not be regenerated'__METHOD__TL_ERROR);
  354.         }
  355.         else
  356.         {
  357.             $this->log('Regenerated the symlinks'__METHOD__TL_CRON);
  358.         }
  359.     }
  360.     /**
  361.      * Generate the internal cache
  362.      */
  363.     public function generateInternalCache()
  364.     {
  365.         $container System::getContainer();
  366.         $warmer $container->get('contao.cache.warm_internal');
  367.         $warmer->warmUp($container->getParameter('kernel.cache_dir'));
  368.         // Add a log entry
  369.         $this->log('Generated the internal cache'__METHOD__TL_CRON);
  370.     }
  371.     /**
  372.      * Rotate the log files
  373.      *
  374.      * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  375.      *             Use the logger service instead, which rotates its log files automatically.
  376.      */
  377.     public function rotateLogs()
  378.     {
  379.         @trigger_error('Using Automator::rotateLogs() has been deprecated and will no longer work in Contao 5.0. Use the logger service instead, which rotates its log files automatically.'E_USER_DEPRECATED);
  380.         $projectDir System::getContainer()->getParameter('kernel.project_dir');
  381.         $arrFiles preg_grep('/\.log$/'scan($projectDir '/system/logs'));
  382.         foreach ($arrFiles as $strFile)
  383.         {
  384.             // Ignore Monolog log files (see #2579)
  385.             if (preg_match('/-\d{4}-\d{2}-\d{2}\.log$/'$strFile))
  386.             {
  387.                 continue;
  388.             }
  389.             $objFile = new File('system/logs/' $strFile '.9');
  390.             // Delete the oldest file
  391.             if ($objFile->exists())
  392.             {
  393.                 $objFile->delete();
  394.             }
  395.             // Rotate the files (e.g. error.log.4 becomes error.log.5)
  396.             for ($i=8$i>0$i--)
  397.             {
  398.                 $strGzName 'system/logs/' $strFile '.' $i;
  399.                 if (file_exists($projectDir '/' $strGzName))
  400.                 {
  401.                     $objFile = new File($strGzName);
  402.                     $objFile->renameTo('system/logs/' $strFile '.' . ($i+1));
  403.                 }
  404.             }
  405.             // Add .1 to the latest file
  406.             $objFile = new File('system/logs/' $strFile);
  407.             $objFile->renameTo('system/logs/' $strFile '.1');
  408.         }
  409.     }
  410. }
  411. class_alias(Automator::class, 'Automator');