Merge remote branch 'origin/2.0' into 2.0-class-loading

Conflicts:
	lib/Cake/config/ini_file.php
This commit is contained in:
José Lorenzo Rodríguez 2010-12-06 09:42:52 -04:30
commit 5dddb362ec
24 changed files with 568 additions and 171 deletions

View file

@ -211,7 +211,6 @@ class FixtureTask extends BakeTask {
$this->_Schema = new CakeSchema(); $this->_Schema = new CakeSchema();
$data = $this->_Schema->read(array('models' => false, 'connection' => $this->connection)); $data = $this->_Schema->read(array('models' => false, 'connection' => $this->connection));
if (!isset($data['tables'][$useTable])) { if (!isset($data['tables'][$useTable])) {
$this->err('Could not find your selected table ' . $useTable); $this->err('Could not find your selected table ' . $useTable);
return false; return false;

View file

@ -0,0 +1,109 @@
<?php
/**
* IniReader
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake
* @subpackage cake.libs.config
* @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Ini file configuration parser. Since IniReader uses parse_ini_file underneath,
* you should be aware that this class shares the same behavior, especially with
* regards to boolean and null values.
*
* In addition to the native parse_ini_file features, IniReader also allows you
* to create nested array structures through usage of . delimited names. This allows
* you to create nested arrays structures in an ini config file. For example:
*
* `db.password = secret` would turn into `array('db' => array('password' => 'secret'))`
*
* You can nest properties as deeply as needed using .'s. IniReader also manipulates
* how the special ini values of 'yes', 'no', 'on', 'off', 'null' are handled.
* These values will be converted to their boolean equivalents.
*
* @package cake.config
* @see http://php.net/parse_ini_file
*/
class IniReader implements ConfigReaderInterface {
/**
* The path to read ini files from.
*
* @var array
*/
protected $_path;
/**
* The section to read, if null all sections will be read.
*
* @var string
*/
protected $_section;
/**
* Build and construct a new ini file parser. The parser can be used to read
* ini files that are on the filesystem.
*
* @param string $path Path to load ini config files from.
* @param string $section Only get one section.
*/
public function __construct($path, $section = null) {
$this->_path = $path;
$this->_section = $section;
}
/**
* Read an ini file and return the results as an array.
*
* @param string $file Name of the file to read.
* @return array
*/
public function read($file) {
$filename = $this->_path . $file;
$contents = parse_ini_file($filename, true);
if (!empty($this->_section) && isset($contents[$this->_section])) {
$values = $this->_parseNestedValues($contents[$this->_section]);
} else {
$values = array();
foreach ($contents as $section => $attribs) {
$values[$section] = $this->_parseNestedValues($attribs);
}
}
return $values;
}
/**
* parses nested values out of keys.
*
* @param array $values Values to be exploded.
* @return array Array of values exploded
*/
protected function _parseNestedValues($values) {
foreach ($values as $key => $value) {
if ($value === '1') {
$value = true;
}
if ($value === '') {
$value = false;
}
if (strpos($key, '.') !== false) {
$values = Set::insert($values, $key, $value);
} else {
$values[$key] = $value;
}
}
return $values;
}
}

View file

@ -0,0 +1,79 @@
<?php
/**
* PhpReader file
*
* PHP 5
*
* CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
* @package cake
* @subpackage cake.tests.cases.libs
* @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* PHP Reader allows Configure to load configuration values from
* files containing simple PHP arrays.
*
* @package cake.libs.config
*/
class PhpReader implements ConfigReaderInterface {
/**
* The path this reader finds files on.
*
* @var string
*/
protected $_path = null;
/**
* Constructor for PHP Config file reading.
*
* @param string $path The path to read config files from. Defaults to CONFIGS
*/
public function __construct($path = CONFIGS) {
$this->_path = $path;
}
/**
* Read a config file and return its contents.
*
* Keys with `.` will be treated as values in plugins. Instead of reading from
* the initialized path, plugin keys will be located using App::pluginPath().
*
*
* @param string $key The identifier to read from. If the key has a . it will be treated
* as a plugin prefix.
* @return array Parsed configuration values.
* @throws RuntimeException when files don't exist or they don't contain `$config`.
* InvalidArgumentException when files contain '..' as this could lead to abusive reads.
*/
public function read($key) {
if (strpos($key, '..') !== false) {
throw new InvalidArgumentException(__('Cannot load configuration files with ../ in them.'));
}
list($plugin, $key) = pluginSplit($key);
if ($plugin) {
$file = App::pluginPath($plugin) . 'config' . DS . $key . '.php';
} else {
$file = $this->_path . $key . '.php';
}
if (!file_exists($file)) {
throw new RuntimeException(__('Could not load configuration file: ') . $file);
}
include $file;
if (!isset($config)) {
throw new RuntimeException(
sprintf(__('No variable $config found in %s.php'), $file)
);
}
return $config;
}
}

View file

@ -43,7 +43,6 @@ class ConsoleErrorHandlerTest extends CakeTestCase {
*/ */
function tearDown() { function tearDown() {
parent::tearDown(); parent::tearDown();
ConsoleErrorHandler::$stderr = null;
} }
/** /**
@ -52,7 +51,7 @@ class ConsoleErrorHandlerTest extends CakeTestCase {
* @return void * @return void
*/ */
function testHandleError() { function testHandleError() {
$content = '<error>Notice Error:</error> This is a notice error in [/some/file, line 275]'; $content = "<error>Notice Error:</error> This is a notice error in [/some/file, line 275]\n";
ConsoleErrorHandler::$stderr->expects($this->once())->method('write') ConsoleErrorHandler::$stderr->expects($this->once())->method('write')
->with($content); ->with($content);

View file

@ -84,7 +84,7 @@ class ApiShellTest extends CakeTestCase {
$this->Shell->expects($this->at(2))->method('out')->with($expected); $this->Shell->expects($this->at(2))->method('out')->with($expected);
$this->Shell->args = array('controller'); $this->Shell->args = array('controller');
$this->Shell->paths['controller'] = CAKE_CORE_INCLUDE_PATH . DS . LIBS . 'controller' . DS; $this->Shell->paths['controller'] = LIBS . 'controller' . DS;
$this->Shell->main(); $this->Shell->main();
} }
} }

View file

@ -25,12 +25,12 @@ App::import('Shell', 'Shell', false);
require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CAKE . 'console' . DS . 'shell_dispatcher.php';
/** /**
* TestShell class * ShellTestShell class
* *
* @package cake * @package cake
* @subpackage cake.tests.cases.console.libs * @subpackage cake.tests.cases.console.libs
*/ */
class TestShell extends Shell { class ShellTestShell extends Shell {
/** /**
* name property * name property
@ -38,7 +38,7 @@ class TestShell extends Shell {
* @var name * @var name
* @access public * @access public
*/ */
public $name = 'TestShell'; public $name = 'ShellTestShell';
/** /**
* stopped property * stopped property
@ -133,7 +133,7 @@ class ShellTest extends CakeTestCase {
$output = $this->getMock('ConsoleOutput', array(), array(), '', false); $output = $this->getMock('ConsoleOutput', array(), array(), '', false);
$error = $this->getMock('ConsoleOutput', array(), array(), '', false); $error = $this->getMock('ConsoleOutput', array(), array(), '', false);
$in = $this->getMock('ConsoleInput', array(), array(), '', false); $in = $this->getMock('ConsoleInput', array(), array(), '', false);
$this->Shell = new TestShell($output, $error, $in); $this->Shell = new ShellTestShell($output, $error, $in);
} }
/** /**
@ -142,7 +142,7 @@ class ShellTest extends CakeTestCase {
* @return void * @return void
*/ */
public function testConstruct() { public function testConstruct() {
$this->assertEqual($this->Shell->name, 'TestShell'); $this->assertEqual($this->Shell->name, 'ShellTestShell');
$this->assertType('ConsoleInput', $this->Shell->stdin); $this->assertType('ConsoleInput', $this->Shell->stdin);
$this->assertType('ConsoleOutput', $this->Shell->stdout); $this->assertType('ConsoleOutput', $this->Shell->stdout);
$this->assertType('ConsoleOutput', $this->Shell->stderr); $this->assertType('ConsoleOutput', $this->Shell->stderr);

View file

@ -70,7 +70,7 @@ class ProjectTaskTest extends CakeTestCase {
* @return void * @return void
*/ */
protected function _setupTestProject() { protected function _setupTestProject() {
$skel = CAKE_CORE_INCLUDE_PATH . DS . CAKE . 'console' . DS . 'templates' . DS . 'skel'; $skel = CAKE . 'console' . DS . 'templates' . DS . 'skel';
$this->Task->expects($this->at(0))->method('in')->will($this->returnValue('y')); $this->Task->expects($this->at(0))->method('in')->will($this->returnValue('y'));
$this->Task->bake($this->Task->path . 'bake_test_app', $skel); $this->Task->bake($this->Task->path . 'bake_test_app', $skel);
} }
@ -256,7 +256,7 @@ class ProjectTaskTest extends CakeTestCase {
* @return void * @return void
*/ */
public function testExecute() { public function testExecute() {
$this->Task->params['skel'] = CAKE_CORE_INCLUDE_PATH . DS . CAKE . DS . 'console' . DS. 'templates' . DS . 'skel'; $this->Task->params['skel'] = CAKE . DS . 'console' . DS. 'templates' . DS . 'skel';
$this->Task->params['working'] = TMP . 'tests' . DS; $this->Task->params['working'] = TMP . 'tests' . DS;
$path = $this->Task->path . 'bake_test_app'; $path = $this->Task->path . 'bake_test_app';
@ -264,7 +264,7 @@ class ProjectTaskTest extends CakeTestCase {
$this->Task->expects($this->at(1))->method('in')->will($this->returnValue('y')); $this->Task->expects($this->at(1))->method('in')->will($this->returnValue('y'));
$this->Task->execute(); $this->Task->execute();
$this->assertTrue(is_dir($path), 'No project dir %s'); $this->assertTrue(is_dir($path), 'No project dir');
$this->assertTrue(is_dir($path . DS . 'controllers'), 'No controllers dir '); $this->assertTrue(is_dir($path . DS . 'controllers'), 'No controllers dir ');
$this->assertTrue(is_dir($path . DS . 'controllers' . DS .'components'), 'No components dir '); $this->assertTrue(is_dir($path . DS . 'controllers' . DS .'components'), 'No components dir ');
$this->assertTrue(is_dir($path . DS . 'models'), 'No models dir'); $this->assertTrue(is_dir($path . DS . 'models'), 'No models dir');

View file

@ -88,7 +88,7 @@ class TemplateTaskTest extends CakeTestCase {
* @return void * @return void
*/ */
public function testFindingInstalledThemesForBake() { public function testFindingInstalledThemesForBake() {
$consoleLibs = CAKE_CORE_INCLUDE_PATH . DS . CAKE . 'console' . DS; $consoleLibs = CAKE . 'console' . DS;
$this->Task->initialize(); $this->Task->initialize();
$this->assertEqual($this->Task->templatePaths, array('default' => $consoleLibs . 'templates' . DS . 'default' . DS)); $this->assertEqual($this->Task->templatePaths, array('default' => $consoleLibs . 'templates' . DS . 'default' . DS));
} }

View file

@ -34,9 +34,10 @@ class AllConfigureTest extends PHPUnit_Framework_TestSuite {
* @return void * @return void
*/ */
public static function suite() { public static function suite() {
$suite = new PHPUnit_Framework_TestSuite('All Configure, App and ClassRegistry related tests'); $suite = new CakeTestSuite('All Configure, App and ClassRegistry related tests');
$suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'configure.test.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'configure.test.php');
$suite->addTestDirectory(CORE_TEST_CASES . DS . 'libs' . DS . 'config');
$suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'app.test.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'app.test.php');
$suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'class_registry.test.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'class_registry.test.php');
return $suite; return $suite;

View file

@ -211,9 +211,9 @@ class AppImportTest extends CakeTestCase {
$this->assertTrue($file); $this->assertTrue($file);
$this->assertTrue(class_exists('Shell')); $this->assertTrue(class_exists('Shell'));
$file = App::import('Lib', 'cache/Apc'); $file = App::import('Lib', 'config/PhpReader');
$this->assertTrue($file); $this->assertTrue($file);
$this->assertTrue(class_exists('ApcEngine')); $this->assertTrue(class_exists('PhpReader'));
$file = App::import('Model', 'SomeRandomModelThatDoesNotExist', false); $file = App::import('Model', 'SomeRandomModelThatDoesNotExist', false);
$this->assertFalse($file); $this->assertFalse($file);

View file

@ -20,8 +20,7 @@
if (!class_exists('Cache')) { if (!class_exists('Cache')) {
require LIBS . 'cache.php'; require LIBS . 'cache.php';
} }
App::import('Core', 'cache/Memcache'); require_once LIBS . 'cache' . DS . 'memcache.php';
class TestMemcacheEngine extends MemcacheEngine { class TestMemcacheEngine extends MemcacheEngine {
/** /**

View file

@ -1,6 +1,6 @@
<?php <?php
/** /**
* IniFileTest * IniReaderTest
* *
* PHP 5 * PHP 5
* *
@ -17,9 +17,9 @@
* @since CakePHP(tm) v 2.0 * @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/ */
App::import('Core', 'config/IniFile'); App::import('Core', 'config/IniReader');
class IniFileTest extends CakeTestCase { class IniReaderTest extends CakeTestCase {
/** /**
* The test file that will be read. * The test file that will be read.
@ -35,7 +35,7 @@ class IniFileTest extends CakeTestCase {
*/ */
function setup() { function setup() {
parent::setup(); parent::setup();
$this->file = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'config'. DS . 'acl.ini.php'; $this->path = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'config'. DS;
} }
/** /**
@ -44,7 +44,8 @@ class IniFileTest extends CakeTestCase {
* @return void * @return void
*/ */
function testConstruct() { function testConstruct() {
$config = new IniFile($this->file); $reader = new IniReader($this->path);
$config = $reader->read('acl.ini.php');
$this->assertTrue(isset($config['admin'])); $this->assertTrue(isset($config['admin']));
$this->assertTrue(isset($config['paul']['groups'])); $this->assertTrue(isset($config['paul']['groups']));
@ -57,32 +58,45 @@ class IniFileTest extends CakeTestCase {
* @return void * @return void
*/ */
function testReadingOnlyOneSection() { function testReadingOnlyOneSection() {
$config = new IniFile($this->file, 'admin'); $reader = new IniReader($this->path, 'admin');
$config = $reader->read('acl.ini.php');
$this->assertTrue(isset($config['groups'])); $this->assertTrue(isset($config['groups']));
$this->assertEquals('administrators', $config['groups']); $this->assertEquals('administrators', $config['groups']);
} }
/** /**
* test getting all the values as an array * test that names with .'s get exploded into arrays.
* *
* @return void * @return void
*/ */
function testAsArray() { function testReadingValuesWithDots() {
$config = new IniFile($this->file); $reader = new IniReader($this->path);
$content = $config->asArray(); $config = $reader->read('nested.ini');
$this->assertTrue(isset($content['admin']['groups'])); $this->assertTrue(isset($config['database']['db']['username']));
$this->assertTrue(isset($content['paul']['groups'])); $this->assertEquals('mark', $config['database']['db']['username']);
$this->assertEquals(3, $config['nesting']['one']['two']['three']);
} }
/** /**
* test that values cannot be modified * test boolean reading
* *
* @expectedException LogicException * @return void
*/ */
function testNoModification() { function testBooleanReading() {
$config = new IniFile($this->file); $reader = new IniReader($this->path);
$config['admin'] = 'something'; $config = $reader->read('nested.ini');
$this->assertTrue($config['bools']['test_on']);
$this->assertFalse($config['bools']['test_off']);
$this->assertTrue($config['bools']['test_yes']);
$this->assertFalse($config['bools']['test_no']);
$this->assertTrue($config['bools']['test_true']);
$this->assertFalse($config['bools']['test_false']);
$this->assertFalse($config['bools']['test_null']);
} }
} }

View file

@ -0,0 +1,91 @@
<?php
/**
* PhpConfigReaderTest
*
* PHP 5
*
* CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
* @package cake
* @subpackage cake.tests.cases
* @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Core', 'config/PhpReader');
class PhpReaderTest extends CakeTestCase {
/**
* setup
*
* @return void
*/
function setUp() {
parent::setUp();
$this->path = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'config'. DS;
}
/**
* test reading files
*
* @return void
*/
function testRead() {
$reader = new PhpReader($this->path);
$values = $reader->read('var_test');
$this->assertEquals('value', $values['Read']);
$this->assertEquals('buried', $values['Deep']['Deeper']['Deepest']);
}
/**
* Test an exception is thrown by reading files that don't exist.
*
* @expectedException RuntimeException
* @return void
*/
function testReadWithNonExistantFile() {
$reader = new PhpReader($this->path);
$reader->read('fake_values');
}
/**
* test reading an empty file.
*
* @expectedException RuntimeException
* @return void
*/
function testReadEmptyFile() {
$reader = new PhpReader($this->path);
$reader->read('empty');
}
/**
* test reading keys with ../ doesn't work
*
* @expectedException InvalidArgumentException
* @return void
*/
function testReadWithDots() {
$reader = new PhpReader($this->path);
$reader->read('../empty');
}
/**
* test reading from plugins
*
* @return void
*/
function testReadPluginValue() {
App::build(array(
'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
), true);
$reader = new PhpReader($this->path);
$result = $reader->read('TestPlugin.load');
$this->assertTrue(isset($result['plugin_load']));
}
}

View file

@ -19,6 +19,7 @@
* @since CakePHP(tm) v 1.2.0.5432 * @since CakePHP(tm) v 1.2.0.5432
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/ */
App::import('Core', 'config/PhpReader');
/** /**
* ConfigureTest * ConfigureTest
@ -70,6 +71,7 @@ class ConfigureTest extends CakeTestCase {
} }
Configure::write('debug', $this->_debug); Configure::write('debug', $this->_debug);
Configure::write('Cache.disable', $this->_cacheDisable); Configure::write('Cache.disable', $this->_cacheDisable);
Configure::drop('test');
} }
/** /**
@ -184,18 +186,26 @@ class ConfigureTest extends CakeTestCase {
/** /**
* testLoad method * testLoad method
* *
* @access public * @expectedException RuntimeException
* @return void
*/
function testLoadExceptionOnNonExistantFile() {
Configure::config('test', new PhpReader());
$result = Configure::load('non_existing_configuration_file', 'test');
}
/**
* test load
*
* @return void * @return void
*/ */
function testLoad() { function testLoad() {
$result = Configure::load('non_existing_configuration_file'); Configure::config('test', new PhpReader(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'config' . DS));
$this->assertFalse($result);
$result = Configure::load('config'); $result = Configure::load('var_test', 'test');
$this->assertTrue($result); $this->assertTrue($result);
$result = Configure::load('../../index'); $this->assertEquals('value', Configure::read('Read'));
$this->assertFalse($result);
} }
/** /**
@ -206,13 +216,15 @@ class ConfigureTest extends CakeTestCase {
*/ */
function testLoadPlugin() { function testLoadPlugin() {
App::build(array('plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)), true); App::build(array('plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)), true);
$result = Configure::load('test_plugin.load'); Configure::config('test', new PhpReader());
$result = Configure::load('test_plugin.load', 'test');
$this->assertTrue($result); $this->assertTrue($result);
$expected = '/test_app/plugins/test_plugin/config/load.php'; $expected = '/test_app/plugins/test_plugin/config/load.php';
$config = Configure::read('plugin_load'); $config = Configure::read('plugin_load');
$this->assertEqual($config, $expected); $this->assertEqual($config, $expected);
$result = Configure::load('test_plugin.more.load'); $result = Configure::load('test_plugin.more.load', 'test');
$this->assertTrue($result); $this->assertTrue($result);
$expected = '/test_app/plugins/test_plugin/config/more.load.php'; $expected = '/test_app/plugins/test_plugin/config/more.load.php';
$config = Configure::read('plugin_more_load'); $config = Configure::read('plugin_more_load');
@ -225,25 +237,39 @@ class ConfigureTest extends CakeTestCase {
* @access public * @access public
* @return void * @return void
*/ */
function testStoreAndLoad() { function testStoreAndRestore() {
Configure::write('Cache.disable', false); Configure::write('Cache.disable', false);
$expected = array('data' => 'value with backslash \, \'singlequote\' and "doublequotes"'); Configure::write('Testing', 'yummy');
Configure::store('SomeExample', 'test', $expected); $this->assertTrue(Configure::store('store_test', 'default'));
Configure::load('test'); Configure::delete('Testing');
$config = Configure::read('SomeExample'); $this->assertNull(Configure::read('Testing'));
$this->assertEqual($config, $expected);
$expected = array( Configure::restore('store_test', 'default');
'data' => array('first' => 'value with backslash \, \'singlequote\' and "doublequotes"', 'second' => 'value2'), $this->assertEquals('yummy', Configure::read('Testing'));
'data2' => 'value'
);
Configure::store('AnotherExample', 'test_config', $expected);
Configure::load('test_config'); Cache::delete('store_test', 'default');
$config = Configure::read('AnotherExample'); }
$this->assertEqual($config, $expected);
/**
* test that store and restore only store/restore the provided data.
*
* @return void
*/
function testStoreAndRestoreWithData() {
Configure::write('Cache.disable', false);
Configure::write('testing', 'value');
Configure::store('store_test', 'default', array('store_test' => 'one'));
Configure::delete('testing');
$this->assertNull(Configure::read('store_test'), 'Calling store with data shouldnt modify runtime.');
Configure::restore('store_test', 'default');
$this->assertEquals('one', Configure::read('store_test'));
$this->assertNull(Configure::read('testing'), 'Values that were not stored are not restored.');
Cache::delete('store_test', 'default');
} }
/** /**
@ -256,5 +282,35 @@ class ConfigureTest extends CakeTestCase {
$result = Configure::version(); $result = Configure::version();
$this->assertTrue(version_compare($result, '1.2', '>=')); $this->assertTrue(version_compare($result, '1.2', '>='));
} }
/**
* test adding new readers.
*
* @return void
*/
function testReaderSetup() {
$reader = new PhpReader();
Configure::config('test', $reader);
$configured = Configure::configured();
$this->assertTrue(in_array('test', $configured));
$this->assertTrue(Configure::configured('test'));
$this->assertFalse(Configure::configured('fake_garbage'));
$this->assertTrue(Configure::drop('test'));
$this->assertFalse(Configure::drop('test'), 'dropping things that do not exist should return false.');
}
/**
* test reader() throwing exceptions on missing interface.
*
* @expectedException Exception
* @return void
*/
function testReaderExceptionOnIncorrectClass() {
$reader = new StdClass();
Configure::config('test', $reader);
}
} }

View file

@ -347,7 +347,7 @@ class ScaffoldViewTest extends CakeTestCase {
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
$result = $ScaffoldView->testGetFilename('error'); $result = $ScaffoldView->testGetFilename('error');
$expected = 'cake' . DS . 'libs' . DS . 'view' . DS . 'errors' . DS . 'scaffold_error.ctp'; $expected = CAKE . 'libs' . DS . 'view' . DS . 'errors' . DS . 'scaffold_error.ctp';
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
$Controller = new ScaffoldMockController($this->request); $Controller = new ScaffoldMockController($this->request);

View file

@ -0,0 +1,2 @@
<?php
//do nothing this is an empty file.

View file

@ -0,0 +1,17 @@
; Test file for testing ini files with . syntax
[database]
db.username = mark
db.password = secret
[nesting]
one.two.three = 3
a.b.c.d = On
[bools]
test_on = on
test_off = off
test_yes = yes
test_no = no
test_true = true
test_false = false
test_null = null

View file

@ -0,0 +1,9 @@
<?php
$config = array(
'Read' => 'value',
'Deep' => array(
'Deeper' => array(
'Deepest' => 'buried'
)
)
);

View file

@ -662,9 +662,9 @@ class IniAcl extends Object implements AclInterface {
* @return array INI section structure * @return array INI section structure
*/ */
public function readConfigFile($filename) { public function readConfigFile($filename) {
App::import('Core', 'config/IniFile'); App::import('Core', 'config/IniReader');
$iniFile = new IniFile($filename); $iniFile = new IniReader(dirname($filename) . DS);
return $iniFile->asArray(); return $iniFile->read(basename($filename));
} }
/** /**

View file

@ -473,9 +473,10 @@ class Controller extends Object {
} else { } else {
$id = $this->passedArgs['0']; $id = $this->passedArgs['0'];
} }
$plugin = $this->plugin ? $this->plugin . '.' : null;
if ($this->uses === false) { if ($this->uses === false) {
$this->loadModel($this->modelClass, $id); $this->loadModel($plugin . $this->modelClass, $id);
} elseif ($this->uses) { } elseif ($this->uses) {
$uses = is_array($this->uses) ? $this->uses : array($this->uses); $uses = is_array($this->uses) ? $this->uses : array($this->uses);
list($plugin, $modelClassName) = pluginSplit($uses[0]); list($plugin, $modelClassName) = pluginSplit($uses[0]);

View file

@ -40,6 +40,14 @@ class Configure {
'debug' => 0 'debug' => 0
); );
/**
* Configured reader classes, used to load config files from resources
*
* @var array
* @see Configure::load()
*/
protected static $_readers = array();
/** /**
* Initializes configure and runs the bootstrap process. * Initializes configure and runs the bootstrap process.
* Bootstrapping includes the following steps: * Bootstrapping includes the following steps:
@ -55,13 +63,19 @@ class Configure {
*/ */
public static function bootstrap($boot = true) { public static function bootstrap($boot = true) {
if ($boot) { if ($boot) {
self::write('App', array('base' => false, 'baseUrl' => false, 'dir' => APP_DIR, 'webroot' => WEBROOT_DIR, 'www_root' => WWW_ROOT)); self::write('App', array(
'base' => false,
'baseUrl' => false,
'dir' => APP_DIR,
'webroot' => WEBROOT_DIR,
'www_root' => WWW_ROOT
));
if (!include(CONFIGS . 'core.php')) { if (!include(CONFIGS . 'core.php')) {
trigger_error(__("Can't find application core file. Please create %score.php, and make sure it is readable by PHP.", CONFIGS), E_USER_ERROR); trigger_error(__("Can't find application core file. Please create %score.php, and make sure it is readable by PHP.", CONFIGS), E_USER_ERROR);
} }
if (Configure::read('Cache.disable') !== true) { if (empty(self::$_values['Cache']['disable'])) {
$cache = Cache::config('default'); $cache = Cache::config('default');
if (empty($cache['settings'])) { if (empty($cache['settings'])) {
@ -76,7 +90,7 @@ class Configure {
$prefix = $cache['settings']['prefix']; $prefix = $cache['settings']['prefix'];
} }
if (Configure::read('debug') >= 1) { if (self::$_values['debug'] >= 1) {
$duration = '+10 seconds'; $duration = '+10 seconds';
} else { } else {
$duration = '+999 days'; $duration = '+999 days';
@ -167,15 +181,13 @@ class Configure {
} }
} }
if (isset($config['debug']) || isset($config['log'])) { if (isset($config['debug']) && function_exists('ini_set')) {
if (function_exists('ini_set')) {
if (self::$_values['debug']) { if (self::$_values['debug']) {
ini_set('display_errors', 1); ini_set('display_errors', 1);
} else { } else {
ini_set('display_errors', 0); ini_set('display_errors', 0);
} }
} }
}
return true; return true;
} }
@ -250,59 +262,77 @@ class Configure {
} }
/** /**
* Loads a file from app/config/configure_file.php. * Add a new reader to Configure. Readers allow you to read configuration
* files in various formats/storage locations. CakePHP comes with two built-in readers
* PhpReader and IniReader. You can also implement your own reader classes in your application.
* *
* Config file variables should be formated like: * To add a new reader to Configure:
* `$config['name'] = 'value';`
* These will be used to create dynamic Configure vars. load() is also used to
* load stored config files created with Configure::store()
* *
* - To load config files from app/config use `Configure::load('configure_file');`. * `Configure::config('ini', new IniReader());`
* - To load config files from a plugin `Configure::load('plugin.configure_file');`. *
* @param string $name The name of the reader being configured. This alias is used later to
* read values from a specific reader.
* @param ConfigReaderInterface $reader The reader to append.
* @return void
*/
public static function config($name, ConfigReaderInterface $reader) {
self::$_readers[$name] = $reader;
}
/**
* Gets the names of the configured reader objects.
*
* @return array Array of the configured reader objects.
*/
public static function configured($name = null) {
if ($name) {
return isset(self::$_readers[$name]);
}
return array_keys(self::$_readers);
}
/**
* Remove a configured reader. This will unset the reader
* and make any future attempts to use it cause an Exception.
*
* @param string $name Name of the reader to drop.
* @return boolean Success
*/
public static function drop($name) {
if (!isset(self::$_readers[$name])) {
return false;
}
unset(self::$_readers[$name]);
return true;
}
/**
* Loads stored configuration information from a resource. You can add
* config file resource readers with `Configure::config()`.
*
* Loaded configuration infomration will be merged with the current
* runtime configuration. You can load configuration files from plugins
* by preceeding the filename with the plugin name.
*
* `Configure::load('Users.user', 'default')`
*
* Would load the 'user' config file using the default config reader. You can load
* app config files by giving the name of the resource you want loaded.
*
* `Configure::load('setup', 'default');`
* *
* @link http://book.cakephp.org/view/929/load * @link http://book.cakephp.org/view/929/load
* @param string $fileName name of file to load, extension must be .php and only the name * @param string $key name of configuration resource to load.
* should be used, not the extenstion * @param string $config Name of the configured reader to use to read the resource identfied by $key.
* @return mixed false if file not found, void if load successful * @return mixed false if file not found, void if load successful.
* @throws Exception Will throw any exceptions the reader raises.
*/ */
public static function load($fileName) { public static function load($key, $config = 'default') {
$found = $plugin = $pluginPath = false; if (!isset(self::$_readers[$config])) {
list($plugin, $fileName) = pluginSplit($fileName);
if ($plugin) {
$pluginPath = App::pluginPath($plugin);
}
$pos = strpos($fileName, '..');
if ($pos === false) {
if ($pluginPath && file_exists($pluginPath . 'config' . DS . $fileName . '.php')) {
include($pluginPath . 'config' . DS . $fileName . '.php');
$found = true;
} elseif (file_exists(CONFIGS . $fileName . '.php')) {
include(CONFIGS . $fileName . '.php');
$found = true;
} elseif (file_exists(CACHE . 'persistent' . DS . $fileName . '.php')) {
include(CACHE . 'persistent' . DS . $fileName . '.php');
$found = true;
} else {
foreach (App::core('cake') as $key => $path) {
if (file_exists($path . DS . 'config' . DS . $fileName . '.php')) {
include($path . DS . 'config' . DS . $fileName . '.php');
$found = true;
break;
}
}
}
}
if (!$found) {
return false; return false;
} }
$values = self::$_readers[$config]->read($key);
if (!isset($config)) { return self::write($values);
trigger_error(__('Configure::load() - no variable $config found in %s.php', $fileName), E_USER_WARNING);
return false;
}
return self::write($config);
} }
/** /**
@ -322,62 +352,52 @@ class Configure {
} }
/** /**
* Used to write a config file to disk. * Used to write runtime configuration into Cache. Stored runtime configuration can be
* restored using `Configure::restore()`. These methods can be used to enable configuration managers
* frontends, or other GUI type interfaces for configuration.
* *
* {{{ * @param string $name The storage name for the saved configuration.
* Configure::store('Model', 'class_paths', array('Users' => array( * @param string $cacheConfig The cache configuration to save into. Defaults to 'default'
* 'path' => 'users', 'plugin' => true * @param array $data Either an array of data to store, or leave empty to store all values.
* ))); * @return boolean Success
* }}}
*
* @param string $type Type of config file to write, ex: Models, Controllers, Helpers, Components
* @param string $name file name.
* @param array $data array of values to store.
* @return void
*/ */
public static function store($type, $name, $data = array()) { public static function store($name, $cacheConfig = 'default', $data = null) {
$write = true; if ($data === null) {
$content = ''; $data = self::$_values;
foreach ($data as $key => $value) {
$content .= "\$config['$type']['$key'] = " . var_export($value, true) . ";\n";
} }
if (is_null($type)) { return Cache::write($name, $data, $cacheConfig);
$write = false;
}
self::__writeConfig($content, $name, $write);
} }
/** /**
* Creates a cached version of a configuration file. * Restores configuration data stored in the Cache into configure. Restored
* Appends values passed from Configure::store() to the cached file * values will overwrite existing ones.
* *
* @param string $content Content to write on file * @param string $name Name of the stored config file to load.
* @param string $name Name to use for cache file * @param string $cacheConfig Name of the Cache configuration to read from.
* @param boolean $write true if content should be written, false otherwise * @return boolean Success.
* @return void
* @access private
*/ */
private static function __writeConfig($content, $name, $write = true) { public static function restore($name, $cacheConfig = 'default') {
$file = CACHE . 'persistent' . DS . $name . '.php'; $values = Cache::read($name, $cacheConfig);
if ($values) {
if (self::read('debug') > 0) { return self::write($values);
$expires = "+10 seconds";
} else {
$expires = "+999 days";
} }
$cache = cache('persistent' . DS . $name . '.php', null, $expires); return false;
if ($cache === null) {
cache('persistent' . DS . $name . '.php', "<?php\n\$config = array();\n", $expires);
} }
}
if ($write === true) {
$fileClass = new SplFileObject($file, 'a'); /**
if ($fileClass->isWritable()) { * An interface for creating objects compatible with Configure::load()
$fileClass->fwrite($content); *
} * @package cake.libs
} */
} interface ConfigReaderInterface {
/**
* Read method is used for reading configuration information from sources.
* These sources can either be static resources like files, or dynamic ones like
* a database, or other datasource.
*
* @param string $key
* @return array An array of data to merge into the runtime configuration
*/
function read($key);
} }

View file

@ -1077,7 +1077,11 @@ class Multibyte {
if ($range === false) { if ($range === false) {
return null; return null;
} }
Configure::load('unicode' . DS . 'casefolding' . DS . $range); if (!Configure::configured('_cake_core_')) {
App::import('Core', 'config/PhpReader');
Configure::config('_cake_core_', new PhpReader(CAKE . 'config' . DS));
}
Configure::load('unicode' . DS . 'casefolding' . DS . $range, '_cake_core_');
self::$__caseFold[$range] = Configure::read($range); self::$__caseFold[$range] = Configure::read($range);
Configure::delete($range); Configure::delete($range);
} }

View file

@ -295,9 +295,6 @@ class CakeSchema extends Object {
$systemTables = array( $systemTables = array(
'aros', 'acos', 'aros_acos', Configure::read('Session.table'), 'i18n' 'aros', 'acos', 'aros_acos', Configure::read('Session.table'), 'i18n'
); );
if (get_class($Object) === 'AppModel') {
continue;
}
if (in_array($table, $systemTables)) { if (in_array($table, $systemTables)) {
$tables[$Object->table] = $this->__columns($Object); $tables[$Object->table] = $this->__columns($Object);
$tables[$Object->table]['indexes'] = $db->index($Object); $tables[$Object->table]['indexes'] = $db->index($Object);

View file

@ -17,7 +17,7 @@
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/ */
if (Configure::read() == 0): if (Configure::read() == 0):
$this->cakeError('error404'); throw new NotFoundException();
endif; endif;
?> ?>
<h2><?php echo __('Release Notes for CakePHP %s.', Configure::version()); ?></h2> <h2><?php echo __('Release Notes for CakePHP %s.', Configure::version()); ?></h2>