<commit_before><?php
/*
 * This file is part of the vxPHP/vxWeb framework
 *
 * (c) <NAME> <<EMAIL>>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace vxPHP\Application;

use vxPHP\Application\Exception\ConfigException;
use vxPHP\Webpage\Menu\Menu;

/**
 * Config
 * creates a configuration singleton by parsing an XML configuration
 * file
 *
 * @version 3.1.0 2020-11-13
 */
class Config {
	/**
	 * @var \stdClass
	 */
	public $site;

	/**
	 * db settings
	 * will be replaced by vxpdo settings
	 *
	 * @deprecated
	 * @var \stdClass
	 */
	public $db;

	/**
	 * vxpdo settings
	 *
	 * @var array
	 */
	public $vxpdo;

	/**
	 * @var \stdClass
	 */
	public $mail;

	/**
	 * @var \stdClass
	 */
	public $binaries;

	/**
	 * @var array
	 */
	public $paths;

	/**
	 * @var array
	 */
	public $routes;

	/**
	 * @var Menu[]
	 */
	public $menus;

	/**
	 * @var array
	 */
	public $server;

	/**
	 * @var array
	 *
	 * holds configuration of services
	 */
	public $services;

	/**
	 * @var array
	 *
	 * holds all configured plugins (event subscribers)
	 */
	public	$plugins;

	/**
	 * @var \stdClass
	 *
	 * holds configuration for templating
	 */
	public	$templating;

	/**
	 * @var boolean
	 */
	public $isLocalhost;

    /**
     * @var array
     */
    private $parserClasses = [];

	/**
	 * a list of already processed XML files
	 * any XML file can only be parsed once
	 * avoids circular references but (currently) also disallows the
	 * re-use of XML "snippets"
	 *
	 * @var array
	 */
	private $parsedXmlFiles = [];

    /**
     * create config instance
     * additional XmlParserInterfaces can be passed to parse custom config settings
     *
     * @param string $xmlFile
     * @param array $customXmlParsers
     * @throws ConfigException
     */
	public function __construct(string $xmlFile, array $customXmlParsers = [])
    {
		$xmlFile = realpath($xmlFile);

		$previousUseErrors = libxml_use_internal_errors(true);

		$config = new \DOMDocument();

		if(!$config->load($xmlFile, LIBXML_NOCDATA)) {
			$this->dumpXmlErrors($xmlFile);
			exit();
		}

		// skip any comments

		while($config->firstChild instanceof \DOMComment) {
			$config->removeChild($config->firstChild);
		}

		if('config' !== $config->firstChild->nodeName) {
			throw new ConfigException(sprintf("No 'config' root element found in %s.", $xmlFile));
		}

		// recursively add all includes to main document

		$this->includeIncludes($config, $xmlFile);

		// set up default parsers

        foreach (glob (__DIR__ . '/Config/Parser/Xml/*.php') as $filename) {
            $className = basename($filename, '.php');
            $sectionName = substr(preg_replace_callback('/([A-Z])/', static function ($match) { return '_' . strtolower($match[1]); }, $className), 1);
            $this->parserClasses[$sectionName] = __NAMESPACE__ . '\\Config\\Parser\\Xml\\' . $className;
        }

        // add custom parsers

        foreach ($customXmlParsers as $section => $classname) {
            $reflectionClass = new \ReflectionClass($classname);

            if (!$reflectionClass->implementsInterface(__NAMESPACE__ . '\\Config\\Parser\\Xml\\XmlParserInterface')) {<commit_msg>Moved optional configuration parsers into a more generic "options" argument<commit_after><?php
/*
 * This file is part of the vxPHP/vxWeb framework
 *
 * (c) <NAME> <<EMAIL>>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace vxPHP\Application;

use vxPHP\Application\Config\Parser\XmlParserInterface;
use vxPHP\Application\Exception\ConfigException;
use vxPHP\Webpage\Menu\Menu;

/**
 * Config
 * creates a configuration singleton by parsing an XML configuration
 * file
 *
 * @version 3.2.0 2020-11-14
 */
class Config {
	/**
	 * @var \stdClass
	 */
	public $site;

	/**
	 * db settings
	 * will be replaced by vxpdo settings
	 *
	 * @deprecated
	 * @var \stdClass
	 */
	public $db;

	/**
	 * vxpdo settings
	 *
	 * @var array
	 */
	public $vxpdo;

	/**
	 * @var \stdClass
	 */
	public $mail;

	/**
	 * @var \stdClass
	 */
	public $binaries;

	/**
	 * @var array
	 */
	public $paths;

	/**
	 * @var array
	 */
	public $routes;

	/**
	 * @var Menu[]
	 */
	public $menus;

	/**
	 * @var array
	 */
	public $server;

	/**
	 * @var array
	 *
	 * holds configuration of services
	 */
	public $services;

	/**
	 * @var array
	 *
	 * holds all configured plugins (event subscribers)
	 */
	public	$plugins;

	/**
	 * @var \stdClass
	 *
	 * holds configuration for templating
	 */
	public	$templating;

	/**
	 * @var boolean
	 */
	public $isLocalhost;

    /**
     * @var array
     */
    private $parserClasses = [];

	/**
	 * a list of already processed XML files
	 * any XML file can only be parsed once
	 * avoids circular references but (currently) also disallows the
	 * re-use of XML "snippets"
	 *
	 * @var array
	 */
	private $parsedXmlFiles = [];

    /**
     * create config instance
     * possible options are currently
     * parsers: array of additional XmlParserInterfaces to parse custom config settings
     *
     * @param string $xmlFile
     * @param array $options
     * @throws ConfigException|\ReflectionException
     */
	public function __construct(string $xmlFile, array $options = [])
    {
		$xmlFile = realpath($xmlFile);

		$previousUseErrors = libxml_use_internal_errors(true);

		$config = new \DOMDocument();

		if(!$config->load($xmlFile, LIBXML_NOCDATA)) {
			$this->dumpXmlErrors($xmlFile);
			exit();
		}

		// skip any comments

		while($config->firstChild instanceof \DOMComment) {
			$config->removeChild($config->firstChild);
		}

		if('config' !== $config->firstChild->nodeName) {
			throw new ConfigException(sprintf("No 'config' root element found in %s.", $xmlFile));
		}

		// recursively add all includes to main document

		$this->includeIncludes($config, $xmlFile);

		// set up default parsers

        foreach (glob (__DIR__ . '/Config/Parser/Xml/*.php') as $filename) {
            $className = basename($filename, '.php');
            $sectionName = substr(preg_replace_callback('/([A-Z])/', static function ($match) { return '_' . strtolower($match[1]); }, $className), 1);
            $this->parserClasses[$sectionName] = __NAMESPACE__ . '\\Config\\Parser\\Xml\\' . $className;
        }

        // add custom parsers

        foreach ($options['parsers'] ?? [] as $section => $classname) {
            $reflectionClass = new \ReflectionClass($classname);

            if (!$reflectionClass->implementsInterface(XmlParserInterface::class)) {
{'fraction_non_alphanumeric': 0.14184610751392862, 'fraction_numerical': 0.003915073031170005, 'mean_word_length': 2.84375, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 18}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2457910', 'n_tokens_mistral': 2215, 'n_tokens_neox': 1999, 'n_words': 556}