_registry->get( 'theme.loader' ); header( 'Content-Type: text/plain; charset=utf-8' ); $start = microtime( true ); if ( ! $this->clean_and_check_dir( AI1EC_TWIG_CACHE_PATH ) ) { throw new Ai1ec_Bootstrap_Exception( 'Failed to create cache directory: ' . AI1EC_TWIG_CACHE_PATH ); } foreach ( array( true, false ) as $for_admin ) { $twig = $loader->get_twig_instance( $for_admin, true ); $files = $this->get_files( $twig ); $this->compile( $twig, $files ); } echo 'Re-compiled in ' . ( microtime( true ) - $start ) . "\n"; exit( 0 ); } /** * Extract files locatable within provided Twig Environment. * * @param Twig_Environment $twig Instance to check. * * @return array Map of files => Twig templates. */ public function get_files( Twig_Environment $twig ) { $files = array(); try { $paths = $twig->getLoader()->getPaths(); foreach ( $paths as $path ) { $files += $this->read_files( $path, strlen( $path ) + 1 ); } } catch ( Exception $excpt ) { } return $files; } /** * Actually compile templates to cache directory. * * @param Twig_Environment $twig Instance to use for compilation. * @param array $file_list Map of files located previously. * * @return void */ public function compile( Twig_Environment $twig, array $file_list ) { foreach ( $file_list as $file => $template ) { $twig->loadTemplate( $template ); echo 'Compiled: ', $template, ' (', $file, ')', "\n"; } } /** * Read file system searching for twig files. * * @param string $path Directory to search in. * @param int $trim_length Number of characters to omit for templates. * * @return array Map of files => Twig templates. */ public function read_files( $path, $trim_length ) { $handle = opendir( $path ); $files = array(); if ( false === $handle ) { return $files; } while ( false !== ( $file = readdir( $handle ) ) ) { if ( '.' === $file{0} ) { continue; } $new_path = $path . DIRECTORY_SEPARATOR . $file; if ( is_dir( $new_path ) ) { $files += $this->read_files( $new_path, $trim_length ); } else if ( is_file( $new_path ) && '.twig' === strrchr( $new_path, '.' ) ) { $files[$new_path] = substr( $new_path, $trim_length ); } } closedir( $handle ); return $files; } /** * Adjust Twig environment for compilation. * * @param array $environment Initial environment arguments. * * @return */ public function ai1ec_twig_environment( array $environment ) { $environment['debug'] = false; $environment['cache'] = AI1EC_TWIG_CACHE_PATH; $environment['auto_reload'] = true; return $environment; } /** * Ensure cache directory pre-conditions. * * Before compilation starts cache directory must be empty but existing. * NOTE: it attempts to preserve `.gitignore` file in cache/ directory. * * @param string $cache_dir Directory to check. * * @return bool Validity. */ public function clean_and_check_dir( $cache_dir ) { try { // PROD-3918 - Check if we are cleaning the TWIG cache dir // This validation will fail if a custom directory is used to store twig cache if ( empty( $cache_dir ) || strpos( $cache_dir, 'cache/twig' ) === false ) { return false; } $parent = realpath( $cache_dir ); // $parent will return empty if the directory doesn't exist // Only clean directory if directory exists if ( ! empty( $parent ) && ! $this->_prune_dir( $parent ) ) { return false; } if ( is_dir( $cache_dir ) && chmod( $cache_dir, 0754 ) || mkdir( $cache_dir, 0754, true ) ) { return true; } return false; } catch ( Exception $exc ) { return false; } } /** * Remove directory and all it's contents. * * @param string $cache_dir Absolute path to remove. * * @return bool Success. */ protected function _prune_dir( $cache_dir ) { if ( ! file_exists( $cache_dir ) ) { return true; } $handle = opendir( $cache_dir ); if ( ! $handle ) { return false; } while ( false !== ( $file = readdir( $handle ) ) ) { if ( '.' === $file{0} ) { continue; } $basename = basename( $file, '.php' ); // continue deleting only if: // - it's 60 characters length (filename w/o '.php') // - OR it's 2 characters length (directory) // - AND (with two above) it's hex encoded string if ( ! ( ( isset( $basename{59} ) && ! isset( $basename{60} ) ) || ( isset( $basename{1} ) && ! isset( $basename{2} ) ) && ctype_xdigit( $basename ) ) ) { continue; } $path = $cache_dir . DIRECTORY_SEPARATOR . $file; if ( is_file( $path ) ) { if ( ! unlink( $path ) ) { return false; } } else { if ( ! $this->_prune_dir( $path ) ) { return false; } } } closedir( $handle ); if ( is_file( $cache_dir . DIRECTORY_SEPARATOR . 'EMPTY' ) ) { return true; // ignore, this directory is intentionally here } return rmdir( $cache_dir ); } }