<commit_before><?php

namespace Zend\Module;

use Traversable,
    Zend\Config\Config,
    Zend\EventManager\EventCollection,
    Zend\EventManager\EventManager;

class Manager
{
    /**
     * @var array An array of Module classes of loaded modules
     */
    protected $loadedModules = array();

    /**
     * @var EventCollection
     */
    protected $events;

    /**
     * @var ManagerOptions
     */
    protected $options;

    /**
     * @var Zend\Config\Config
     */
    protected $mergedConfig;

    /**
     * @var bool
     */
    protected $skipConfig = false;

    /**
     * __construct 
     * 
     * @param array|Traversable $modules 
     * @param ManagerOptions $options 
     * @return void
     */
    public function __construct($modules, ManagerOptions $options = null)
    {
        if ($options === null) {
            $this->setOptions(new ManagerOptions);
        } else {
            $this->setOptions($options);
        }
        if ($this->hasCachedConfig()) {
            $this->skipConfig = true;
            $this->setMergedConfig($this->getCachedConfig());
        }
        $this->loadModules($modules);
        $this->updateCache();
        $this->events()->trigger('init.post', $this);
    }

    /**
     * loadModules 
     * 
     * @param array|Traversable $modules 
     * @return Manager
     */
    public function loadModules($modules)
    {
        if (is_array($modules) || $modules instanceof Traversable) {
            foreach ($modules as $moduleName) {
                $this->loadModule($moduleName);
            }
        } else {
            throw new \InvalidArgumentException(
                'Parameter to \\ZendModule\\Manager\'s '
                . 'loadModules method must be an array or '
                . 'implement the \\Traversable interface'
            );
        }
        return $this;
    }

    /**
     * loadModule 
     * 
     * @param string $moduleName 
     * @return mixed Module's Module class
     */
    public function loadModule($moduleName)
    {
        if (!isset($this->loadedModules[$moduleName])) {
            $class = $moduleName . '\Module';
            $module = new $class;
            $this->runModuleInit($module);
            $this->mergeModuleConfig($module);
            $this->loadedModules[$moduleName] = $module;
        }
        return $this->loadedModules[$moduleName];
    }

    /**
     * Loop through loaded modules and verify that all dependencies are met 
     *
     * @TODO: 
     *  - This could probably be much more efficient (do not check satisfied 
     *  deps again, etc)
     *  - Do more isset() checking on the dep arrays
     * 
     * @return array An array of unsatisfied, optional dependencies
     */
    public function resolveDependencies()
    {
        foreach ($this->getLoadedModules() as $moduleName => $module) {
            if (!is_callable(array($module, 'getDependencies'))) {
                continue;
            }
            $unsatisfiedDeps = array();
            foreach ($module->getDependencies() as $dep => $depInfo) {
                preg_match('/(<|lt|<=|le|>|gt|>=|ge|==|=|eq|!=|<>|ne)?(\d.*)/',$depInfo['version'], $matches, PREG_OFFSET_CAPTURE);
                if ($dep === 'php') {
                    if (!version_compare(PHP_VERSION, $matches[2][0], $matches[1][0] ?: '>=')) {
                        if ($depInfo['required'] == true) {
                            throw new \RuntimeException("Required dependency unsatisfied: {$dep} {$depInfo['version']}");
                        } else {
                            $unsatifiedDeps[$moduleName][$dep] = $depInfo;
                        }
                    }
                } elseif (substr($dep, 0, 4) === 'ext/') {
                    $extName = substr($dep, 4);
                    if (!version_compare(phpversion($extName), $matches[2][0], $matches[1][0] ?: '>=')) {
                        if ($depInfo['required'] == true) {
                            throw new \RuntimeException("Required dependency unsatisfied: {$dep} {$depInfo['version']}");
                        } else {
                            $unsatifiedDeps[$moduleName][$dep] = $depInfo;
                        }
                    }
                } else {
                    $satisfied = false;
                    foreach ($this->getLoadedModules() as $depModuleName => $depModule) {
                        if (!is_callable(array($depModule, 'getProvides'))) {
                            continue;
                        }
                        $provides = $depModule->getProvides();
                        if ($provides['name'] !== $dep) {
                            continue;
                        }
                        if (version_compare($provides['version'], $matches[2][0], $matches[1][0] ?: '>=')) {
                            $satisfied = true;
                            break;
                        }
                    }
                    if (!$satisfied) {
                        if ($depInfo['required'] == true) {
                            throw new \RuntimeException("Required dependency unsatisfied: {$dep} {$depInfo['version']}");
                        } else {
                            $unsatifiedDeps[$moduleName][$dep] = $depInfo;
                        }
                    }
                }
            }
        }
        return $unsatisfiedDeps;
    }

    /**
     * Set the event manager instance used by this context
     * 
     * @param  EventCollection $events 
     * @return Manager
     */
    public function setEventManager(EventCollection $events)
    {
        $this->events = $events;
        return $this;
    }<commit_msg>Updated Module/Manager for improved dependencies<commit_after><?php

namespace Zend\Module;

use Traversable,
    Zend\Config\Config,
    Zend\EventManager\EventCollection,
    Zend\EventManager\EventManager;

class Manager
{
    /**
     * @var array An array of Module classes of loaded modules
     */
    protected $loadedModules = array();

    /**
     * @var EventCollection
     */
    protected $events;

    /**
     * @var ManagerOptions
     */
    protected $options;

    /**
     * @var Zend\Config\Config
     */
    protected $mergedConfig;

    /**
     * @var bool
     */
    protected $skipConfig = false;

    /**
     * An array containing all of the provided data for modules
     * @var array
     */
    protected $provided = array();

    /**
     * An array containing all of the dependencies for modules
     * @var array
     */
    protected $dependencies = array();
    
    /**
     * regex for getting version operators
     * @var string
     */
    protected $operators = '/(<|lt|<=|le|>|gt|>=|ge|==|=|eq|!=|<>|ne)?(\d.*)/';
    /**
     * __construct 
     * 
     * @param array|Traversable $modules 
     * @param ManagerOptions $options 
     * @return void
     */
    public function __construct($modules, ManagerOptions $options = null)
    {
        if ($options === null) {
            $this->setOptions(new ManagerOptions);
        } else {
            $this->setOptions($options);
        }
        if ($this->hasCachedConfig()) {
            $this->skipConfig = true;
            $this->setMergedConfig($this->getCachedConfig());
        }
        $this->loadModules($modules);
        $this->updateCache();
        $this->events()->trigger('init.post', $this);
    }

    /**
     * loadModules 
     * 
     * @param array|Traversable $modules 
     * @return Manager
     */
    public function loadModules($modules)
    {
        if (is_array($modules) || $modules instanceof Traversable) {
            foreach ($modules as $moduleName) {
                $this->loadModule($moduleName);
            }
        } else {
            throw new \InvalidArgumentException(
                'Parameter to \\ZendModule\\Manager\'s '
                . 'loadModules method must be an array or '
                . 'implement the \\Traversable interface'
            );
        }
        
        $this->resolveDependencies();
        return $this;
    }

    /**
     * loadModule 
     * 
     * @param string $moduleName 
     * @return mixed Module's Module class
     */
    public function loadModule($moduleName)
    {
        if (!isset($this->loadedModules[$moduleName])) {
            $class = $moduleName . '\Module';
            $module = new $class;
            
            $this->addProvision($module);
            $this->addDependency($module);
            $this->runModuleInit($module);
            $this->mergeModuleConfig($module);
            $this->loadedModules[$moduleName] = $module;
        }
        return $this->loadedModules[$moduleName];
    }

    /**
     * add details of module provisions
     * 
     * test for an uses getProvides method from module class
     * 
     * getProvides need to return as a minimum
     * 
     * <code>
     * return array(
     *		__NAMESPACE__ => array(
     *	 		'version' => $this->version,
     *		),
     *	);
     * </code>
     * 
     * @param Module $module
     * @return Manager
     */
    public function addProvision($module)
    {
     	// check for and load provides
        if (method_exists($module, 'getProvides')) {
        	$provision = $module->getProvides();
        	foreach ($provision AS $name => $info) {
	        	if (isset($this->provided[$name])) {
	        		throw new \RuntimeException("Double provision has occured for: {$name} {$info['version']}");
	        	}
	        	$this->provided[$name] = $info;	
        	}
        }
    	return $this;
    }
    
    /**
     * add dependencies from module
     * 
     * test for and use getDependencies from Module class
     * 
     * get dependencies must return 
     * 
     * <code>
     * return array(
	 *		'php' => array(
     *			'version' => '5.3.0',
     *			'required' => true,
     *		),
     *		'ext/pdo_mysql' => true
     *	);
     * </code>
     * 
     * version and required data are optional
     * 
     * @param Module $module
     * @param Manager
     */
    public function addDependency($module)
    {
    	// check for an load dependencies required
    	if (method_exists($module, 'getDependencies')) {
        	// iterate over dependencies to evaluate min required version
        	foreach ($module->getDependencies() AS $dep => $depInfo) {
        		if (!isset($this->dependencies[$dep])) { // if the dep isnt present just add it
        			$this->dependencies[$dep] = $depInfo; 	
        		} else { // dep already present
        			if (is_array($depInfo)) { // if is array need to check versions
        				if (version_compare($this->dependencies[$dep]['version'], $depInfo['version']) < 0) {
        					$depInfo['version'] = $this->dependencies[$dep]['version'];// set to highest version
        				}
        			} 
        			$this->dependencies[$moduleName] = array_merge($this->dependencies[$moduleName], $depInfo);
        		}
        	}
        }
        return $this;
    }
    
    /**
     * return the currently loaded dependencies
     * 
     * @return array
     */
    public function getDependencies()
    {
    	return $this->dependencies;
    }
    
    /**
     * return the currently loaded provisions
     * 
     * @retrun array
     */
    public function getProvisions()
    {
    	return $this->provided;
    }
    
    /**
     * Takes arrays created for provides and depends on module load 
     * and iterates over to check for satisfied dependencies  
     * 
     * updates dependency array with key 'satisfied' for each dependency
     * 
     * This allows quick retrieval of provisions and dependencies 
     * 
     * @todo: add library detection withoiy having to load files
     * @todo: review code for performance 
     * 
     * @return Manager
     */
    public function resolveDependencies()
    {
        foreach ($this->getDependencies() as $dep => $depInfo) {
            preg_match($this->operators,$depInfo['version'], $matches, PREG_OFFSET_CAPTURE);
            if ($dep === 'php') { // is php version requirement
            	$this->dependencies[$dep]['satisfied'] = true;
                if (!version_compare(PHP_VERSION, $matches[2][0], $matches[1][0] ?: '>=')) {
                    if (isset($depInfo['required']) && $depInfo['required'] == true) {
                        throw new \RuntimeException("Required dependency unsatisfied: {$dep} {$depInfo['version']}");
                    } else {
                        $this->dependencies[$dep]['satisfied'] = false;
                    }
                }
            } elseif (substr($dep, 0, 4) === 'ext/') { // is php extension requirement
                $extName = substr($dep, 4);
                $this->dependencies[$dep]['satisfied'] = true;
                if (!version_compare(phpversion($extName), $matches[2][0], $matches[1][0] ?: '>=')) {
                    if (isset($depInfo['required']) && $depInfo['required'] == true) {
                        throw new \RuntimeException("Required dependency unsatisfied: {$dep} {$depInfo['version']}");
                    } else {
                        $this->dependencies[$dep]['satisfied'] = false;
                    }
                }
            } elseif (substr($dep, 0, 4) === 'lib/') { // is library requirement
            // @todo: add library detection
            
            
            } else { // is module requirement
             	if (!isset($depInfo['satisfied'])) {
               		$this->dependencies[$dep]['satisfied'] = false;
	                if (isset($this->provided[$dep])) { // if provided set satisfaction
	                  	if (isset($depInfo['version'])){ // if dep have version requirement
	                   		if (version_compare($this->provided[$dep]['version'], $matches[2][0], $matches[1][0] ?: '>=') <= 0) {
			                   	$this->dependencies[$dep]['satisfied'] = true;
			                }
	                  	} else {
	                  		$this->dependencies[$dep]['satisfied'] = true;
	                   	}
	                 }
               	}
                if (!$this->dependencies[$dep]['satisfied']) {
                    if (isset($depInfo['required']) && $depInfo['required'] == true ) {
                        throw new \RuntimeException("Required dependency unsatisfied: {$dep} {$depInfo['version']}");
                    }
                }
            }
        }
        return $this;
    }

    /**
     * Set the event manager instance used by this context
     * 
     * @param  EventCollection $events 
     * @return Manager
     */
    public function setEventManager(EventCollection $events)
    {
        $this->events = $events;
        return $this;
    }
{'fraction_non_alphanumeric': 0.13535930382758854, 'fraction_numerical': 0.0025154667210551362, 'mean_word_length': 1.6475881929445644, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 97, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, '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': '498068', 'n_tokens_mistral': 4094, 'n_tokens_neox': 3872, 'n_words': 887}