@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Loads files for admin and frontend.
|
||||
*
|
||||
* @author Time.ly Network Inc.
|
||||
* @since 2.0
|
||||
*
|
||||
* @package AI1EC
|
||||
* @subpackage AI1EC.Theme
|
||||
*/
|
||||
class Ai1ec_Theme_Compiler extends Ai1ec_Base {
|
||||
|
||||
/**
|
||||
* Register filters early on.
|
||||
*
|
||||
* @param Ai1ec_Registry_Object $registry Instance to use.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( Ai1ec_Registry_Object $registry ) {
|
||||
parent::__construct( $registry );
|
||||
add_filter(
|
||||
'ai1ec_twig_environment',
|
||||
array( $this, 'ai1ec_twig_environment' )
|
||||
);
|
||||
add_filter(
|
||||
'ai1ec_twig_add_debug',
|
||||
'__return_false'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform actual templates (re-)compilation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function generate() {
|
||||
$loader = $this->_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 );
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user