diff --git a/config/global.php b/config/global.php index aff73fcdba00409d733a1a115c5c5020e1f536b7..0cb88f5b3dc00c2eb0a3c130a9b73c0f5c9082ee 100644 --- a/config/global.php +++ b/config/global.php @@ -1,6 +1,7 @@ <?php use Interop\Container\ContainerInterface; +use Interop\Container\Exception\NotFoundException; use Piwik\Cache\Eager; use Piwik\SettingsServer; @@ -44,7 +45,13 @@ return array( return $cache; }, 'Piwik\Cache\Backend' => function (ContainerInterface $c) { - return \Piwik\Cache::buildBackend($c->get('ini.Cache.backend')); + try { + $backend = $c->get('ini.Cache.backend'); + } catch (NotFoundException $ex) { + $backend = 'chained'; // happens if global.ini.php is not available + } + + return \Piwik\Cache::buildBackend($backend); }, 'cache.eager.cache_id' => function () { return 'eagercache-' . str_replace(array('.', '-'), '', \Piwik\Version::VERSION) . '-'; @@ -54,5 +61,4 @@ return array( 'Piwik\Translation\Loader\LoaderInterface' => DI\object('Piwik\Translation\Loader\LoaderCache') ->constructor(DI\link('Piwik\Translation\Loader\JsonFileLoader')), - ); diff --git a/core/Application/Environment.php b/core/Application/Environment.php index 38a6c2eb5c24ffb52b04e3f80639bcb0c35f1d3f..6687b015c46cba228e33884b5ac4f101f378284f 100644 --- a/core/Application/Environment.php +++ b/core/Application/Environment.php @@ -150,6 +150,8 @@ class Environment */ protected function getGlobalSettings() { + // TODO: need to be able to set path global/local/etc. which is in DI... for now works because TestingEnvironment creates + // singleton instance before this method. return IniSettingsProvider::getSingletonInstance(); } diff --git a/core/Application/Kernel/EnvironmentValidator.php b/core/Application/Kernel/EnvironmentValidator.php index 9474652ac938a6ab60a50c2c44d24de8f77c7b8a..9e116a7cb180582d6abb76be80b11af617ef11c9 100644 --- a/core/Application/Kernel/EnvironmentValidator.php +++ b/core/Application/Kernel/EnvironmentValidator.php @@ -12,6 +12,7 @@ use Piwik\Application\Kernel\GlobalSettingsProvider\IniSettingsProvider; use Piwik\Common; use Piwik\Piwik; use Piwik\SettingsServer; +use Piwik\Translate; use Piwik\Translation\Translator; /** @@ -33,6 +34,7 @@ class EnvironmentValidator public function __construct(GlobalSettingsProvider $settingsProvider, Translator $translator) { $this->iniSettingsProvider = $settingsProvider; + $this->translator = $translator; } public function validate() @@ -40,23 +42,23 @@ class EnvironmentValidator $inTrackerRequest = SettingsServer::isTrackerApiRequest(); $inConsole = Common::isPhpCliMode(); - $this->checkConfigFileExists('global.ini.php'); - $this->checkConfigFileExists('config.ini.php', $startInstaller = !$inTrackerRequest && !$inConsole); + $this->checkConfigFileExists($this->iniSettingsProvider->getPathGlobal()); + $this->checkConfigFileExists($this->iniSettingsProvider->getPathLocal(), $startInstaller = !$inTrackerRequest && !$inConsole); } /** - * @param $filename + * @param $path * @param bool $startInstaller * @throws \Exception */ - private function checkConfigFileExists($filename, $startInstaller = false) + private function checkConfigFileExists($path, $startInstaller = false) { - $path = PIWIK_INCLUDE_PATH . '/config/' . $filename; - if (is_readable($path)) { return; } + Translate::loadAllTranslations(); + $message = $this->translator->translate('General_ExceptionConfigurationFileNotFound', array($path)); $exception = new \Exception($message); diff --git a/core/Application/Kernel/GlobalSettingsProvider/IniSettingsProvider.php b/core/Application/Kernel/GlobalSettingsProvider/IniSettingsProvider.php index 07dd25606ee1166f4d2adf48ff8039f454ddd35a..75123b1645b13e8e13d30cf636cbacf56918820b 100644 --- a/core/Application/Kernel/GlobalSettingsProvider/IniSettingsProvider.php +++ b/core/Application/Kernel/GlobalSettingsProvider/IniSettingsProvider.php @@ -27,6 +27,21 @@ class IniSettingsProvider implements GlobalSettingsProvider */ private $iniFileChain; + /** + * @var string + */ + protected $pathGlobal = null; + + /** + * @var string + */ + protected $pathCommon = null; + + /** + * @var string + */ + protected $pathLocal = null; + /** * @param string|null $pathGlobal Path to the global.ini.php file. Or null to use the default. * @param string|null $pathLocal Path to the config.ini.php file. Or null to use the default. @@ -34,11 +49,21 @@ class IniSettingsProvider implements GlobalSettingsProvider */ public function __construct($pathGlobal = null, $pathLocal = null, $pathCommon = null) { - $pathGlobal = $pathGlobal ?: Config::getGlobalConfigPath(); - $pathCommon = $pathCommon ?: Config::getCommonConfigPath(); - $pathLocal = $pathLocal ?: Config::getLocalConfigPath(); + $this->pathGlobal = $pathGlobal ?: Config::getGlobalConfigPath(); + $this->pathCommon = $pathCommon ?: Config::getCommonConfigPath(); + $this->pathLocal = $pathLocal ?: Config::getLocalConfigPath(); + + $this->iniFileChain = new IniFileChain(); + $this->reload(); + } + + public function reload($pathGlobal = null, $pathLocal = null, $pathCommon = null) + { + $this->pathGlobal = $pathGlobal ?: $this->pathGlobal; + $this->pathCommon = $pathCommon ?: $this->pathCommon; + $this->pathLocal = $pathLocal ?: $this->pathLocal; - $this->iniFileChain = new IniFileChain(array($pathGlobal, $pathCommon), $pathLocal); + $this->iniFileChain->reload(array($this->pathGlobal, $this->pathCommon), $this->pathLocal); } public function &getSection($name) @@ -57,6 +82,21 @@ class IniSettingsProvider implements GlobalSettingsProvider return $this->iniFileChain; } + public function getPathGlobal() + { + return $this->pathGlobal; + } + + public function getPathLocal() + { + return $this->pathLocal; + } + + public function getPathCommon() + { + return $this->pathCommon; + } + public static function getSingletonInstance($pathGlobal = null, $pathLocal = null, $pathCommon = null) { if (self::$instance === null) { diff --git a/core/Application/Kernel/PluginList.php b/core/Application/Kernel/PluginList.php index 9d5e03cc0a1872f4007d837f504ce23f8918a36c..672110853b131e555c4946a8d8c4c5eccf82b376 100644 --- a/core/Application/Kernel/PluginList.php +++ b/core/Application/Kernel/PluginList.php @@ -23,4 +23,11 @@ interface PluginList * @return string[] */ public function getActivatedPlugins(); + + /** + * Returns the list of plugins that are bundled with Piwik. + * + * @return string[] + */ + public function getPluginsBundledWithPiwik(); } \ No newline at end of file diff --git a/core/Application/Kernel/PluginList/IniPluginList.php b/core/Application/Kernel/PluginList/IniPluginList.php index 6636cc4fd60934fdce1d099b42d2774794f6369b..8825661e8255220118ae9acdc515ceacc17c6c8e 100644 --- a/core/Application/Kernel/PluginList/IniPluginList.php +++ b/core/Application/Kernel/PluginList/IniPluginList.php @@ -9,16 +9,19 @@ namespace Piwik\Application\Kernel\PluginList; use Piwik\Application\Kernel\GlobalSettingsProvider; +use Piwik\Application\Kernel\GlobalSettingsProvider\IniSettingsProvider; use Piwik\Application\Kernel\PluginList; /** * Default implementation of the PluginList interface. Uses the [Plugins] section * in Piwik's INI config to get the activated plugins. + * + * Depends on IniSettingsProvider being used. */ class IniPluginList implements PluginList { /** - * @var GlobalSettingsProvider + * @var IniSettingsProvider */ private $settings; @@ -35,4 +38,15 @@ class IniPluginList implements PluginList $section = $this->settings->getSection('Plugins'); return @$section['Plugins'] ?: array(); } + + /** + * @return string[] + */ + public function getPluginsBundledWithPiwik() + { + $pathGlobal = $this->settings->getPathGlobal(); + + $section = $this->settings->getIniFileChain()->getFrom($pathGlobal, 'Plugins'); + return $section['Plugins']; + } } \ No newline at end of file diff --git a/core/Config.php b/core/Config.php index 42cec6670233e7ae2d028b411732fd945776a6a5..edc54d6a074d01914f71b177f1e17d294c9dfcab 100644 --- a/core/Config.php +++ b/core/Config.php @@ -49,20 +49,13 @@ class Config extends Singleton const DEFAULT_COMMON_CONFIG_PATH = '/config/common.config.ini.php'; const DEFAULT_GLOBAL_CONFIG_PATH = '/config/global.ini.php'; - /** - * @var boolean - */ - protected $pathGlobal = null; - protected $pathCommon = null; - protected $pathLocal = null; - /** * @var boolean */ protected $doNotWriteConfigInTests = false; /** - * @var IniFileChain + * @var IniSettingsProvider */ protected $settings; @@ -70,11 +63,7 @@ class Config extends Singleton public function __construct($pathGlobal = null, $pathLocal = null, $pathCommon = null) { - $this->pathGlobal = $pathGlobal ?: self::getGlobalConfigPath(); - $this->pathCommon = $pathCommon ?: self::getCommonConfigPath(); - $this->pathLocal = $pathLocal ?: self::getLocalConfigPath(); - - $this->settings = IniSettingsProvider::getSingletonInstance($pathGlobal, $pathLocal, $pathCommon)->getIniFileChain(); + $this->settings = IniSettingsProvider::getSingletonInstance($pathGlobal, $pathLocal, $pathCommon); } /** @@ -84,7 +73,7 @@ class Config extends Singleton */ public function getLocalPath() { - return $this->pathLocal; + return $this->settings->getPathLocal(); } /** @@ -94,7 +83,7 @@ class Config extends Singleton */ public function getGlobalPath() { - return $this->pathGlobal; + return $this->settings->getPathGlobal(); } /** @@ -104,7 +93,7 @@ class Config extends Singleton */ public function getCommonPath() { - return $this->pathCommon; + return $this->settings->getPathCommon(); } /** @@ -121,37 +110,35 @@ class Config extends Singleton $this->doNotWriteConfigInTests = true; } - $this->pathLocal = $pathLocal ?: Config::getLocalConfigPath(); - $this->pathGlobal = $pathGlobal ?: Config::getGlobalConfigPath(); - $this->pathCommon = $pathCommon ?: Config::getCommonConfigPath(); + $this->reload($pathLocal, $pathGlobal, $pathCommon); - $this->reload(); + $chain = $this->settings->getIniFileChain(); - $databaseTestsSettings = $this->settings->get('database_tests'); // has to be __get otherwise when called from TestConfig, PHP will issue a NOTICE + $databaseTestsSettings = $chain->get('database_tests'); // has to be __get otherwise when called from TestConfig, PHP will issue a NOTICE if (!empty($databaseTestsSettings)) { - $this->settings->set('database', $databaseTestsSettings); + $chain->set('database', $databaseTestsSettings); } // Ensure local mods do not affect tests if (empty($pathGlobal)) { - $this->settings->set('Debug', $this->settings->getFrom($this->pathGlobal, 'Debug')); - $this->settings->set('mail', $this->settings->getFrom($this->pathGlobal, 'mail')); - $this->settings->set('General', $this->settings->getFrom($this->pathGlobal, 'General')); - $this->settings->set('Segments', $this->settings->getFrom($this->pathGlobal, 'Segments')); - $this->settings->set('Tracker', $this->settings->getFrom($this->pathGlobal, 'Tracker')); - $this->settings->set('Deletelogs', $this->settings->getFrom($this->pathGlobal, 'Deletelogs')); - $this->settings->set('Deletereports', $this->settings->getFrom($this->pathGlobal, 'Deletereports')); - $this->settings->set('Development', $this->settings->getFrom($this->pathGlobal, 'Development')); + $chain->set('Debug', $chain->getFrom($this->getGlobalPath(), 'Debug')); + $chain->set('mail', $chain->getFrom($this->getGlobalPath(), 'mail')); + $chain->set('General', $chain->getFrom($this->getGlobalPath(), 'General')); + $chain->set('Segments', $chain->getFrom($this->getGlobalPath(), 'Segments')); + $chain->set('Tracker', $chain->getFrom($this->getGlobalPath(), 'Tracker')); + $chain->set('Deletelogs', $chain->getFrom($this->getGlobalPath(), 'Deletelogs')); + $chain->set('Deletereports', $chain->getFrom($this->getGlobalPath(), 'Deletereports')); + $chain->set('Development', $chain->getFrom($this->getGlobalPath(), 'Development')); } // for unit tests, we set that no plugin is installed. This will force // the test initialization to create the plugins tables, execute ALTER queries, etc. - $this->settings->set('PluginsInstalled', array('PluginsInstalled' => array())); + $chain->set('PluginsInstalled', array('PluginsInstalled' => array())); } protected function postConfigTestEvent() { - Piwik::postTestEvent('Config.createConfigSingleton', array($this->settings, $this) ); + Piwik::postTestEvent('Config.createConfigSingleton', array($this->settings->getIniFileChain(), $this) ); } /** @@ -230,7 +217,7 @@ class Config extends Singleton return $limits; } - protected static function getByDomainConfigPath() + public static function getByDomainConfigPath() { $host = self::getHostname(); $hostConfig = self::getLocalConfigInfoForHostname($host); @@ -281,16 +268,15 @@ class Config extends Singleton throw new Exception('Piwik domain is not a valid looking hostname (' . $filename . ').'); } - $this->pathLocal = $hostConfig['path']; - $this->initialized = false; + $pathLocal = $hostConfig['path']; try { - $this->reload(); + $this->reload($pathLocal); } catch (Exception $ex) { // pass (not required for local file to exist at this point) } - return $this->pathLocal; + return $pathLocal; } /** @@ -300,7 +286,7 @@ class Config extends Singleton */ public function isFileWritable() { - return is_writable($this->pathLocal); + return is_writable($this->settings->getPathLocal()); } /** @@ -330,11 +316,10 @@ class Config extends Singleton * @throws \Exception if the global config file is not found and this is a tracker request, or * if the local config file is not found and this is NOT a tracker request. */ - protected function reload() + protected function reload($pathLocal = null, $pathGlobal = null, $pathCommon = null) { $this->initialized = false; - - $this->settings->reload(array($this->pathGlobal, $this->pathCommon), $this->pathLocal); + $this->settings->reload($pathGlobal, $pathLocal, $pathCommon); } /** @@ -342,7 +327,7 @@ class Config extends Singleton */ public function existsLocalConfig() { - return is_readable($this->pathLocal); + return is_readable($this->getLocalPath()); } public function deleteLocalConfig() @@ -351,13 +336,6 @@ class Config extends Singleton unlink($configLocal); } -/* public function checkLocalConfigFound() - { - if (!$this->existsLocalConfig()) { - throw new ConfigNotFoundException(Piwik::translate('General_ExceptionConfigurationFileNotFound', array($this->pathLocal))); - } - }*/ - /** * Decode HTML entities * @@ -412,28 +390,21 @@ class Config extends Singleton if (!$this->initialized) { $this->initialized = true; - // this is done lazily and not in __construct so Installation will properly be triggered. ideally, it should be - // done in __construct, but the exception that is thrown depends on Translator which depends on Config. the - // circular dependency causes problems. - /*if (!SettingsServer::isTrackerApiRequest()) { - $this->checkLocalConfigFound(); - }*/ - $this->postConfigTestEvent(); } - $section =& $this->settings->get($name); + $section =& $this->settings->getIniFileChain()->get($name); return $section; } public function getFromGlobalConfig($name) { - return $this->settings->getFrom($this->pathGlobal, $name); + return $this->settings->getIniFileChain()->getFrom($this->getGlobalPath(), $name); } public function getFromCommonConfig($name) { - return $this->settings->getFrom($this->pathCommon, $name); + return $this->settings->getIniFileChain()->getFrom($this->getCommonPath(), $name); } /** @@ -445,7 +416,7 @@ class Config extends Singleton */ public function __set($name, $value) { - $this->settings->set($name, $value); + $this->settings->getIniFileChain()->set($name, $value); } /** @@ -456,16 +427,18 @@ class Config extends Singleton */ public function dumpConfig() { - $this->encodeValues($this->settings->getAll()); + $chain = $this->settings->getIniFileChain(); + + $this->encodeValues($chain->getAll()); try { $header = "; <?php exit; ?> DO NOT REMOVE THIS LINE\n"; $header .= "; file automatically generated or modified by Piwik; you can manually override the default values in global.ini.php by redefining them in this file.\n"; - $dumpedString = $this->settings->dumpChanges($header); + $dumpedString = $chain->dumpChanges($header); - $this->decodeValues($this->settings->getAll()); + $this->decodeValues($chain->getAll()); } catch (Exception $ex) { - $this->decodeValues($this->settings->getAll()); + $this->decodeValues($chain->getAll()); throw $ex; } @@ -495,7 +468,7 @@ class Config extends Singleton if ($output !== null && $output !== false ) { - $success = @file_put_contents($this->pathLocal, $output); + $success = @file_put_contents($this->getLocalPath(), $output); if ($success === false) { throw $this->getConfigNotWritableException(); } @@ -522,7 +495,7 @@ class Config extends Singleton */ public function getConfigNotWritableException() { - $path = "config/" . basename($this->pathLocal); + $path = "config/" . basename($this->getLocalPath()); return new Exception(Piwik::translate('General_ConfigFileIsNotWritable', array("(" . $path . ")", ""))); } } \ No newline at end of file diff --git a/core/Config/IniFileChain.php b/core/Config/IniFileChain.php index bffca9cbc6dd9313b5510ee15b1a89ab5be92af7..95516921b846c39d1fd2a29356228a2613cdf1e4 100644 --- a/core/Config/IniFileChain.php +++ b/core/Config/IniFileChain.php @@ -206,12 +206,7 @@ class IniFileChain $reader = new IniReader(); foreach ($this->settingsChain as $file => $ignore) { if (is_readable($file)) { - try { - $this->settingsChain[$file] = $reader->readFile($file); - } catch (IniReadingException $ex) { - $message = Piwik::translate('General_ExceptionUnreadableFileDisabledMethod', array($file, "parse_ini_file()")); - throw new IniReadingException($message, $code = 0, $ex); - } + $this->settingsChain[$file] = $reader->readFile($file); } } diff --git a/core/ExceptionHandler.php b/core/ExceptionHandler.php index e68a058e4ae1219e899332c5e2c380bcc35b93a5..5b02cd77f6a706bdf58deb6b2530be128e3dd6f7 100644 --- a/core/ExceptionHandler.php +++ b/core/ExceptionHandler.php @@ -78,7 +78,11 @@ class ExceptionHandler $logoHeaderUrl = $logo->getHeaderLogoUrl(); $logoFaviconUrl = $logo->getPathUserFavicon(); } catch (Exception $ex) { - Log::debug($ex); + try { + Log::debug($ex); + } catch (\Exception $otherEx) { + // DI container may not be setup at this point + } } $result = Piwik_GetErrorMessagePage($message, $debugTrace, true, true, $logoHeaderUrl, $logoFaviconUrl); diff --git a/core/Plugin/Manager.php b/core/Plugin/Manager.php index 0d69083af74e2b0200302d33c16ad6aab6ebce93..1cdf9ebb2ee8ff860e2eca333b9ddd2e99ce6b64 100644 --- a/core/Plugin/Manager.php +++ b/core/Plugin/Manager.php @@ -1280,9 +1280,7 @@ class Manager */ protected function getPluginsFromGlobalIniConfigFile() // TODO: if this is only used for sorting, move to PluginList { - $pluginsBundledWithPiwik = PiwikConfig::getInstance()->getFromGlobalConfig('Plugins'); - $pluginsBundledWithPiwik = $pluginsBundledWithPiwik['Plugins']; - return $pluginsBundledWithPiwik; + return $this->pluginList->getPluginsBundledWithPiwik(); } /** @@ -1292,7 +1290,6 @@ class Manager protected function isPluginEnabledByDefault($name) { $pluginsBundledWithPiwik = $this->getPluginsFromGlobalIniConfigFile(); - if(empty($pluginsBundledWithPiwik)) { return false; } diff --git a/core/Translation/Translator.php b/core/Translation/Translator.php index dda946ccd9bc0c0b511235a6c35cc610ee76109a..46503a018568fa8c0219ace7ed124a0374b7fb9e 100644 --- a/core/Translation/Translator.php +++ b/core/Translation/Translator.php @@ -114,7 +114,8 @@ class Translator */ public function getDefaultLanguage() { - return Config::getInstance()->General['default_language']; + $generalSection = Config::getInstance()->General; + return @$generalSection['default_language'] ?: 'en'; } /** diff --git a/piwik.php b/piwik.php index 8eb43b39b47e6368bfc47a28395481f72e47758a..b0915e38a15bcf23fbb6e34f6a83460c93153b61 100644 --- a/piwik.php +++ b/piwik.php @@ -8,6 +8,7 @@ * @package Piwik */ +use Piwik\SettingsServer; use Piwik\Tracker\RequestSet; use Piwik\Tracker; use Piwik\Tracker\Handler; @@ -48,6 +49,9 @@ require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Cache.php'; require_once PIWIK_INCLUDE_PATH . '/core/Tracker/Request.php'; require_once PIWIK_INCLUDE_PATH . '/core/Cookie.php'; +// TODO should move to Tracker application class later. currently needed for environment validation. +SettingsServer::setIsTrackerApiRequest(); + $environment = new \Piwik\Application\Environment(null); $environment->init(); diff --git a/tests/PHPUnit/Framework/Fixture.php b/tests/PHPUnit/Framework/Fixture.php index fb8fd9e54d3e2ae858f72814ebf95e557a49f3ae..87ccf6d07199edd011174fe7c63b46926071a169 100644 --- a/tests/PHPUnit/Framework/Fixture.php +++ b/tests/PHPUnit/Framework/Fixture.php @@ -152,12 +152,15 @@ class Fixture extends \PHPUnit_Framework_Assert { if ($this->createConfig) { IniSettingsProvider::unsetSingletonInstance(); - Config::setSingletonInstance(new TestConfig()); } $this->piwikEnvironment = new Environment('test'); $this->piwikEnvironment->init(); + if ($this->createConfig) { + Config::setSingletonInstance(new TestConfig()); + } + try { $this->dbName = $this->getDbName(); diff --git a/tests/PHPUnit/Framework/Mock/TestConfig.php b/tests/PHPUnit/Framework/Mock/TestConfig.php index ebdb6e473a59b6753f9d105087e7d4f779a7b8fa..4feb2b3e583fba0c20605afba4baa29156a71d11 100644 --- a/tests/PHPUnit/Framework/Mock/TestConfig.php +++ b/tests/PHPUnit/Framework/Mock/TestConfig.php @@ -25,16 +25,16 @@ class TestConfig extends Config $this->allowSave = $allowSave; $this->doSetTestEnvironment = $doSetTestEnvironment; - $this->reload(); + $this->reload($pathGlobal, $pathLocal, $pathCommon); } - public function reload() + public function reload($pathLocal = null, $pathGlobal = null, $pathCommon = null) { if ($this->isSettingTestEnv) { - parent::reload(); + parent::reload($pathGlobal, $pathLocal, $pathCommon); } else { $this->isSettingTestEnv = true; - $this->setTestEnvironment($this->getLocalPath(), $this->getGlobalPath(), $this->getCommonPath(), $this->allowSave); + $this->setTestEnvironment($pathLocal, $pathGlobal, $pathCommon, $this->allowSave); $this->isSettingTestEnv = false; } } diff --git a/tests/PHPUnit/System/EnvironmentValidationTest.php b/tests/PHPUnit/System/EnvironmentValidationTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e80cb4382285f9d828fdb28df38c1d0108e9f706 --- /dev/null +++ b/tests/PHPUnit/System/EnvironmentValidationTest.php @@ -0,0 +1,222 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\System; + +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\TestCase\SystemTestCase; + +/** + * @group System + */ +class EnvironmentValidationTest extends SystemTestCase +{ + public function getEntryPointsToTest() + { + return array( + array('tracker'), + array('web'), + array('console') + ); + } + + public function setUp() + { + parent::setUp(); + + $testingEnvironment = new \Piwik_TestingEnvironment(); + $testingEnvironment->configFileGlobal = null; + $testingEnvironment->configFileLocal = null; + $testingEnvironment->configFileCommon = null; + $testingEnvironment->save(); + } + + /** + * @dataProvider getEntryPointsToTest + */ + public function test_NoGlobalConfigFile_TriggersError($entryPoint) + { + $this->simulateAbsentConfigFile('global.ini.php'); + + $output = $this->triggerPiwikFrom($entryPoint); + $this->assertOutputContainsConfigFileMissingError('global.ini.php', $output); + } + + public function getEntryPointsThatErrorWithNoLocal() + { + return array( + array('tracker'), + array('console') + ); + } + + /** + * @dataProvider getEntryPointsThatErrorWithNoLocal + */ + public function test_NoLocalConfigFile_TriggersError($entryPoint) + { + $this->simulateAbsentConfigFile('config.ini.php'); + + $output = $this->triggerPiwikFrom($entryPoint); + $this->assertOutputContainsConfigFileMissingError('config.ini.php', $output); + } + + public function test_NoLocalConfigFile_StartsInstallation_PiwikAccessedThroughWeb() + { + $this->simulateAbsentConfigFile('config.ini.php'); + + $output = $this->triggerPiwikFrom('web'); + $this->assertInstallationProcessStarted($output); + } + + public function getEntryPointsAndConfigFilesToTest() + { + return array( + array('global.ini.php', 'tracker'), + array('global.ini.php', 'web'), + array('global.ini.php', 'console'), + + array('config.ini.php', 'tracker'), + array('config.ini.php', 'web'), + array('config.ini.php', 'console'), + + array('common.config.ini.php', 'tracker'), + array('common.config.ini.php', 'web'), + array('common.config.ini.php', 'console'), + ); + } + + /** + * @dataProvider getEntryPointsAndConfigFilesToTest + */ + public function test_BadConfigFile_TriggersError($configFile, $entryPoint) + { + $this->simulateBadConfigFile($configFile); + + $output = $this->triggerPiwikFrom($entryPoint); + $this->assertOutputContainsConfigFileMissingError($configFile, $output); + } + + /** + * @dataProvider getEntryPointsToTest + */ + public function test_BadDomainSpecificLocalConfigFile_TriggersError($entryPoint) + { + $this->simulateHost('piwik.kobra.org'); + + $configFile = 'piwik.kobra.org.config.ini.php'; + $this->simulateBadConfigFile($configFile); + + $output = $this->triggerPiwikFrom($entryPoint); + $this->assertOutputContainsBadConfigFileError($output); + } + + private function assertOutputContainsConfigFileMissingError($fileName, $output) + { + // TODO: need to tweak error message displayed. + $this->assertRegExp("/The configuration file \\{.*\\/" . preg_quote($fileName) . "\\} has not been found or could not be read\\./", $output); + } + + private function assertOutputContainsBadConfigFileError($output) + { + // TODO: also mention bad INI format possible + $this->assertRegExp("/The configuration file \\{.*\\/piwik.php\\} could not be read\\. Your host may have disabled parse_ini_file\\(\\)/", $output); + } + + private function assertInstallationProcessStarted($output) + { + $this->assertRegExp('<div id="installationPage">', $output); + } + + private function simulateAbsentConfigFile($fileName) + { + $testingEnvironment = new \Piwik_TestingEnvironment(); + + if ($fileName == 'global.ini.php') { + $testingEnvironment->configFileGlobal = PIWIK_INCLUDE_PATH . '/tmp/nonexistant/global.ini.php'; + } else if ($fileName == 'common.config.ini.php') { + $testingEnvironment->configFileCommon = PIWIK_INCLUDE_PATH . '/tmp/nonexistant/common.config.ini.php'; + } else { + $testingEnvironment->configFileLocal = PIWIK_INCLUDE_PATH . '/tmp/nonexistant/' . $fileName; + } + + $testingEnvironment->save(); + } + + private function simulateBadConfigFile($fileName) + { + $testingEnvironment = new \Piwik_TestingEnvironment(); + + if ($fileName == 'global.ini.php') { + $testingEnvironment->configFileGlobal = PIWIK_INCLUDE_PATH . '/piwik.php'; + } else if ($fileName == 'common.config.ini.php') { + $testingEnvironment->configFileCommon = PIWIK_INCLUDE_PATH . '/piwik.php'; + } else { + $testingEnvironment->configFileLocal = PIWIK_INCLUDE_PATH . '/piwik.php'; + } + + $testingEnvironment->save(); + } + + private function simulateHost($host) + { + $testingEnvironment = new \Piwik_TestingEnvironment(); + $testingEnvironment->hostOverride = $host; + $testingEnvironment->save(); + } + + private function triggerPiwikFrom($entryPoint) + { + if ($entryPoint == 'tracker') { + return $this->sendRequestToTracker(); + } else if ($entryPoint == 'web') { + return $this->sendRequestToWeb(); + } else if ($entryPoint == 'console') { + return $this->startConsoleProcess(); + } else { + throw new \Exception("Don't know how to access '$entryPoint'."); + } + } + + private function sendRequestToTracker() + { + return $this->curl(Fixture::getRootUrl() . 'tests/PHPUnit/proxy/piwik.php?idsite=1&rec=1&action_name=something'); + } + + private function sendRequestToWeb() + { + return $this->curl(Fixture::getRootUrl() . 'tests/PHPUnit/proxy/index.php'); + } + + private function startConsoleProcess() + { + $pathToProxyConsole = PIWIK_INCLUDE_PATH . '/tests/PHPUnit/proxy/console'; + return shell_exec("php '$pathToProxyConsole' list 2>&1"); + } + + private function curl($url) + { + if (!function_exists('curl_init')) { + $this->markTestSkipped('Curl is not installed'); + } + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + + $response = curl_exec($ch); + $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + $response = substr($response, $headerSize); + + curl_close($ch); + + return $response; + } +} \ No newline at end of file diff --git a/tests/PHPUnit/TestingEnvironment.php b/tests/PHPUnit/TestingEnvironment.php index afd398c01b879faf3861ef425b3b79f7fc2d662d..dc0b6adf774f6572260eea3f5a4af711ae8e1f5b 100644 --- a/tests/PHPUnit/TestingEnvironment.php +++ b/tests/PHPUnit/TestingEnvironment.php @@ -102,7 +102,10 @@ class Piwik_TestingEnvironment public function getCoreAndSupportedPlugins() { - $disabledPlugins = PluginManager::getInstance()->getCorePluginsDisabledByDefault(); + $settings = new \Piwik\Application\Kernel\GlobalSettingsProvider\IniSettingsProvider(); + $pluginManager = new PluginManager(new \Piwik\Application\Kernel\PluginList\IniPluginList($settings)); + + $disabledPlugins = $pluginManager->getCorePluginsDisabledByDefault(); $disabledPlugins[] = 'LoginHttpAuth'; $disabledPlugins[] = 'ExampleVisualization'; @@ -110,13 +113,13 @@ class Piwik_TestingEnvironment 'DBStats', 'ExampleUI', 'ExampleCommand', 'ExampleSettingsPlugin' )); - $plugins = array_filter(PluginManager::getInstance()->readPluginsDirectory(), function ($pluginName) use ($disabledPlugins) { + $plugins = array_filter($pluginManager->readPluginsDirectory(), function ($pluginName) use ($disabledPlugins, $pluginManager) { if (in_array($pluginName, $disabledPlugins)) { return false; } - return PluginManager::getInstance()->isPluginBundledWithCore($pluginName) - || PluginManager::getInstance()->isPluginOfficialAndNotBundledWithCore($pluginName); + return $pluginManager->isPluginBundledWithCore($pluginName) + || $pluginManager->isPluginOfficialAndNotBundledWithCore($pluginName); }); sort($plugins); @@ -148,25 +151,27 @@ class Piwik_TestingEnvironment \Piwik\Profiler::setupProfilerXHProf($mainRun = false, $setupDuringTracking = true); } - if ($testingEnvironment->dontUseTestConfig) { - Config::setSingletonInstance(new Config( - $testingEnvironment->configFileGlobal, $testingEnvironment->configFileLocal, $testingEnvironment->configFileCommon - )); - } + \Piwik\Application\Kernel\GlobalSettingsProvider\IniSettingsProvider::getSingletonInstance( + $testingEnvironment->configFileGlobal, + $testingEnvironment->configFileLocal, + $testingEnvironment->configFileCommon + ); // Apply DI config from the fixture + $diConfig = array(); if ($testingEnvironment->fixtureClass) { $fixtureClass = $testingEnvironment->fixtureClass; if (class_exists($fixtureClass)) { /** @var Fixture $fixture */ $fixture = new $fixtureClass; $diConfig = $fixture->provideContainerConfig(); - if (!empty($diConfig)) { - StaticContainer::addDefinitions($diConfig); - } } } + if (!empty($diConfig)) { + StaticContainer::addDefinitions($diConfig); + } + \Piwik\Cache\Backend\File::$invalidateOpCacheBeforeRead = true; Piwik::addAction('Access.createAccessSingleton', function($access) use ($testingEnvironment) { @@ -175,8 +180,16 @@ class Piwik_TestingEnvironment \Piwik\Access::setSingletonInstance($access); } }); + + $pluginsToLoad = $testingEnvironment->getCoreAndSupportedPlugins(); + if (!empty($testingEnvironment->pluginsToLoad)) { + $pluginsToLoad = array_unique(array_merge($pluginsToLoad, $testingEnvironment->pluginsToLoad)); + } + + sort($pluginsToLoad); + if (!$testingEnvironment->dontUseTestConfig) { - Piwik::addAction('Config.createConfigSingleton', function(IniFileChain $chain) use ($testingEnvironment) { + Piwik::addAction('Config.createConfigSingleton', function(IniFileChain $chain) use ($testingEnvironment, $pluginsToLoad) { $general =& $chain->get('General'); $plugins =& $chain->get('Plugins'); $log =& $chain->get('log'); @@ -186,14 +199,6 @@ class Piwik_TestingEnvironment $general['session_save_handler'] = 'dbtable'; } - $manager = \Piwik\Plugin\Manager::getInstance(); - $pluginsToLoad = $testingEnvironment->getCoreAndSupportedPlugins(); - if (!empty($testingEnvironment->pluginsToLoad)) { - $pluginsToLoad = array_unique(array_merge($pluginsToLoad, $testingEnvironment->pluginsToLoad)); - } - - sort($pluginsToLoad); - $plugins['Plugins'] = $pluginsToLoad; $log['log_writers'] = array('file'); @@ -216,15 +221,27 @@ class Piwik_TestingEnvironment Config::setSingletonInstance(new TestConfig( $testingEnvironment->configFileGlobal, $testingEnvironment->configFileLocal, $testingEnvironment->configFileCommon )); + } else { + Config::setSingletonInstance(new Config( + $testingEnvironment->configFileGlobal, $testingEnvironment->configFileLocal, $testingEnvironment->configFileCommon + )); } Piwik::addAction('Request.dispatch', function() use ($testingEnvironment) { if (empty($_GET['ignoreClearAllViewDataTableParameters'])) { // TODO: should use testingEnvironment variable, not query param - \Piwik\ViewDataTable\Manager::clearAllViewDataTableParameters(); + try { + \Piwik\ViewDataTable\Manager::clearAllViewDataTableParameters(); + } catch (\Exception $ex) { + // ignore (in case DB is not setup) + } } if ($testingEnvironment->optionsOverride) { - foreach ($testingEnvironment->optionsOverride as $name => $value) { - Option::set($name, $value); + try { + foreach ($testingEnvironment->optionsOverride as $name => $value) { + Option::set($name, $value); + } + } catch (\Exception $ex) { + // ignore (in case DB is not setup) } } diff --git a/tests/PHPUnit/proxy/console b/tests/PHPUnit/proxy/console new file mode 100644 index 0000000000000000000000000000000000000000..1ccef19ecc0dafbd4506c608cb4241e87a1b7223 --- /dev/null +++ b/tests/PHPUnit/proxy/console @@ -0,0 +1,7 @@ +#!/usr/bin/env php +<?php +require realpath(dirname(__FILE__)) . "/includes.php"; + +Piwik_TestingEnvironment::addHooks(); + +require_once PIWIK_INCLUDE_PATH . "/console";