patternphpMinor
Is this a proper way of "loading" views in PHP?
Viewed 0 times
thisphpwayviewsloadingproper
Problem
As a learning exercise, I'm developing my own PHP framework. I'm looking for a way to "load views" (kinda like CodeIgniter does it), without polluting my general scope.
I came up with the following, basic, example:
view.php
I could also use
Edit: I'm mostly worried about performance. As Peter pointed out, I'm aware that the function should be a class method that's seperate from the logic.
I came up with the following, basic, example:
$data['test'] = 'Hello world';
$str = load_view($data, true);
echo $str;
function load_view($data, $store = false) {
extract($data);
ob_start();
include('view.php');
if($store) return ob_get_clean();
else ob_end_flush();
}
// End of fileview.php
I could also use
load_view($data), which would output the contents of view.php immediately. Edit: I'm mostly worried about performance. As Peter pointed out, I'm aware that the function should be a class method that's seperate from the logic.
Solution
I agree that its a better pattern to wrap your view logic in a class.
Here is some code I whipped together tonight - It represents the simplest class I could devise that contains the minimum logic that I require in a view:
-
Includes - Ability to include other views
-
Captures - Ability to easily capture content within your view
-
Layouts - Ability to inject data into a re-usable layout template
-
Fetching - Ability to fetch view output instead of sending it to output buffer
-
data - Ability to access the resulting data once the view is finished
[edit] Refactored per conversation in comments regarding passing $data by reference.
View.php
run.php
view_main_simple.php
view_main_complex.php
view_include.php
view_layout.php
Program Output
Here is some code I whipped together tonight - It represents the simplest class I could devise that contains the minimum logic that I require in a view:
-
Includes - Ability to include other views
-
Captures - Ability to easily capture content within your view
-
Layouts - Ability to inject data into a re-usable layout template
-
Fetching - Ability to fetch view output instead of sending it to output buffer
-
data - Ability to access the resulting data once the view is finished
[edit] Refactored per conversation in comments regarding passing $data by reference.
View.php
*/
class View implements ArrayAccess
{
/**
* View file to include
* @var string
*/
private $file;
/**
* View data
* @var array
*/
private $data;
/**
* Layout to include (optional)
* @var string
*/
private $layout;
/**
* Constructor
*
* @param string $file file to include
*/
public function __construct($file)
{
$this->file = $file;
}
/**
* render Renders the view using the given data
*
* @param array $data
* @return void
*/
public function render($data)
{
$this->data = $data;
$this->layout = null;
ob_start();
include ($this->file);
// If we did not set a layout
if (null === $this->layout)
{
// flush view output
ob_end_flush();
}
// We set a layout
else
{
// Ignore view output
ob_end_clean();
// Include the layout
$this->include_file($this->layout);
}
}
/**
* fetch Fetches the view result intead of sending it to the output buffer
*
* @param array $data
* @return string The rendered view content
*/
public function fetch($data)
{
ob_start();
$this->render($data);
return ob_get_clean();
}
/**
* get_data Returns the view data
*
* @return array
*/
public function get_data()
{
return $this->data;
}
/**
* include_file Used by view to include sub-views
*
* @param string $file
* @return void
*/
protected function include_file($file)
{
$v = new View($file);
$v->render($this->data);
$this->data = $v->get_data();
}
/**
* set_layout Used by view to indicate the use of a layout.
*
* If a layout is selected, the normal output of the view wil be
* discarded. The only way to send data to the layout is via
* capture()
*
* @param string $file
* @return void
*/
protected function set_layout($file)
{
$this->layout = $file;
}
/**
* capture Used by view to capture output.
*
* When a view is using a layout (via set_layout()), the only way to pass
* data to the layout is via capture(), but the view can use capture()
* to capture text any time, for any reason, even if the view is not using
* a layout
*
* @return void
*/
protected function capture()
{
ob_start();
}
/**
* end_capture Used by view to signal end of a capture().
*
* The content of the capture is stored under $name
*
* @param string $name
* @return void
*/
protected function end_capture($name)
{
$this->data[$name] = ob_get_clean();
}
/* ArrayAccess methods */
public function offsetExists($offset) { return isset($this->data[$offset]); }
public function offsetGet($offset) { return $this->data[$offset]; }
public function offsetSet($offset, $value) { $this->data[$offset] = $value; }
public function offsetUnset($offset) { unset($this->data[$offset]); }
}run.php
fetch(array('message' => 'Hello, world'));
print("Fetch result: {$fetch}\n");
$v = new View('view_main_complex.php');
$v->render(array('one' => 1, 'two' => 2, 'rows' => array('a','b','c')));
$data = $v->get_data();
print("\n");
var_export($data);view_main_simple.php
The message is: view_main_complex.php
set_layout('view_layout.php') ?>
capture() ?>
one=
include_file('view_include.php') ?>
three=
end_capture('body') ?>view_include.php
two=
view_layout.php
Program Output
Fetch result: The message is: Hello, world
one=1
two=2
a
b
c
three=3
array (
'one' => 1,
'two' => 2,
'rows' =>
array (
0 => 'a',
1 => 'b',
2 => 'c',
),
'three' => 3,
'body' => ' one=1
two=2
a
b
c
three=3
',
)Code Snippets
<?php
/**
* Simple view class that supports includes, capturing, and layouts, as well
* as retrieving rendered view content and resulting data.
*
* *NOTE* When a view uses a layout, the output of the view is ignored, as
* as the view is expected to use capture() to send data to the layout.
*
* @author David Farrell <DavidPFarrell@gmail.com>
*/
class View implements ArrayAccess
{
/**
* View file to include
* @var string
*/
private $file;
/**
* View data
* @var array
*/
private $data;
/**
* Layout to include (optional)
* @var string
*/
private $layout;
/**
* Constructor
*
* @param string $file file to include
*/
public function __construct($file)
{
$this->file = $file;
}
/**
* render Renders the view using the given data
*
* @param array $data
* @return void
*/
public function render($data)
{
$this->data = $data;
$this->layout = null;
ob_start();
include ($this->file);
// If we did not set a layout
if (null === $this->layout)
{
// flush view output
ob_end_flush();
}
// We set a layout
else
{
// Ignore view output
ob_end_clean();
// Include the layout
$this->include_file($this->layout);
}
}
/**
* fetch Fetches the view result intead of sending it to the output buffer
*
* @param array $data
* @return string The rendered view content
*/
public function fetch($data)
{
ob_start();
$this->render($data);
return ob_get_clean();
}
/**
* get_data Returns the view data
*
* @return array
*/
public function get_data()
{
return $this->data;
}
/**
* include_file Used by view to include sub-views
*
* @param string $file
* @return void
*/
protected function include_file($file)
{
$v = new View($file);
$v->render($this->data);
$this->data = $v->get_data();
}
/**
* set_layout Used by view to indicate the use of a layout.
*
* If a layout is selected, the normal output of the view wil be
* discarded. The only way to send data to the layout is via
* capture()
*
* @param string $file
* @return void
*/
protected function set_layout($file)
{
$this->layout = $file;
}
/**
* capture Used by view to capture output.
*
* When a view is using a layout (via set_layout()), the only way to pass
* data to the layout is via capture(), but the view can use capture()
* to capture text any time, for any reason, even if the view is not using
* a layout
*
* @return void
*/
protected function capture()
{
ob_start();
}
/**
* end_cap<?php
require "View.php";
$v = new View('view_main_simple.php');
$fetch = $v->fetch(array('message' => 'Hello, world'));
print("Fetch result: {$fetch}\n");
$v = new View('view_main_complex.php');
$v->render(array('one' => 1, 'two' => 2, 'rows' => array('a','b','c')));
$data = $v->get_data();
print("\n");
var_export($data);The message is: <?php echo $this['message'] ?><br/><?php $this->set_layout('view_layout.php') ?>
<?php $this->capture() ?>
one=<?php echo $this['one'] ?><br/>
<?php $this->include_file('view_include.php') ?>
three=<?php echo $this['three'] ?><br/>
<?php $this->end_capture('body') ?>two=<?php echo $this['two'] ?><br/>
<?php $this['three'] = 3 ?>
<ul>
<?php foreach($this['rows'] as $row) { ?>
<li><?php echo $row ?></li>
<?php } ?>
</ul>Context
StackExchange Code Review Q#19460, answer score: 6
Revisions (0)
No revisions yet.