installed plugin Easy Digital Downloads
version 3.1.0.3
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Array2XML {
|
||||
|
||||
private static $xml = null;
|
||||
private static $encoding = 'UTF-8';
|
||||
|
||||
/**
|
||||
* Initialize the root XML node [optional]
|
||||
* @param $version
|
||||
* @param $encoding
|
||||
* @param $format_output
|
||||
*/
|
||||
public static function init($version = '1.0', $encoding = 'UTF-8', $format_output = true) {
|
||||
self::$xml = new DomDocument($version, $encoding);
|
||||
self::$xml->formatOutput = $format_output;
|
||||
self::$encoding = $encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an Array to XML
|
||||
* @param string $node_name - name of the root node to be converted
|
||||
* @param array $arr - aray to be converterd
|
||||
* @return DomDocument
|
||||
*/
|
||||
public static function &createXML($node_name, $arr=array()) {
|
||||
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
|
||||
_edd_deprecated_function( 'array2xml::createXML', '2.8.11', 'ArrayToXML::buildXML' );
|
||||
}
|
||||
|
||||
$xml = self::getXMLRoot();
|
||||
$xml->appendChild(self::convert($node_name, $arr));
|
||||
|
||||
self::$xml = null; // clear the xml node in the class for 2nd time use.
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an Array to XML
|
||||
* @param string $node_name - name of the root node to be converted
|
||||
* @param array $arr - aray to be converterd
|
||||
* @return DOMNode
|
||||
*/
|
||||
private static function &convert($node_name, $arr=array()) {
|
||||
|
||||
//print_arr($node_name);
|
||||
$xml = self::getXMLRoot();
|
||||
$node = $xml->createElement($node_name);
|
||||
|
||||
if(is_array($arr)){
|
||||
// get the attributes first.;
|
||||
if(isset($arr['@attributes'])) {
|
||||
foreach($arr['@attributes'] as $key => $value) {
|
||||
if(!self::isValidTagName($key)) {
|
||||
throw new Exception('[Array2XML] Illegal character in attribute name. attribute: '.$key.' in node: '.$node_name);
|
||||
}
|
||||
$node->setAttribute($key, self::bool2str($value));
|
||||
}
|
||||
unset($arr['@attributes']); //remove the key from the array once done.
|
||||
}
|
||||
|
||||
// check if it has a value stored in @value, if yes store the value and return
|
||||
// else check if its directly stored as string
|
||||
if(isset($arr['@value'])) {
|
||||
$node->appendChild($xml->createTextNode(self::bool2str($arr['@value'])));
|
||||
unset($arr['@value']); //remove the key from the array once done.
|
||||
//return from recursion, as a note with value cannot have child nodes.
|
||||
return $node;
|
||||
} else if(isset($arr['@cdata'])) {
|
||||
$node->appendChild($xml->createCDATASection(self::bool2str($arr['@cdata'])));
|
||||
unset($arr['@cdata']); //remove the key from the array once done.
|
||||
//return from recursion, as a note with cdata cannot have child nodes.
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
|
||||
//create subnodes using recursion
|
||||
if(is_array($arr)){
|
||||
// recurse to get the node for that key
|
||||
foreach($arr as $key=>$value){
|
||||
if(!self::isValidTagName($key)) {
|
||||
throw new Exception('[Array2XML] Illegal character in tag name. tag: '.$key.' in node: '.$node_name);
|
||||
}
|
||||
if(is_array($value) && is_numeric(key($value))) {
|
||||
// MORE THAN ONE NODE OF ITS KIND;
|
||||
// if the new array is numeric index, means it is array of nodes of the same kind
|
||||
// it should follow the parent key name
|
||||
foreach($value as $k=>$v){
|
||||
$node->appendChild(self::convert($key, $v));
|
||||
}
|
||||
} else {
|
||||
// ONLY ONE NODE OF ITS KIND
|
||||
$node->appendChild(self::convert($key, $value));
|
||||
}
|
||||
unset($arr[$key]); //remove the key from the array once done.
|
||||
}
|
||||
}
|
||||
|
||||
// after we are done with all the keys in the array (if it is one)
|
||||
// we check if it has any text value, if yes, append it.
|
||||
if(!is_array($arr)) {
|
||||
$node->appendChild($xml->createTextNode(self::bool2str($arr)));
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the root XML node, if there isn't one, create it.
|
||||
*/
|
||||
private static function getXMLRoot(){
|
||||
if(empty(self::$xml)) {
|
||||
self::init();
|
||||
}
|
||||
return self::$xml;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get string representation of boolean value
|
||||
*/
|
||||
private static function bool2str($v){
|
||||
//convert boolean to text value.
|
||||
$v = $v === true ? 'true' : $v;
|
||||
$v = $v === false ? 'false' : $v;
|
||||
return $v;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the tag name or attribute name contains illegal characters
|
||||
* Ref: http://www.w3.org/TR/xml/#sec-common-syn
|
||||
*/
|
||||
private static function isValidTagName($tag){
|
||||
$pattern = '/^[a-z_]+[a-z0-9\:\-\.\_]*[^:]*$/i';
|
||||
return preg_match($pattern, $tag, $matches) && $matches[0] == $tag;
|
||||
}
|
||||
}
|
||||
?>
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Based on: http://stackoverflow.com/questions/99350/passing-php-associative-arrays-to-and-from-xml
|
||||
*/
|
||||
class ArrayToXML
|
||||
{
|
||||
private $version;
|
||||
private $encoding;
|
||||
|
||||
/**
|
||||
* Construct ArrayToXML object with selected version and encoding
|
||||
*
|
||||
* for available values check XmlWriter docs http://www.php.net/manual/en/function.xmlwriter-start-document.php
|
||||
* @param string $xmlVersion XML Version, default 1.0
|
||||
* @param string $xmlEncoding XML Encoding, default UTF-8
|
||||
*/
|
||||
public function __construct($xmlVersion = '1.0', $xmlEncoding = 'UTF-8')
|
||||
{
|
||||
$this->version = $xmlVersion;
|
||||
$this->encoding = $xmlEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an XML Data Set
|
||||
*
|
||||
* @param array $data Associative Array containing values to be parsed into an XML Data Set(s)
|
||||
* @param string $startElement Root Opening Tag, default data
|
||||
* @return string XML String containing values
|
||||
* @return mixed Boolean false on failure, string XML result on success
|
||||
*/
|
||||
public function buildXML($data, $startElement = 'data')
|
||||
{
|
||||
if (!is_array($data)) {
|
||||
$err = 'Invalid variable type supplied, expected array not found on line ' . __LINE__ . ' in Class: ' . __CLASS__ . ' Method: ' . __METHOD__;
|
||||
trigger_error($err);
|
||||
return false; //return false error occurred
|
||||
}
|
||||
$xml = new XmlWriter();
|
||||
$xml->openMemory();
|
||||
$xml->startDocument($this->version, $this->encoding);
|
||||
$xml->startElement($startElement);
|
||||
|
||||
$data = $this->writeAttr($xml, $data);
|
||||
$this->writeEl($xml, $data);
|
||||
|
||||
$xml->endElement(); //write end element
|
||||
//returns the XML results
|
||||
return $xml->outputMemory(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write keys in $data prefixed with @ as XML attributes, if $data is an array.
|
||||
* When an @ prefixed key is found, a '%' key is expected to indicate the element itself,
|
||||
* and '#' prefixed key indicates CDATA content
|
||||
*
|
||||
* @param XMLWriter $xml object
|
||||
* @param array $data with attributes filtered out
|
||||
* @return array $data | $nonAttributes
|
||||
*/
|
||||
protected function writeAttr(XMLWriter $xml, $data)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
$nonAttributes = array();
|
||||
foreach ($data as $key => $val) {
|
||||
//handle an attribute with elements
|
||||
if ($key[0] == '@') {
|
||||
$xml->writeAttribute(substr($key, 1), $val);
|
||||
} else if ($key[0] == '%') {
|
||||
if (is_array($val)) $nonAttributes = $val;
|
||||
else $xml->text($val);
|
||||
} elseif ($key[0] == '#') {
|
||||
if (is_array($val)) $nonAttributes = $val;
|
||||
else {
|
||||
$xml->startElement(substr($key, 1));
|
||||
$xml->writeCData($val);
|
||||
$xml->endElement();
|
||||
}
|
||||
}else if($key[0] == "!"){
|
||||
if (is_array($val)) $nonAttributes = $val;
|
||||
else $xml->writeCData($val);
|
||||
}
|
||||
//ignore normal elements
|
||||
else $nonAttributes[$key] = $val;
|
||||
}
|
||||
return $nonAttributes;
|
||||
} else return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write XML as per Associative Array
|
||||
*
|
||||
* @param XMLWriter $xml object
|
||||
* @param array $data Associative Data Array
|
||||
*/
|
||||
protected function writeEl(XMLWriter $xml, $data)
|
||||
{
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($value) && !$this->isAssoc($value)) { //numeric array
|
||||
foreach ($value as $itemValue) {
|
||||
if (is_array($itemValue)) {
|
||||
$xml->startElement($key);
|
||||
$itemValue = $this->writeAttr($xml, $itemValue);
|
||||
$this->writeEl($xml, $itemValue);
|
||||
$xml->endElement();
|
||||
} else {
|
||||
$itemValue = $this->writeAttr($xml, $itemValue);
|
||||
$xml->writeElement($key, "$itemValue");
|
||||
}
|
||||
}
|
||||
} else if (is_array($value)) { //associative array
|
||||
$xml->startElement($key);
|
||||
$value = $this->writeAttr($xml, $value);
|
||||
$this->writeEl($xml, $value);
|
||||
$xml->endElement();
|
||||
} else { //scalar
|
||||
$value = $this->writeAttr($xml, $value);
|
||||
$xml->writeElement($key, "$value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if array is associative with string based keys
|
||||
* FROM: http://stackoverflow.com/questions/173400/php-arrays-a-good-way-to-check-if-an-array-is-associative-or-sequential/4254008#4254008
|
||||
*
|
||||
* @param array $array Array to check
|
||||
* @return bool
|
||||
*/
|
||||
protected function isAssoc($array)
|
||||
{
|
||||
return (bool)count(array_filter(array_keys($array), 'is_string'));
|
||||
}
|
||||
}
|
@ -0,0 +1,270 @@
|
||||
<?php
|
||||
/**
|
||||
* Sandhills Development Persistent Dismissible Utility
|
||||
*
|
||||
* @package SandhillsDev
|
||||
* @subpackage Utilities
|
||||
*/
|
||||
namespace Sandhills\Utils;
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* This class_exists() check avoids a fatal error if this class exists in more
|
||||
* than one included plugin/theme, and should not be removed.
|
||||
*/
|
||||
if ( ! class_exists( 'Sandhills\Utils\Persistent_Dismissible' ) ) :
|
||||
|
||||
/**
|
||||
* Class for encapsulating the logic required to maintain a relationship between
|
||||
* the database, a dismissible UI element with an optional lifespan, and a
|
||||
* user's desire to dismiss that UI element.
|
||||
*
|
||||
* Think of this like a WordPress Transient, but without in-memory cache support
|
||||
* and that uses the `wp_usermeta` database table instead of `wp_options`.
|
||||
*
|
||||
* @version 1.0.0
|
||||
*/
|
||||
class Persistent_Dismissible {
|
||||
|
||||
/**
|
||||
* Get the value of a persistent dismissible.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args See parse_args().
|
||||
* @return mixed User meta value on success, false on failure.
|
||||
*/
|
||||
public static function get( $args = array() ) {
|
||||
|
||||
// Parse arguments.
|
||||
$r = self::parse_args( $args );
|
||||
|
||||
// Bail if invalid arguments.
|
||||
if ( ! self::check_args( $r ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get prefixed option names.
|
||||
$eol_id = self::get_eol_id( $r );
|
||||
$prefix = self::get_prefix( $r );
|
||||
$prefixed_id = $prefix . $r['id'];
|
||||
$prefixed_eol = $prefix . $eol_id;
|
||||
|
||||
// Get return value & end-of-life.
|
||||
$retval = get_user_meta( $r['user_id'], $prefixed_id, true );
|
||||
$lifespan = get_user_meta( $r['user_id'], $prefixed_eol, true );
|
||||
|
||||
// Prefer false over default return value of get_user_meta()
|
||||
if ( '' === $retval ) {
|
||||
$retval = false;
|
||||
}
|
||||
|
||||
// If end-of-life, delete it. This needs to be inside get() because we
|
||||
// are not relying on WP Cron for garbage collection. This mirrors
|
||||
// behavior found inside of WordPress core.
|
||||
if ( self::is_eol( $lifespan ) ) {
|
||||
delete_user_option( $r['user_id'], $r['id'], $r['global'] );
|
||||
delete_user_option( $r['user_id'], $eol_id, $r['global'] );
|
||||
$retval = false;
|
||||
}
|
||||
|
||||
// Return the value.
|
||||
return $retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a persistent dismissible.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args See parse_args().
|
||||
* @return int|bool User meta ID if the option didn't exist, true on
|
||||
* successful update, false on failure.
|
||||
*/
|
||||
public static function set( $args = array() ) {
|
||||
|
||||
// Parse arguments.
|
||||
$r = self::parse_args( $args );
|
||||
|
||||
// Bail if invalid arguments.
|
||||
if ( ! self::check_args( $r ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get lifespan and prefixed option names.
|
||||
$lifespan = self::get_lifespan( $r );
|
||||
$eol_id = self::get_eol_id( $r );
|
||||
$prefix = self::get_prefix( $r );
|
||||
$prefixed_id = $prefix . $r['id'];
|
||||
$prefixed_eol = $prefix . $eol_id;
|
||||
|
||||
// No dismissible data, so add it.
|
||||
if ( '' === get_user_meta( $r['user_id'], $prefixed_id, true ) ) {
|
||||
|
||||
// Add lifespan.
|
||||
if ( ! empty( $lifespan ) ) {
|
||||
add_user_meta( $r['user_id'], $prefixed_eol, $lifespan, true );
|
||||
}
|
||||
|
||||
// Add dismissible data.
|
||||
$retval = add_user_meta( $r['user_id'], $prefixed_id, $r['value'], true );
|
||||
|
||||
// Dismissible data found in database.
|
||||
} else {
|
||||
|
||||
// Plan to update.
|
||||
$update = true;
|
||||
|
||||
// Dismissible to update has new lifespan.
|
||||
if ( ! empty( $lifespan ) ) {
|
||||
|
||||
// If lifespan is requested but the dismissible has no end-of-life,
|
||||
// delete them both and re-create them, to avoid race conditions.
|
||||
if ( '' === get_user_meta( $r['user_id'], $prefixed_eol, true ) ) {
|
||||
delete_user_option( $r['user_id'], $r['id'], $r['global'] );
|
||||
add_user_meta( $r['user_id'], $prefixed_eol, $lifespan, true );
|
||||
$retval = add_user_meta( $r['user_id'], $prefixed_id, $r['value'], true );
|
||||
$update = false;
|
||||
|
||||
// Update the lifespan.
|
||||
} else {
|
||||
update_user_option( $r['user_id'], $eol_id, $lifespan, $r['global'] );
|
||||
}
|
||||
}
|
||||
|
||||
// Update the dismissible value.
|
||||
if ( ! empty( $update ) ) {
|
||||
$retval = update_user_option( $r['user_id'], $r['id'], $r['value'], $r['global'] );
|
||||
}
|
||||
}
|
||||
|
||||
// Return the value.
|
||||
return $retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a persistent dismissible.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args See parse_args().
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public static function delete( $args = array() ) {
|
||||
|
||||
// Parse arguments.
|
||||
$r = self::parse_args( $args );
|
||||
|
||||
// Bail if invalid arguments.
|
||||
if ( ! self::check_args( $r ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the end-of-life ID.
|
||||
$eol_id = self::get_eol_id( $r );
|
||||
|
||||
// Delete.
|
||||
delete_user_option( $r['user_id'], $r['id'], $r['global'] );
|
||||
delete_user_option( $r['user_id'], $eol_id, $r['global'] );
|
||||
|
||||
// Success.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse array of key/value arguments.
|
||||
*
|
||||
* Used by get(), set(), and delete(), to ensure default arguments are set.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array|string $args {
|
||||
* Array or string of arguments to identify the persistent dismissible.
|
||||
*
|
||||
* @type string $id Required. ID of the persistent dismissible.
|
||||
* @type string $user_id Optional. User ID. Default to current user ID.
|
||||
* @type int|string $value Optional. Value to store. Default to true.
|
||||
* @type int|string $life Optional. Lifespan. Default to 0 (infinite)
|
||||
* @type bool $global Optional. Multisite, all sites. Default true.
|
||||
* }
|
||||
* @return array
|
||||
*/
|
||||
private static function parse_args( $args = array() ) {
|
||||
return wp_parse_args( $args, array(
|
||||
'id' => '',
|
||||
'user_id' => get_current_user_id(),
|
||||
'value' => true,
|
||||
'life' => 0,
|
||||
'global' => true,
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that required arguments exist.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args See parse_args().
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
private static function check_args( $args = array() ) {
|
||||
return ! empty( $args['id'] ) && ! empty( $args['user_id'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string used to prefix user meta for non-global dismissibles.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @global WPDB $wpdb
|
||||
* @param array $args See parse_args().
|
||||
* @return string Maybe includes the blog prefix.
|
||||
*/
|
||||
private static function get_prefix( $args = array() ) {
|
||||
global $wpdb;
|
||||
|
||||
// Default value
|
||||
$retval = '';
|
||||
|
||||
// Maybe append the blog prefix for non-global dismissibles
|
||||
if ( empty( $args['global'] ) ) {
|
||||
$retval = $wpdb->get_blog_prefix();
|
||||
}
|
||||
|
||||
// Return
|
||||
return $retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the lifespan for a persistent dismissible.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args See parse_args().
|
||||
* @return int
|
||||
*/
|
||||
private static function get_lifespan( $args = array() ) {
|
||||
return ! empty( $args['life'] ) && is_numeric( $args['life'] )
|
||||
? time() + absint( $args['life'] )
|
||||
: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string used to identify the ID for storing the end-of-life.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args See parse_args().
|
||||
* @return string '_eol' appended to the ID (for its end-of-life timestamp).
|
||||
*/
|
||||
private static function get_eol_id( $args = array() ) {
|
||||
return sanitize_key( $args['id'] ) . '_eol';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a timestamp is beyond the current time.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param int $timestamp A Unix timestamp. Default 0.
|
||||
* @return bool True if end-of-life, false if not.
|
||||
*/
|
||||
private static function is_eol( $timestamp = 0 ) {
|
||||
return is_numeric( $timestamp ) && ( $timestamp < time() );
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/**
|
||||
* Multidimensional ArrayAccess
|
||||
*
|
||||
* Allows ArrayAccess-like functionality with multidimensional arrays. Fully supports
|
||||
* both sets and unsets.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Session
|
||||
* @since 3.6.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Recursive array class to allow multidimensional array access.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 3.6.0
|
||||
*/
|
||||
class Recursive_ArrayAccess implements ArrayAccess {
|
||||
/**
|
||||
* Internal data collection.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $container = array();
|
||||
|
||||
/**
|
||||
* Flag whether or not the internal collection has been changed.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $dirty = false;
|
||||
|
||||
/**
|
||||
* Default object constructor.
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
protected function __construct( $data = array() ) {
|
||||
foreach ( $data as $key => $value ) {
|
||||
$this[ $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow deep copies of objects
|
||||
*/
|
||||
public function __clone() {
|
||||
foreach ( $this->container as $key => $value ) {
|
||||
if ( $value instanceof self ) {
|
||||
$this[ $key ] = clone $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the data container as a multidimensional array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray() {
|
||||
$data = $this->container;
|
||||
foreach ( $data as $key => $value ) {
|
||||
if ( $value instanceof self ) {
|
||||
$data[ $key ] = $value->toArray();
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayAccess Implementation
|
||||
**/
|
||||
|
||||
/**
|
||||
* Whether a offset exists
|
||||
*
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
|
||||
*
|
||||
* @param mixed $offset An offset to check for.
|
||||
*
|
||||
* @return boolean true on success or false on failure.
|
||||
*/
|
||||
public function offsetExists( $offset ) {
|
||||
return isset( $this->container[ $offset ]) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to retrieve
|
||||
*
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetget.php
|
||||
*
|
||||
* @param mixed $offset The offset to retrieve.
|
||||
*
|
||||
* @return mixed Can return all value types.
|
||||
*/
|
||||
public function offsetGet( $offset ) {
|
||||
return isset( $this->container[ $offset ] ) ? $this->container[ $offset ] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to set
|
||||
*
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetset.php
|
||||
*
|
||||
* @param mixed $offset The offset to assign the value to.
|
||||
* @param mixed $value The value to set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet( $offset, $data ) {
|
||||
if ( is_array( $data ) ) {
|
||||
$data = new self( $data );
|
||||
}
|
||||
if ( $offset === null ) { // don't forget this!
|
||||
$this->container[] = $data;
|
||||
} else {
|
||||
$this->container[ $offset ] = $data;
|
||||
}
|
||||
|
||||
$this->dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to unset
|
||||
*
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
|
||||
*
|
||||
* @param mixed $offset The offset to unset.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset( $offset ) {
|
||||
unset( $this->container[ $offset ] );
|
||||
}
|
||||
}
|
@ -0,0 +1,332 @@
|
||||
<?php
|
||||
/**
|
||||
* WordPress session managment.
|
||||
*
|
||||
* Standardizes WordPress session data using database-backed options for storage.
|
||||
* for storing user session information.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Session
|
||||
* @since 3.7.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* WordPress Session class for managing user session data.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 3.7.0
|
||||
*/
|
||||
final class WP_Session extends Recursive_ArrayAccess implements Iterator, Countable {
|
||||
/**
|
||||
* ID of the current session.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $session_id;
|
||||
|
||||
/**
|
||||
* Unix timestamp when session expires.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $expires;
|
||||
|
||||
/**
|
||||
* Unix timestamp indicating when the expiration time needs to be reset.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $exp_variant;
|
||||
|
||||
/**
|
||||
* Singleton instance.
|
||||
*
|
||||
* @var bool|WP_Session
|
||||
*/
|
||||
private static $instance = false;
|
||||
|
||||
/**
|
||||
* Retrieve the current session instance.
|
||||
*
|
||||
* @param bool $session_id Session ID from which to populate data.
|
||||
*
|
||||
* @return bool|WP_Session
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
* Will rebuild the session collection from the given session ID if it exists. Otherwise, will
|
||||
* create a new session with that ID.
|
||||
*
|
||||
* @param $session_id
|
||||
* @uses apply_filters Calls `wp_session_expiration` to determine how long until sessions expire.
|
||||
*/
|
||||
protected function __construct() {
|
||||
if ( isset( $_COOKIE[WP_SESSION_COOKIE] ) ) {
|
||||
$cookie = stripslashes( $_COOKIE[WP_SESSION_COOKIE] );
|
||||
$cookie_crumbs = explode( '||', $cookie );
|
||||
|
||||
if( $this->is_valid_md5( $cookie_crumbs[0] ) ) {
|
||||
|
||||
$this->session_id = $cookie_crumbs[0];
|
||||
|
||||
} else {
|
||||
|
||||
$this->regenerate_id( true );
|
||||
|
||||
}
|
||||
|
||||
$this->expires = $cookie_crumbs[1];
|
||||
$this->exp_variant = $cookie_crumbs[2];
|
||||
|
||||
// Update the session expiration if we're past the variant time
|
||||
if ( time() > $this->exp_variant ) {
|
||||
$this->set_expiration();
|
||||
delete_option( "_wp_session_expires_{$this->session_id}" );
|
||||
add_option( "_wp_session_expires_{$this->session_id}", $this->expires, '', 'no' );
|
||||
}
|
||||
} else {
|
||||
$this->session_id = $this->generate_id();
|
||||
$this->set_expiration();
|
||||
}
|
||||
|
||||
$this->read_data();
|
||||
|
||||
$this->set_cookie();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set both the expiration time and the expiration variant.
|
||||
*
|
||||
* If the current time is below the variant, we don't update the session's expiration time. If it's
|
||||
* greater than the variant, then we update the expiration time in the database. This prevents
|
||||
* writing to the database on every page load for active sessions and only updates the expiration
|
||||
* time if we're nearing when the session actually expires.
|
||||
*
|
||||
* By default, the expiration time is set to 30 minutes.
|
||||
* By default, the expiration variant is set to 24 minutes.
|
||||
*
|
||||
* As a result, the session expiration time - at a maximum - will only be written to the database once
|
||||
* every 24 minutes. After 30 minutes, the session will have been expired. No cookie will be sent by
|
||||
* the browser, and the old session will be queued for deletion by the garbage collector.
|
||||
*
|
||||
* @uses apply_filters Calls `wp_session_expiration_variant` to get the max update window for session data.
|
||||
* @uses apply_filters Calls `wp_session_expiration` to get the standard expiration time for sessions.
|
||||
*/
|
||||
protected function set_expiration() {
|
||||
$this->exp_variant = time() + (int) apply_filters( 'wp_session_expiration_variant', 24 * 60 );
|
||||
$this->expires = time() + (int) apply_filters( 'wp_session_expiration', 30 * 60 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the session cookie
|
||||
*/
|
||||
protected function set_cookie() {
|
||||
@setcookie( WP_SESSION_COOKIE, $this->session_id . '||' . $this->expires . '||' . $this->exp_variant , $this->expires, COOKIEPATH, COOKIE_DOMAIN );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a cryptographically strong unique ID for the session token.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generate_id() {
|
||||
require_once( ABSPATH . 'wp-includes/class-phpass.php');
|
||||
$hasher = new PasswordHash( 8, false );
|
||||
|
||||
return md5( $hasher->get_random_bytes( 32 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if is valid md5 string
|
||||
*
|
||||
* @param string $md5
|
||||
* @return int
|
||||
*/
|
||||
protected function is_valid_md5( $md5 = '' ){
|
||||
return preg_match( '/^[a-f0-9]{32}$/', $md5 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data from a transient for the current session.
|
||||
*
|
||||
* Automatically resets the expiration time for the session transient to some time in the future.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function read_data() {
|
||||
$this->container = get_option( "_wp_session_{$this->session_id}", array() );
|
||||
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the data from the current session to the data storage system.
|
||||
*/
|
||||
public function write_data() {
|
||||
$option_key = "_wp_session_{$this->session_id}";
|
||||
|
||||
// Only write the collection to the DB if it's changed.
|
||||
if ( $this->dirty ) {
|
||||
if ( false === get_option( $option_key ) ) {
|
||||
add_option( "_wp_session_{$this->session_id}", $this->container, '', 'no' );
|
||||
add_option( "_wp_session_expires_{$this->session_id}", $this->expires, '', 'no' );
|
||||
} else {
|
||||
delete_option( "_wp_session_{$this->session_id}" );
|
||||
add_option( "_wp_session_{$this->session_id}", $this->container, '', 'no' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the current container contents as a JSON-encoded string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function json_out() {
|
||||
return json_encode( $this->container );
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a JSON string and, if the object is an array, overwrites the session container with its contents.
|
||||
*
|
||||
* @param string $data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function json_in( $data ) {
|
||||
$array = json_decode( $data );
|
||||
|
||||
if ( is_array( $array ) ) {
|
||||
$this->container = $array;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate the current session's ID.
|
||||
*
|
||||
* @param bool $delete_old Flag whether or not to delete the old session data from the server.
|
||||
*/
|
||||
public function regenerate_id( $delete_old = false ) {
|
||||
if ( $delete_old ) {
|
||||
delete_option( "_wp_session_{$this->session_id}" );
|
||||
}
|
||||
|
||||
$this->session_id = $this->generate_id();
|
||||
|
||||
$this->set_cookie();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a session has been initialized.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function session_started() {
|
||||
return !!self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the read-only cache expiration value.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function cache_expiration() {
|
||||
return $this->expires;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes all session variables.
|
||||
*/
|
||||
public function reset() {
|
||||
$this->container = array();
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/* Iterator Implementation */
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* Current position of the array.
|
||||
*
|
||||
* @link http://php.net/manual/en/iterator.current.php
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function current() {
|
||||
return current( $this->container );
|
||||
}
|
||||
|
||||
/**
|
||||
* Key of the current element.
|
||||
*
|
||||
* @link http://php.net/manual/en/iterator.key.php
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function key() {
|
||||
return key( $this->container );
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the internal point of the container array to the next item
|
||||
*
|
||||
* @link http://php.net/manual/en/iterator.next.php
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function next() {
|
||||
next( $this->container );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the internal point of the container array.
|
||||
*
|
||||
* @link http://php.net/manual/en/iterator.rewind.php
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind() {
|
||||
reset( $this->container );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the current key valid?
|
||||
*
|
||||
* @link http://php.net/manual/en/iterator.rewind.php
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function valid() {
|
||||
return $this->offsetExists( $this->key() );
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/* Countable Implementation */
|
||||
/*****************************************************************/
|
||||
|
||||
/**
|
||||
* Get the count of elements in the container array.
|
||||
*
|
||||
* @link http://php.net/manual/en/countable.count.php
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count() {
|
||||
return count( $this->container );
|
||||
}
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
<?php
|
||||
/**
|
||||
* WordPress session management.
|
||||
*
|
||||
* Standardizes WordPress session data and uses either database transients or
|
||||
* in-memory caching for storing user session information.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Session
|
||||
* @since 3.7.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Return the current cache expire setting.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function wp_session_cache_expire() {
|
||||
$wp_session = WP_Session::get_instance();
|
||||
|
||||
return $wp_session->cache_expiration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of wp_session_write_close()
|
||||
*/
|
||||
function wp_session_commit() {
|
||||
wp_session_write_close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a JSON-encoded string into the current session.
|
||||
*
|
||||
* @param string $data
|
||||
*/
|
||||
function wp_session_decode( $data ) {
|
||||
$wp_session = WP_Session::get_instance();
|
||||
|
||||
return $wp_session->json_in( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the current session's data as a JSON string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function wp_session_encode() {
|
||||
$wp_session = WP_Session::get_instance();
|
||||
|
||||
return $wp_session->json_out();
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate the session ID.
|
||||
*
|
||||
* @param bool $delete_old_session
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function wp_session_regenerate_id( $delete_old_session = false ) {
|
||||
$wp_session = WP_Session::get_instance();
|
||||
|
||||
$wp_session->regenerate_id( $delete_old_session );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start new or resume existing session.
|
||||
*
|
||||
* Resumes an existing session based on a value sent by the _wp_session cookie.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function wp_session_start() {
|
||||
$wp_session = WP_Session::get_instance();
|
||||
do_action( 'wp_session_start' );
|
||||
|
||||
return $wp_session->session_started();
|
||||
}
|
||||
add_action( 'plugins_loaded', 'wp_session_start' );
|
||||
|
||||
/**
|
||||
* Return the current session status.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function wp_session_status() {
|
||||
$wp_session = WP_Session::get_instance();
|
||||
|
||||
if ( $wp_session->session_started() ) {
|
||||
return PHP_SESSION_ACTIVE;
|
||||
}
|
||||
|
||||
return PHP_SESSION_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset all session variables.
|
||||
*/
|
||||
function wp_session_unset() {
|
||||
$wp_session = WP_Session::get_instance();
|
||||
|
||||
$wp_session->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write session data and end session
|
||||
*/
|
||||
function wp_session_write_close() {
|
||||
$wp_session = WP_Session::get_instance();
|
||||
|
||||
$wp_session->write_data();
|
||||
do_action( 'wp_session_commit' );
|
||||
}
|
||||
add_action( 'shutdown', 'wp_session_write_close' );
|
||||
|
||||
/**
|
||||
* Clean up expired sessions by removing data and their expiration entries from
|
||||
* the WordPress options table.
|
||||
*
|
||||
* This method should never be called directly and should instead be triggered as part
|
||||
* of a scheduled task or cron job.
|
||||
*/
|
||||
function wp_session_cleanup() {
|
||||
global $wpdb;
|
||||
|
||||
if ( defined( 'WP_SETUP_CONFIG' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! defined( 'WP_INSTALLING' ) ) {
|
||||
$expiration_keys = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options WHERE option_name LIKE '_wp_session_expires_%'" );
|
||||
|
||||
$now = current_time( 'timestamp' );
|
||||
$expired_sessions = array();
|
||||
|
||||
foreach( $expiration_keys as $expiration ) {
|
||||
|
||||
// If the session has expired
|
||||
if ( $now > intval( $expiration->option_value ) ) {
|
||||
|
||||
// Get the session ID by parsing the option_name
|
||||
$session_id = substr( $expiration->option_name, 20 );
|
||||
|
||||
if( (int) -1 === (int) $session_id || ! preg_match( '/^[a-f0-9]{32}$/', $session_id ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$expired_sessions[] = $expiration->option_name;
|
||||
$expired_sessions[] = esc_sql( "_wp_session_$session_id" );
|
||||
}
|
||||
}
|
||||
|
||||
// Delete all expired sessions in a single query
|
||||
if ( ! empty( $expired_sessions ) ) {
|
||||
$option_names = implode( "','", $expired_sessions );
|
||||
$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name IN ('$option_names')" );
|
||||
}
|
||||
}
|
||||
|
||||
// Allow other plugins to hook in to the garbage collection process.
|
||||
do_action( 'wp_session_cleanup' );
|
||||
}
|
||||
add_action( 'wp_session_garbage_collection', 'wp_session_cleanup' );
|
||||
|
||||
/**
|
||||
* Register the garbage collector as a twice daily event.
|
||||
*/
|
||||
function wp_session_register_garbage_collection() {
|
||||
if ( ! wp_next_scheduled( 'wp_session_garbage_collection' ) ) {
|
||||
wp_schedule_event( current_time( 'timestamp' ), 'twicedaily', 'wp_session_garbage_collection' );
|
||||
}
|
||||
}
|
||||
add_action( 'wp', 'wp_session_register_garbage_collection' );
|
Reference in New Issue
Block a user