I put together some demo code that loads the dependencies of a script first, then loads the script. There's some sample output to demonstrate what's going on as well. Make sure to read all of the comments as there's too much to explain here. Please let me know if you want any changes.
+1, interesting challenge!
#!/usr/bin/php
<?php
/*
* -------
* There's room for improvement, but this is a good start.
* Let me know if you need any changes.
* -------
*
* This loads scripts with dependencies being loaded
* first, with efficiency being key here.
* Reference counting is also performed, and can be
* seen in the $loaded array. A script can be referenced
* indirectly many times through the loading of various
* scripts and their dependencies.
* Circular dependencies are handled by checking if the
* script has already been loaded. Since it can only
* be loaded once, a circular dependency is avoided.
*
* Original Sample Data:
* ╔══════════════════╦═════════════════════════════╗
* ║ Script Name ║ Dependencies ║
* ╠══════════════════╬═════════════════════════════╣
* ║ jquery ║ jquery-core, jquery-migrate ║
* ║ jquery-core ║ null ║
* ║ jquery-migrate ║ null ║
* ║ statistics ║ null ║
* ║ backtotop ║ jquery ║
* ║ jquery-circle ║ jquery ║
* ║ interface-slider ║ jquery-circle ║
* ║ test1 ║ jquery, statistics ║
* ║ test2 ║ test1 ║
* ║ test3 ║ null ║
* ╚══════════════════╩═════════════════════════════╝
*
*/
define('NO_DEPENDENCIES_LIST', TRUE); //create a list of scripts with no dependencies
//sample data, taken from OP
$scripts = array(
'jquery'=>array('jquery-core','jquery-migrate',),
'jquery-core'=>array(),
'jquery-migrate'=>array(null ),
'statistics'=>array( ),
'backtotop'=>array('jquery' ),
'jquery-circle'=>array('jquery' ),
'interface-slider'=>array('jquery-circle' ),
'test1'=>array('jquery','statistics', 'test3' ),
'test2'=>array('test1' ),
'test3'=>array( ),
);
$loaded = array(); //list of loaded scripts, order is important
$nodepends = array(); //list of scripts with no dependencies. async load perhaps?
/**
* Adds a null item to an empty array, strictly for json output
* as the OP used null instead of empty arrays in the example.
* @param array $scripts
*/
function process_array(&$scripts) {
foreach($scripts as $s=>&$data)
if (count($data)==0)
$data = array(null);
}
/**
* Finds dependents of $scriptName.
* @param array $scripts script test data
* @param string $scriptName name of script to search for dependcies for
* @param boolean $retNames TRUE to return script names, false to return ref count
* @return multitype:number unknown
*/
function find_dependencies(array $scripts, $scriptName, $retNames=FALSE) {
$ret = array();
foreach($scripts as $s=>$data)
foreach($data as $d) {
if ($d == $scriptName)
if (!$retNames) {
$ret[$s] = (isset($ret[$s])?$ret[$s] + 1 : 0);
} else {
$ret[$s] = $s;
}
}
return $ret;
}
/**
* Checks $script to see if it has already been added to the list of
* loaded scripts.
* @param string|array $script script name or array of script names
* @return boolean
*/
function script_loaded($script) {
global $loaded;
if (is_array($script)) {
foreach($script as $s)
if (!isset($loaded[$s]))
return FALSE;
}
if (is_string($script))
return isset($loaded[$script]);
return TRUE;
}
/**
* Loads a script into the $loaded array, first loading all
* dependencies, and the dependencies of those, etc., etc.
* Ensures that scripts are only loaded after all required
* scripts are loaded. Remember - order of $loaded is important!
* Return value is unimportant in this version.
*
* @param array $scripts
* @param string $script
* @param array|null $children
* @param integer $level
* @return boolean
*/
function load_script(array &$scripts, $script, &$children, $level) {
global $loaded, $nodepends;
if ($script == null)
return FALSE;
if (script_loaded($script))
return TRUE;
if (count($scripts[$script]) > 0 && $scripts[$script][0] != null) {
for($i=0;$i<count($scripts[$script]);$i++) {
if (!isset($scripts[$script][$i]))
break;
if ($i >= count($scripts[$script]))
break;
if (!script_loaded($scripts[$script][$i])) {
load_script($scripts, $scripts[$script][$i], $scripts[$script], $level+1);
}
if (isset($children[$i]) && script_loaded($children[$i]))
$children[$i] = null;
}
}
if ($scripts[$script][0]==null) {
if (!isset($loaded[$script]))
$loaded[$script] = $script;
if (NO_DEPENDENCIES_LIST)
$nodepends[$script] = $loaded[$script];
}
if (!isset($loaded[$script])) {
$loaded[$script] = 0;
} else {
$loaded[$script] = $loaded[$script] + 1;
return TRUE;
}
$loaded[$script] = $loaded[$script] + 1;
echo "load_script($script)\n";
return TRUE;
}
/**
* demo main function
* @param array $scripts - array of scripts and their dependencies: test data
*/
function main(&$scripts, &$loaded, &$nodepends) {
process_array($scripts);
foreach($scripts as $s=>$data) {
load_script($scripts, $s, $data, 0);
}
if (NO_DEPENDENCIES_LIST)
//reverse this to put the less-referenced scripts at the top
$nodepends = array_reverse($nodepends);
//here we print out a table of the $loaded array.
//it's just here for a visual representation of
//what scripts were loaded and what their dependent scripts
//are.
//The left column is the loaded script, the right column
//is a list of scripts that tried to load the script.
echo "
╔══════════════════════════════════════════════════╗
║ Loaded Scripts: with scripts that loaded them ║
╚══════════════════════════════════════════════════╝
╔══════════════════╦═══════════════════════════════╗
║ Script Name ║ Loading Scripts ║
╠══════════════════╬═══════════════════════════════╣\n";
foreach($loaded as $s=>$n) {
$d = find_dependencies($scripts, $s, TRUE);
$n = 16-strlen($s);
$s2 = implode(",", $d);
if ($s2=="")
$s2 = "null";
$n2 = 29-strlen($s2);
printf (" ║ %s%s ║ %s%s ║\n", $s, str_repeat(" ", $n), $s2, str_repeat(" ", $n2));
}
echo " ╚══════════════════╩═══════════════════════════════╝\n";
//print json of loaded scripts -- just because OP used json
print_r( json_encode($scripts, JSON_PRETTY_PRINT) );
//print array of loaded scripts: the order of this array is important;
//scripts that are depended on by other scripts are loaded first. If
//a script has no dependencies, its order is not significant.
//--
//this is the actual result that we're looking for: all scripts loaded
//with dependencies loaded first.
print_r( $loaded );
//print array of scripts that have no dependencies. Since efficiency is
//a requirement, you could async load these first, since it doesn't matter
//what order they load in.
if (NO_DEPENDENCIES_LIST)
print_r( $nodepends );
}
//run the demo
main($scripts, $loaded, $nodepends);