basedir, '\.xml$', true, true); // Iterate through all the xml files foreach ($files as $file) { // Try to check the file $this->find($file); } } /** * Reads a file and validate XML manifest * * @param string $file - The path to the file * * @return boolean True if the manifest file was found, otherwise False. */ protected function find($file) { $xml = simplexml_load_file($file); // Failed to parse the xml file. // Assume that this is not a extension manifest if (!$xml) { return false; } // Check if this is an extension manifest if ($xml->getName() !== 'extension') { return false; } $this->errors = array(); // Check declared files and folders do exist $this->basedir = dirname($file) . '/'; $sitedir = ''; // Check: files[folder] (filename|folder)* // ( for package: files[folder] (file|folder)* ) if (isset($xml->files)) { $node = $xml->files; // Get path to site files from "folder" attribute $sitedir = isset($node['folder']) ? $node['folder'] . '/' : ''; $this->checkFiles($node->filename, $sitedir); $this->checkFiles($node->file, $sitedir); $this->checkFolders($node->folder, $sitedir); } // Check: media[folder] (filename|file|folder)* if (isset($xml->media)) { $node = $xml->media; $dir = isset($node['folder']) ? $node['folder'] . '/' : ''; $this->checkFiles($node->filename, $dir); $this->checkFiles($node->file, $dir); $this->checkFolders($node->folder, $dir); } // Check: fonts[folder] (filename|file|folder)* if (isset($xml->fonts)) { $node = $xml->fonts; $dir = isset($node['folder']) ? $node['folder'] . '/' : ''; $this->checkFiles($node->filename, $dir); $this->checkFiles($node->file, $dir); $this->checkFolders($node->folder, $dir); } // Check files: languages[folder] language* if (isset($xml->languages)) { $node = $xml->languages; $dir = isset($node['folder']) ? $node['folder'] . '/' : ''; $this->checkFiles($node->language, $dir); } $admindir = ''; // Check: administration files[folder] (filename|file|folder)* if (isset($xml->administration->files)) { $node = $xml->administration->files; // Get path to admin files from "folder" attribute $admindir = isset($node['folder']) ? $node['folder'] . '/' : ''; $this->checkFiles($node->filename, $admindir); $this->checkFiles($node->file, $admindir); $this->checkFolders($node->folder, $admindir); } // Check: administration media[folder] (filename|file|folder)* if (isset($xml->administration->media)) { $node = $xml->administration->media; $dir = isset($node['folder']) ? $node['folder'] . '/' : ''; $this->checkFiles($node->filename, $dir); $this->checkFiles($node->file, $dir); $this->checkFolders($node->folder, $dir); } // Check files: administration languages[folder] language* if (isset($xml->administration->languages)) { $node = $xml->administration->languages; $dir = isset($node['folder']) ? $node['folder'] . '/' : ''; $this->checkFiles($node->language, $dir); } // For type="file" extensions: // Check files: fileset files[folder] (filename|file|folder)* if (isset($xml->fileset->files)) { $node = $xml->fileset->files; $dir = isset($node['folder']) ? $node['folder'] . '/' : ''; $this->checkFiles($node->filename, $dir); $this->checkFiles($node->file, $dir); $this->checkFolders($node->folder, $dir); } // Check file: scriptfile if (isset($xml->scriptfile)) { $this->checkFiles($xml->scriptfile); } // Check files: install sql file* if (isset($xml->install->sql->file)) { $this->checkFiles($xml->install->sql->file, $admindir); } // Check files: uninstall sql file* if (isset($xml->uninstall->sql->file)) { $this->checkFiles($xml->uninstall->sql->file, $admindir); } // Check folders: update schemas schemapath* if (isset($xml->update->schemas->schemapath)) { $this->checkFolders($xml->update->schemas->schemapath, $admindir); } // Check: config [add...path] directories exist if (isset($xml->config)) { $attributes = array('addfieldpath', 'addformpath', 'addrulepath'); $extensionPath = false; // @TODO move element name extraction into a helper (similar code is used in XMLinfo rule) switch ((string) $xml['type']) { case 'module': if (isset($xml->element)) { $element = (string) $xml->element; } else { $element = (string) $xml->name; if (isset($xml->files)) { foreach ($xml->files->children() as $child) { if (isset($child['module'])) { $element = (string) $child['module']; break; } } } } $element = strtolower(JFilterInput::getInstance()->clean($element, 'cmd')); $extensionPath = 'modules/' . $element . '/'; break; case 'plugin': if (isset($xml->element)) { $element = (string) $xml->element; } else { $element = (string) $xml->name; if (isset($xml->files)) { foreach ($xml->files->children() as $child) { if (isset($child['plugin'])) { $element = (string) $child['plugin']; break; } } } } $element = strtolower(JFilterInput::getInstance()->clean($element, 'cmd')); $group = (string) $xml['group']; $extensionPath = 'plugins/' . $group . '/' . $element . '/'; break; case 'template': if (isset($xml->element)) { $element = (string) $xml->element; } else { $element = (string) $xml->name; } $element = strtolower(JFilterInput::getInstance()->clean($element, 'cmd')); $extensionPath = 'templates/' . $element . '/'; } if ($extensionPath !== false) { foreach ($attributes as $attribute) { foreach ($xml->config->xpath('//*[@' . $attribute . ']') as $node) { $attrPath = (string) $node[$attribute]; $folder = ltrim($attrPath, '/'); // Convert absolute path to relative (if matches extension path) if (strpos($folder, $extensionPath) === 0) { $folder = $this->basedir . $sitedir . substr($folder, strlen($extensionPath)); if (!is_dir($folder)) { $this->errors[] = JText::sprintf('COM_JEDCHECKER_XML_FILES_FOLDER_NOT_FOUND', $attrPath); } } } } } } // Check /namespace[path] directory exists (Joomla!4) if (isset($xml->namespace['path'])) { $folder = (string) $xml->namespace['path']; if (!is_dir($this->basedir . $admindir . $folder) && !is_dir($this->basedir . $sitedir . $folder)) { $this->errors[] = JText::sprintf('COM_JEDCHECKER_XML_FILES_FOLDER_NOT_FOUND', $folder); } } if (count($this->errors)) { $this->report->addError($file, implode('
', $this->errors)); } // All checks passed. Return true return true; } /** * Check files exist * * @param SimpleXMLElement $files Files to check * @param string $dir Base directory * * @return void */ protected function checkFiles($files, $dir = '') { foreach ($files as $file) { $filename = $this->basedir . $dir . $file; if (is_file($filename)) { continue; } // Extra check for unzipped files if (preg_match('/^(.*)\.(zip|tgz|tar\.gz)$/', $filename, $matches) && is_dir($matches[1])) { continue; } $this->errors[] = JText::sprintf('COM_JEDCHECKER_XML_FILES_FILE_NOT_FOUND', $dir . $file); } } /** * Check folders exist * * @param SimpleXMLElement $folders Directories to check * @param string $dir Base directory * * @return void */ protected function checkFolders($folders, $dir = '') { foreach ($folders as $folder) { if (!is_dir($this->basedir . $dir . $folder)) { $this->errors[] = JText::sprintf('COM_JEDCHECKER_XML_FILES_FOLDER_NOT_FOUND', $dir . $folder); } } } }