Current File : /home/aventura/www/site/wp-content/plugins/victheme_core/vtcore/html/base.php |
<?php
/**
* Defining the API Version number
*/
define('HTML_API', '2.0');
/**
* HTML Builder Class
*
* The main class for trully building HTML elements
* as a true OOP object.
*
* @author jason.xie@victheme.com
*
*/
class VTCore_Html_Base {
static protected $delta = 0;
static protected $vtcore_classes = array();
protected $context = array();
protected $overloaderPrefix = array('VTCore_Html_');
protected $childrenPointer = '';
protected $booleans = array();
protected $int = array();
protected $lastKey = false;
protected $lastResult;
// Obsolete, use $booleans instead with dotted array key.
protected $convertBool = array();
private $closers = array('input', 'img', 'hr', 'br', 'meta', 'link');
private $objectID = '';
private $innerHTML = '';
private $type;
private $attributes = array();
private $parent;
private $children = array();
private $styles = array();
private $allowEmpty = array('value');
private $raw = FALSE;
private $clean = TRUE;
private $data = array();
private $html5Attributes = array(
'controls',
'autoplay',
);
/**
* Constructing the object and processing
* the context array
*/
public function __construct($context = array()) {
$this->buildObject($context);
$this->buildElement();
}
/**
* Helper function to build the object
* This class must be called in any child base class that
* override the parent __construct()
*/
public function buildObject($context) {
$this->objectID = self::$delta++;
$this->setContext($context);
if (isset($this->context['type'])) {
$this->type = $this->context['type'];
}
if (isset($this->context['raw'])) {
$this->raw = $this->context['raw'];
}
if (isset($this->context['styles'])) {
$this->styles = $this->context['styles'];
}
if (isset($this->context['clean'])) {
$this->clean = (bool) $this->context['clean'];
}
if (isset($this->context['data'])) {
$this->data = $this->context['data'];
}
if (isset($this->context['children'])) {
if (!empty($this->context['children'])) {
$children = $this->context['children'];
if (is_array($children)) {
foreach ($this->context['children'] as $child) {
$this->addChildren($child);
unset($child);
}
} else {
$this->addChildren($children);
}
}
}
if (!empty($this->booleans)) {
$this->processBooleans();
}
if (!empty($this->int)) {
$this->processInt();
}
// Garbage cleanup
$children = NULL;
unset($children);
$context = NULL;
unset($context);
return $this;
}
/**
* This function is meant to be extended
*/
public function buildElement() {
$this->addAttributes($this->getContext('attributes'));
return $this;
}
/**
* Overloading method that is declared on child subclass
* but not in this main class.
*
* Note: this is very expensive, avoid at all cost when
* building your chainable model.
*/
public function __call($method, $context) {
// @PHP7Fixes - warning thrown, ensure that we got proper arrays
if (!is_array(self::$vtcore_classes)) {
self::$vtcore_classes = (array) self::$vtcore_classes;
}
foreach ($this->overloaderPrefix as $prefix) {
$class = $prefix . $method;
$name = '';
if (isset(self::$vtcore_classes[$class])) {
if (self::$vtcore_classes[$class]) {
$name = $class;
break;
}
}
else {
if (class_exists($class, TRUE)) {
$name = $class;
self::$vtcore_classes[$class] = TRUE;
break;
}
else {
self::$vtcore_classes[$class] = FALSE;
}
}
}
if (!empty($name)) {
$object = new $name(array_shift($context));
unset($context);
}
if (isset($object) && is_object($object)) {
$object->setParent($this);
$this->addChildren($object);
unset($object);
}
else {
throw new Exception('Error Class VTCore_Html_' . $method . ' does\'t exists');
}
return $this;
}
/**
* Injecting new self closing element if needed
*/
public function addSelfClosers($element) {
$this->closers[] = $element;
return $this;
}
/**
* Merging element default context array with
* the user configured context array.
*/
public function setContext($context) {
$this->lastKey = false;
$this->context = VTCore_Utility::arrayMergeRecursiveDistinct($context, $this->context);
$context = NULL;
unset($context);
return $this;
}
/**
* Retrieving stored context
*/
public function getContext($type) {
if ($this->lastKey !== false && $this->lastKey === $type) {
return $this->lastResult;
}
$this->lastResult = VTCore_Utility::getArrayValueKeys($this->context, $type);
$this->lastKey = $type;
return $this->lastResult;
}
/**
* Find context by its array key
*/
public function findContext($key) {
return VTCore_Utility::searchArrayValueByKey($this->context, $key);
}
/**
* Retrieving all context
*/
public function getContexts() {
return $this->context;
}
/**
* Add or replace context with new value
*
* $keys can be string or arrays, string will be converted
* to arrays.
*
* it supports dotted and hashed drilling.
* example :
* arraykey.arraykey2.arraykey3
* or
* arraykey#arraykey2#arraykey3
*
* is equal to
* array(arraykey, arraykey2, arraykey3)
*/
public function addContext($key, $value) {
$this->lastKey = false;
$this->lastResult = false;
$this->context = VTCore_Utility::setArrayValueKeys($this->context, $key, $value);
return $this;
}
/**
* Remove context per keys
*
* $keys can be string or arrays, string will be converted
* to arrays. the last key will be removed from context
*
* If the last key doesn't exist the method will not
* remove anything.
*
* it supports dotted and hashed drilling.
* example :
* arraykey.arraykey2.arraykey3
* or
* arraykey#arraykey2#arraykey3
*
* is equal to
* array(arraykey, arraykey2, arraykey3)
*/
public function removeContext($key) {
$this->lastKey = false;
$this->lastResult = false;
$this->context = VTCore_Utility::removeArrayValueKeys($this->context, $key);
return $this;
}
/**
* Merge context
*/
public function mergeContext(array $context) {
$this->lastKey = false;
$this->lastResult = false;
$this->context = VTCore_Utility::arrayMergeRecursiveDistinct($context, $this->context);
return $this;
}
/**
* Remove all context value
*/
public function resetContext() {
$this->lastKey = false;
$this->lastResult = false;
$this->context = array();
return $this;
}
/**
* Method for replacing the whole context
* array with new context
*/
public function replaceContext($context) {
$this->lastKey = false;
$this->lastResult = false;
$this->context = $context;
return $this;
}
/**
* Experimental method for cleaning context array
*/
public function cleanEmptyContext($key = FALSE) {
$array = $key ? $this->getContext($key) : $this->getContexts();
if (is_array($array)) {
$array = VTCore_Utility::arrayFilterEmpty($array);
}
if ($key) {
empty($array) ? $this->removeContext($key) : $this->addContext($key, $array);
}
else {
empty($array) ? $this->resetContext() : $this->replaceContext($array);
}
unset($array);
$this->lastKey = false;
$this->lastResult = false;
return $this;
}
/**
* Declaring the object html tag type.
* Empty tags will not be printed although the stored children
* will be processed.
*/
public function setType($type) {
$this->type = $type;
return $this;
}
/**
* Retrieving the current object element type
*/
public function getType() {
return $this->type;
}
/**
* Adding a single attribute
* $keys can be string or arrays, string will be converted
* to arrays.
*
* it supports dotted and hashed drilling.
* example :
* arraykey.arraykey2.arraykey3
* or
* arraykey#arraykey2#arraykey3
*
* is equal to
* array(arraykey, arraykey2, arraykey3)
*/
public function addAttribute($key, $value) {
$this->attributes = VTCore_Utility::setArrayValueKeys($this->attributes, $key, $value);
return $this;
}
/**
* Removing Attributes from object by its key, if recursive
* is set to true, it will remove the attributes recursively.
*/
public function removeAttribute($key, $recursive = FALSE) {
if ($recursive) {
VTCore_Utility::removeArrayValueByKey($this->attributes, $key);
}
elseif (isset($this->attributes[$key])) {
unset($this->attributes[$key]);
}
return $this;
}
/**
* Returning attributed based on $key
*/
public function getAttribute($key) {
return VTCore_Utility::getArrayValueKeys($this->attributes, $key);
}
/**
* Add attributes to object
* @param array $attributes
*/
public function addAttributes($attributes) {
$this->attributes = VTCore_Utility::arrayMergeRecursiveDistinct($attributes, $this->attributes);
return $this;
}
/**
* Returning all attributes
*/
public function getAttributes() {
return $this->attributes;
}
/**
* Convert the context data to html5
* data-* attributes
*/
protected function buildData() {
$dataCount = count($this->data);
$dataKeys = array_keys($this->data);
//foreach ($this->data as $key => $value) {
for ($i = 0; $i < $dataCount; $i++) {
$key = $dataKeys[$i];
$value = $this->data[$key];
// HTML5 specs need all lowercase key
// and no underscore
$key = strtolower(str_replace('_', '-', $key));
// HTML5 specs needs string "true" or "false" for
// true booleans
if ($value === TRUE) {
$value = 'true';
}
if ($value === FALSE) {
$value = 'false';
}
// Can't determine automatically for value such as
// 0 or 1, child object need to define these data
// entry in the $convertBool array
// Obsolete, use $booleans and $int instead to convert
// the value while still in context mode.
if (in_array($key, $this->convertBool)) {
$value = filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 'true' : 'false';
}
// Dont build really "empty" value
if (empty($value)) {
continue;
}
// HTML5 can only store string
// Convert to json so jQuery can pick it up easily.
if ((array) $value === $value || (object) $value === $value) {
$value = json_encode($value);
}
$this->addAttribute('data-' . $key, $value);
}
return $this;
}
/**
* Set the HTML 5 data
*/
public function setData($data) {
$this->data = $data;
}
/**
* Retrieving HTML5 data array
*/
public function getDatas() {
return $this->data;
}
/**
* Removing all HTML5 data
*/
public function cleanDatas() {
$this->data = array();
return $this;
}
/**
* Retrieving single HTML5 data
*/
public function getData($type) {
return VTCore_Utility::getArrayValueKeys($this->data, $type);
}
/**
* Adding single HTML5 Data
*/
public function addData($key, $type) {
$this->data = VTCore_Utility::setArrayValueKeys($this->data, $key, $type);
return $this;
}
/**
* Removing single HTML5 data
*/
public function removeData($key) {
unset($this->data[$key]);
return $this;
}
/**
* Retrieve object machine id
* @return string
*/
public function getMachineID() {
return $this->objectID;
}
/**
* Set the machine ID
*/
protected function setMachineID($id) {
$this->objectID = $id;
return $this;
}
/**
* Inject string to object as child
* This can serve to override innerHTML string
* as well.
*/
public function setText($text) {
$this->innerHTML = (string) $text;
return $this;
}
/**
* Adding string text directly to innerHTML
*/
public function addText($text) {
$this->innerHTML .= (string) $text;
return $this;
}
/**
* Replace text in innerHTML using preg_replace()
*/
public function replaceText($pattern, $replacement) {
$this->innerHTML = preg_replace($pattern, $replacement, $this->innerHTML);
return $this;
}
/**
* Get the innerHTML
*/
public function getText() {
return $this->innerHTML;
}
/**
* Set raw HTML mode, if set to true no escaping to html output is performed.
*/
public function setRaw($mode) {
$this->raw = $mode;
return $this;
}
/**
* Set if object should clean the context after rendered
* for minimizing memory usage.
*/
public function setClean($clean) {
if (is_bool($clean)) {
$this->clean = $clean;
}
return $this;
}
/**
* Setting the children pointer variables.
* This is useful for using other variables than $children for
* storing the children.
*
* This must be invoked or set in object that have
* nested wrapper object and would like the other object
* to have an uniform way to add children by using the
* addChildren() method.
*/
public function setChildrenPointer($pointer) {
$this->childrenPointer = $pointer;
return $this;
}
/**
* Retrieving the pointer variable name
*/
protected function getChildrenPointer() {
if (!empty($this->childrenPointer)) {
return $this->{$this->childrenPointer};
}
return $this;
}
/**
* Inserting children object to the parent object
*/
public function addChildren($object) {
if (!is_object($object)) {
$text = (string) $object;
$object = new VTCore_Html_Base();
$object->addText($text);
}
$object->setParent($this);
$this->getChildrenPointer()->children[$object->getMachineID()] = $object;
$object = NULL;
unset($object);
return $this;
}
/**
* Insert new children at the beginning of children array
*/
public function prependChild($object) {
if (!is_object($object)) {
$text = (string) $object;
$object = new VTCore_Html_Base();
$object->addText($text);
}
$array1 = $this->getChildrenPointer()->children;
$array2[$object->getMachineID()] = $object;
$this->setChildren($array2 + $array1);
unset($object);
unset($array1);
unset($array2);
return $this;
}
/**
* Retrieving children object by its id
*/
public function getChildren($id = FALSE) {
if (isset($this->getChildrenPointer()->children[$id])) {
return $this->getChildrenPointer()->children[$id];
}
return FALSE;
}
/**
* Force Overwrite all childrens
*/
public function setChildren($children) {
$this->getChildrenPointer()->children = $children;
return $this;
}
/**
* Get all childrens
*/
public function getChildrens() {
return $this->getChildrenPointer()->children;
}
/**
* Method for checking if object has children
* @return boolean
*/
public function hasChildren() {
return ($this->getChildrens() != array());
}
/**
* Retrieving the last children in array
*/
public function lastChild() {
return end($this->getChildrenPointer()->children);
}
/**
* Retrieving the first children in array
*/
public function firstChild() {
return reset($this->getChildrenPointer()->children);
}
/**
* Retrieving the parent object
*/
public function getParent() {
return $this->parent;
}
/**
* Set the parent object
*/
public function setParent($object) {
$this->parent = $object;
}
/**
* Reset the chained object to the main parent object
*/
public function resetObject($object) {
return $object;
}
/**
* Allowing user to inject overloader prefix when
* chaining the object build process.
*/
public function addOverloaderPrefix($prefix) {
array_unshift($this->overloaderPrefix, $prefix);
return $this;
}
/**
* Find the children object by specifying children property
* key and value.
*
* Valid search options :
*
* Machine ID
* $type = ID
* $key = the machine id value
* $value = FALSE
*
* Type
* $type = type
* $key = the object type
* $value = FALSE
*
* CSS Class
* $type = class
* $key = the class name
* $value = FALSE
*
* Text
* $type = text
* $key = text searched in innerHTML
* $value = use preg_match by inserting patterns
*
* Attributes
* $type = attributes
* $key = the attribute keys
* $value = the value of the key, if the source is an array it will use in_array()
*
* @param string $type - children property type
* @param string $key - children property key
* @param string $value - children property value
* @return array - array of children object found
*/
public function findChildren($type, $key, $value = FALSE) {
$childrens = array();
$iterators = new RecursiveIteratorIterator(new VTCore_Html_Iterators($this), RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterators as $object) {
switch ($type) {
case 'id' :
if (!is_array($key)) {
$key = (array) $key;
}
foreach ($key as $id) {
if ($id == $object->getMachineID()) {
$childrens[] = $object;
}
}
break;
case 'type' :
if (strtolower($key) == strtolower($object->getType())) {
$childrens[] = $object;
}
break;
case 'class' :
if (!is_array($key)) {
$key = (array) $key;
}
foreach ($key as $class) {
if (is_array($object->getAttribute('class')) && in_array($class, $object->getAttribute('class'))) {
$childrens[] = $object;
}
}
break;
case 'text' :
if (!is_array($key)) {
$key = (array) $key;
}
foreach ($key as $text) {
if (strpos($object->getText(), $text) !== FALSE) {
$childrens[] = $object;
}
}
if (!is_array($value)) {
$value = (array) $value;
}
foreach ($value as $text) {
if ($text !== false && preg_match($text, $object->getText()) !== NULL) {
$childrens[] = $object;
}
}
break;
case 'attributes' :
if (!is_array($value)) {
$value = (array) $value;
}
$attribute = $object->getAttribute($key);
foreach ($value as $val) {
if (is_array($attribute) && in_array($val, $attribute)) {
$childrens[] = $object;
}
if (!is_array($attribute) && $attribute == $val) {
$childrens[] = $object;
}
}
break;
case 'context' :
if (!is_array($value)) {
$value = (array) $value;
}
foreach ($value as $val) {
if ($val != FALSE && $val == $object->getContext($key)) {
$childrens[] = $object;
}
if ($val == FALSE && $object->getContext($key)) {
$childrens[] = $object;
}
}
break;
case 'objectType' :
if (!is_array($key)) {
$key = (array) $key;
}
foreach ($key as $objectType) {
if (is_a($object, $objectType)) {
$childrens[] = $object;
}
}
break;
}
}
return $childrens;
}
/**
* Injecting children after $key array value
*/
public function insertChildrenAfter($key, $new_key, $new_value) {
if (array_key_exists($key, $this->getChildrens())) {
$new = array();
foreach ($this->getChildrens() as $k => $value) {
$new[$k] = $value;
if ($k === $key) {
$new[$new_key] = $new_value;
}
}
if (!empty($new)) {
$this->resetChildren();
$this->setChildren($new);
}
unset($new);
}
return $this;
}
/**
* Remove children by its id
*/
public function removeChildren($delta) {
if (isset($this->children[$delta])) {
unset($this->children[$delta]);
}
return $this;
}
/**
* Remove all childrens
*/
public function resetChildren() {
$this->children = array();
return $this;
}
/**
* Adding inline style
*/
public function addStyle($key, $value) {
$this->styles = VTCore_Utility::setArrayValueKeys($this->styles, $key, $value);
return $this;
}
/**
* Removing inline style by key
*/
public function removeStyle($key) {
if (isset($this->styles[$key])) {
unset($this->styles[$key]);
}
return $this;
}
/**
* Overriding the styles array
*/
public function setStyle($styles) {
$this->styles = $styles;
return $this;
}
/**
* Retrieving style value by key
*/
public function getStyle($key) {
return VTCore_Utility::getArrayValueKeys($this->styles, $key);
}
/**
* Retrieving all styles array
*/
public function getStyles() {
return $this->styles;
}
/**
* Reset styles into empty styles variables
*/
public function resetStyles() {
$this->styles = array();
return $this;
}
/**
* Clean object stored variables to free up
* memory usage.
*/
public function cleanObject() {
if ($this->clean) {
foreach ($this as $key => $value) {
$this->$key = NULL;
unset($this->$key);
}
}
return $this;
}
/**
* Build and convert the object into HTML value
*/
protected function buildContent() {
$type = $this->getType();
$children = $this->getChildrens();
if (empty($type) && empty($children)) {
$build = $this->getText();
$this->cleanObject();
return $build;
}
if (empty($type) && !empty($this->children)) {
return $this->buildInnerHtml();
}
// Start building the HTML output
$build = '<' . $type;
// Build inline style and add them to attributes
$styles = array_filter($this->getStyles());
if (!empty($styles)) {
$style = array();
$stylesCount = count($styles);
$stylesKeys = array_keys($styles);
for ($i = 0; $i < $stylesCount; $i++) {
$key = $stylesKeys[$i];
$value = $styles[$key];
$style[] = $key . ':' . $value;
}
$this->attributes['style'] = implode('; ', $style);
}
if (!empty($this->data)) {
$this->buildData();
}
// Add attributes
$atts = $this->getAttributes();
$attsCount = count($atts);
$htmlAttributes = array();
foreach ($this->html5Attributes as $key) {
$htmlAttributes[$key] = true;
}
$allowEmpty = array();
foreach ($this->allowEmpty as $key) {
$allowEmpty[$key] = true;
}
if ($attsCount) {
$attsKeys = array_keys($atts);
for ($i=0; $i < $attsCount; $i++) {
$key = $attsKeys[$i];
$value = $atts[$key];
if ($value === FALSE) {
continue;
}
if (is_array($value)) {
$value = implode(' ', $value);
}
$value = (string) $value;
if (!$this->raw) {
$value = $this->specialchars($this->checkInvalidUTF8($value), ENT_QUOTES);
}
if (isset($htmlAttributes[$key])) {
if (filter_var($value, FILTER_VALIDATE_BOOLEAN)) {
$build .= ' ' . $key;
}
}
else {
if (strlen($value) || isset($allowEmpty[$key])) {
$build .= ' ' . $key . '="' . $value . '"';
}
}
}
}
$closers = array();
foreach ($this->closers as $key) {
$closers[$key] = true;
}
if (!isset($closers[$type])) {
$build .= '>' . $this->buildInnerHtml() . '</' . $type . '>';
}
else {
$build .= ' />';
}
$this->cleanObject();
return $build;
}
/**
* Build the inner html value
*/
private function buildInnerHtml() {
$build = '';
if (strlen($this->getText())) {
$build = ($this->raw) ? $this->getText() : htmlspecialchars($this->getText());
}
if (!empty($this->children) && is_array($this->children)) {
// Don't use getChildrens() method, it may not reflect the correct
// Children hiearchy if object use different pointers!.
foreach ($this->children as $k => $child) {
$build .= $child->buildContent();
unset($child);
}
}
return $build;
}
/**
* Extra Methods for manipulating class element
*/
public function addClass($class, $key = FALSE) {
if (!isset($this->attributes['class']) || !in_array($class, $this->attributes['class'])) {
if (!$key) {
$this->attributes['class'][] = $class;
}
else {
$this->attributes['class'][$key] = $class;
}
}
return $this;
}
/**
* Removing class from attributes
*/
public function removeClass($class) {
if (isset($this->attributes['class'])) {
$this->attributes['class'] = array_diff($this->attributes['class'], (array) $class);
}
return $this;
}
/**
* Check if element has certain css class
*/
public function hasClass($class) {
return isset($this->attributes['class']) ? in_array($class, $this->attributes['class']) : FALSE;
}
/**
* Build and echo the HTML value
*
* If context clean is set to true, After invoking
* this method the object will be destroyed and
* all property is removed. Make sure to call this
* as the final invocation and no more object
* operational will be performed afterwards.
*/
public function render() {
$clean = $this->clean;
echo $this->buildContent();
if ($clean) {
$removal = array(
'context',
'overloaderPrefix',
'booleans',
'overloaderPrefix',
'int',
'convertBool',
'closers',
'objectID',
'innerHTML',
'type',
'parent',
'attributes',
'children',
'allowEmpty',
'raw',
'clean',
'data',
'html5Attributes',
);
foreach ($removal as $key) {
unset($this->$key);
}
}
unset($clean);
unset($removal);
}
/**
* Build and return the HTML value
*
* If context clean is set to true, After invoking
* this method the object will be destroyed and
* all property is removed. Make sure to call this
* as the final invocation and no more object
* operational will be performed afterwards.
*/
public function __toString() {
return $this->buildContent();
}
/**
* Preprocess booleans
* This method will search for attributes as specified
* in the $booleans class variables and attempt to
* convert value such as "true", "false", 0, 1 into
* the corresponding booleans
*
* This method will inject the key to the context and
* set the value as false if there is no original
* context by the search key defined originally
*/
public function processBooleans() {
foreach ($this->booleans as $key) {
$this->addContext($key, filter_var($this->getContext($key), FILTER_VALIDATE_BOOLEAN));
}
}
/**
* Preprocess Int
* This method will search for attributes as specified
* in the $int class variables and attempt to
* convert value to true integers
*
* This method will inject the key to the context and
* set the value as false if there is no original
* context by the search key defined originally
*/
public function processInt() {
foreach ($this->int as $key) {
if ($this->getContext($key) !== NULL) {
$this->addContext($key, (int) $this->getContext($key));
}
}
}
/**
* Checks for invalid UTF8 in a string.
*
* @since 2.8.0
*
* @staticvar bool $is_utf8
* @staticvar bool $utf8_pcre
*
* @param string $string The text which is to be checked.
* @param bool $strip Optional. Whether to attempt to strip out invalid UTF8. Default is false.
* @return string The checked text.
*/
function checkInvalidUTF8( $string, $strip = false ) {
$string = (string) $string;
if ( 0 === strlen( $string ) ) {
return '';
}
// Store the site charset as a static to avoid multiple calls to get_option()
static $is_utf8 = null;
if ( ! isset( $is_utf8 ) ) {
$is_utf8 = in_array( get_option( 'blog_charset' ), array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) );
}
if ( ! $is_utf8 ) {
return $string;
}
// Check for support for utf8 in the installed PCRE library once and store the result in a static
static $utf8_pcre = null;
if ( ! isset( $utf8_pcre ) ) {
$utf8_pcre = @preg_match( '/^./u', 'a' );
}
// We can't demand utf8 in the PCRE installation, so just return the string in those cases
if ( !$utf8_pcre ) {
return $string;
}
// preg_match fails when it encounters invalid UTF8 in $string
if ( 1 === @preg_match( '/^./us', $string ) ) {
return $string;
}
// Attempt to strip the bad chars if requested (not recommended)
if ( $strip && function_exists( 'iconv' ) ) {
return iconv( 'utf-8', 'utf-8', $string );
}
return '';
}
/**
* Converts a number of special characters into their HTML entities.
*
* Specifically deals with: &, <, >, ", and '.
*
* $quote_style can be set to ENT_COMPAT to encode " to
* ", or ENT_QUOTES to do both. Default is ENT_NOQUOTES where no quotes are encoded.
*
* @since 1.2.2
* @access private
*
* @staticvar string $_charset
*
* @param string $string The text which is to be encoded.
* @param int $quote_style Optional. Converts double quotes if set to ENT_COMPAT, both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES. Also compatible with old values; converting single quotes if set to 'single', double if set to 'double' or both if otherwise set. Default is ENT_NOQUOTES.
* @param string $charset Optional. The character encoding of the string. Default is false.
* @param bool $double_encode Optional. Whether to encode existing html entities. Default is false.
* @return string The encoded text with HTML entities.
*/
function specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) {
$string = (string) $string;
if ( 0 === strlen( $string ) )
return '';
// Don't bother if there are no specialchars - saves some processing
if ( ! preg_match( '/[&<>"\']/', $string ) )
return $string;
// Account for the previous behaviour of the function when the $quote_style is not an accepted value
if ( empty( $quote_style ) )
$quote_style = ENT_NOQUOTES;
elseif ( ! in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) )
$quote_style = ENT_QUOTES;
// Store the site charset as a static to avoid multiple calls to wp_load_alloptions()
if ( ! $charset ) {
static $_charset = null;
if ( ! isset( $_charset ) ) {
$alloptions = wp_load_alloptions();
$_charset = isset( $alloptions['blog_charset'] ) ? $alloptions['blog_charset'] : '';
}
$charset = $_charset;
}
if ( in_array( $charset, array( 'utf8', 'utf-8', 'UTF8' ) ) )
$charset = 'UTF-8';
$_quote_style = $quote_style;
if ( $quote_style === 'double' ) {
$quote_style = ENT_COMPAT;
$_quote_style = ENT_COMPAT;
} elseif ( $quote_style === 'single' ) {
$quote_style = ENT_NOQUOTES;
}
if ( ! $double_encode ) {
// Guarantee every &entity; is valid, convert &garbage; into &garbage;
// This is required for PHP < 5.4.0 because ENT_HTML401 flag is unavailable.
$string = wp_kses_normalize_entities( $string );
}
$string = @htmlspecialchars( $string, $quote_style, $charset, $double_encode );
// Backwards compatibility
if ( 'single' === $_quote_style )
$string = str_replace( "'", ''', $string );
return $string;
}
}