diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php new file mode 100644 index 0000000..4ead054 --- /dev/null +++ b/src/Exception/ExceptionInterface.php @@ -0,0 +1,18 @@ +setOptions(new ListenerOptions); + } else { + $this->setOptions($options); + } + } + + /** + * Get options. + * + * @return ListenerOptions + */ + public function getOptions() + { + return $this->options; + } + + /** + * Set options. + * + * @param ListenerOptions $options the value to be set + * @return AbstractListener + */ + public function setOptions(ListenerOptions $options) + { + $this->options = $options; + return $this; + } + + /** + * Write a simple array of scalars to a file + * + * @param string $filePath + * @param array $array + * @return AbstractListener + */ + protected function writeArrayToFile($filePath, $array) + { + $content = "getModule(); + if (!$module instanceof AutoloaderProviderInterface + && !method_exists($module, 'getAutoloaderConfig') + ) { + return; + } + $autoloaderConfig = $module->getAutoloaderConfig(); + AutoloaderFactory::factory($autoloaderConfig); + } +} diff --git a/src/Listener/ConfigListener.php b/src/Listener/ConfigListener.php new file mode 100644 index 0000000..f204ade --- /dev/null +++ b/src/Listener/ConfigListener.php @@ -0,0 +1,406 @@ +hasCachedConfig()) { + $this->skipConfig = true; + $this->setMergedConfig($this->getCachedConfig()); + } else { + $this->addConfigGlobPaths($this->getOptions()->getConfigGlobPaths()); + $this->addConfigStaticPaths($this->getOptions()->getConfigStaticPaths()); + } + } + + /** + * __invoke proxy to loadModule for easier attaching + * + * @param ModuleEvent $e + * @return ConfigListener + */ + public function __invoke(ModuleEvent $e) + { + return $this->loadModule($e); + } + + /** + * Attach one or more listeners + * + * @param EventManagerInterface $events + * @return ConfigListener + */ + public function attach(EventManagerInterface $events) + { + $this->listeners[] = $events->attach('loadModule', array($this, 'loadModule'), 1000); + $this->listeners[] = $events->attach('loadModules.pre', array($this, 'loadModulesPre'), 9000); + $this->listeners[] = $events->attach('loadModules.post', array($this, 'loadModulesPost'), 9000); + return $this; + } + + /** + * Pass self to the ModuleEvent object early so everyone has access. + * + * @param ModuleEvent $e + * @return ConfigListener + */ + public function loadModulesPre(ModuleEvent $e) + { + $e->setConfigListener($this); + return $this; + } + + /** + * Merge the config for each module + * + * @param ModuleEvent $e + * @return ConfigListener + */ + public function loadModule(ModuleEvent $e) + { + if (true === $this->skipConfig) { + return; + } + $module = $e->getParam('module'); + if (is_callable(array($module, 'getConfig'))) { + $this->mergeModuleConfig($module); + } + return $this; + } + + /** + * Merge all config files matched by the given glob()s + * + * This should really only be called by the module manager. + * + * @param ModuleEvent $e + * @return ConfigListener + */ + public function loadModulesPost(ModuleEvent $e) + { + if (true === $this->skipConfig) { + return $this; + } + foreach ($this->paths as $path) { + $this->mergePath($path); + } + return $this; + } + + /** + * Detach all previously attached listeners + * + * @param EventManagerInterface $events + * @return ConfigListener + */ + public function detach(EventManagerInterface $events) + { + foreach ($this->listeners as $key => $listener) { + $events->detach($listener); + unset($this->listeners[$key]); + } + $this->listeners = array(); + return $this; + } + + /** + * getMergedConfig + * + * @param bool $returnConfigAsObject + * @return mixed + */ + public function getMergedConfig($returnConfigAsObject = true) + { + if ($returnConfigAsObject === true) { + if ($this->mergedConfigObject === null) { + $this->mergedConfigObject = new Config($this->mergedConfig); + } + return $this->mergedConfigObject; + } else { + return $this->mergedConfig; + } + } + + /** + * setMergedConfig + * + * @param array $config + * @return ConfigListener + */ + public function setMergedConfig(array $config) + { + $this->mergedConfig = $config; + $this->mergedConfigObject = null; + return $this; + } + + /** + * Add a path of config files to merge after loading modules + * + * @param string $path + * @return ConfigListener + */ + protected function addConfigPath($path, $type) + { + if (!is_string($path)) { + throw new Exception\InvalidArgumentException( + sprintf('Parameter to %s::%s() must be a string; %s given.', + __CLASS__, __METHOD__, gettype($path)) + ); + } + $this->paths[] = array('type' => $type, 'path' => $path); + return $this; + } + + /** + * Add an array of glob paths of config files to merge after loading modules + * + * @param mixed $globPaths + * @return ConfigListener + */ + public function addConfigGlobPaths($globPaths) + { + $this->addConfigPaths($globPaths, self::GLOB_PATH); + return $this; + } + + /** + * Add a glob path of config files to merge after loading modules + * + * @param string $globPath + * @return ConfigListener + */ + public function addConfigGlobPath($globPath) + { + $this->addConfigPath($globPath, self::GLOB_PATH); + return $this; + } + + /** + * Add an array of static paths of config files to merge after loading modules + * + * @param mixed $staticPaths + * @return ConfigListener + */ + public function addConfigStaticPaths($staticPaths) + { + $this->addConfigPaths($staticPaths, self::STATIC_PATH); + return $this; + } + + /** + * Add a static path of config files to merge after loading modules + * + * @param string $globPath + * @return ConfigListener + */ + public function addConfigStaticPath($staticPath) + { + $this->addConfigPath($staticPath, self::STATIC_PATH); + return $this; + } + + /** + * Add an array of paths of config files to merge after loading modules + * + * @param mixed $paths + * @return ConfigListener + */ + protected function addConfigPaths($paths, $type) + { + if ($paths instanceof Traversable) { + $paths = ArrayUtils::iteratorToArray($paths); + } + + if (!is_array($paths)) { + throw new Exception\InvalidArgumentException( + sprintf('Argument passed to %::%s() must be an array, ' + . 'implement the \Traversable interface, or be an ' + . 'instance of Zend\Config\Config. %s given.', + __CLASS__, __METHOD__, gettype($paths)) + ); + } + + foreach ($paths as $path) { + $this->addConfigPath($path, $type); + } + } + + /** + * Merge all config files matching a glob + * + * @param mixed $path + * @return ConfigListener + */ + protected function mergePath($path) + { + switch ($path['type']) { + case self::STATIC_PATH: + $config = ConfigFactory::fromFile($path['path']); + break; + + case self::GLOB_PATH: + $config = ConfigFactory::fromFiles(Glob::glob($path['path'], Glob::GLOB_BRACE)); + break; + } + $this->mergeTraversableConfig($config); + if ($this->getOptions()->getConfigCacheEnabled()) { + $this->updateCache(); + } + return $this; + } + + /** + * mergeModuleConfig + * + * @param mixed $module + * @return ConfigListener + */ + protected function mergeModuleConfig($module) + { + if (false !== $this->skipConfig + || (!$module instanceof ConfigProviderInterface + && !is_callable(array($module, 'getConfig'))) + ) { + return $this; + } + + $config = $module->getConfig(); + try { + $this->mergeTraversableConfig($config); + } catch (Exception\InvalidArgumentException $e) { + // Throw a more descriptive exception + throw new Exception\InvalidArgumentException( + sprintf('getConfig() method of %s must be an array, ' + . 'implement the \Traversable interface, or be an ' + . 'instance of Zend\Config\Config. %s given.', + get_class($module), gettype($config)) + ); + } + + if ($this->getOptions()->getConfigCacheEnabled()) { + $this->updateCache(); + } + + return $this; + } + + /** + * @param $config + * @throws Exception\InvalidArgumentException + * @return void + */ + protected function mergeTraversableConfig($config) + { + if ($config instanceof Traversable) { + $config = ArrayUtils::iteratorToArray($config); + } + if (!is_array($config)) { + throw new Exception\InvalidArgumentException( + sprintf('Config being merged must be an array, ' + . 'implement the \Traversable interface, or be an ' + . 'instance of Zend\Config\Config. %s given.', gettype($config)) + ); + } + $this->setMergedConfig(ArrayUtils::merge($this->mergedConfig, $config)); + } + + /** + * @return bool + */ + protected function hasCachedConfig() + { + if (($this->getOptions()->getConfigCacheEnabled()) + && (file_exists($this->getOptions()->getConfigCacheFile())) + ) { + return true; + } + return false; + } + + /** + * @return mixed + */ + protected function getCachedConfig() + { + return include $this->getOptions()->getConfigCacheFile(); + } + + /** + * @return ConfigListener + */ + protected function updateCache() + { + if (($this->getOptions()->getConfigCacheEnabled()) + && (false === $this->skipConfig) + ) { + $configFile = $this->getOptions()->getConfigCacheFile(); + $this->writeArrayToFile($configFile, $this->getMergedConfig(false)); + } + return $this; + } +} diff --git a/src/Listener/ConfigMergerInterface.php b/src/Listener/ConfigMergerInterface.php new file mode 100644 index 0000000..ec14619 --- /dev/null +++ b/src/Listener/ConfigMergerInterface.php @@ -0,0 +1,37 @@ +getOptions(); + $configListener = $this->getConfigListener(); + $locatorRegistrationListener = new LocatorRegistrationListener($options); + $moduleAutoloader = new ModuleAutoloader($options->getModulePaths()); + + $this->listeners[] = $events->attach('loadModules.pre', array($moduleAutoloader, 'register'), 1000); + $this->listeners[] = $events->attach('loadModule.resolve', new ModuleResolverListener, 1000); + $this->listeners[] = $events->attach('loadModule', new AutoloaderListener($options), 2000); + $this->listeners[] = $events->attach('loadModule', new InitTrigger($options), 1000); + $this->listeners[] = $events->attach('loadModule', new OnBootstrapListener($options), 1000); + $this->listeners[] = $events->attach($locatorRegistrationListener); + $this->listeners[] = $events->attach($configListener); + return $this; + } + + /** + * Detach all previously attached listeners + * + * @param EventManagerInterface $events + * @return void + */ + public function detach(EventManagerInterface $events) + { + foreach ($this->listeners as $key => $listener) { + $detached = false; + if ($listener === $this) { + continue; + } + if ($listener instanceof ListenerAggregateInterface) { + $detached = $listener->detach($events); + } elseif ($listener instanceof CallbackHandler) { + $detached = $events->detach($listener); + } + + if ($detached) { + unset($this->listeners[$key]); + } + } + } + + /** + * Get the config merger. + * + * @return ConfigMergerInterface + */ + public function getConfigListener() + { + if (!$this->configListener instanceof ConfigMergerInterface) { + $this->setConfigListener(new ConfigListener($this->getOptions())); + } + return $this->configListener; + } + + /** + * Set the config merger to use. + * + * @param ConfigMergerInterface $configListener + * @return DefaultListenerAggregate + */ + public function setConfigListener(ConfigMergerInterface $configListener) + { + $this->configListener = $configListener; + return $this; + } +} diff --git a/src/Listener/Exception/ExceptionInterface.php b/src/Listener/Exception/ExceptionInterface.php new file mode 100644 index 0000000..dc52766 --- /dev/null +++ b/src/Listener/Exception/ExceptionInterface.php @@ -0,0 +1,23 @@ +getModule(); + if (!$module instanceof InitProviderInterface + && !method_exists($module, 'init') + ) { + return; + } + + $module->init($e->getTarget()); + } +} diff --git a/src/Listener/ListenerOptions.php b/src/Listener/ListenerOptions.php new file mode 100644 index 0000000..bf518bb --- /dev/null +++ b/src/Listener/ListenerOptions.php @@ -0,0 +1,238 @@ +modulePaths; + } + + /** + * Set an array of paths where modules reside + * + * @param array|Traversable $modulePaths + * @return ListenerOptions + */ + public function setModulePaths($modulePaths) + { + if (!is_array($modulePaths) && !$modulePaths instanceof Traversable) { + throw new Exception\InvalidArgumentException( + sprintf('Argument passed to %s::%s() must be an array, ' + . 'implement the \Traversable interface, or be an ' + . 'instance of Zend\Config\Config. %s given.', + __CLASS__, __METHOD__, gettype($modulePaths)) + ); + } + $this->modulePaths = $modulePaths; + return $this; + } + + /** + * Get the glob patterns to load additional config files + * + * @return array + */ + public function getConfigGlobPaths() + { + return $this->configGlobPaths; + } + + /** + * Get the static paths to load additional config files + * + * @return array + */ + public function getConfigStaticPaths() + { + return $this->configStaticPaths; + } + + /** + * Set the glob patterns to use for loading additional config files + * + * @param array $configGlobPaths + * @return ListenerOptions + */ + public function setConfigGlobPaths($configGlobPaths) + { + if (!is_array($configGlobPaths) && !$configGlobPaths instanceof Traversable) { + throw new Exception\InvalidArgumentException( + sprintf('Argument passed to %s::%s() must be an array, ' + . 'implement the \Traversable interface, or be an ' + . 'instance of Zend\Config\Config. %s given.', + __CLASS__, __METHOD__, gettype($configGlobPaths)) + ); + } + $this->configGlobPaths = $configGlobPaths; + } + + /** + * Set the static paths to use for loading additional config files + * + * @param array $configStaticPaths + * @return ListenerOptions + */ + public function setConfigStaticPaths($configStaticPaths) + { + if (!is_array($configStaticPaths) && !$configStaticPaths instanceof Traversable) { + throw new Exception\InvalidArgumentException( + sprintf('Argument passed to %s::%s() must be an array, ' + . 'implement the \Traversable interface, or be an ' + . 'instance of Zend\Config\Config. %s given.', + __CLASS__, __METHOD__, gettype($configStaticPaths)) + ); + } + $this->configStaticPaths = $configStaticPaths; + } + + /** + * Check if the config cache is enabled + * + * @return bool + */ + public function getConfigCacheEnabled() + { + return $this->configCacheEnabled; + } + + /** + * Set if the config cache should be enabled or not + * + * @param bool $enabled + * @return ListenerOptions + */ + public function setConfigCacheEnabled($enabled) + { + $this->configCacheEnabled = (bool) $enabled; + return $this; + } + + /** + * Get key used to create the cache file name + * + * @return string + */ + public function getConfigCacheKey() + { + return (string) $this->configCacheKey; + } + + /** + * Set key used to create the cache file name + * + * @param string $configCacheKey the value to be set + * @return ListenerOptions + */ + public function setConfigCacheKey($configCacheKey) + { + $this->configCacheKey = $configCacheKey; + return $this; + } + + /** + * Get the path to the config cache + * + * Should this be an option, or should the dir option include the + * filename, or should it simply remain hard-coded? Thoughts? + * + * @return string + */ + public function getConfigCacheFile() + { + return $this->getCacheDir() . '/module-config-cache.'.$this->getConfigCacheKey().'.php'; + } + + /** + * Get the path where cache file(s) are stored + * + * @return string + */ + public function getCacheDir() + { + return $this->cacheDir; + } + + /** + * Set the path where cache files can be stored + * + * @param string $cacheDir the value to be set + * @return ListenerOptions + */ + public function setCacheDir($cacheDir) + { + if (null === $cacheDir) { + $this->cacheDir = $cacheDir; + } else { + $this->cacheDir = static::normalizePath($cacheDir); + } + return $this; + } + + /** + * Normalize a path for insertion in the stack + * + * @param string $path + * @return string + */ + public static function normalizePath($path) + { + $path = rtrim($path, '/'); + $path = rtrim($path, '\\'); + return $path; + } +} diff --git a/src/Listener/LocatorRegistrationListener.php b/src/Listener/LocatorRegistrationListener.php new file mode 100644 index 0000000..65d576a --- /dev/null +++ b/src/Listener/LocatorRegistrationListener.php @@ -0,0 +1,138 @@ +getModule() instanceof LocatorRegisteredInterface) { + return; + } + $this->modules[] = $e->getModule(); + } + + /** + * loadModulesPost + * + * Once all the modules are loaded, loop + * + * @param Event $e + * @return void + */ + public function loadModulesPost(Event $e) + { + $moduleManager = $e->getTarget(); + $events = $moduleManager->events()->getSharedManager(); + + // Shared instance for module manager + $events->attach('application', 'bootstrap', function ($e) use ($moduleManager) { + $moduleClassName = get_class($moduleManager); + $application = $e->getApplication(); + $services = $application->getServiceManager(); + if (!$services->has($moduleClassName)) { + $services->setService($moduleClassName, $moduleManager); + } + }, 1000); + + if (0 === count($this->modules)) { + return; + } + + // Attach to the bootstrap event if there are modules we need to process + $events->attach('application', 'bootstrap', array($this, 'onBootstrap'), 1000); + } + + /** + * Bootstrap listener + * + * This is ran during the MVC bootstrap event because it requires access to + * the DI container. + * + * @TODO: Check the application / locator / etc a bit better to make sure + * the env looks how we're expecting it to? + * @param Event $e + * @return void + */ + public function onBootstrap(Event $e) + { + $application = $e->getApplication(); + $services = $application->getServiceManager(); + + foreach ($this->modules as $module) { + $moduleClassName = get_class($module); + if (!$services->has($moduleClassName)) { + $services->setService($moduleClassName, $module); + } + } + } + + /** + * Attach one or more listeners + * + * @param EventManagerInterface $events + * @return void + */ + public function attach(EventManagerInterface $events) + { + $this->listeners[] = $events->attach('loadModule', array($this, 'loadModule'), 1000); + $this->listeners[] = $events->attach('loadModules.post', array($this, 'loadModulesPost'), 9000); + return $this; + } + + /** + * Detach all previously attached listeners + * + * @param EventManagerInterface $events + * @return void + */ + public function detach(EventManagerInterface $events) + { + foreach ($this->listeners as $key => $listener) { + if ($events->detach($listener)) { + unset($this->listeners[$key]); + } + } + } +} diff --git a/src/Listener/ModuleResolverListener.php b/src/Listener/ModuleResolverListener.php new file mode 100644 index 0000000..5093f30 --- /dev/null +++ b/src/Listener/ModuleResolverListener.php @@ -0,0 +1,38 @@ +getModuleName(); + $class = $moduleName . '\Module'; + + if (!class_exists($class)) { + return false; + } + + $module = new $class; + return $module; + } +} diff --git a/src/Listener/OnBootstrapListener.php b/src/Listener/OnBootstrapListener.php new file mode 100644 index 0000000..087c2f5 --- /dev/null +++ b/src/Listener/OnBootstrapListener.php @@ -0,0 +1,44 @@ +getModule(); + if (!$module instanceof BootstrapListenerInterface + && !method_exists($module, 'onBootstrap') + ) { + return; + } + + $moduleManager = $e->getTarget(); + $events = $moduleManager->events(); + $sharedEvents = $events->getSharedManager(); + $sharedEvents->attach('application', 'bootstrap', array($module, 'onBootstrap')); + } +} diff --git a/src/Listener/ServiceListener.php b/src/Listener/ServiceListener.php new file mode 100644 index 0000000..72f8cff --- /dev/null +++ b/src/Listener/ServiceListener.php @@ -0,0 +1,208 @@ + array(), + 'aliases' => array(), + 'factories' => array(), + 'invokables' => array(), + 'services' => array(), + 'shared' => array(), + ); + + /** + * @var ServiceLocatorInterface + */ + protected $services; + + /** + * @param ServiceLocatorInterface $services + * @return void + */ + public function __construct(ServiceLocatorInterface $services) + { + $this->services = $services; + } + + /** + * @param EventManagerInterface $events + * @return void + */ + public function attach(EventManagerInterface $events) + { + $this->listeners[] = $events->attach('loadModule', array($this, 'onLoadModule'), 1500); + $this->listeners[] = $events->attach('loadModules.post', array($this, 'onLoadModulesPost'), 8500); + return $this; + } + + /** + * @param EventManagerInterface $events + * @return void + */ + public function detach(EventManagerInterface $events) + { + foreach ($this->listeners as $key => $listener) { + if ($events->detach($listener)) { + unset($this->listeners[$key]); + } + } + } + + /** + * Retrieve service manager configuration from module, and + * configure the service manager. + * + * If the module does not implement ServiceProviderInterface and does not + * implement the "getServiceConfiguration()" method, does nothing. Also, + * if the return value of that method is not a ServiceConfiguration object, + * or not an array or Traversable that can seed one, does nothing. + * + * @param ModuleEvent $e + * @return void + */ + public function onLoadModule(ModuleEvent $e) + { + $module = $e->getModule(); + if (!$module instanceof ServiceProviderInterface + && !method_exists($module, 'getServiceConfiguration') + ) { + return; + } + + $config = $module->getServiceConfiguration(); + + if ($config instanceof ServiceConfiguration) { + $this->mergeServiceConfiguration($config); + return; + } + + if ($config instanceof Traversable) { + $config = ArrayUtils::iteratorToArray($config); + } + + if (!is_array($config)) { + // If we don't have an array by this point, nothing left to do. + return; + } + + $this->serviceConfig = ArrayUtils::merge($this->serviceConfig, $config); + } + + /** + * Use merged configuration to configure service manager + * + * If the merged configuration has a non-empty, array 'service_manager' + * key, it will be passed to a ServiceManager Configuration object, and + * used to configure the service manager. + * + * @param ModuleEvent $e + * @return void + */ + public function onLoadModulesPost(ModuleEvent $e) + { + $configListener = $e->getConfigListener(); + $config = $configListener->getMergedConfig(false); + if (isset($config['service_manager']) + && is_array($config['service_manager']) + && !empty($config['service_manager']) + ) { + $this->serviceConfig = ArrayUtils::merge($this->serviceConfig, $config['service_manager']); + } + + $this->configureServiceManager(); + } + + /** + * Configure the service manager + * + * Configures the service manager based on the internal, merged + * service configuration. + * + * @return void + */ + public function configureServiceManager() + { + if ($this->configured) { + // Don't configure twice + return; + } + $serviceConfig = new ServiceConfiguration($this->serviceConfig); + $serviceConfig->configureServiceManager($this->services); + $this->configured = true; + } + + /** + * Merge a service configuration container + * + * Extracts the various service configuration arrays, and then merges with + * the internal service configuration. + * + * @param ServiceConfiguration $config + * @return void + */ + protected function mergeServiceConfiguration(ServiceConfiguration $config) + { + $serviceConfig = array( + 'abstract_factories' => $config->getAbstractFactories(), + 'aliases' => $config->getAliases(), + 'factories' => $config->getFactories(), + 'invokables' => $config->getInvokables(), + 'services' => $config->getServices(), + 'shared' => $config->getShared(), + ); + $this->serviceConfig = ArrayUtils::merge($this->serviceConfig, $serviceConfig); + } +} diff --git a/src/ModuleEvent.php b/src/ModuleEvent.php new file mode 100644 index 0000000..deb1c0e --- /dev/null +++ b/src/ModuleEvent.php @@ -0,0 +1,101 @@ +getParam('moduleName'); + } + + /** + * Set the name of a given module + * + * @param string $moduleName + * @return ModuleEvent + */ + public function setModuleName($moduleName) + { + if (!is_string($moduleName)) { + throw new Exception\InvalidArgumentException(sprintf( + '%s expects a string as an argument; %s provided' + ,__METHOD__, gettype($moduleName) + )); + } + $this->setParam('moduleName', $moduleName); + return $this; + } + + /** + * Get module object + * + * @return null|object + */ + public function getModule() + { + return $this->getParam('module'); + } + + /** + * Set module object to compose in this event + * + * @param object $module + * @return ModuleEvent + */ + public function setModule($module) + { + if (!is_object($module)) { + throw new Exception\InvalidArgumentException(sprintf( + '%s expects a module object as an argument; %s provided' + ,__METHOD__, gettype($module) + )); + } + $this->setParam('module', $module); + return $this; + } + + /** + * Get the config listner + * + * @return null|Listener\ConfigMergerInterface + */ + public function getConfigListener() + { + return $this->getParam('configListener'); + } + + /** + * Set module object to compose in this event + * + * @param Listener\ConfigMergerInterface $configListener + * @return ModuleEvent + */ + public function setConfigListener(Listener\ConfigMergerInterface $configListener) + { + $this->setParam('configListener', $configListener); + return $this; + } +} diff --git a/src/ModuleManager.php b/src/ModuleManager.php new file mode 100644 index 0000000..2f70f5e --- /dev/null +++ b/src/ModuleManager.php @@ -0,0 +1,243 @@ +setModules($modules); + if ($eventManager instanceof EventManagerInterface) { + $this->setEventManager($eventManager); + } + } + + /** + * Load the provided modules. + * + * @triggers loadModules.pre + * @triggers loadModules.post + * @return ModuleManager + */ + public function loadModules() + { + if (true === $this->modulesAreLoaded) { + return $this; + } + + $this->events()->trigger(__FUNCTION__ . '.pre', $this, $this->getEvent()); + + foreach ($this->getModules() as $moduleName) { + $this->loadModule($moduleName); + } + + $this->events()->trigger(__FUNCTION__ . '.post', $this, $this->getEvent()); + + $this->modulesAreLoaded = true; + return $this; + } + + /** + * Load a specific module by name. + * + * @param string $moduleName + * @triggers loadModule.resolve + * @triggers loadModule + * @return mixed Module's Module class + */ + public function loadModule($moduleName) + { + if (isset($this->loadedModules[$moduleName])) { + return $this->loadedModules[$moduleName]; + } + + $event = $this->getEvent(); + $event->setModuleName($moduleName); + + $result = $this->events()->trigger(__FUNCTION__ . '.resolve', $this, $event, function ($r) { + return (is_object($r)); + }); + + $module = $result->last(); + + if (!is_object($module)) { + throw new Exception\RuntimeException(sprintf( + 'Module (%s) could not be initialized.', + $moduleName + )); + } + $event->setModule($module); + + $this->events()->trigger(__FUNCTION__, $this, $event); + $this->loadedModules[$moduleName] = $module; + return $module; + } + + /** + * Get an array of the loaded modules. + * + * @param bool $loadModules If true, load modules if they're not already + * @return array An array of Module objects, keyed by module name + */ + public function getLoadedModules($loadModules = false) + { + if (true === $loadModules) { + $this->loadModules(); + } + return $this->loadedModules; + } + + /** + * Get an instance of a module class by the module name + * + * @param string $moduleName + * @return mixed + */ + public function getModule($moduleName) + { + if (!isset($this->loadedModules[$moduleName])) { + return null; + } + return $this->loadedModules[$moduleName]; + } + + /** + * Get the array of module names that this manager should load. + * + * @return array + */ + public function getModules() + { + return $this->modules; + } + + /** + * Set an array or Traversable of module names that this module manager should load. + * + * @param mixed $modules array or Traversable of module names + * @return ModuleManager + */ + public function setModules($modules) + { + if (is_array($modules) || $modules instanceof Traversable) { + $this->modules = $modules; + } else { + throw new Exception\InvalidArgumentException(sprintf( + 'Parameter to %s\'s %s method must be an array or implement the \\Traversable interface', + __CLASS__, __METHOD__ + )); + } + return $this; + } + + /** + * Get the module event + * + * @return ModuleEvent + */ + public function getEvent() + { + if (!$this->event instanceof ModuleEvent) { + $this->setEvent(new ModuleEvent); + } + return $this->event; + } + + /** + * Set the module event + * + * @param ModuleEvent $event + * @return ModuleManager + */ + public function setEvent(ModuleEvent $event) + { + $this->event = $event; + return $this; + } + + /** + * Set the event manager instance used by this module manager. + * + * @param EventManagerInterface $events + * @return ModuleManager + */ + public function setEventManager(EventManagerInterface $events) + { + $events->setIdentifiers(array( + __CLASS__, + get_called_class(), + 'module_manager', + )); + $this->events = $events; + return $this; + } + + /** + * Retrieve the event manager + * + * Lazy-loads an EventManager instance if none registered. + * + * @return EventManagerInterface + */ + public function events() + { + if (!$this->events instanceof EventManagerInterface) { + $this->setEventManager(new EventManager()); + } + return $this->events; + } +} diff --git a/src/ModuleManagerInterface.php b/src/ModuleManagerInterface.php new file mode 100644 index 0000000..a041d34 --- /dev/null +++ b/src/ModuleManagerInterface.php @@ -0,0 +1,61 @@ +loaders = spl_autoload_functions(); + if (!is_array($this->loaders)) { + // spl_autoload_functions does not return empty array when no + // autoloaders registered... + $this->loaders = array(); + } + + // Store original include_path + $this->includePath = get_include_path(); + + $autoloader = new ModuleAutoloader(array( + dirname(__DIR__) . '/TestAsset', + )); + $autoloader->register(); + + $this->moduleManager = new ModuleManager(array()); + $this->moduleManager->events()->attach('loadModule.resolve', new ModuleResolverListener, 1000); + $this->moduleManager->events()->attach('loadModule', new AutoloaderListener, 2000); + } + + public function tearDown() + { + // Restore original autoloaders + AutoloaderFactory::unregisterAutoloaders(); + $loaders = spl_autoload_functions(); + if (is_array($loaders)) { + foreach ($loaders as $loader) { + spl_autoload_unregister($loader); + } + } + + foreach ($this->loaders as $loader) { + spl_autoload_register($loader); + } + + // Restore original include_path + set_include_path($this->includePath); + } + + public function testAutoloadersRegisteredByAutoloaderListener() + { + $moduleManager = $this->moduleManager; + $moduleManager->setModules(array('ListenerTestModule')); + $moduleManager->loadModules(); + $modules = $moduleManager->getLoadedModules(); + $this->assertTrue($modules['ListenerTestModule']->getAutoloaderConfigCalled); + $this->assertTrue(class_exists('Foo\Bar')); + } + + public function testAutoloadersRegisteredIfModuleDoesNotInheritAutoloaderProviderInterfaceButDefinesGetAutoloaderConfigMethod() + { + $moduleManager = $this->moduleManager; + $moduleManager->setModules(array('NotAutoloaderModule')); + $moduleManager->loadModules(); + $modules = $moduleManager->getLoadedModules(); + $this->assertTrue($modules['NotAutoloaderModule']->getAutoloaderConfigCalled); + $this->assertTrue(class_exists('Foo\Bar')); + } +} diff --git a/test/Listener/ConfigListenerTest.php b/test/Listener/ConfigListenerTest.php new file mode 100644 index 0000000..f080c40 --- /dev/null +++ b/test/Listener/ConfigListenerTest.php @@ -0,0 +1,397 @@ +tmpdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'zend_module_cache_dir'; + @mkdir($this->tmpdir); + $this->configCache = $this->tmpdir . DIRECTORY_SEPARATOR . 'config.cache.php'; + // Store original autoloaders + $this->loaders = spl_autoload_functions(); + if (!is_array($this->loaders)) { + // spl_autoload_functions does not return empty array when no + // autoloaders registered... + $this->loaders = array(); + } + + // Store original include_path + $this->includePath = get_include_path(); + + $autoloader = new ModuleAutoloader(array( + dirname(__DIR__) . '/TestAsset', + )); + $autoloader->register(); + + $this->moduleManager = new ModuleManager(array()); + $this->moduleManager->events()->attach('loadModule.resolve', new ModuleResolverListener, 1000); + } + + public function tearDown() + { + $file = glob($this->tmpdir . DIRECTORY_SEPARATOR . '*'); + @unlink($file[0]); // change this if there's ever > 1 file + @rmdir($this->tmpdir); + // Restore original autoloaders + AutoloaderFactory::unregisterAutoloaders(); + $loaders = spl_autoload_functions(); + if (is_array($loaders)) { + foreach ($loaders as $loader) { + spl_autoload_unregister($loader); + } + } + + foreach ($this->loaders as $loader) { + spl_autoload_register($loader); + } + + // Restore original include_path + set_include_path($this->includePath); + } + + public function testMultipleConfigsAreMerged() + { + $configListener = new ConfigListener; + + $moduleManager = $this->moduleManager; + $moduleManager->events()->attach('loadModule', $configListener); + $moduleManager->setModules(array('SomeModule', 'ListenerTestModule')); + $moduleManager->loadModules(); + + $config = $configListener->getMergedConfig(false); + $this->assertSame(2, count($config)); + $this->assertSame('test', $config['listener']); + $this->assertSame('thing', $config['some']); + $configObject = $configListener->getMergedConfig(); + $this->assertInstanceOf('Zend\Config\Config', $configObject); + } + + public function testCanCacheMergedConfig() + { + $options = new ListenerOptions(array( + 'cache_dir' => $this->tmpdir, + 'config_cache_enabled' => true, + )); + $configListener = new ConfigListener($options); + + $moduleManager = $this->moduleManager; + $moduleManager->setModules(array('SomeModule', 'ListenerTestModule')); + $moduleManager->events()->attach('loadModule', $configListener); + $moduleManager->loadModules(); // This should cache the config + + $modules = $moduleManager->getLoadedModules(); + $this->assertTrue($modules['ListenerTestModule']->getConfigCalled); + + // Now we check to make sure it uses the config and doesn't hit + // the module objects getConfig() method(s) + $moduleManager = new ModuleManager(array('SomeModule', 'ListenerTestModule')); + $moduleManager->events()->attach('loadModule.resolve', new ModuleResolverListener, 1000); + $configListener = new ConfigListener($options); + $moduleManager->events()->attach('loadModule', $configListener); + $moduleManager->loadModules(); + $modules = $moduleManager->getLoadedModules(); + $this->assertFalse($modules['ListenerTestModule']->getConfigCalled); + } + + public function testBadConfigValueThrowsInvalidArgumentException() + { + $this->setExpectedException('InvalidArgumentException'); + + $configListener = new ConfigListener; + + $moduleManager = $this->moduleManager; + $moduleManager->setModules(array('BadConfigModule', 'SomeModule')); + $moduleManager->events()->attach('loadModule', $configListener); + $moduleManager->loadModules(); + } + + public function testBadGlobPathTrowsInvalidArgumentException() + { + $this->setExpectedException('InvalidArgumentException'); + $configListener = new ConfigListener; + $configListener->addConfigGlobPath(array('asd')); + } + + public function testBadGlobPathArrayTrowsInvalidArgumentException() + { + $this->setExpectedException('InvalidArgumentException'); + $configListener = new ConfigListener; + $configListener->addConfigGlobPaths('asd'); + } + + public function testBadStaticPathArrayTrowsInvalidArgumentException() + { + $this->setExpectedException('InvalidArgumentException'); + $configListener = new ConfigListener; + $configListener->addConfigStaticPaths('asd'); + } + + public function testCanMergeConfigFromGlob() + { + $configListener = new ConfigListener; + $configListener->addConfigGlobPath(__DIR__ . '/_files/good/*.{ini,php,xml}'); + + $moduleManager = $this->moduleManager; + $moduleManager->setModules(array('SomeModule')); + + $moduleManager->events()->attachAggregate($configListener); + + $moduleManager->loadModules(); + $configObjectCheck = $configListener->getMergedConfig(); + + // Test as object + $configObject = $configListener->getMergedConfig(); + $this->assertSame(spl_object_hash($configObjectCheck), spl_object_hash($configObject)); + $this->assertSame('loaded', $configObject->ini); + $this->assertSame('loaded', $configObject->php); + $this->assertSame('loaded', $configObject->xml); + // Test as array + $config = $configListener->getMergedConfig(false); + $this->assertSame('loaded', $config['ini']); + $this->assertSame('loaded', $config['php']); + $this->assertSame('loaded', $config['xml']); + } + + public function testCanMergeConfigFromStaticPath() + { + $configListener = new ConfigListener; + $configListener->addConfigStaticPath(__DIR__ . '/_files/good/config.ini'); + $configListener->addConfigStaticPath(__DIR__ . '/_files/good/config.php'); + $configListener->addConfigStaticPath(__DIR__ . '/_files/good/config.xml'); + + $moduleManager = $this->moduleManager; + $moduleManager->setModules(array('SomeModule')); + + $moduleManager->events()->attachAggregate($configListener); + + $moduleManager->loadModules(); + $configObjectCheck = $configListener->getMergedConfig(); + + // Test as object + $configObject = $configListener->getMergedConfig(); + $this->assertSame(spl_object_hash($configObjectCheck), spl_object_hash($configObject)); + $this->assertSame('loaded', $configObject->ini); + $this->assertSame('loaded', $configObject->php); + $this->assertSame('loaded', $configObject->xml); + // Test as array + $config = $configListener->getMergedConfig(false); + $this->assertSame('loaded', $config['ini']); + $this->assertSame('loaded', $config['php']); + $this->assertSame('loaded', $config['xml']); + } + + public function testCanMergeConfigFromStaticPaths() + { + $configListener = new ConfigListener; + $configListener->addConfigStaticPaths(array( + __DIR__ . '/_files/good/config.ini', + __DIR__ . '/_files/good/config.php', + __DIR__ . '/_files/good/config.xml') + ); + + $moduleManager = $this->moduleManager; + $moduleManager->setModules(array('SomeModule')); + + $moduleManager->events()->attachAggregate($configListener); + + $moduleManager->loadModules(); + $configObjectCheck = $configListener->getMergedConfig(); + + // Test as object + $configObject = $configListener->getMergedConfig(); + $this->assertSame(spl_object_hash($configObjectCheck), spl_object_hash($configObject)); + $this->assertSame('loaded', $configObject->ini); + $this->assertSame('loaded', $configObject->php); + $this->assertSame('loaded', $configObject->xml); + // Test as array + $config = $configListener->getMergedConfig(false); + $this->assertSame('loaded', $config['ini']); + $this->assertSame('loaded', $config['php']); + $this->assertSame('loaded', $config['xml']); + } + + public function testCanCacheMergedConfigFromGlob() + { + $options = new ListenerOptions(array( + 'cache_dir' => $this->tmpdir, + 'config_cache_enabled' => true, + )); + $configListener = new ConfigListener($options); + $configListener->addConfigGlobPath(__DIR__ . '/_files/good/*.{ini,php,xml}'); + + $moduleManager = $this->moduleManager; + $moduleManager->setModules(array('SomeModule')); + + $moduleManager->events()->attachAggregate($configListener); + + $moduleManager->loadModules(); + $configObjectFromGlob = $configListener->getMergedConfig(); + + // This time, don't add the glob path + $configListener = new ConfigListener($options); + $moduleManager = new ModuleManager(array('SomeModule')); + $moduleManager->events()->attach('loadModule.resolve', new ModuleResolverListener, 1000); + + $moduleManager->events()->attachAggregate($configListener); + + $moduleManager->loadModules(); + + // Check if values from glob object and cache object are the same + $configObjectFromCache = $configListener->getMergedConfig(); + $this->assertNotNull($configObjectFromGlob->ini); + $this->assertSame($configObjectFromGlob->ini, $configObjectFromCache->ini); + $this->assertNotNull($configObjectFromGlob->php); + $this->assertSame($configObjectFromGlob->php, $configObjectFromCache->php); + $this->assertNotNull($configObjectFromGlob->xml); + $this->assertSame($configObjectFromGlob->xml, $configObjectFromCache->xml); + } + + public function testCanCacheMergedConfigFromStatic() + { + $options = new ListenerOptions(array( + 'cache_dir' => $this->tmpdir, + 'config_cache_enabled' => true, + )); + $configListener = new ConfigListener($options); + $configListener->addConfigStaticPaths(array( + __DIR__ . '/_files/good/config.ini', + __DIR__ . '/_files/good/config.php', + __DIR__ . '/_files/good/config.xml') + ); + + $moduleManager = $this->moduleManager; + $moduleManager->setModules(array('SomeModule')); + + $moduleManager->events()->attachAggregate($configListener); + + $moduleManager->loadModules(); + $configObjectFromGlob = $configListener->getMergedConfig(); + + // This time, don't add the glob path + $configListener = new ConfigListener($options); + $moduleManager = new ModuleManager(array('SomeModule')); + $moduleManager->events()->attach('loadModule.resolve', new ModuleResolverListener, 1000); + + $moduleManager->events()->attachAggregate($configListener); + + $moduleManager->loadModules(); + + // Check if values from glob object and cache object are the same + $configObjectFromCache = $configListener->getMergedConfig(); + $this->assertNotNull($configObjectFromGlob->ini); + $this->assertSame($configObjectFromGlob->ini, $configObjectFromCache->ini); + $this->assertNotNull($configObjectFromGlob->php); + $this->assertSame($configObjectFromGlob->php, $configObjectFromCache->php); + $this->assertNotNull($configObjectFromGlob->xml); + $this->assertSame($configObjectFromGlob->xml, $configObjectFromCache->xml); + } + + public function testCanMergeConfigFromArrayOfGlobs() + { + $configListener = new ConfigListener; + $configListener->addConfigGlobPaths(new ArrayObject(array( + __DIR__ . '/_files/good/*.ini', + __DIR__ . '/_files/good/*.php', + __DIR__ . '/_files/good/*.xml', + ))); + + $moduleManager = $this->moduleManager; + $moduleManager->setModules(array('SomeModule')); + + $moduleManager->events()->attachAggregate($configListener); + $moduleManager->loadModules(); + + // Test as object + $configObject = $configListener->getMergedConfig(); + $this->assertSame('loaded', $configObject->ini); + $this->assertSame('loaded', $configObject->php); + $this->assertSame('loaded', $configObject->xml); + } + + public function testCanMergeConfigFromArrayOfStatic() + { + $configListener = new ConfigListener; + $configListener->addConfigStaticPaths(new ArrayObject(array( + __DIR__ . '/_files/good/config.ini', + __DIR__ . '/_files/good/config.php', + __DIR__ . '/_files/good/config.xml', + ))); + + $moduleManager = $this->moduleManager; + $moduleManager->setModules(array('SomeModule')); + + $moduleManager->events()->attachAggregate($configListener); + $moduleManager->loadModules(); + + // Test as object + $configObject = $configListener->getMergedConfig(); + $this->assertSame('loaded', $configObject->ini); + $this->assertSame('loaded', $configObject->php); + $this->assertSame('loaded', $configObject->xml); + } + + public function testMergesWithMergeAndReplaceBehavior() + { + $configListener = new ConfigListener(); + + $moduleManager = $this->moduleManager; + $moduleManager->setModules(array('SomeModule')); + + $configListener->addConfigStaticPaths(array( + __DIR__ . '/_files/good/merge1.php', + __DIR__ . '/_files/good/merge2.php', + )); + + $moduleManager->events()->attachAggregate($configListener); + $moduleManager->loadModules(); + + $mergedConfig = $configListener->getMergedConfig(false); + $this->assertSame(array('foo', 'bar'), $mergedConfig['indexed']); + $this->assertSame('bar', $mergedConfig['keyed']); + } + + public function testConfigListenerFunctionsAsAggregateListener() + { + $configListener = new ConfigListener; + + $moduleManager = $this->moduleManager; + $this->assertEquals(1, count($moduleManager->events()->getEvents())); + + $configListener->attach($moduleManager->events()); + $this->assertEquals(4, count($moduleManager->events()->getEvents())); + + $configListener->detach($moduleManager->events()); + $this->assertEquals(1, count($moduleManager->events()->getEvents())); + } +} diff --git a/test/Listener/DefaultListenerAggregateTest.php b/test/Listener/DefaultListenerAggregateTest.php new file mode 100644 index 0000000..4e7a507 --- /dev/null +++ b/test/Listener/DefaultListenerAggregateTest.php @@ -0,0 +1,128 @@ +loaders = spl_autoload_functions(); + if (!is_array($this->loaders)) { + // spl_autoload_functions does not return empty array when no + // autoloaders registered... + $this->loaders = array(); + } + + // Store original include_path + $this->includePath = get_include_path(); + + $this->defaultListeners = new DefaultListenerAggregate( + new ListenerOptions(array( + 'module_paths' => array( + realpath(__DIR__ . '/TestAsset'), + ), + )) + ); + } + + public function tearDown() + { + // Restore original autoloaders + AutoloaderFactory::unregisterAutoloaders(); + $loaders = spl_autoload_functions(); + if (is_array($loaders)) { + foreach ($loaders as $loader) { + spl_autoload_unregister($loader); + } + } + + foreach ($this->loaders as $loader) { + spl_autoload_register($loader); + } + + // Restore original include_path + set_include_path($this->includePath); + } + + public function testDefaultListenerAggregateCanAttachItself() + { + $moduleManager = new ModuleManager(array('ListenerTestModule')); + $moduleManager->events()->attachAggregate(new DefaultListenerAggregate); + + $events = $moduleManager->events()->getEvents(); + $expectedEvents = array( + 'loadModules.pre' => array( + 'Zend\Loader\ModuleAutoloader', + 'Zend\ModuleManager\Listener\ConfigListener', + ), + 'loadModule.resolve' => array( + 'Zend\ModuleManager\Listener\ModuleResolverListener', + ), + 'loadModule' => array( + 'Zend\ModuleManager\Listener\AutoloaderListener', + 'Zend\ModuleManager\Listener\InitTrigger', + 'Zend\ModuleManager\Listener\OnBootstrapListener', + 'Zend\ModuleManager\Listener\ConfigListener', + 'Zend\ModuleManager\Listener\LocatorRegistrationListener', + ), + 'loadModules.post' => array( + 'Zend\ModuleManager\Listener\ConfigListener', + 'Zend\ModuleManager\Listener\LocatorRegistrationListener', + ), + ); + foreach ($expectedEvents as $event => $expectedListeners) { + $this->assertContains($event, $events); + $listeners = $moduleManager->events()->getListeners($event); + $this->assertSame(count($expectedListeners), count($listeners)); + foreach ($listeners as $listener) { + $callback = $listener->getCallback(); + if (is_array($callback)) { + $callback = $callback[0]; + } + $listenerClass = get_class($callback); + $this->assertContains($listenerClass, $expectedListeners); + } + } + } + + public function testDefaultListenerAggregateCanDetachItself() + { + $listenerAggregate = new DefaultListenerAggregate; + $moduleManager = new ModuleManager(array('ListenerTestModule')); + + $listenerAggregate->attach($moduleManager->events()); + $this->assertEquals(4, count($moduleManager->events()->getEvents())); + + $listenerAggregate->detach($moduleManager->events()); + $this->assertEquals(0, count($moduleManager->events()->getEvents())); + } +} diff --git a/test/Listener/InitTriggerTest.php b/test/Listener/InitTriggerTest.php new file mode 100644 index 0000000..41af809 --- /dev/null +++ b/test/Listener/InitTriggerTest.php @@ -0,0 +1,83 @@ +loaders = spl_autoload_functions(); + if (!is_array($this->loaders)) { + // spl_autoload_functions does not return empty array when no + // autoloaders registered... + $this->loaders = array(); + } + + // Store original include_path + $this->includePath = get_include_path(); + + $autoloader = new ModuleAutoloader(array( + dirname(__DIR__) . '/TestAsset', + )); + $autoloader->register(); + + $this->moduleManager = new ModuleManager(array()); + $this->moduleManager->events()->attach('loadModule.resolve', new ModuleResolverListener, 1000); + $this->moduleManager->events()->attach('loadModule', new InitTrigger, 2000); + } + + public function tearDown() + { + // Restore original autoloaders + AutoloaderFactory::unregisterAutoloaders(); + $loaders = spl_autoload_functions(); + if (is_array($loaders)) { + foreach ($loaders as $loader) { + spl_autoload_unregister($loader); + } + } + + foreach ($this->loaders as $loader) { + spl_autoload_register($loader); + } + + // Restore original include_path + set_include_path($this->includePath); + } + + public function testInitMethodCalledByInitTriggerListener() + { + $moduleManager = $this->moduleManager; + $moduleManager->setModules(array('ListenerTestModule')); + $moduleManager->loadModules(); + $modules = $moduleManager->getLoadedModules(); + $this->assertTrue($modules['ListenerTestModule']->initCalled); + } +} diff --git a/test/Listener/ListenerOptionsTest.php b/test/Listener/ListenerOptionsTest.php new file mode 100644 index 0000000..b415e8d --- /dev/null +++ b/test/Listener/ListenerOptionsTest.php @@ -0,0 +1,107 @@ + __DIR__, + 'config_cache_enabled' => true, + 'config_cache_key' => 'foo', + 'module_paths' => array('module','paths'), + 'config_glob_paths' => array('glob','paths'), + 'config_static_paths' => array('static','custom_paths'), + )); + $this->assertSame($options->getCacheDir(), __DIR__); + $this->assertTrue($options->getConfigCacheEnabled()); + $this->assertNotNull(strstr($options->getConfigCacheFile(), __DIR__)); + $this->assertNotNull(strstr($options->getConfigCacheFile(), '.php')); + $this->assertSame('foo', $options->getConfigCacheKey()); + $this->assertSame(array('module', 'paths'), $options->getModulePaths()); + $this->assertSame(array('glob', 'paths'), $options->getConfigGlobPaths()); + $this->assertSame(array('static', 'custom_paths'), $options->getConfigStaticPaths()); + } + + public function testCanAccessKeysAsProperties() + { + $options = new ListenerOptions(array( + 'cache_dir' => __DIR__, + 'config_cache_enabled' => true, + 'config_cache_key' => 'foo', + 'module_paths' => array('module','paths'), + 'config_glob_paths' => array('glob','paths'), + 'config_static_paths' => array('static','custom_paths'), + )); + $this->assertSame($options->cache_dir, __DIR__); + $options->cache_dir = 'foo'; + $this->assertSame($options->cache_dir, 'foo'); + $this->assertTrue(isset($options->cache_dir)); + unset($options->cache_dir); + $this->assertFalse(isset($options->cache_dir)); + + $this->assertTrue($options->config_cache_enabled); + $options->config_cache_enabled = false; + $this->assertFalse($options->config_cache_enabled); + $this->assertEquals('foo', $options->config_cache_key); + $this->assertSame(array('module', 'paths'), $options->module_paths); + $this->assertSame(array('glob', 'paths'), $options->config_glob_paths); + $this->assertSame(array('static', 'custom_paths'), $options->config_static_paths); + } + + public function testSetModulePathsAcceptsConfigOrTraverable() + { + $config = new Config(array(__DIR__)); + $options = new ListenerOptions; + $options->setModulePaths($config); + $this->assertSame($config, $options->getModulePaths()); + } + + public function testSetModulePathsThrowsInvalidArgumentException() + { + $this->setExpectedException('InvalidArgumentException'); + $options = new ListenerOptions; + $options->setModulePaths('asd'); + } + + public function testSetConfigGlobPathsAcceptsConfigOrTraverable() + { + $config = new Config(array(__DIR__)); + $options = new ListenerOptions; + $options->setConfigGlobPaths($config); + $this->assertSame($config, $options->getConfigGlobPaths()); + } + + public function testSetConfigGlobPathsThrowsInvalidArgumentException() + { + $this->setExpectedException('InvalidArgumentException'); + $options = new ListenerOptions; + $options->setConfigGlobPaths('asd'); + } +} diff --git a/test/Listener/LocatorRegistrationListenerTest.php b/test/Listener/LocatorRegistrationListenerTest.php new file mode 100644 index 0000000..b43bbd0 --- /dev/null +++ b/test/Listener/LocatorRegistrationListenerTest.php @@ -0,0 +1,136 @@ +loaders = spl_autoload_functions(); + if (!is_array($this->loaders)) { + // spl_autoload_functions does not return empty array when no + // autoloaders registered... + $this->loaders = array(); + } + + // Store original include_path + $this->includePath = get_include_path(); + + $autoloader = new ModuleAutoloader(array( + dirname(__DIR__) . '/TestAsset', + )); + $autoloader->register(); + + $this->sharedEvents = new SharedEventManager(); + + $this->moduleManager = new ModuleManager(array('ListenerTestModule')); + $this->moduleManager->events()->setSharedManager($this->sharedEvents); + $this->moduleManager->events()->attach('loadModule.resolve', new ModuleResolverListener, 1000); + + $this->application = new MockApplication; + $events = new EventManager(array('Zend\Mvc\Application', 'ZendTest\Module\TestAsset\MockApplication', 'application')); + $events->setSharedManager($this->sharedEvents); + $this->application->setEventManager($events); + + $this->serviceManager = new ServiceManager(); + $this->serviceManager->setService('ModuleManager', $this->moduleManager); + $this->application->setServiceManager($this->serviceManager); + } + + public function tearDown() + { + // Restore original autoloaders + AutoloaderFactory::unregisterAutoloaders(); + $loaders = spl_autoload_functions(); + if (is_array($loaders)) { + foreach ($loaders as $loader) { + spl_autoload_unregister($loader); + } + } + + foreach ($this->loaders as $loader) { + spl_autoload_register($loader); + } + + // Restore original include_path + set_include_path($this->includePath); + } + + public function testModuleClassIsRegisteredWithDiAndInjectedWithSharedInstances() + { + $locator = $this->serviceManager; + $locator->setFactory('Foo\Bar', function($s) { + $module = $s->get('ListenerTestModule\Module'); + $manager = $s->get('Zend\ModuleManager\ModuleManager'); + $instance = new \Foo\Bar($module, $manager); + return $instance; + }); + + $locatorRegistrationListener = new LocatorRegistrationListener; + $this->moduleManager->events()->attachAggregate($locatorRegistrationListener); + $test = $this; + $this->moduleManager->events()->attach('loadModule', function ($e) use ($test) { + $test->module = $e->getModule(); + }, -1000); + $this->moduleManager->loadModules(); + + $this->application->bootstrap(); + $sharedInstance1 = $locator->get('ListenerTestModule\Module'); + $sharedInstance2 = $locator->get('Zend\ModuleManager\ModuleManager'); + + $this->assertInstanceOf('ListenerTestModule\Module', $sharedInstance1); + $foo = false; + $message = ''; + try { + $foo = $locator->get('Foo\Bar'); + } catch (\Exception $e) { + $message = $e->getMessage(); + while ($e = $e->getPrevious()) { + $message .= "\n" . $e->getMessage(); + } + } + if (!$foo) { + $this->fail($message); + } + $this->assertSame($this->module, $foo->module); + + $this->assertInstanceOf('Zend\ModuleManager\ModuleManager', $sharedInstance2); + $this->assertSame($this->moduleManager, $locator->get('Foo\Bar')->moduleManager); + } +} diff --git a/test/Listener/ModuleResolverListenerTest.php b/test/Listener/ModuleResolverListenerTest.php new file mode 100644 index 0000000..834ae4a --- /dev/null +++ b/test/Listener/ModuleResolverListenerTest.php @@ -0,0 +1,81 @@ +loaders = spl_autoload_functions(); + if (!is_array($this->loaders)) { + // spl_autoload_functions does not return empty array when no + // autoloaders registered... + $this->loaders = array(); + } + + // Store original include_path + $this->includePath = get_include_path(); + + $autoloader = new ModuleAutoloader(array( + dirname(__DIR__) . '/TestAsset', + )); + $autoloader->register(); + } + + public function tearDown() + { + // Restore original autoloaders + AutoloaderFactory::unregisterAutoloaders(); + $loaders = spl_autoload_functions(); + if (is_array($loaders)) { + foreach ($loaders as $loader) { + spl_autoload_unregister($loader); + } + } + + foreach ($this->loaders as $loader) { + spl_autoload_register($loader); + } + + // Restore original include_path + set_include_path($this->includePath); + } + + public function testModuleResolverListenerCanResolveModuleClasses() + { + $moduleResolver = new ModuleResolverListener; + $e = new ModuleEvent; + + $e->setModuleName('ListenerTestModule'); + $this->assertInstanceOf('ListenerTestModule\Module', $moduleResolver($e)); + + $e->setModuleName('DoesNotExist'); + $this->assertFalse($moduleResolver($e)); + } +} diff --git a/test/Listener/OnBootstrapListenerTest.php b/test/Listener/OnBootstrapListenerTest.php new file mode 100644 index 0000000..627402b --- /dev/null +++ b/test/Listener/OnBootstrapListenerTest.php @@ -0,0 +1,98 @@ +loaders = spl_autoload_functions(); + if (!is_array($this->loaders)) { + // spl_autoload_functions does not return empty array when no + // autoloaders registered... + $this->loaders = array(); + } + + // Store original include_path + $this->includePath = get_include_path(); + + $autoloader = new ModuleAutoloader(array( + dirname(__DIR__) . '/TestAsset', + )); + $autoloader->register(); + + $sharedEvents = new SharedEventManager(); + $this->moduleManager = new ModuleManager(array()); + $this->moduleManager->events()->setSharedManager($sharedEvents); + $this->moduleManager->events()->attach('loadModule.resolve', new ModuleResolverListener, 1000); + $this->moduleManager->events()->attach('loadModule', new OnBootstrapListener, 1000); + + $this->application = new MockApplication; + $events = new EventManager(array('Zend\Mvc\Application', 'ZendTest\Module\TestAsset\MockApplication', 'application')); + $events->setSharedManager($sharedEvents); + $this->application->setEventManager($events); + } + + public function tearDown() + { + // Restore original autoloaders + AutoloaderFactory::unregisterAutoloaders(); + $loaders = spl_autoload_functions(); + if (is_array($loaders)) { + foreach ($loaders as $loader) { + spl_autoload_unregister($loader); + } + } + + foreach ($this->loaders as $loader) { + spl_autoload_register($loader); + } + + // Restore original include_path + set_include_path($this->includePath); + } + + public function testOnBootstrapMethodCalledByOnBootstrapListener() + { + $moduleManager = $this->moduleManager; + $moduleManager->setModules(array('ListenerTestModule')); + $moduleManager->loadModules(); + $this->application->bootstrap(); + $modules = $moduleManager->getLoadedModules(); + $this->assertTrue($modules['ListenerTestModule']->onBootstrapCalled); + } +} diff --git a/test/Listener/ServiceListenerTest.php b/test/Listener/ServiceListenerTest.php new file mode 100644 index 0000000..38feb63 --- /dev/null +++ b/test/Listener/ServiceListenerTest.php @@ -0,0 +1,153 @@ +services = new ServiceManager(); + $this->listener = new ServiceListener($this->services); + $this->event = new ModuleEvent(); + } + + public function testPassingInvalidModuleDoesNothing() + { + $module = new stdClass(); + $this->event->setModule($module); + $this->listener->onLoadModule($this->event); + + foreach ($this->serviceManagerProps as $prop) { + $this->assertAttributeEquals(array(), $prop, $this->services); + } + } + + public function testInvalidReturnFromModuleDoesNothing() + { + $module = new TestAsset\ServiceInvalidReturnModule(); + $this->event->setModule($module); + $this->listener->onLoadModule($this->event); + + foreach ($this->serviceManagerProps as $prop) { + $this->assertAttributeEquals(array(), $prop, $this->services); + } + } + + public function getServiceConfiguration() + { + return array( + 'invokables' => array(__CLASS__ => __CLASS__), + 'factories' => array( + 'foo' => function($sm) { }, + ), + 'abstract_factories' => array( + new \Zend\ServiceManager\Di\DiAbstractServiceFactory(new \Zend\Di\Di()), + ), + 'shared' => array( + 'foo' => false, + 'zendtestmodulemanagerlistenerservicelistenertest' => true, + ), + 'aliases' => array( + 'bar' => 'foo', + ), + ); + } + + public function assertServiceManagerIsConfigured() + { + $this->listener->configureServiceManager(); + foreach ($this->getServiceConfiguration() as $prop => $expected) { + if ($prop == 'invokables') { + $prop = 'invokableClasses'; + foreach ($expected as $key => $value) { + $normalized = strtolower($key); + $normalized = str_replace(array('\\', '_'), '', $normalized); + unset($expected[$key]); + $expected[$normalized] = $value; + } + } + if ($prop == 'abstract_factories') { + $prop = 'abstractFactories'; + } + $this->assertAttributeEquals($expected, $prop, $this->services, "$prop assertion failed"); + } + } + + public function testModuleReturningArrayConfiguresServiceManager() + { + $config = $this->getServiceConfiguration(); + $module = new TestAsset\ServiceProviderModule($config); + $this->event->setModule($module); + $this->listener->onLoadModule($this->event); + $this->assertServiceManagerIsConfigured(); + } + + public function testModuleReturningTraversableConfiguresServiceManager() + { + $config = $this->getServiceConfiguration(); + $config = new ArrayObject($config); + $module = new TestAsset\ServiceProviderModule($config); + $this->event->setModule($module); + $this->listener->onLoadModule($this->event); + $this->assertServiceManagerIsConfigured(); + } + + public function testModuleReturningServiceConfigurationConfiguresServiceManager() + { + $config = $this->getServiceConfiguration(); + $config = new ServiceConfiguration($config); + $module = new TestAsset\ServiceProviderModule($config); + $this->event->setModule($module); + $this->listener->onLoadModule($this->event); + $this->assertServiceManagerIsConfigured(); + } + + public function testMergedConfigurationContainingServiceManagerKeyWillConfigureServiceManagerPostLoadModules() + { + $config = array('service_manager' => $this->getServiceConfiguration()); + $configListener = new ConfigListener(); + $configListener->setMergedConfig($config); + $this->event->setConfigListener($configListener); + $this->listener->onLoadModulesPost($this->event); + $this->assertServiceManagerIsConfigured(); + } +} diff --git a/test/Listener/TestAsset/ServiceInvalidReturnModule.php b/test/Listener/TestAsset/ServiceInvalidReturnModule.php new file mode 100644 index 0000000..6487690 --- /dev/null +++ b/test/Listener/TestAsset/ServiceInvalidReturnModule.php @@ -0,0 +1,39 @@ +config = $config; + } + + public function getServiceConfiguration() + { + return $this->config; + } +} diff --git a/test/Listener/_files/bad/config.badext b/test/Listener/_files/bad/config.badext new file mode 100644 index 0000000..e69de29 diff --git a/test/Listener/_files/bad/config.php b/test/Listener/_files/bad/config.php new file mode 100644 index 0000000..fc3f376 --- /dev/null +++ b/test/Listener/_files/bad/config.php @@ -0,0 +1,3 @@ + 'loaded', +); diff --git a/test/Listener/_files/good/config.xml b/test/Listener/_files/good/config.xml new file mode 100644 index 0000000..fbdf865 --- /dev/null +++ b/test/Listener/_files/good/config.xml @@ -0,0 +1,4 @@ + + + loaded + diff --git a/test/Listener/_files/good/config.yml b/test/Listener/_files/good/config.yml new file mode 100644 index 0000000..d2da9b6 --- /dev/null +++ b/test/Listener/_files/good/config.yml @@ -0,0 +1 @@ +yml: loaded diff --git a/test/Listener/_files/good/merge1.php b/test/Listener/_files/good/merge1.php new file mode 100644 index 0000000..70bc649 --- /dev/null +++ b/test/Listener/_files/good/merge1.php @@ -0,0 +1,5 @@ + array('foo'), + 'keyed' => 'foo', +); diff --git a/test/Listener/_files/good/merge2.php b/test/Listener/_files/good/merge2.php new file mode 100644 index 0000000..2d032ca --- /dev/null +++ b/test/Listener/_files/good/merge2.php @@ -0,0 +1,5 @@ + array('bar'), + 'keyed' => 'bar', +); diff --git a/test/ModuleEventTest.php b/test/ModuleEventTest.php new file mode 100644 index 0000000..1e32ddc --- /dev/null +++ b/test/ModuleEventTest.php @@ -0,0 +1,96 @@ +event = new ModuleEvent(); + } + + public function testSettingModuleProxiesToParameters() + { + $module = new stdClass; + $this->event->setModule($module); + $test = $this->event->getParam('module'); + $this->assertSame($module, $test); + } + + public function testCanRetrieveModuleViaGetter() + { + $module = new stdClass; + $this->event->setModule($module); + $test = $this->event->getModule(); + $this->assertSame($module, $test); + } + + public function testPassingNonObjectToSetModuleRaisesException() + { + $this->setExpectedException('Zend\ModuleManager\Exception\InvalidArgumentException'); + $this->event->setModule('foo'); + } + + public function testSettingModuleNameProxiesToParameters() + { + $moduleName = 'MyModule'; + $this->event->setModuleName($moduleName); + $test = $this->event->getParam('moduleName'); + $this->assertSame($moduleName, $test); + } + + public function testCanRetrieveModuleNameViaGetter() + { + $moduleName = 'MyModule'; + $this->event->setModuleName($moduleName); + $test = $this->event->getModuleName(); + $this->assertSame($moduleName, $test); + } + + public function testPassingNonStringToSetModuleNameRaisesException() + { + $this->setExpectedException('Zend\ModuleManager\Exception\InvalidArgumentException'); + $this->event->setModuleName(new StdClass); + } + + public function testSettingConfigListenerProxiesToParameters() + { + $configListener = new ConfigListener; + $this->event->setConfigListener($configListener); + $test = $this->event->getParam('configListener'); + $this->assertSame($configListener, $test); + } + + public function testCanRetrieveConfigListenerViaGetter() + { + $configListener = new ConfigListener; + $this->event->setConfigListener($configListener); + $test = $this->event->getConfigListener(); + $this->assertSame($configListener, $test); + } +} diff --git a/test/ModuleManagerTest.php b/test/ModuleManagerTest.php new file mode 100644 index 0000000..d3d7861 --- /dev/null +++ b/test/ModuleManagerTest.php @@ -0,0 +1,145 @@ +tmpdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'zend_module_cache_dir'; + @mkdir($this->tmpdir); + $this->configCache = $this->tmpdir . DIRECTORY_SEPARATOR . 'config.cache.php'; + // Store original autoloaders + $this->loaders = spl_autoload_functions(); + if (!is_array($this->loaders)) { + // spl_autoload_functions does not return empty array when no + // autoloaders registered... + $this->loaders = array(); + } + + // Store original include_path + $this->includePath = get_include_path(); + + $this->defaultListeners = new DefaultListenerAggregate( + new ListenerOptions(array( + 'module_paths' => array( + realpath(__DIR__ . '/TestAsset'), + ), + )) + ); + } + + public function tearDown() + { + $file = glob($this->tmpdir . DIRECTORY_SEPARATOR . '*'); + @unlink($file[0]); // change this if there's ever > 1 file + @rmdir($this->tmpdir); + // Restore original autoloaders + AutoloaderFactory::unregisterAutoloaders(); + $loaders = spl_autoload_functions(); + if (is_array($loaders)) { + foreach ($loaders as $loader) { + spl_autoload_unregister($loader); + } + } + + foreach ($this->loaders as $loader) { + spl_autoload_register($loader); + } + + // Restore original include_path + set_include_path($this->includePath); + } + + public function testEventManagerIdentifiers() + { + $moduleManager = new ModuleManager(array()); + $identifiers = $moduleManager->events()->getIdentifiers(); + $expected = array('Zend\ModuleManager\ModuleManager', 'module_manager'); + $this->assertEquals($expected, array_values($identifiers)); + } + + public function testCanLoadSomeModule() + { + $configListener = $this->defaultListeners->getConfigListener(); + $moduleManager = new ModuleManager(array('SomeModule'), new EventManager); + $moduleManager->events()->attachAggregate($this->defaultListeners); + $moduleManager->loadModules(); + $loadedModules = $moduleManager->getLoadedModules(); + $this->assertInstanceOf('SomeModule\Module', $loadedModules['SomeModule']); + $config = $configListener->getMergedConfig(); + $this->assertSame($config->some, 'thing'); + } + + public function testCanLoadMultipleModules() + { + $configListener = $this->defaultListeners->getConfigListener(); + $moduleManager = new ModuleManager(array('BarModule', 'BazModule')); + $moduleManager->events()->attachAggregate($this->defaultListeners); + $moduleManager->loadModules(); + $loadedModules = $moduleManager->getLoadedModules(); + $this->assertInstanceOf('BarModule\Module', $loadedModules['BarModule']); + $this->assertInstanceOf('BazModule\Module', $loadedModules['BazModule']); + $this->assertInstanceOf('BarModule\Module', $moduleManager->getModule('BarModule')); + $this->assertInstanceOf('BazModule\Module', $moduleManager->getModule('BazModule')); + $this->assertNull($moduleManager->getModule('NotLoaded')); + $config = $configListener->getMergedConfig(); + $this->assertSame('foo', $config->bar); + $this->assertSame('bar', $config->baz); + } + + public function testModuleLoadingBehavior() + { + $moduleManager = new ModuleManager(array('BarModule')); + $moduleManager->events()->attachAggregate($this->defaultListeners); + $modules = $moduleManager->getLoadedModules(); + $this->assertSame(0, count($modules)); + $modules = $moduleManager->getLoadedModules(true); + $this->assertSame(1, count($modules)); + $moduleManager->loadModules(); // should not cause any problems + $moduleManager->loadModule('BarModule'); // should not cause any problems + $modules = $moduleManager->getLoadedModules(true); // BarModule already loaded so nothing happens + $this->assertSame(1, count($modules)); + } + + public function testConstructorThrowsInvalidArgumentException() + { + $this->setExpectedException('InvalidArgumentException'); + $moduleManager = new ModuleManager('stringShouldBeArray'); + } + + public function testNotFoundModuleThrowsRuntimeException() + { + $this->setExpectedException('RuntimeException'); + $moduleManager = new ModuleManager(array('NotFoundModule')); + $moduleManager->loadModules(); + } +} diff --git a/test/TestAsset/AutoInstallModule/Module.php b/test/TestAsset/AutoInstallModule/Module.php new file mode 100644 index 0000000..fe3df25 --- /dev/null +++ b/test/TestAsset/AutoInstallModule/Module.php @@ -0,0 +1,38 @@ +install(); + } + + public function autoUpgrade($version = null) + { + return $this->upgrade($version); + } + + public function install() + { + return static::$RESPONSE; + } + + public function upgrade($version = null) + { + return static::$RESPONSE; + } + + public function getProvides() + { + return array( + __NAMESPACE__ => array( + 'version' => static::$VERSION, + ), + ); + } +} diff --git a/test/TestAsset/BadConfigModule/Module.php b/test/TestAsset/BadConfigModule/Module.php new file mode 100644 index 0000000..5e37f3c --- /dev/null +++ b/test/TestAsset/BadConfigModule/Module.php @@ -0,0 +1,11 @@ +initAutoloader(); + } + + protected function initAutoloader() + { + include __DIR__ . '/autoload_register.php'; + } + + public function getConfig() + { + return new Config(include __DIR__ . '/configs/config.php'); + } + + public function getProvides() + { + return array( + __NAMESPACE__ => array( + 'version' => $this->version, + ), + ); + } + + public function getDependencies() + { + return array( + 'php' => array( + 'version' => '5.3.0', + 'required' => true, + ), + 'ext/core' => array( + 'version' => '0.1', + 'required' => true, + ), + 'BooModule' => array( + 'required' => true, + ), + ); + } +} diff --git a/test/TestAsset/BafModule/autoload_classmap.php b/test/TestAsset/BafModule/autoload_classmap.php new file mode 100644 index 0000000..668cf05 --- /dev/null +++ b/test/TestAsset/BafModule/autoload_classmap.php @@ -0,0 +1,4 @@ + __DIR__ . DIRECTORY_SEPARATOR . 'Module.php', +); diff --git a/test/TestAsset/BafModule/autoload_function.php b/test/TestAsset/BafModule/autoload_function.php new file mode 100644 index 0000000..3ea81c4 --- /dev/null +++ b/test/TestAsset/BafModule/autoload_function.php @@ -0,0 +1,12 @@ + 'foo', +); diff --git a/test/TestAsset/BamModule/Module.php b/test/TestAsset/BamModule/Module.php new file mode 100644 index 0000000..8b9c754 --- /dev/null +++ b/test/TestAsset/BamModule/Module.php @@ -0,0 +1,49 @@ +initAutoloader(); + } + + protected function initAutoloader() + { + include __DIR__ . '/autoload_register.php'; + } + + public function getConfig() + { + return new Config(include __DIR__ . '/configs/config.php'); + } + + public function getProvides() + { + return array( + __NAMESPACE__ => array( + 'version' => $this->version, + ), + ); + } + + public function getDependencies() + { + return array( + 'php' => array( + 'version' => '5.3.0', + 'required' => true, + ), + 'ext/core' => array( + 'version' => '0.1', + 'required' => true, + ), + 'BooModule' => true, + ); + } +} diff --git a/test/TestAsset/BamModule/autoload_classmap.php b/test/TestAsset/BamModule/autoload_classmap.php new file mode 100644 index 0000000..477422f --- /dev/null +++ b/test/TestAsset/BamModule/autoload_classmap.php @@ -0,0 +1,4 @@ + __DIR__ . DIRECTORY_SEPARATOR . 'Module.php', +); diff --git a/test/TestAsset/BamModule/autoload_function.php b/test/TestAsset/BamModule/autoload_function.php new file mode 100644 index 0000000..3ea81c4 --- /dev/null +++ b/test/TestAsset/BamModule/autoload_function.php @@ -0,0 +1,12 @@ + 'foo', +); diff --git a/test/TestAsset/BarModule/Module.php b/test/TestAsset/BarModule/Module.php new file mode 100644 index 0000000..01f245e --- /dev/null +++ b/test/TestAsset/BarModule/Module.php @@ -0,0 +1,44 @@ +initAutoloader(); + } + + protected function initAutoloader() + { + include __DIR__ . '/autoload_register.php'; + } + + public function getConfig() + { + return new Config(include __DIR__ . '/configs/config.php'); + } + + public function getProvides() + { + return array( + __NAMESPACE__ => array( + 'version' => $this->version, + ), + ); + } + + public function getDependencies() + { + return array( + 'php' => array( + 'version' => '5.3.0', + 'required' => true, + ), + ); + } +} diff --git a/test/TestAsset/BarModule/autoload_classmap.php b/test/TestAsset/BarModule/autoload_classmap.php new file mode 100644 index 0000000..97b5539 --- /dev/null +++ b/test/TestAsset/BarModule/autoload_classmap.php @@ -0,0 +1,4 @@ + __DIR__ . DIRECTORY_SEPARATOR . 'Module.php', +); diff --git a/test/TestAsset/BarModule/autoload_function.php b/test/TestAsset/BarModule/autoload_function.php new file mode 100644 index 0000000..3ea81c4 --- /dev/null +++ b/test/TestAsset/BarModule/autoload_function.php @@ -0,0 +1,12 @@ + 'foo', +); diff --git a/test/TestAsset/BazModule/Module.php b/test/TestAsset/BazModule/Module.php new file mode 100644 index 0000000..cb627f4 --- /dev/null +++ b/test/TestAsset/BazModule/Module.php @@ -0,0 +1,23 @@ +initAutoloader(); + } + + protected function initAutoloader() + { + include __DIR__ . '/autoload_register.php'; + } + + public function getConfig() + { + return new Config(include __DIR__ . '/configs/config.php'); + } +} diff --git a/test/TestAsset/BazModule/autoload_classmap.php b/test/TestAsset/BazModule/autoload_classmap.php new file mode 100644 index 0000000..e514a02 --- /dev/null +++ b/test/TestAsset/BazModule/autoload_classmap.php @@ -0,0 +1,4 @@ + __DIR__ . DIRECTORY_SEPARATOR . 'Module.php', +); diff --git a/test/TestAsset/BazModule/autoload_function.php b/test/TestAsset/BazModule/autoload_function.php new file mode 100644 index 0000000..3ea81c4 --- /dev/null +++ b/test/TestAsset/BazModule/autoload_function.php @@ -0,0 +1,12 @@ + 'bar', +); diff --git a/test/TestAsset/BooModule/Module.php b/test/TestAsset/BooModule/Module.php new file mode 100644 index 0000000..ea2182a --- /dev/null +++ b/test/TestAsset/BooModule/Module.php @@ -0,0 +1,48 @@ +initAutoloader(); + } + + protected function initAutoloader() + { + include __DIR__ . '/autoload_register.php'; + } + + public function getConfig() + { + return new Config(include __DIR__ . '/configs/config.php'); + } + + public function getProvides() + { + return array( + __NAMESPACE__ => array( + 'version' => $this->version, + ), + ); + } + + public function getDependencies() + { + return array( + 'php' => array( + 'version' => '5.3.0', + ), + 'ext/monkey' => array( + 'version' => '0.1', + ), + 'BarModule' => array( + ) + ); + } +} diff --git a/test/TestAsset/BooModule/autoload_classmap.php b/test/TestAsset/BooModule/autoload_classmap.php new file mode 100644 index 0000000..cb7b5f5 --- /dev/null +++ b/test/TestAsset/BooModule/autoload_classmap.php @@ -0,0 +1,4 @@ + __DIR__ . DIRECTORY_SEPARATOR . 'Module.php', +); diff --git a/test/TestAsset/BooModule/autoload_function.php b/test/TestAsset/BooModule/autoload_function.php new file mode 100644 index 0000000..3ea81c4 --- /dev/null +++ b/test/TestAsset/BooModule/autoload_function.php @@ -0,0 +1,12 @@ + 'foo', +); diff --git a/test/TestAsset/BorModule/Module.php b/test/TestAsset/BorModule/Module.php new file mode 100644 index 0000000..8869f99 --- /dev/null +++ b/test/TestAsset/BorModule/Module.php @@ -0,0 +1,51 @@ +initAutoloader(); + } + + protected function initAutoloader() + { + include __DIR__ . '/autoload_register.php'; + } + + public function getConfig() + { + return new Config(include __DIR__ . '/configs/config.php'); + } + + public function getProvides() + { + return array( + __NAMESPACE__ => array( + 'version' => $this->version, + ), + ); + } + + public function getDependencies() + { + return array( + 'php' => array( + 'version' => '5.3.0', + 'required' => true, + ), + 'ext/monkey' => array( + 'version' => '0.1', + 'required' => true, + ), + 'BooModule' => array( + 'required' => true, + ), + ); + } +} diff --git a/test/TestAsset/BorModule/autoload_classmap.php b/test/TestAsset/BorModule/autoload_classmap.php new file mode 100644 index 0000000..976cc40 --- /dev/null +++ b/test/TestAsset/BorModule/autoload_classmap.php @@ -0,0 +1,4 @@ + __DIR__ . DIRECTORY_SEPARATOR . 'Module.php', +); diff --git a/test/TestAsset/BorModule/autoload_function.php b/test/TestAsset/BorModule/autoload_function.php new file mode 100644 index 0000000..3ea81c4 --- /dev/null +++ b/test/TestAsset/BorModule/autoload_function.php @@ -0,0 +1,12 @@ + 'foo', +); diff --git a/test/TestAsset/DoubleModule/Module.php b/test/TestAsset/DoubleModule/Module.php new file mode 100644 index 0000000..5ecfff0 --- /dev/null +++ b/test/TestAsset/DoubleModule/Module.php @@ -0,0 +1,43 @@ +initAutoloader(); + } + + protected function initAutoloader() + { + include __DIR__ . '/autoload_register.php'; + } + + public function getConfig() + { + return new Config(include __DIR__ . '/configs/config.php'); + } + + public function getProvides() + { + return array( + 'BarModule' => array( + 'version' => $this->version, + ), + ); + } + + public function getDependencies() + { + return array( + 'php' => array( + 'version' => '5.3.0', + ) + ); + } +} diff --git a/test/TestAsset/DoubleModule/autoload_classmap.php b/test/TestAsset/DoubleModule/autoload_classmap.php new file mode 100644 index 0000000..1127c34 --- /dev/null +++ b/test/TestAsset/DoubleModule/autoload_classmap.php @@ -0,0 +1,4 @@ + __DIR__ . DIRECTORY_SEPARATOR . 'Module.php', +); diff --git a/test/TestAsset/DoubleModule/autoload_function.php b/test/TestAsset/DoubleModule/autoload_function.php new file mode 100644 index 0000000..3ea81c4 --- /dev/null +++ b/test/TestAsset/DoubleModule/autoload_function.php @@ -0,0 +1,12 @@ + 'foo', +); diff --git a/test/TestAsset/ImpossibleModule/Module.php b/test/TestAsset/ImpossibleModule/Module.php new file mode 100644 index 0000000..3279285 --- /dev/null +++ b/test/TestAsset/ImpossibleModule/Module.php @@ -0,0 +1,44 @@ +initAutoloader(); + } + + protected function initAutoloader() + { + include __DIR__ . '/autoload_register.php'; + } + + public function getConfig() + { + return new Config(include __DIR__ . '/configs/config.php'); + } + + public function getProvides() + { + return array( + __NAMESPACE__ => array( + 'version' => $this->version, + ), + ); + } + + public function getDependencies() + { + return array( + 'php' => array( + 'version' => '99.3.0', + ), + 'BarModule' => true, + ); + } +} diff --git a/test/TestAsset/ImpossibleModule/autoload_classmap.php b/test/TestAsset/ImpossibleModule/autoload_classmap.php new file mode 100644 index 0000000..bb989be --- /dev/null +++ b/test/TestAsset/ImpossibleModule/autoload_classmap.php @@ -0,0 +1,4 @@ + __DIR__ . DIRECTORY_SEPARATOR . 'Module.php', +); diff --git a/test/TestAsset/ImpossibleModule/autoload_function.php b/test/TestAsset/ImpossibleModule/autoload_function.php new file mode 100644 index 0000000..3ea81c4 --- /dev/null +++ b/test/TestAsset/ImpossibleModule/autoload_function.php @@ -0,0 +1,12 @@ + 'foo', +); diff --git a/test/TestAsset/ListenerTestModule/Module.php b/test/TestAsset/ListenerTestModule/Module.php new file mode 100644 index 0000000..1d37203 --- /dev/null +++ b/test/TestAsset/ListenerTestModule/Module.php @@ -0,0 +1,75 @@ +initCalled = true; + } + + public function getConfig() + { + $this->getConfigCalled = true; + return array( + 'listener' => 'test' + ); + } + + public function getAutoloaderConfig() + { + $this->getAutoloaderConfigCalled = true; + return array( + 'Zend\Loader\StandardAutoloader' => array( + 'namespaces' => array( + 'Foo' => __DIR__ . '/src/Foo', + ), + ), + ); + } + + public function onBootstrap(Event $e) + { + $this->onBootstrapCalled = true; + } +} diff --git a/test/TestAsset/ListenerTestModule/src/Foo/Bar.php b/test/TestAsset/ListenerTestModule/src/Foo/Bar.php new file mode 100644 index 0000000..19b4a1c --- /dev/null +++ b/test/TestAsset/ListenerTestModule/src/Foo/Bar.php @@ -0,0 +1,37 @@ +module = $module; + $this->moduleManager = $moduleManager; + } +} diff --git a/test/TestAsset/MockApplication.php b/test/TestAsset/MockApplication.php new file mode 100644 index 0000000..8267a74 --- /dev/null +++ b/test/TestAsset/MockApplication.php @@ -0,0 +1,97 @@ +events = $events; + } + + public function events() + { + return $this->events; + } + + public function setServiceManager($serviceManager) + { + $this->serviceManager = $serviceManager; + } + + /** + * Get the locator object + * + * @return \Zend\ServiceManager\ServiceLocatorInterface + */ + public function getServiceManager() + { + return $this->serviceManager; + } + + /** + * Get the request object + * + * @return Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * Get the response object + * + * @return Response + */ + public function getResponse() + { + return $this->response; + } + + /** + * Run the application + * + * @return \Zend\Http\Response + */ + public function run() + { + return $this->response; + } + + public function bootstrap() + { + $event = new MvcEvent(); + $event->setApplication($this); + $event->setTarget($this); + $this->events()->trigger('bootstrap', $event); + } +} diff --git a/test/TestAsset/NotAutoloaderModule/Module.php b/test/TestAsset/NotAutoloaderModule/Module.php new file mode 100644 index 0000000..43e2d1c --- /dev/null +++ b/test/TestAsset/NotAutoloaderModule/Module.php @@ -0,0 +1,20 @@ +getAutoloaderConfigCalled = true; + return array( + 'Zend\Loader\StandardAutoloader' => array( + 'namespaces' => array( + 'Foo' => __DIR__ . '/src/Foo', + ), + ), + ); + } +} diff --git a/test/TestAsset/NotAutoloaderModule/src/Foo/Bar.php b/test/TestAsset/NotAutoloaderModule/src/Foo/Bar.php new file mode 100644 index 0000000..10d441b --- /dev/null +++ b/test/TestAsset/NotAutoloaderModule/src/Foo/Bar.php @@ -0,0 +1,6 @@ +initAutoloader(); + } + + protected function initAutoloader() + { + include __DIR__ . '/autoload_register.php'; + } + + public function getConfig() + { + return new Config(include __DIR__ . '/configs/config.php'); + } +} diff --git a/test/TestAsset/SomeModule/autoload_classmap.php b/test/TestAsset/SomeModule/autoload_classmap.php new file mode 100644 index 0000000..e514a02 --- /dev/null +++ b/test/TestAsset/SomeModule/autoload_classmap.php @@ -0,0 +1,4 @@ + __DIR__ . DIRECTORY_SEPARATOR . 'Module.php', +); diff --git a/test/TestAsset/SomeModule/autoload_function.php b/test/TestAsset/SomeModule/autoload_function.php new file mode 100644 index 0000000..3ea81c4 --- /dev/null +++ b/test/TestAsset/SomeModule/autoload_function.php @@ -0,0 +1,12 @@ + 'thing', +); diff --git a/test/_files/config.bad b/test/_files/config.bad new file mode 100644 index 0000000..e69de29 diff --git a/test/_files/config.ini b/test/_files/config.ini new file mode 100644 index 0000000..9eb2b99 --- /dev/null +++ b/test/_files/config.ini @@ -0,0 +1,2 @@ +[all] +ini = "yes" diff --git a/test/_files/config.json b/test/_files/config.json new file mode 100644 index 0000000..3aff3a9 --- /dev/null +++ b/test/_files/config.json @@ -0,0 +1 @@ +{"all":{"json":"yes"}} diff --git a/test/_files/config.php b/test/_files/config.php new file mode 100644 index 0000000..6e754c4 --- /dev/null +++ b/test/_files/config.php @@ -0,0 +1,6 @@ + array( + 'php' => 'yes' + ), +); diff --git a/test/_files/config.xml b/test/_files/config.xml new file mode 100644 index 0000000..1026da5 --- /dev/null +++ b/test/_files/config.xml @@ -0,0 +1,6 @@ + + + + yes + + diff --git a/test/_files/config.yaml b/test/_files/config.yaml new file mode 100644 index 0000000..eb36052 --- /dev/null +++ b/test/_files/config.yaml @@ -0,0 +1,2 @@ +all: + yaml: true