Location: PHPKode > scripts > superGlobals > superglobals/superglobals.php
<?
/**
 * superGlobals library
 *
 * Transparently injects into superglobal or global variable with keeping all functionality of regular
 * array, but extending it with type casting and URI parsing.
 *
 * Usage:
 *
 *     superGlobal::db($instance);
 *
 *         Set DB instance for all DB-related operations.
 *
 *     superGlobal::memcached($instance)
 *
 *         Set Memcache instance to enable built-in cache
 *
 *     superGlobal::inject($array|$string);
 *
 *         Inject superGlobal functionality into global or superglobal variable.
 *         If additional parameters passed with registerParam or registerSQLParam overlaps already existing
 *         parameters in array, initial value will be stored in parameter array, which is accessible as
 *         $array['array:param'].
 *
 *         If passed variable contains string, it will be stored as URI for future parsing by registerParam or
 *         registerSQLParam methods.
 *
 *     superGlobal::registerParam('name', 'callback');
 *     superGlobal::registerParam('name', array(values));
 *
 *         Inject into $_GET (if was not injected previously), then parse URI parameters into all of injected global arrays
 *         and only $_GET superglobal array, so if URI string parameter (REQUEST_URI for $_GET or manually provided for
 *         other arrays) contains parameter from values array provided or provided callback function will return something
 *         different from false, this piece will be stored in resulting array with name key.
 *
 *     superGlobal::registerParam('type:name', 'callback');
 *     superGlobal::registerParam('type:name', array(values));
 *
 *         Same as previous example, except URI parameter will be additionally casted into provided type.
 *
 *     superGlobal::registerSQLParam('name', 'table', 'field');
 *     superGlobal::registerSQLParam('type:name', 'table', 'field');
 *
 *         List of possible values for parameter will be fetched from SQL table 'table' from field 'field'.
 *         (DB abstraction instance should be provided and getCol method should be implemented)
 *
 *     superGlobal::registerParam('name', 'callback', $array);
 *     superGlobal::registerSQLParam('name', 'table', 'field', $array);
 *
 *         Register param into only one array. Also allows to inject parameters into superglobal arrays different
 *         from $_GET.
 *
 * After injecting it provides several casting methods for all of array variables, which should be defined in
 * requesting variable name and separated from it by semicolon:
 *
 *     Fetch variable as boolean (returns boolean):
 *         $_SUPERGLOBAL['bool:key']
 *
 *     Fetch variable as integer (returns integer):
 *         $_SUPERGLOBAL['int:key']
 *
 *     Fetch variable as float (returns float):
 *         $_SUPERGLOBAL['float:key']
 *
 *     Fetch variable as regular string (returns string):
 *         $_SUPERGLOBAL['string:key']
 *
 *     Fetch variable as array (returns array):
 *         $_SUPERGLOBAL['array:key']
 *
 *     Fetch variable as IP address (IPv6 or IPv4) (returns string):
 *         $_SUPERGLOBAL['ip:key']
 *
 *     Fetch variable as IP address (IPv4 or IPv6), only from public networks (returns string):
 *         $_SUPERGLOBAL['ippub:key']
 *
 *     Fetch variable as IP address (only IPv4) (returns string):
 *         $_SUPERGLOBAL['ipv4:key']
 *
 *     Fetch variable as IP address (only IPv6) (returns string):
 *         $_SUPERGLOBAL['ipv6:key']
 *
 *     Fetch variable as regular expression (returns string):
 *         $_SUPERGLOBAL['regexp:key']
 *
 *     Fetch variable as correct URL (returns string):
 *         $_SUPERGLOBAL['url:key']
 *
 *     Fetch variable as URL encoded string (returns string):
 *         $_SUPERGLOBAL['encode:key']
 *
 *     Fetch variable as HTML escaped string (returns string):
 *         $_SUPERGLOBAL['escape:key']
 *
 *     Fetch variable as regular object (returns object):
 *         $_SUPERGLOBAL['object:key']
 *
 *     Fetch variable as SQL escaped string (returns string):
 *         $_SUPERGLOBAL['sql:key']
 *
 * It is possible to pass function name callback as casting method, for example, next will return trimmed
 * value of $_SUPERGLOBAL['key'].
 *         $_SUPERGLOBAL['trim:key']
 *
 * Also, it is possible to pass multiple casting methods, which is useful for array processing, next will return an
 * array with all matching values for $_SUPERGLOBAL['key'] casted to float and then cos() function applied.
 *         $_SUPERGLOBAL['array:float:cos:key']
 *
 * May throw exceptions (Exception $e) with codes:
 *    1:      Trying to directly create an instance of class
 *    100:    DB driver is not set or method missing
 *    200:    There is no filter.so installed
 *    300:    Callback or casting (if enabled) exception
 *    400:    Trying to register variable that is not global
 *    500:    Incorrect use of registerParam or registerSQLParam functions
 *    600:    Memcache exception
 *
 * License
 *
 *         This source file is subject to the new BSD license that is bundled
 *         with this package in the file license.txt.
 *         It is also available through the world-wide-web at this URL:
 *         http://ics.net.ua/license/new-bsd
 *         If you did not receive a copy of the license and are unable to
 *         obtain it through the world-wide-web, please send an email
 *         to hide@address.com so we can send you a copy immediately.
 *
 * @package superGlobals
 * @author Max S. Yarchevsky
 * @license http://ics.net.ua/license/new-bsd        New BSD License
 * @version 1.133
 */

/**
 * Helper class to inject into superglobal.
 * Provides value casting and URL string parsing.
 * Should not be called directly.
 *
 * @uses ArrayAccess
 * @uses Iterator
 * @uses Countable
 * @package superGlobals
 * @author Max S. Yarchevsky
 * @license New BSD License
 * @version 1.133
 */
class requestCaster extends superGlobal implements ArrayAccess, Iterator, Countable
{
    /**
     * Storage of params found from URL or passed from request
     *
     * @var array
     */
    private $storage;

    /**
     * Array of possible param values
     *
     * @var array
     */
    private $values;

    /**
     * If param need additional casting, callback function or type should be stored here
     *
     * @var array
     */
    private $castCallbacks;

    /**
     * Data types and regular expressions for variable casting
     *
     * @var array
     */
    private static $castMethods;

    /**
     * Unparsed values that left after last iteration
     *
     * @var array
     */
    private $unParsed;

    /**
     * General class contructor. Should not be called directly.
     *
     * @param array $params Array data to merge into instance
     * @param string $URI Uniform resource identifier string to parse values from
     * @return boolean|void
     */
    public function __construct(array &$params, $URI = false)
    {
        // Define casting methods
        if (!is_array(self::$castMethods))
        {
            self::$castMethods = array(
                // Possible variable types for filtering
                'varTypes' => array(
                    // Boolean type. Can be false|true|1|0
                    'bool' => array(
                        'filter' => FILTER_VALIDATE_BOOLEAN,
                        'options' => FILTER_NULL_ON_FAILURE,
                        'cast' => 'bool'
                    ),

                    // Integer type
                    'int' => array(
                        'filter' => FILTER_SANITIZE_NUMBER_INT,
                        'options' => FILTER_NULL_ON_FAILURE,
                        'cast' => 'int'
                    ),

                    // Float type
                    'float' => array(
                        'filter' => FILTER_SANITIZE_NUMBER_FLOAT,
                        'options' => FILTER_NULL_ON_FAILURE + FILTER_FLAG_ALLOW_FRACTION + FILTER_FLAG_ALLOW_SCIENTIFIC,
                        'cast' => 'float'
                    ),

                    // Regular string
                    'string' => array(
                        'filter' => FILTER_SANITIZE_STRING,
                        'options' => FILTER_NULL_ON_FAILURE,
                        'cast' => 'string'
                    ),

                    // Regular array
                    'array' => array(
                        'cast' => 'array'
                    ),

                    // IP address (IPv4 or IPv6)
                    'ip' => array(
                        'filter' => FILTER_VALIDATE_IP,
                        'options' => FILTER_NULL_ON_FAILURE,
                        'cast' => 'string'
                    ),

                    // IP address (IPv4 or IPv6), only from public networks
                    'ippub' => array(
                        'filter' => FILTER_VALIDATE_IP,
                        'options' => FILTER_NULL_ON_FAILURE + FILTER_FLAG_NO_PRIV_RANGE,
                        'cast' => 'string'
                    ),

                    // IP address (only IPv4)
                    'ipv4' => array(
                        'filter' => FILTER_VALIDATE_IP,
                        'options' => FILTER_NULL_ON_FAILURE + FILTER_FLAG_IPV4,
                        'cast' => 'string'
                    ),

                    // IP address (only IPv6)
                    'ipv6' => array(
                        'filter' => FILTER_VALIDATE_IP,
                        'options' => FILTER_NULL_ON_FAILURE + FILTER_FLAG_IPV6,
                        'cast' => 'string'
                    ),

                    // Regular expression
                    'regexp' => array(
                        'filter' => FILTER_VALIDATE_REGEXP,
                        'options' => FILTER_NULL_ON_FAILURE,
                        'cast' => 'string'
                    ),

                    // Uniform resource locator
                    'url' => array(
                        'filter' => FILTER_SANITIZE_URL,
                        'options' => FILTER_NULL_ON_FAILURE,
                        'cast' => 'string'
                    ),

                    // URL encoded string
                    'encode' => array(
                        'filter' => FILTER_SANITIZE_ENCODED,
                        'options' => FILTER_NULL_ON_FAILURE + FILTER_FLAG_ENCODE_LOW + FILTER_FLAG_ENCODE_HIGH,
                        'cast' => 'string'
                    ),

                    // HTML escaped string
                    'escape' => array(
                        'filter' => FILTER_SANITIZE_SPECIAL_CHARS,
                        'options' => FILTER_NULL_ON_FAILURE + FILTER_FLAG_STRIP_LOW,
                        'cast' => 'string'
                    ),

                    // Regular object
                    'object' => array(
                        'cast' => 'object'
                    )
                ),

                // Built-in cast functions
                'callback' => array(
                    'sql' => 'castEscape'
                )
            );
        }

        // If no params got for initial launch we'll use empty array for init
        $this->storage = is_array($params) ? $params : array();
        $this->unParsed = ($URI) ? explode("/", substr(parse_url($URI,PHP_URL_PATH), 1)) : array();

        // Prepare params got from array
        foreach ($this->storage as $k => $v)
        {
            $this->values[$k] = array();
            $this->storage[$k] = array('value' => $v);
        }

        // Init system arrays
        $this->castCallbacks = array();

        $params = &$this;
    }

    /**
     * Checks if value need to be casted before return
     *
     * @param string reference $param Asked superglobal key reference
     * @return void
     */
    private function checkCast(&$param)
    {
        if (strpos($param,':') != false)
        {
            $cast = explode(':',$param);
            $param = array_pop($cast);
            $this->castCallbacks[$param] = $cast;
        }
    }

    /**
     * Returns string escaped for DB driver
     *
     * @param string $value String to escape
     * @return string Escaped string
     */
    private function castEscape($value)
    {
        // Check DB initialized
        if (!parent::$mdb || !method_exists(parent::$mdb,'escape'))
        {
            if (parent::$throwExceptionOnCast)
            {
                throw new Exception('DB driver is not set or escape method missing, cannot escape as SQL correctly', 100);
            }
            return mysql_escape_string($value);
        }

        // Get DB driver instance from parent
        return parent::$mdb->escape($value);
    }

    /**
     * Throw an exception if cast failed
     *
     * @param mixed $value Casted value
     * @return mixed Casted value
     */
    private function checkCastValue($value, $exception)
    {
        if (null === $value && parent::$throwExceptionOnCast)
        {
            throw new Exception($exception, 300);
        }

        return $value;
    }

    /**
     * Returns filter options for casting method
     *
     * @param string $method Filter method
     * @return int Filter parameters
     */
    private function getCastOptions($method)
    {
        return isset(self::$castMethods['varTypes'][$method]['options']) ?
            self::$castMethods['varTypes'][$method]['options'] : FILTER_FLAG_NONE;
    }

    /**
     * Value casting by type or function callback
     *
     * @param mixed $value Parameter value
     * @param string|array $callback Cast type or function callback
     * @return mixed Casting result
     */
    private function castValue($value, $callback)
    {
        // If we have additional parameters passed for callback as array we should separate them from main filter
        if (is_array($callback))
        {
            $shifted = array_shift($callback);
            $additionalCast    = count($callback) > 0 ? $callback : false;
            $callback = $shifted;
        }
        else
        {
            $additionalCast = false;
        }

        // Generate memcached key and check for parsed value for it
        if ($memKey = array($value,$callback,$additionalCast) && $cached = $this->memcache($memKey))
        {
            return $cached;
        }

        // If we only need to cast type
        if (array_key_exists($callback, self::$castMethods['varTypes']))
        {
            // If type casting failed return null, else return casted variable
            if (isset(self::$castMethods['varTypes'][$callback]['filter']))
            {
                // Check for filters available
                if (!function_exists('filter_list'))
                {
                    throw new Exception('There are no filters available because filter.so is missing', 200);
                }

                $value = filter_var($value, self::$castMethods['varTypes'][$callback]['filter'], $this->getCastOptions($callback));
            }

            // Check for cast needed
            if (isset(self::$castMethods['varTypes'][$callback]['cast']) && !empty($value))
            {
                $value = settype($value,self::$castMethods['varTypes'][$callback]['cast']) ? $value : null;
            }

                        // Additional nulling of empty
                        if (empty($value))
                        {
                            $value = null;
                        }

            // If we have additional casting parameters
            if ($additionalCast)
            {
                if (is_array($value))
                {
                    // If we have array
                    foreach ($additionalCast as $castMethod)
                    {
                        /*/ Use faster way of casting
                        if (array_key_exists($castMethod, self::$castMethods['varTypes']))
                        {
                            $value = filter_var_array($value, self::$castMethods['varTypes'][$castMethod]['filter'] + $this->getCastOptions($castMethod));
                            var_dump($value);
                        }
                        else
                        {*/
                        // Use recursion. Slow but it works anyway.
                        // Some versions of PHP (for ex., current stable debian 5.2.0.8) do not support int as second parameter
                        // for filter_var_array. Even more, it do not support arrays with numeric keys as input for filtering.
                        // I can create associative array for storage (absolutely useless), but i think there is an idiotic way to
                        // also copy such array with all values set to array, which should contain filter method and filter options
                        // separated from each other.
                        foreach ($value as &$v)
                        {
                            $v = $this->castValue($v, $castMethod);
                        }
                    }
                }
                else
                {
                    // Prevent passing null into callback functions
                        if (null !== $value)
                    {
                            // Maybe we'll need to use casting twice. I'm not sure, so i'll include here some piece of recursion
                        foreach ($additionalCast as $castMethod)
                        {
                            $value = $this->castValue($value, $castMethod);
                        }
                    }
                }
            }

            // Cache value processing result
            $this->memcache($memKey, $value);

            return $this->checkCastValue($value, 'Value casting to '.$callback.' data type failed');
        }

        // Also we should try built-in cast functions
        // We do not cache it when used alone because in some situations callback result may
        // differ for same value.
        // Please note, called with variable casting it will be cached.
        if (array_key_exists($callback, self::$castMethods['callback']) && method_exists($this, self::$castMethods['callback'][$callback]))
        {
            $method = self::$castMethods['callback'][$callback];
            return $this->checkCastValue($this->$method($value), 'Value casting by '.$callback.' built-in cast failed');
        }

        // Else, if we need to make a callback
        // We do not cache it when used alone because in some situations callback result may
        // differ for same value.
        // Please note, called with variable casting it will be cached.
        if (function_exists($callback))
        {
            return $this->checkCastValue($callback($value), 'Value casting by "'.$callback.'" function failed');
        }

        throw new Exception('Value casting callback function "'.$callback.'" do not exist', 300);
        return $value;
    }

    /**
     * Add new URL parameter from DB column
     *
     * @param string $param Url parameter key
     * @param string $table Table to fetch from
     * @param string $field Field containing possible values for parameter
     * @return void|false
     */
    public function addSQLParam($param, $table, $field)
    {
        // Check for casting request
        $this->checkCast($param);

        // Keep previously passed parameter into values array
        if (array_key_exists($param,$this->storage))
        {
            $this->storage[$param] = array('array' => array($this->storage[$param]['value']));
        }

        if ($cached = $this->memcache(array($param, $table, $field)))
        {
            $this->values[$param] = $cached;
            return;
        }

        // Check DB initialized and methods implemented
        if (!parent::$mdb || (!method_exists(parent::$mdb,'getCol') && !method_exists(parent::$mdb,'getOne')))
        {
            throw new Exception('DB driver is not set or getCol method missing, cannot add parameter from DB', 100);
            return false;
        }

        // Select method
        if (method_exists(parent::$mdb,'getCol'))
        {
            $values = parent::$mdb->getCol('select DISTINCT '.$field.' from '.$table);
        }
        else
        {
            $values = explode(',',parent::$mdb->getOne('select GROUP_CONCAT( DISTINCT '.$field.' SEPARATOR ",") from '.$table));
        }

        $this->memcache(array($param, $table, $field), $values);

        // Get DB driver instance from parent
        $this->values[$param] = $values;
    }

    /**
     * Add new URL parameter as array of values or callback function
     *
     * @param string $param Url parameter key
     * @param mixed $value Array of possible values or string containing callback function name
     */
    public function addParam($param, $value)
    {
        // Check for casting request
        $this->checkCast($param);

        // Keep previously passed parameter into values array
        if (array_key_exists($param,$this->storage))
        {
            $this->storage[$param] = array('array' => array($this->storage[$param]['value']));
        }

        $this->values[$param] = $value;
    }

    /**
     * Returns matching value for key, adding it into array of found values.
     *
     * @param string $key Url parameter key
     * @return string
     */
    private function parseValue($key)
    {
      // Check for key existance
      if (!array_key_exists($key, $this->values) && !array_key_exists($key, $this->storage))
      {
          return false;
      }

      // Check for callback
      $callback = (!is_array($this->values[$key])) ? $this->values[$key] : false;

      // Check for previously found key
      if (array_key_exists($key, $this->storage) && isset($this->storage[$key]['value']))
      {
          return $this->storage[$key]['value'];
      }

        // Init array of passed values
        if (!isset($this->storage[$key]['array']))
        {
            $this->storage[$key]['array'] = array();
        }

        // If we have item in cache
        if ($cached = $this->memcache(array($this->unParsed, $callback)))
        {
            $this->storage[$key] = $cached;
        }
        else
        {
            // Iterate params and find if param is defined
            foreach ($this->unParsed as $k => $v)
            {
              // Are qe using callback or an array ?
              $condition = ($callback) ? $this->castValue($v, $callback) : in_array($v, $this->values[$key]);

              // We found it
                if ($condition)
                {
                  // Cast value if callback present, else just store
                    $this->storage[$key]['value'] = array_key_exists($key,$this->castCallbacks) ? $this->castValue($v,$this->castCallbacks[$key]) : $v;


                    // Add value into array of passed values, should be distinct
                    if (!in_array($this->storage[$key]['value'], $this->storage[$key]['array']));
                    {
                        $this->storage[$key]['array'][] = $this->storage[$key]['value'];
                    }
                }
            }
        }

      // Do not return from cycle to prevent multiple params for one key. We get only last one.
        return isset($this->storage[$key]['value']) ? $this->storage[$key]['value'] : null;
    }

    /**
     * Does offset exist ?
     *
     * @param string|int $key
     * @return bool
     */
    public function offsetExists($key)
    {
        return (bool)$this->parseValue($key);
    }

    /**
     * Retrieve by offset
     *
     * @param string|int $key
     * @return mixed
     */
    public function offsetGet($key)
    {
        // Check for runtime casting
        $cast = false;

        if (strpos($key,':') != false)
        {
            $cast = explode(':',$key);
            $key = array_pop($cast);
        }

        // Get value
        $value = $this->parseValue($key);

        // If array casting passed, but we do not hold an array as value, we need to return array of all input for key
        if ($cast[0] == 'array' && !is_array($value))
        {
            $value = $this->storage[$key]['array'];
        }

        return $cast ? $this->castValue($value, $cast) : $this->parseValue($key);
    }

    /**
     * Set value by offset
     *
     * @param string $key
     * @param mixed $value
     * @return void
     */
    public function offsetSet($key, $value)
    {
        $this->storage[$key]['value'] = $value;
    }

    /**
     * Unset value by offset
     *
     * @param string $key
     * @return void
     */
    public function offsetUnset($key)
    {
        unset($key);
    }

    /**
     * How many items are present
     *
     * @return int
     */
    public function count()
    {
        return count($this->values);
    }

    /**
     * Get current value
     *
     * @return array
     */
    public function current()
    {
        $current = key($this->values);
        return $this->parseValue($current);
    }

    /**
     * Get next item
     *
     * @return void
     */
    public function next()
    {
        next($this->values);
    }

    /**
     * Get current key
     *
     * @return string|int
     */
    public function key()
    {
        return key($this->values);
    }

    /**
     * Rewind to first value in collection
     *
     * @return void
     */
    public function rewind()
    {
        reset($this->values);
    }

    /**
     * Is item valid ?
     *
     * @return bool
     */
    public function valid()
    {
        return (bool) $this->current();
    }

    /**
     * Serialize current state
     *
     * @return array
     */
    public function __sleep()
    {
        return array('params', 'storage', 'unParsed', 'castCallbacks', 'castMethods');
    }

    /**
     * Store value into memcache by key or get value for key
     *
     * @param string|array $key Key for object in memcached
     * @param string[optional] $value Value to set for key
     * @return mixed Value from memcached server or boolean of request result
     */
    private function memcache($key, $value = false)
    {
        // If we have memcache instance
            if(parent::$memcached)
            {
                // Prevent whitespaces or non-string keys
                $key = md5(serialize($key));

                // Set value if passed
                if($value)
                {
                    return parent::$memcached->set($key, $value, 0, parent::$memcacheTimeout);
                }

                // Try to get value
                return parent::$memcached->get($key);
            }

            return false;
    }

    /**
     * Magic method, used here to generate URI representation of parameters
     *
     * @return string URI string
     */
    public function __toString()
    {
        $return = array();
        $returnstr = array();

        // Iterate self to get all of params
        foreach ($this as $k => $v)
        {
            if (empty($this->values[$k]))
            // If it is not registered variable process it as usual $_GET
            {
                $return[] = $k.'='.$v;
            }
            else
            // If we already found it we can return it as a part of URI
            {
                foreach ($this->storage[$k]['array'] as $v)
                {
                    // Don't duplicate
                    if (!in_array($v,$returnstr))
                    {
                        $returnstr[] = $v;
                    }
                }
            }
        }

        return implode('/',$returnstr).(!empty($return) ? '?'.implode('&',$return) : '');
    }
}

/**
 * Worker class for transparent managing of superglobal arrays
 *
 * @uses requestCaster
 * @package superGlobals
 * @author Max S. Yarchevsky
 * @license New BSD License
 * @version 1.133
 */
class superGlobal
{
    /**
     * Singleton instance of self
     *
     * @var object
     */
    private static $instance;

    /**
     * DB abstraction instance.
     *
     * @var object
     */
    protected  static $mdb = false;

    /**
     * Should we throw an exception on value cast failure or just return null ?
     *
     * @var boolean
     */
    protected static $throwExceptionOnCast = false;

    /**
     * Timeout for memcached cache
     *
     * @var int
     */
    protected static $memcacheTimeout = 300;

    /**
     * Memcache instance.
     *
     * @var object
     */
    protected static $memcached = false;

    /**
     * Instances of superglobal replacers
     *
     * @var array
     */
    protected static $superglobals = array();

    /**
     * Creates singleton for class
     *
     * @return object
     */
    private static function singleton()
    {
        // Still did not initialized ?
        if (!is_object(self::$instance))
        {
            self::$instance = new superGlobal();
            self::$instance->injectInto('_GET');
        }
        return self::$instance;
    }

    /**
     * Sets DB abstraction instance for all of superglobals
     *
     * @param object $DB DB abstraction instance
     * @return void
     */
    public static function db(&$DB)
    {
        self::$mdb = &$DB;
    }

    /**
     * Sets Memcache instance for all of superglobals
     *
     * @param object $memcache Memcache instance
     * @param int $timeout Memcache keep timeout
     * @return void
     */
    public static function memcached(&$memcache, $timeout = 300)
    {
        self::$memcached = &$memcache;
        self::$memcacheTimeout = $timeout;
    }

    /**
     * Inject requestCaster into variable (only for internal calls)
     * Useful because sometimes we use instance of class to call it
     *
     * @param string $variable Array name to inject
     * @return void
     */
    private function injectInto($variable)
    {
        $globs = array(
            '_GET' => $_GET,
            '_POST' => $_POST,
            '_COOKIE' => $_COOKIE,
            '_SERVER' => $_SERVER,
            '_REQUEST' => $_REQUEST,
        );

        if (isset($_SESSION))
        {
            $globs['_SESSION'] = &$_SESSION;
        }

        // Is a superglobal
        if(array_key_exists($variable,$globs))
        {
            // Check if we already injected variable
            // If we did there is no need to create new object
            if(!array_key_exists($variable,self::$superglobals))
            {
                // Parse URL only into _GET
                self::$superglobals[$variable] = ($variable == '_GET') ?
                // Create instance of requestCaster
                    new requestCaster($globs[$variable], $_SERVER['REQUEST_URI']) : new requestCaster($globs[$variable]);
            }

            // We need to do so because superglobals should be accessed directly (bug ?)
            switch ($variable)
            {
                case '_GET': $_GET = &self::$superglobals[$variable]; break;
                case '_POST': $_POST = &self::$superglobals[$variable]; break;
                case '_COOKIE': $_COOKIE = &self::$superglobals[$variable]; break;
                case '_SERVER': $_SERVER = &self::$superglobals[$variable]; break;
                case '_SESSION': $_SESSION = &self::$superglobals[$variable]; break;
                case '_REQUEST': $_REQUEST = &self::$superglobals[$variable]; break;
            }
            // Exit from function
            return;
        }

        // Variable is not superglobal, so we should check if it is at least global
        global $$variable;

        // Check for consistency
        if (!isset($$variable) || (!is_array($$variable) && !is_string($$variable)))
        {
            throw new Exception('Only global or superglobal variable may be passed as a place to inject requestCaster into it', 400);
        }

        // Check if we already injected variable
        // If we did there is no need to create new object
        if(!array_key_exists($variable,self::$superglobals))
        {
            // We need to pass string contents of variable as URI, if given
            self::$superglobals[$variable] = is_array($$variable) ?
                // Create requestCaster instance
                new requestCaster($$variable) : new requestCaster(array(), $$variable);
        }

        // Inject into it
        $$variable = &self::$superglobals[$variable];
    }

    /**
     * Inject requestCaster into variable
     *
     * @param string $superglobal Superglobal name
     */
    public static function inject($superglobal)
    {
        self::$instance->injectInto($superglobal);
    }

    /**
     * General class contructor
     *
     * @return boolean|void
     */
    public function __construct()
    {
        // Maybe we'll try to create class manually.. But we should not.
        if (is_object(self::$instance))
        {
            throw new Exception('You can\'t directly create instance of class requestCaster', 1);
        }
    }

    /**
     * Add new URL parameter from DB column
     *
     * @param string $param Url parameter key
     * @param string $table Table to fetch from
     * @param string $field Field containing possible values for parameter
     * @param string[optional] $array Global array variable name to register into
     * @return void
     */
    public static function registerSQLParam($param, $table, $field, $array = false)
    {
        self::singleton()->register('sql', $param, array('table' => $table, 'field' => $field), $array);
    }

    /**
     * Add new URL parameter as array of values or callback function
     *
     * @param string $param Url parameter key
     * @param mixed $value Array of possible values or string containing callback function name
     * @param string[optional] $array Global array variable name to register into
     * @return void
     */
    public static function registerParam($param, $value, $array = false)
    {
        self::singleton()->register('array', $param, array('value' => $value), $array);
    }

    /**
     * Add new URL parameter from array or DB
     *
     * @param string $type Type of registering. Can be 'db' or 'array'
     * @param string $param Url parameter key
     * @param array $options Options for registering. Should contain 'value' array or 'table' and 'field' strings
     * @param string[optional] $array Global array variable name to register into
     */
    private function register($type, $param, $options, $array = false)
    {
        // It's useless to register params into these arrays. Believe me.
        $forbiddenArrays = array('_POST', '_COOKIE', '_SERVER', '_SESSION', '_REQUEST');

        // Make sure that we have worker instance
        self::singleton();

        // If we need to register parameter into known array
        if ($array)
        {
            // Try to inject into array if it is not present
            if (!array_key_exists($array,self::$superglobals))
            {
                self::singleton()->injectInto($array);
            }

            // We need to check for array presence again
            if (!isset(self::$superglobals[$array]))
            {
                throw new Exception('Requested array '.$array.' do not exists, so parameter '.$param.' cannot be registered', 400);
                return;
            }

            // Proxy to requestCaster functions of given array, depending on parameter type
            if ($type == 'sql' && isset($options['table']) && isset($options['field']))
            {
                self::$superglobals[$array]->addSQLParam($param, $options['table'], $options['field']);
                return;
            }

            if ($type == 'array' && isset($options['value']) && is_array($options['value']))
            {
                self::$superglobals[$array]->addParam($param, $options['value']);
                return;
            }

            // We did not got nor correct sql nor correct array options set
            throw new Exception('Incorrect use of superGlobal::register* functions set', 500);
            return;
        }

        // Register parameter into each of global arrays
        foreach (array_keys(self::$superglobals) as $k)
        {
            // If array is not in forbidden list
            if(!in_array($k,$forbiddenArrays))
            {
                // Proxy to requestCaster functions of given array, depending on parameter type
                if ($type == 'sql' && isset($options['table']) && isset($options['field']))
                {
                    self::$superglobals[$k]->addSQLParam($param, $options['table'], $options['field']);
                    return;
                }

                if ($type == 'array' && isset($options['value']) && isset($options['value']))
                {
                    self::$superglobals[$k]->addParam($param, $options['value']);
                    return;
                }

                // We did not got nor correct sql nor correct array options set
                throw new Exception('Incorrect use of superGlobal::register* function set', 500);
                return;
            }
        }
    }

    /**
     * Enable or disable exceptions on casting errors
     *
     * @param boolean[optional] $enable Use false to disable such exceptions back
     * @return void
     */
    public static function enableCastExceptions($enable = true)
    {
        self::$throwExceptionOnCast = $enable;
    }
}

?>
Return current item: superGlobals