<?php
namespace Lia\Addon;
/**
* Convenience class for sorting css & js files before they are concatenated together.
*/
class ResourceSorter extends \Lia\Addon {
public string $fqn = 'lia:server.resourcesorter';
protected array $prepend = ['css'=>[], 'js'=>[]];
protected array $postpend=['css'=>[], 'js'=>[]];
/**
* Give an array of file names in the order they should be sent to the browser.
*
* @param $ext the file extension js or css
* @param $names an array of relative file paths, in the order you want them. There MUST NOT be leading `/`. There MAY be as many `/` as you like
* @param $prepend_all TRUE to put these BEFORE unsorted files. FALSE to put them AFTER unsorted files
* @param $prepend_sorted TRUE to put these BEFORE other sorted files. FALSE to put them AFTER other sorted files.
*/
public function setResourceOrder(string $ext, array $names, bool $prepend_all=true, bool $prepend_sorted = true){
$ext = strtolower($ext);
\Lia\Addon\Resources::from($this->lia)->setSorter($ext, [$this, 'getSortedFiles']);
$target = &$this->prepend;
if (!$prepend_all)$target = &$this->postpend;
foreach ($names as $name){
if (isset($target[$ext][$name])){
unset($target[$ext][$name]);
}
}
$new_names = array_combine($names,$names);
if ($prepend_sorted){
$target[$ext] =
array_merge(
$new_names,
$target[$ext]
);
} else {
$target[$ext] =
array_merge(
$target[$ext],
$new_names
);
}
}
public function getSortedFiles(string $ext, array $unsorted_list): array {
//@TODO add support for file paths using backslash separators like C:\Whatever\Folder\File.js
if (count($unsorted_list)==0)return $unsorted_list;
// get file extension
$ext = strtolower($ext);
$prepend_list = $this->prepend[$ext];
$list_with_prepend =
$this->get_sorted_files(
$unsorted_list,
$this->prepend[$ext],
true
);
$final_list =
$this->get_sorted_files(
$list_with_prepend,
$this->postpend[$ext],
false
);
return $final_list;
}
/**
* @param $full_list full list of files to sort
* @param $sort_list an ordered list of files
* @param $prepend whether the `$sort_list` files should go at the beginning or end of `$full_list`
*/
protected function get_sorted_files(array $full_list, array $sort_list, bool $prepend): array {
if (count($sort_list) == 0)return $full_list;
// for each resource file,
// check the entire 'sort' list for any matches
// One absolute file path might match multiple entries
// For Example: '/abs/path/theme.css' would match BOTH 'theme.css' AND 'path/theme.css'
// So the 'last_match_len' check makes sure that the longest-matching relative path take precedence over a shorter path.
// So if `theme.css` comes BEFORE `path/theme.css` then the actual file `/abs/path/theme.css` will end up going AFTER other files named 'theme.css'.
$sorted_files = [];
foreach ($full_list as $full_list_key => $absolute_path_dirty){
$absolute_path = str_replace(['///','//'], '/', $absolute_path_dirty);
$last_match_len = 0;
$sort_index = 0;
foreach ($sort_list as $rel_path){
$len = strlen($rel_path);
if (substr($absolute_path, -$len) == $rel_path){
if (!isset($sorted_files[$absolute_path])
|| $len > $last_match_len){
unset($full_list[$full_list_key]);
$sorted_files[$absolute_path] = $sort_index;
$last_match_len = $len;
}
}
$sort_index++;
}
}
asort($sorted_files, SORT_NUMERIC);
$sorted_files = array_keys($sorted_files);
if ($prepend){
return array_merge(
$sorted_files,
$full_list
);
} else {
return array_merge(
$full_list,
$sorted_files
);
}
}
}