<?php
namespace Lia\Addon;
class ResourceSorter extends \Lia\Addon {
public string $fqn = 'lia:server.resourcesorter';
protected $didSet=false;
protected $orders=['css'=>[], 'js'=>[]];
public function init_lia(){
$this->lia->methods['setResourceOrder'] = [$this,'setResourceOrder'];
}
/**
* 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 TRUE to put these $names BEFORE any previous names set this way. FALSE to put these names at the end. WILL replace any identical names for sorting
*/
public function setResourceOrder($ext,$names, $prepend=true){
$ext = strtolower($ext);
if (!$this->didSet){
$this->lia->setResourceSorter($ext, [$this, 'getSortedFiles']);
}
$resources = [];
foreach ($names as $name){
if (isset($this->orders[$ext][$name])){
unset ($this->orders[$ext][$name]);
}
$resources[$ext][$name] = $name;
}
if ($prepend){
$this->orders = array_merge_recursive($resources, $this->orders);
} else {
$this->orders = array_merge_recursive($this->orders, $resources);
}
}
public function getSortedFiles($from_unsorted){
//@TODO add support for file paths using backslash separators like C:\Whatever\Folder\File.js
if (count($from_unsorted)==0)return;
// get file extension
$f = array_values($from_unsorted)[0];
$ext = pathinfo($f, PATHINFO_EXTENSION);
$ext = strtolower($ext);
// generate all match positions
$remaining_unsorted = [];
$sort_values = [];
$sort_orders = array_values($this->orders[$ext]);
foreach ($from_unsorted as $index => $absolute_file){
$absolute_file = str_replace(['///','//'], '/', $absolute_file);
$sort_result = [];
foreach ($sort_orders as $index=>$relative_file){
$len = strlen($relative_file);
if (substr($absolute_file, -$len)==$relative_file){
$sort_result[] = [
'match_order'=>$index,
'match_length'=>$len,
];
}
}
if (count($sort_result)==0){
$remaining_unsorted[] = $absolute_file;
} else {
$sort_values[$absolute_file] = $sort_result;
}
}
// choose match position that has the longest string match between the relative file and absolute file
$best_values = [];
foreach ($sort_values as $absolute_file => $values_list){
if (count($values_list)==1)$best_values[$absolute_file] = $values_list[0];
else {
$best_value = null;
$best_match_length = -1;
foreach ($values_list as $value){
if ($value['match_length'] > $best_match_length)$best_value = $value;
}
$best_values[$absolute_file] = $value;
}
$best_values[$absolute_file]['absolute_file']=$absolute_file;
}
// put the matches in order
$ordered_values = $best_values;
usort($ordered_values,
function ($a, $b){
// return 1 if a before b; -1 if b before a; 0 if same position / don't care
if ($a['match_order'] > $b['match_order']) return 1;
else if ($a['match_order'] < $b['match_order']) return -1;
else if ($a['match_length'] > $b['match_length']) return 1;
else if ($a['match_length'] < $b['match_length']) return 1;
else return 0;
}
);
// finally build the sorted files array
$sorted_files = [];
foreach ($ordered_values as $value){
$sorted_files[] = $value['absolute_file'];
}
$sorted_files = array_merge($sorted_files, $remaining_unsorted);
return $sorted_files;
}
}