Current File : /home/aventura/www/site/wp-content/plugins/victheme_core/wordpress/factory/assets.php |
<?php
/**
* Class for managing VTCore Assets libraries
* Centralizing the assets record so other plugin
* can just load the asset by folder name.
*
* @author jason.xie@victheme.com
*/
class VTCore_Wordpress_Factory_Assets
extends VTCore_Wordpress_Models_Config
implements VTCore_Wordpress_Interfaces_Factory {
protected $options = array();
protected $database = 'vtcore_asset_cache';
protected $filter = FALSE;
private static $reset = false;
private $base;
private $hash;
private $context;
private $admin;
protected function register(array $options) {
$this->admin = defined('WP_ADMIN');
// @bugfix post edit screw up when auto clear + wp_debug enabled
if (!$this->admin) {
$this->maybeByPassCache();
}
$this->options = array(
'library' => new VTCore_Wordpress_Objects_Library(),
'queues' => new VTCore_Wordpress_Objects_Queue(),
'aggregate' => (boolean) VTCore_Wordpress_Init::getFactory('coreConfig')->get('aggregate.frontend'),
'minify' => false,
'default' => array(
'deps' => array('jquery'),
'version' => '',
'footer' => true,
),
'prefix' => 'comp-front-',
'compressed' => array(),
);
// Setting up the library object;
$this
->get('library')
->allowed(array('js', 'css'))
->database('vtcore_asset_library')
->load();
if ($this->admin) {
$this->mutate('prefix', 'comp-admin-');
$this->change('aggregate', (boolean) VTCore_Wordpress_Init::getFactory('coreConfig')->get('aggregate.backend'));
}
$this->merge($options)->load();
$this->maybeByPassAggregate();
return $this;
}
/**
* Method for checking against plugin that is known
* to be incompatible with the asset aggregation.
*
* @return mixed
*/
public function maybeByPassAggregate() {
$incompatible = array(
'zencache/zencache.php' => 'Zen Cache',
'wp-super-cache/wp-cache.php' => 'WP Super Cache',
'wp-fastest-cache/wpFastestCache.php' => 'WP Fastest Cache',
'w3-total-cache/w3-total-cache.php' => 'W3 Total Cache',
'comet-cache/comet-cache.php' => 'Comet Cache',
'autoptimize/autoptimize.php' => 'Auto Optimize',
'wp-minify-fix/wp-minify.php' => 'WP Minify',
'bwp-minify/bwp-minify.php' => 'Better Wordpress Minify',
'wp-super-minify/wp-super-minify.php' => 'WP Super Minify'
);
$active_plugins = get_option('active_plugins', array());
$disable = false;
foreach ($active_plugins as $slug) {
if (isset($incompatible[$slug])) {
$disable = $incompatible[$slug];
break;
}
}
if ($disable) {
$this
->add('incompatible', true)
->add('aggregate', false)
->add('minify', false);
}
return $disable;
}
/**
* Method for generating hashed key
* @return mixed
*/
protected function generateHash() {
$this->hash = $this->get('prefix') . md5(implode('#', array_keys($this->get('queues')->extract())));
return $this;
}
/**
* Main logic for loading assets
* @return VTCore_Wordpress_Objects_Assets_Loader
*/
public function process() {
// Generate unique hash
$this->generateHash();
// Do asset aggregation service
if ($this->get('aggregate')) {
// Try to last minute rebuild cache file
if (!$this->checkCache()) {
$this->compress();
$this->save();
}
// Only load if we got valid cache
if ($this->checkCache()) {
$this->loadCache();
}
}
// Enqueue the assets to wordpress
$this->enqueue();
return $this;
}
/**
* Method for enqueue queued asset to wordpress
* @return VTCore_Wordpress_Objects_Assets_Loader
*/
public function enqueue() {
$queues = $this->get('queues')->extract();
$queueKey = array_keys($queues);
$queueCount = count($queues);
for ($q = 0; $q < $queueCount; $q++) {
$name = $queueKey[$q];
$data = $queues[$name];
if (!$this->get('library')->get($name)) {
continue;
}
$inline = array();
$dynamic = array();
$localize = array();
$data = wp_parse_args($data, $this->get('default'));
$library = $this->get('library')->get($name);
$libraryKey = array_keys($library);
$libraryCount = count($library);
for ($l=0; $l < $libraryCount; $l++) {
$type = $libraryKey[$l];
$files = $library[$type];
$fileCount = count($files);
$fileKey = array_keys($files);
for ($f=0; $f<$fileCount; $f++) {
$filename = $fileKey[$f];
$file = $files[$filename];
if (!is_file($file['path'])) {
continue;
}
if ($type == 'css') {
wp_enqueue_style($filename, $file['url']);
}
if ($type == 'js') {
wp_enqueue_script($filename, $file['url'], $data['deps'], $data['version'], $data['footer']);
}
if (isset($file['inline'])) {
$inline[$filename] = implode("\n", $file['inline']);
}
if (isset($file['dynamic'])) {
$dynamic[$filename] = implode("\n", $file['dynamic']);
}
if (isset($file['localize'])) {
$localize[$filename] = $file['localize'];
}
}
}
// Enqueue inline styles
foreach ($inline as $filename => $css) {
wp_add_inline_style($filename, $css);
}
// Enqueue dynamic styles
foreach ($dynamic as $filename => $css) {
wp_add_inline_style($filename, $css);
}
// Enqueue localized script
foreach ($localize as $filename => $local) {
foreach ($local as $var => $value) {
wp_localize_script($filename, $var, $value);
}
}
$this->get('queues')->processed($name);
}
return $this;
}
/**
* Method for checking if we should bypass cache
* @return VTCore_Wordpress_Factory_Assets
*/
public function maybeByPassCache() {
// Force remove if defined global clear cache constant
if ((defined('WP_DEBUG') && WP_DEBUG)
|| (defined('VTCORE_CLEAR_CACHE') && VTCORE_CLEAR_CACHE)
|| get_option('vtcore_clear_cache', false) == true) {
$this->clearCache();
}
return $this;
}
/**
* Load compressed asset from cache
* @return VTCore_Wordpress_Objects_Assets_Loader
*/
public function loadCache() {
$hash = 'compressed.' . $this->hash;
$id = $this->get($hash . '.id');
$queues = $this->get('queues')->extract();
$queueCount = count($queues);
$queueKeys = array_keys($queues);
// Pre process queue first
for ($q=0; $q < $queueCount; $q++) {
$name = $queueKeys[$q];
$library = $this->get('library')->get($name);
// Inject dynamic assets that cannot be compressed
// @since 4.7.36
if ($library) {
$libraryCount = count($library);
$libraryKey = array_keys($library);
for ($l=0; $l < $libraryCount; $l++) {
$key = $libraryKey[$l];
$files = $library[$key];
$fileCount = count($files);
$fileKey = array_keys($files);
for ($f=0; $f<$fileCount; $f++) {
$filename = $fileKey[$f];
$asset = $files[$filename];
if (isset($asset['dynamic']) && !empty($asset['dynamic'])) {
$this->add($hash . '.assets.' . $key . '.' . $id . '.inline', $asset['dynamic']);
}
}
}
}
// Remove previously cached assets from queues
if ($name != $id) {
$this->get('queues')->processed($name);
}
}
// Add cached custom asset to library
$this
->get('library')
->add($id, $this->get($hash . '.assets'));
// Add the cached assets to queue
$this
->get('queues')
->add($id, TRUE);
return $this;
}
/**
* Check if the cached file actually exists
* to prevent missing file break site bug.
* @return boolean
*/
public function checkCache() {
$check = false;
$assets = $this->get('compressed.' . $this->hash . '.assets');
$assetCount = count($assets);
if ($assets && $assetCount && self::$reset == false) {
$assetKey = array_keys($assets);
for ($a=0; $a<$assetCount; $a++) {
$type = $assetKey[$a];
$data = $assets[$type];
$file = array_shift($data);
if (isset($file['path']) && is_file($file['path'])) {
$check = true;
}
// Bail out if one of the cached file is missing
else {
$check = false;
break;
}
}
if (!$check) {
// Smarter removing files, only nuke a single hashed block
// instead of all file when using clearCache() method.
for ($a=0; $a<$assetCount; $a++) {
$type = $assetKey[$a];
$data = $assets[$type];
$file = array_shift($data);
if (isset($file['path']) && is_file($file['path'])) {
unlink($file['path']);
}
}
$this->remove('compressed.' . $this->hash);
$this->save();
}
}
return $check;
}
/**
* Overriden method for saving to database
* This object doesn't need to save referenced
* assets objects to database.
*
* @return VTCore_Wordpress_Objects_Assets_Loader
*/
public function save() {
$data = $this->extract();
$this->options = array('compressed' => $this->get('compressed'));
parent::save();
$this->options = $data;
unset($data);
return $this;
}
/**
* Method for compressing queued assets
* @return VTCore_Wordpress_Objects_Assets_Loader
*/
public function compress() {
$this->context = array(
'css' => '',
'js' => '',
'inline' => '',
'dynamic' => '',
'id' => uniqid('custom-'),
);
$queues = $this->get('queues')->extract();
$maxQueue = count($queues);
$queueKey = array_keys($queues);
for ($q=0; $q < $maxQueue; $q++) {
$name = $queueKey[$q];
if (!$this->get('library')->get($name)) {
continue;
}
$library = $this->get('library')->get($name);
$libCount = count($library);
$libKey = array_keys($library);
for ($l=0; $l< $libCount; $l++) {
$type = $libKey[$l];
$files = $library[$type];
$content = '';
$filesKey = array_keys($files);
$fileCount = count($files);
for ($f=0; $f<$fileCount; $f++) {
$filename = $filesKey[$f];
$file = $files[$filename];
if (is_file($file['path'])) {
$content .= file_get_contents($file['path']);
}
if (isset($file['inline'])) {
$this->context['inline'] .= implode("\n", $file['inline']);
}
if (isset($file['dynamic'])) {
$this->context['dynamic'] .= implode("\n", $file['dynamic']);
}
if (isset($file['localize'])) {
$this->get('library')
->add($this->context['id'] . '.js.' . $this->context['id'] . '.localize', $file['localize']);
}
}
if ($type == 'css') {
$this->base = trailingslashit(dirname($file['url']));
$content = preg_replace_callback('/url\(\s*[\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\s*\)/i', array(
$this,
'fixCssPath'
), $content);
}
if ($type == 'js' && substr($content, -1) != ';') {
$content .= ";\n";
}
$this->context[$type] .= $content;
}
}
// Merge inline css last
$this->context['css'] .= $this->context['inline'];
if ($this->get('minify')) {
$this->minify();
}
$addAsset = false;
if (!empty($this->context['css'])) {
// Remove multiple @charset
$this->context['css'] = preg_replace('/^@charset\s+[\'"](\S*?)\b[\'"];/i', '', $this->context['css']);
$upload = VTCore_Wordpress_Utility::uploadBits($this->context['id'] . '.css', $this->context['css'], array('vtcore-assets'));
if (!$upload['error'] && is_file($upload['file'])) {
$this->get('queues')->add($this->context['id'], TRUE);
$this->get('library')
->add($this->context['id'] . '.css.' . $this->context['id'] . '.url', $upload['url']);
$this->get('library')
->add($this->context['id'] . '.css.' . $this->context['id'] . '.path', $upload['file']);
if (!empty($this->context['dynamic'])) {
$this->get('library')
->add($this->context['id'] . '.css.' . $this->context['id'] . '.inline.compressed', $this->context['dynamic']);
}
$addAsset = true;
}
}
if (!empty($this->context['js'])) {
$upload = VTCore_Wordpress_Utility::uploadBits($this->context['id'] . '.js', $this->context['js'], array('vtcore-assets'));
if (!$upload['error'] && is_file($upload['file'])) {
$this->get('queues')->add($this->context['id'], TRUE);
$this->get('library')
->add($this->context['id'] . '.js.' . $this->context['id'] . '.url', $upload['url']);
$this->get('library')
->add($this->context['id'] . '.js.' . $this->context['id'] . '.path', $upload['file']);
$addAsset = true;
}
}
// Add the compressed file map to compressed library
// @bugfix compressed file never got removed
if ($addAsset === true) {
// Wipe merged asset from queue
$queues = $this->get('queues')->extract();
$maxQueue = count($queues);
$queueKey = array_keys($queues);
for ($q=0; $q < $maxQueue; $q++) {
$name = $queueKey[$q];
if (strpos($name, 'custom-') !== false) {
continue;
}
$this->get('queues')->processed($name);
}
$this->add('compressed.' . $this->hash, array(
'id' => $this->context['id'],
'assets' => $this->get('library')->get($this->context['id']),
));
}
// free memory
$this->context = array();
unset($this->context);
return $this;
}
/**
* Method for dequeuing assets from wordpress queue
* @return VTCore_Wordpress_Objects_Assets_Loader
*/
public function dequeue($name, $js = TRUE, $css = TRUE) {
if ($this->get('library')->get($name)) {
foreach ($this->get('library')->get($name) as $type => $files) {
foreach ($files as $filename => $file) {
if ($type == 'css') {
wp_dequeue_style($filename);
}
if ($type == 'js') {
wp_dequeue_script($filename);
}
}
}
}
return $this;
}
/**
* Method ripped from drupal for fixing css relative path
*/
public function fixCssPath($matches, $base = NULL) {
// Store base path for preg_replace_callback.
if (isset($base)) {
$this->base = $base;
}
// Prefix with base and remove '../' segments where possible.
$path = $this->base . $matches[1];
$last = '';
while ($path != $last) {
$last = $path;
$path = preg_replace('`(^|/)(?!\.\./)([^/]+)/\.\./`', '$1', $path);
}
return 'url(' . $path . ')';
}
/**
* Minify the assets, ripped off from drupal 7
*
* @return VTCore_Wordpress_Factory_Assets
*/
public function minify() {
if (!empty($this->context['css'])) {
$this->context['css'] = VTCore_CSSBuilder_Minify_CSS::minify($this->context['css']);
}
if (!empty($this->context['js'])) {
$this->context['js'] = VTCore_CSSBuilder_Minify_JS::minify($this->context['js']);
}
return $this;
}
/**
* Removing all compressed assets
* This can only be invoked once per
* page load as it is enough to clear
* all cached file once.
*/
public function clearCache() {
if (self::$reset == false) {
$upload = VTCore_Wordpress_Utility::getUploadDir(false);
$files = glob($upload['basedir'] . DIRECTORY_SEPARATOR . 'vtcore-assets' . DIRECTORY_SEPARATOR . '*');
if (!empty($files)) {
foreach ($files as $file) {
if (is_file($file)) {
unlink($file);
}
}
}
$this->add('compressed', array());
// Remove library asset cache
if (is_object($this->get('library')) && method_exists($this->get('library'), 'delete')) {
$this->get('library')->delete();
}
$this->save();
// Lock this method up
self::$reset = true;
}
return $this;
}
}