diff options
-rw-r--r-- | system/core/Loader.php | 51 | ||||
-rw-r--r-- | tests/codeigniter/core/Loader_test.php | 15 |
2 files changed, 60 insertions, 6 deletions
diff --git a/system/core/Loader.php b/system/core/Loader.php index d9a1539aa..fb8b73b1f 100644 --- a/system/core/Loader.php +++ b/system/core/Loader.php @@ -94,6 +94,13 @@ class CI_Loader { protected $_ci_cached_vars = array(); /** + * Stack of variable arrays to provide nested _ci_load calls all variables from parent calls + * + * @var array + */ + protected $_ci_load_vars_stack = array(); + + /** * List of loaded classes * * @var array @@ -934,15 +941,46 @@ class CI_Loader { } /* - * Extract and cache variables + * Extract and stack variables * * You can either set variables using the dedicated $this->load->vars() * function or via the second parameter of this function. We'll merge - * the two types and cache them so that views that are embedded within - * other views can have access to these variables. + * the two types so that loaded views and files have access to these + * variables. + * Additionally we want all subsequent nested _ci_load() calls embedded + * within the current file to 'inherit' all variables that are + * accessible to the current file. For this purpose we push the current + * variable configuration (_ci_vars) to the stack and remove it again + * after the file or view is completely loaded. Nested _ci_load() calls + * within the current file extend the stack with their variable + * configuration. */ - empty($_ci_vars) OR $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars); - extract($this->_ci_cached_vars); + + // Init current _ci_vars as current variable configuration + if ( ! is_array($_ci_vars)) + { + $_ci_vars = []; + } + + // Include the global cached vars into the current _ci_vars if needed + if ( ! empty($this->_ci_cached_vars)) + { + $_ci_vars = array_merge($this->_ci_cached_vars, $_ci_vars); + } + + // Merge the last variable configuration from a parent _ci_load() + // call into the current _ci_vars + if ( ! empty($this->_ci_load_vars_stack)) + { + $previous_variable_configuration = end($this->_ci_load_vars_stack); + $_ci_vars = array_merge($previous_variable_configuration, $_ci_vars); + } + + // Push the current _ci_vars to the stack + array_push($this->_ci_load_vars_stack, $_ci_vars); + + // Extract the current _ci_vars + extract($_ci_vars); /** * Buffer the output @@ -960,6 +998,9 @@ class CI_Loader { include($_ci_path); // include() vs include_once() allows for multiple views with the same name log_message('info', 'File loaded: '.$_ci_path); + // Remove current _ci_vars from stack again + array_pop($this->_ci_load_vars_stack); + // Return the file data if requested if ($_ci_return === TRUE) { diff --git a/tests/codeigniter/core/Loader_test.php b/tests/codeigniter/core/Loader_test.php index 4fa6d6869..df9c9f44b 100644 --- a/tests/codeigniter/core/Loader_test.php +++ b/tests/codeigniter/core/Loader_test.php @@ -311,12 +311,16 @@ class Loader_test extends CI_TestCase { $var = 'hello'; $value = 'World!'; $content = 'This is my test page. '; - $this->ci_vfs_create($view, $content.'<?php echo $'.$var.';', $this->ci_app_root, 'views'); + $this->ci_vfs_create($view, $content.'<?php echo (isset($'.$var.') ? $'.$var.' : "undefined");', $this->ci_app_root, 'views'); // Test returning view $out = $this->load->view($view, array($var => $value), TRUE); $this->assertEquals($content.$value, $out); + // Test view with missing parameter in $vars + $out = $this->load->view($view, [], TRUE); + $this->assertEquals($content.'undefined', $out); + // Mock output class $output = $this->getMockBuilder('CI_Output')->setMethods(array('append_output'))->getMock(); $output->expects($this->once())->method('append_output')->with($content.$value); @@ -326,6 +330,15 @@ class Loader_test extends CI_TestCase { $vars = new stdClass(); $vars->$var = $value; $this->assertInstanceOf('CI_Loader', $this->load->view($view, $vars)); + + // Create another view in VFS, nesting the first one without its own $vars + $nesting_view = 'unit_test_nesting_view'; + $nesting_content = 'Here comes a nested view. '; + $this->ci_vfs_create($nesting_view, $nesting_content.'<?php $loader->view("'.$view.'");', $this->ci_app_root, 'views'); + + // Test $vars inheritance to nested views + $out = $this->load->view($nesting_view, array("loader" => $this->load, $var => $value), TRUE); + $this->assertEquals($nesting_content.$content.$value, $out); } // -------------------------------------------------------------------- |