Skip to content

Instantly share code, notes, and snippets.

@shadowhand
Last active August 29, 2015 14:03
Show Gist options
  • Save shadowhand/039d72433aa262f10b91 to your computer and use it in GitHub Desktop.
Save shadowhand/039d72433aa262f10b91 to your computer and use it in GitHub Desktop.
Auto-escaped views
<?php
// Lib_Data_Storage is a class that implements the standard magic methods:
// __get, __set, __isset, etc. making it possible to use:
//
// <?= $view->foo ?>
//
// in a template instead of:
//
// <?= $view->get('foo') ?>
//
// but in JS (and other) context, it is still required to use:
//
// <?= $view->get_js('foo') ?>
//
abstract class Viewable extends Lib_Data_Storage {
// read only mode enabled?
private $_read_only = false;
// partials for this view
private $_partials = array();
// errors for this view
private $_errors = array();
/**
* Get a raw value, regardless of read-only mode.
* Be careful with this!
* @param string $key name of key
* @return mixed
*/
final public function raw($key) {
return $this->get($key, false);
}
/**
* Get a data value from the view. By default, the value will be only be
* escaped in read-only mode.
* @param string $key name of key
* @param boolean $escape escape the value?
*/
public function get($key, $escape = null) {
if ($escape === null) {
// escape by default in read-only mode
$escape = $this->_read_only;
}
$value = parent::get($key);
if ($escape) {
$value = escape_html($value);
}
return $value;
}
/**
* Converts a value to JSON.
* @param mixed $key name of key
* @return string
*/
public function get_js($key) {
return escape_js($this->raw($key));
}
/**
* Converts a value to a URL (deny javascript:, etc).
* @param string $key name of key
* @return string
*/
public function get_url($key) {
return escape_attr($this->raw($key), 'href');
}
/**
* Convert a value to an HTML attribute.
* @param string $key name of key
* @param string $attr attribute type
* @return string
*/
public function get_attr($key, $attr = null) {
return escape_attr($this->raw($key), $attr);
}
/**
* Set data value(s). To set multiple values, pass an array or object as
* the key.
* @chainable
* @param mixed $key name of key
* @param mixed $value thing to store
* @return View
*/
public function set($key, $value = null) {
$this->_check_read_only();
return parent::set($key, $value);
}
/**
* Set view errors. Errors will be replaced, not appended.
* @param array $errors new view errors
* @return View
*/
public function errors(array $errors) {
$this->_errors = $errors;
return $this;
}
public function partial($name, Viewable $partial = null) {
if ($partial) {
$this->_check_read_only();
$this->_partials[$name] = $partial;
} else {
if (isset($this->_partials[$name])) {
static::include_view($this, $this->_partials[$name]);
} else {
throw new View_Exception('Attempted to merge partial that has not been defined: ' . $name);
}
}
return $this;
}
/**
* Renders the view:
* - notify view via _before_render() method
* - set read-only mode
* - call renderer
* @return Datex
*/
final public function render() {
$this->_before_render();
$this->_read_only = true;
return static::include_view($this, $this->_template);
}
/**
* Called just before read-only mode starts, this is the last place that the
* view data can be modified!
* @return void
*/
protected function _before_render() {
// overload me!
}
/**
* Throws an exception if the view is in read-only mode.
* @return void
* @throws View_Exception
*/
final private function _check_read_only() {
if ($this->_read_only) {
throw new View_Exception('Cannot modify view data while rendering the view!');
}
}
/**
* Rendering implementation.
* @return Datex
*/
protected static include_view($view, $template) {
ob_start();
// template has (public) access to $view
include $template;
return ob_get_clean();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment