Skip to content

Instantly share code, notes, and snippets.

@tridungpham
Created February 25, 2015 10:19
Show Gist options
  • Save tridungpham/fcf70a091bad4d14d49e to your computer and use it in GitHub Desktop.
Save tridungpham/fcf70a091bad4d14d49e to your computer and use it in GitHub Desktop.
<?php
/**
* Asset dependencies manager
* @author: dphamtri
* @date 20140124
*/
class AssetManager
{
static $instance = null;
public $_resources = array(
'js' => array(),
'css' => array()
);
public $_output = array(
'js' => array(
'optimized' => false,
'items' => array(),
'items_footer' => array(),
'pool' => array(),
'echoed' => false
),
'css' => array(
'optimized' => false,
'items' => array(),
'pool' => array(),
'echoed' => false
)
);
/**
* Return AssetManager instance
* @return AssetManager
*/
public static function getInstance()
{
if (is_null(self::$instance)) {
self::$instance = new self;
}
return self::$instance;
}
/**
* Add a javascript files to AM
* @param string $id
* @param string $src
* @param array $dep
* @param boolean $footer
* @return $this
*/
public function addJs($id, $src = '', $dep = array(), $footer = false)
{
$this->add('js', $id, $src, $dep, $footer);
return $this;
}
/**
* Add a stylesheet file to AM
* @param string $id
* @param string $src
* @param array $dep
* @param boolean $footer
* @return $this
*/
public function addCss($id, $src = '', $dep = array(), $footer = false)
{
$this->add('css', $id, $src, $dep, $footer);
return $this;
}
public function add($type, $id, $src, $dep = array(), $footer = false)
{
if ( $this->_output[$type]['echoed'] ) {
$footer = true;
}
$this->_resources[$type][$id] = array(
'id' => $id,
'src' => $src,
'dependencies' => $dep,
'footer' => $footer
);
$this->setOutputOption($type, 'optimized', false);
return $this;
}
public function setOutputOption($type, $key, $value = null)
{
$this->_output[$type][$key] = $value;
}
public function alterDependencies($type, $key, $value, $append = true)
{
$itemToAlter = null;
if ( isset($this->_resources[$type][$key])) {
$itemToAlter =& $this->_resources[$type][$key];
}
if ( ! $itemToAlter ) {
return false;
}
if ($append) {
$itemToAlter['dependencies'][] = $value;
} else {
$itemToAlter['dependencies'] = (array) $value;
}
if(isset($this->_output[$type]['pool'][$key])) {
unset($this->_output[$type]['pool'][$key]);
}
return true;
}
public function output($type, $footer = false)
{
if (!$this->_output[$type]['optimized']) {
$this->optimize($type);
}
$output = null;
if ($footer) {
$output = implode("\n", $this->_output[$type]['items_footer']);
$this->__remove_outputed_resources($type, array_keys($this->_output[$type]['items_footer']));
} else {
$output = implode("\n", $this->_output[$type]['items']);
$this->__remove_outputed_resources($type, array_keys($this->_output[$type]['items']));
}
$this->_output[$type]['echoed'] = true;
return $output;
}
/**
* Optimize assets for outputing
* @param string $type Type of asset to optimize
* @return void
*/
private function optimize($type)
{
$resources =& $this->_resources[$type];
$output =& $this->_output[$type];
$output['items'] = array();
$output['items_footer'] = array();
do {
$loop = false;
$result = array_filter($resources, array($this, "__filter_{$type}_and_return_matches"));
if ($result) {
// new resource added to pool, continue to run check
$loop = true;
$this->__merge_output_resources($type, $result);
}
} while ($loop);
$this->_output[$type]['optimized'] = true;
}
private function __merge_output_resources($type, $items = array())
{
$output =& $this->_output[$type];
$template = $type === 'js' ? "<script type='text/javascript' id='%s' src='%s'></script>" : "<link rel='stylesheet' id='%s' href='%s' />";
foreach ($items as $item) {
if ($item['footer'] || $this->__filter_dependecies_in_footer($type, $item['dependencies'])) {
$output['items_footer'][$item['id']] = sprintf($template, $item['id'], $item['src']);
$output['pool'][$item['id']] = 1;
} else {
$output['items'][] = sprintf($template, $item['id'], $item['src']);
$output['pool'][$item['id']] = 0;
}
}
}
private function __remove_outputed_resources($type, $ids)
{
$that =& $this;
array_walk(
$this->_resources[$type],
function ($value, $key) use ($ids, $type, $that) {
if (in_array($key, $ids)) {
unset($that->_resources[$type][$key]);
}
}
);
}
private function __filter_dependecies_in_footer($type, $dep)
{
$pool =& $this->_output[$type]['pool'];
foreach ($dep as $d) {
if ($pool[$d]) {
return true;
}
}
return false;
}
/**
* Filter that return TRUE if all item's dependencies are matched
* @param [type] $item [description]
* @return bool [type] [description]
*/
private function __filter_js_and_return_matches($item)
{
if (in_array($item['id'], array_keys($this->_output['js']['pool']))
|| ($item['dependencies'] != array_intersect(
$item['dependencies'],
array_keys($this->_output['js']['pool'])
// $this->_output['js']['pool']
))
) {
return false;
}
return true;
}
private function __filter_css_and_return_matches($item)
{
if (in_array($item['id'], array_keys($this->_output['css']['pool']))
|| ($item['dependencies'] != array_intersect(
$item['dependencies'],
array_keys($this->_output['css']['pool'])
// $this->_output['js']['pool']
))
) {
return false;
}
return true;
}
/**
* @param $type
* @param array $items
* @return $this
*/
public function setPool($type, $items = array())
{
if (!in_array($type, array('js', 'css'))) {
$type = 'js';
}
$pools =& $this->_output[$type]['pool'];
foreach ($items as $key => $value) {
$pools[$key] = $value;
}
return $this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment