loadFiles($path, $installedFeatures); // This ensures that we register everything in the right order. foreach ($installedFeatures as $feature) { $registeredFeature = $this->register($registry, $feature, $alreadyRegistered, $installedFeatures, $registeredFeatures); if ($registeredFeature != null) { $registeredFeatures[] = $registeredFeature; } } return $registeredFeatures; } private function sortFeaturesFiles($feature1, $feature2) { $feature1 = basename(str_replace('/feature.xml', '', $feature1)); $feature2 = basename(str_replace('/feature.xml', '', $feature2)); if ($feature1 == $feature2) { return 0; } return ($feature1 < $feature2) ? - 1 : 1; } private function loadFiles($path, &$features) { $featuresFile = $path . '/features.txt'; if (File::exists($featuresFile)) { $files = explode("\n", file_get_contents($featuresFile)); // custom sort, else core.io seems to bubble up before core, which breaks the dep chain order usort($files, array($this, 'sortFeaturesFiles')); foreach ($files as $file) { if (! empty($file) && strpos($file, 'feature.xml') !== false && substr($file, 0, 1) != '#' && substr($file, 0, 2) != '//') { $file = realpath($path . '/../' . trim($file)); $feature = $this->processFile($file); $features[$feature->name] = $feature; } } } } private function processFile($file) { $feature = null; if (File::exists($file)) { if (($content = @file_get_contents($file))) { $feature = $this->parse($content, dirname($file)); } } return $feature; } private function register(&$registry, $feature, &$alreadyRegistered, $installedFeatures, &$registeredFeatures) { if (in_array($feature->name, $alreadyRegistered)) { return null; } foreach ($feature->deps as $dep) { if (isset($installedFeatures[$dep]) && ! in_array($dep, $alreadyRegistered)) { $registeredFeature = $this->register($registry, $installedFeatures[$dep], $alreadyRegistered, $installedFeatures, $registeredFeatures); if ($registeredFeature != null) { // add dependency to list of loaded features $registeredFeatures[] = $registeredFeature; } } // Note: if a depency is missing, it is simply not loaded. There is no check for that here } $factory = new JsLibraryFeatureFactory($feature->gadgetJs, $feature->containerJs); $alreadyRegistered[] = $feature->name; return $registry->register($feature->name, $feature->deps, $factory); } private function parse($content, $path) { $doc = simplexml_load_string($content); $feature = new ParsedFeature(); $feature->basePath = $path; if (! isset($doc->name)) { throw new GadgetException('Invalid name in feature: ' . $path); } $feature->name = trim($doc->name); foreach ($doc->gadget as $gadget) { $feature = $this->processContext($feature, $gadget, false); } foreach ($doc->container as $container) { $feature = $this->processContext($feature, $container, true); } foreach ($doc->dependency as $dependency) { $feature->deps[] = trim($dependency); } return $feature; } private function processContext(&$feature, $context, $isContainer) { foreach ($context->script as $script) { $attributes = $script->attributes(); if (! isset($attributes['src'])) { // inline content $type = 'INLINE'; $content = (string)$script; } else { $content = trim($attributes['src']); if (strtolower(substr($content, 0, strlen("http://"))) == "http://") { $type = 'URL'; } elseif (strtolower(substr($content, 0, strlen("//"))) == "//") { $type = 'URL'; } else { // as before, we skip over the resource parts since we dont support them $type = 'FILE'; $content = $feature->basePath . '/' . $content; } } $library = JsLibrary::create($type, $content, $feature->name); if ($library != null) { if ($isContainer) { $feature->containerJs[] = $library; } else { $feature->gadgetJs[] = $library; } } } return $feature; } } class ParsedFeature { public $name = ""; public $basePath = ""; public $containerJs = array(); public $gadgetJs = array(); public $deps = array(); }