<?php
/**
 * Zend Framework
 *
 * 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://framework.zend.com/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 license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Cache
 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Core.php 24989 2012-06-21 07:24:13Z mabe $
 */

/**
 * @package    Zend_Cache
 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Cache_Core
{
    /**
     * Messages
     */
    const BACKEND_NOT_SUPPORTS_TAG = 'tags are not supported by the current backend';
    const BACKEND_NOT_IMPLEMENTS_EXTENDED_IF = 'Current backend doesn\'t implement the Zend_Cache_Backend_ExtendedInterface, so this method is not available';

    /**
     * Backend Object
     *
     * @var Zend_Cache_Backend_Interface $_backend
     */
    protected $_backend = null;

    /**
     * Available options
     *
     * ====> (boolean) write_control :
     * - Enable / disable write control (the cache is read just after writing to detect corrupt entries)
     * - Enable write control will lightly slow the cache writing but not the cache reading
     * Write control can detect some corrupt cache files but maybe it's not a perfect control
     *
     * ====> (boolean) caching :
     * - Enable / disable caching
     * (can be very useful for the debug of cached scripts)
     *
     * =====> (string) cache_id_prefix :
     * - prefix for cache ids (namespace)
     *
     * ====> (boolean) automatic_serialization :
     * - Enable / disable automatic serialization
     * - It can be used to save directly datas which aren't strings (but it's slower)
     *
     * ====> (int) automatic_cleaning_factor :
     * - Disable / Tune the automatic cleaning process
     * - The automatic cleaning process destroy too old (for the given life time)
     *   cache files when a new cache file is written :
     *     0               => no automatic cache cleaning
     *     1               => systematic cache cleaning
     *     x (integer) > 1 => automatic cleaning randomly 1 times on x cache write
     *
     * ====> (int) lifetime :
     * - Cache lifetime (in seconds)
     * - If null, the cache is valid forever.
     *
     * ====> (boolean) logging :
     * - If set to true, logging is activated (but the system is slower)
     *
     * ====> (boolean) ignore_user_abort
     * - If set to true, the core will set the ignore_user_abort PHP flag inside the
     *   save() method to avoid cache corruptions in some cases (default false)
     *
     * @var array $_options available options
     */
    protected $_options = array(
        'write_control'             => true,
        'caching'                   => true,
        'cache_id_prefix'           => null,
        'automatic_serialization'   => false,
        'automatic_cleaning_factor' => 10,
        'lifetime'                  => 3600,
        'logging'                   => false,
        'logger'                    => null,
        'ignore_user_abort'         => false
    );

    /**
     * Array of options which have to be transfered to backend
     *
     * @var array $_directivesList
     */
    protected static $_directivesList = array('lifetime', 'logging', 'logger');

    /**
     * Not used for the core, just a sort a hint to get a common setOption() method (for the core and for frontends)
     *
     * @var array $_specificOptions
     */
    protected $_specificOptions = array();

    /**
     * Last used cache id
     *
     * @var string $_lastId
     */
    private $_lastId = null;

    /**
     * True if the backend implements Zend_Cache_Backend_ExtendedInterface
     *
     * @var boolean $_extendedBackend
     */
    protected $_extendedBackend = false;

    /**
     * Array of capabilities of the backend (only if it implements Zend_Cache_Backend_ExtendedInterface)
     *
     * @var array
     */
    protected $_backendCapabilities = array();

    /**
     * Constructor
     *
     * @param  array|Zend_Config $options Associative array of options or Zend_Config instance
     * @throws Zend_Cache_Exception
     * @return void
     */
    public function __construct($options = array())
    {
        if ($options instanceof Zend_Config) {
            $options = $options->toArray();
        }
        if (!is_array($options)) {
            Zend_Cache::throwException("Options passed were not an array"
            . " or Zend_Config instance.");
        }
        while (list($name, $value) = each($options)) {
            $this->setOption($name, $value);
        }
        $this->_loggerSanity();
    }

    /**
     * Set options using an instance of type Zend_Config
     *
     * @param Zend_Config $config
     * @return Zend_Cache_Core
     */
    public function setConfig(Zend_Config $config)
    {
        $options = $config->toArray();
        while (list($name, $value) = each($options)) {
            $this->setOption($name, $value);
        }
        return $this;
    }

    /**
     * Set the backend
     *
     * @param  Zend_Cache_Backend $backendObject
     * @throws Zend_Cache_Exception
     * @return void
     */
    public function setBackend(Zend_Cache_Backend $backendObject)
    {
        $this->_backend= $backendObject;
        // some options (listed in $_directivesList) have to be given
        // to the backend too (even if they are not "backend specific")
        $directives = array();
        foreach (Zend_Cache_Core::$_directivesList as $directive) {
            $directives[$directive] = $this->_options[$directive];
        }
        $this->_backend->setDirectives($directives);
        if (in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_backend))) {
            $this->_extendedBackend = true;
            $this->_backendCapabilities = $this->_backend->getCapabilities();
        }

    }

    /**
     * Returns the backend
     *
     * @return Zend_Cache_Backend backend object
     */
    public function getBackend()
    {
        return $this->_backend;
    }

    /**
     * Public frontend to set an option
     *
     * There is an additional validation (relatively to the protected _setOption method)
     *
     * @param  string $name  Name of the option
     * @param  mixed  $value Value of the option
     * @throws Zend_Cache_Exception
     * @return void
     */
    public function setOption($name, $value)
    {
        if (!is_string($name)) {
            Zend_Cache::throwException("Incorrect option name!");
        }
        $name = strtolower($name);
        if (array_key_exists($name, $this->_options)) {
            // This is a Core option
            $this->_setOption($name, $value);
            return;
        }
        if (array_key_exists($name, $this->_specificOptions)) {
            // This a specic option of this frontend
            $this->_specificOptions[$name] = $value;
            return;
        }
    }

    /**
     * Public frontend to get an option value
     *
     * @param  string $name  Name of the option
     * @throws Zend_Cache_Exception
     * @return mixed option value
     */
    public function getOption($name)
    {
        $name = strtolower($name);

        if (array_key_exists($name, $this->_options)) {
            // This is a Core option
            return $this->_options[$name];
        }

        if (array_key_exists($name, $this->_specificOptions)) {
            // This a specic option of this frontend
            return $this->_specificOptions[$name];
        }

        Zend_Cache::throwException("Incorrect option name : $name");
    }

    /**
     * Set an option
     *
     * @param  string $name  Name of the option
     * @param  mixed  $value Value of the option
     * @throws Zend_Cache_Exception
     * @return void
     */
    private function _setOption($name, $value)
    {
        if (!is_string($name) || !array_key_exists($name, $this->_options)) {
            Zend_Cache::throwException("Incorrect option name : $name");
        }
        if ($name == 'lifetime' && empty($value)) {
            $value = null;
        }
        $this->_options[$name] = $value;
    }

    /**
     * Force a new lifetime
     *
     * The new value is set for the core/frontend but for the backend too (directive)
     *
     * @param  int $newLifetime New lifetime (in seconds)
     * @return void
     */
    public function setLifetime($newLifetime)
    {
        $this->_options['lifetime'] = $newLifetime;
        $this->_backend->setDirectives(array(
            'lifetime' => $newLifetime
        ));
    }

    /**
     * Test if a cache is available for the given id and (if yes) return it (false else)
     *
     * @param  string  $id                     Cache id
     * @param  boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
     * @param  boolean $doNotUnserialize       Do not serialize (even if automatic_serialization is true) => for internal use
     * @return mixed|false Cached datas
     */
    public function load($id, $doNotTestCacheValidity = false, $doNotUnserialize = false)
    {
        if (!$this->_options['caching']) {
            return false;
        }
        $id = $this->_id($id); // cache id may need prefix
        $this->_lastId = $id;
        self::_validateIdOrTag($id);

        $this->_log("Zend_Cache_Core: load item '{$id}'", 7);
        $data = $this->_backend->load($id, $doNotTestCacheValidity);
        if ($data===false) {
            // no cache available
            return false;
        }
        if ((!$doNotUnserialize) && $this->_options['automatic_serialization']) {
            // we need to unserialize before sending the result
            return unserialize($data);
        }
        return $data;
    }

    /**
     * Test if a cache is available for the given id
     *
     * @param  string $id Cache id
     * @return int|false Last modified time of cache entry if it is available, false otherwise
     */
    public function test($id)
    {
        if (!$this->_options['caching']) {
            return false;
        }
        $id = $this->_id($id); // cache id may need prefix
        self::_validateIdOrTag($id);
        $this->_lastId = $id;

        $this->_log("Zend_Cache_Core: test item '{$id}'", 7);
        return $this->_backend->test($id);
    }

    /**
     * Save some data in a cache
     *
     * @param  mixed $data           Data to put in cache (can be another type than string if automatic_serialization is on)
     * @param  string $id             Cache id (if not set, the last cache id will be used)
     * @param  array $tags           Cache tags
     * @param  int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
     * @param  int   $priority         integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends
     * @throws Zend_Cache_Exception
     * @return boolean True if no problem
     */
    public function save($data, $id = null, $tags = array(), $specificLifetime = false, $priority = 8)
    {
        if (!$this->_options['caching']) {
            return true;
        }
        if ($id === null) {
            $id = $this->_lastId;
        } else {
            $id = $this->_id($id);
        }
        self::_validateIdOrTag($id);
        self::_validateTagsArray($tags);
        if ($this->_options['automatic_serialization']) {
            // we need to serialize datas before storing them
            $data = serialize($data);
        } else {
            if (!is_string($data)) {
                Zend_Cache::throwException("Datas must be string or set automatic_serialization = true");
            }
        }

        // automatic cleaning
        if ($this->_options['automatic_cleaning_factor'] > 0) {
            $rand = rand(1, $this->_options['automatic_cleaning_factor']);
            if ($rand==1) {
                //  new way                 || deprecated way
                if ($this->_extendedBackend || method_exists($this->_backend, 'isAutomaticCleaningAvailable')) {
                    $this->_log("Zend_Cache_Core::save(): automatic cleaning running", 7);
                    $this->clean(Zend_Cache::CLEANING_MODE_OLD);
                } else {
                    $this->_log("Zend_Cache_Core::save(): automatic cleaning is not available/necessary with current backend", 4);
                }
            }
        }

        $this->_log("Zend_Cache_Core: save item '{$id}'", 7);
        if ($this->_options['ignore_user_abort']) {
            $abort = ignore_user_abort(true);
        }
        if (($this->_extendedBackend) && ($this->_backendCapabilities['priority'])) {
            $result = $this->_backend->save($data, $id, $tags, $specificLifetime, $priority);
        } else {
            $result = $this->_backend->save($data, $id, $tags, $specificLifetime);
        }
        if ($this->_options['ignore_user_abort']) {
            ignore_user_abort($abort);
        }

        if (!$result) {
            // maybe the cache is corrupted, so we remove it !
            $this->_log("Zend_Cache_Core::save(): failed to save item '{$id}' -> removing it", 4);
            $this->_backend->remove($id);
            return false;
        }

        if ($this->_options['write_control']) {
            $data2 = $this->_backend->load($id, true);
            if ($data!=$data2) {
                $this->_log("Zend_Cache_Core::save(): write control of item '{$id}' failed -> removing it", 4);
                $this->_backend->remove($id);
                return false;
            }
        }

        return true;
    }

    /**
     * Remove a cache
     *
     * @param  string $id Cache id to remove
     * @return boolean True if ok
     */
    public function remove($id)
    {
        if (!$this->_options['caching']) {
            return true;
        }
        $id = $this->_id($id); // cache id may need prefix
        self::_validateIdOrTag($id);

        $this->_log("Zend_Cache_Core: remove item '{$id}'", 7);
        return $this->_backend->remove($id);
    }

    /**
     * Clean cache entries
     *
     * Available modes are :
     * 'all' (default)  => remove all cache entries ($tags is not used)
     * 'old'            => remove too old cache entries ($tags is not used)
     * 'matchingTag'    => remove cache entries matching all given tags
     *                     ($tags can be an array of strings or a single string)
     * 'notMatchingTag' => remove cache entries not matching one of the given tags
     *                     ($tags can be an array of strings or a single string)
     * 'matchingAnyTag' => remove cache entries matching any given tags
     *                     ($tags can be an array of strings or a single string)
     *
     * @param  string       $mode
     * @param  array|string $tags
     * @throws Zend_Cache_Exception
     * @return boolean True if ok
     */
    public function clean($mode = 'all', $tags = array())
    {
        if (!$this->_options['caching']) {
            return true;
        }
        if (!in_array($mode, array(Zend_Cache::CLEANING_MODE_ALL,
                                   Zend_Cache::CLEANING_MODE_OLD,
                                   Zend_Cache::CLEANING_MODE_MATCHING_TAG,
                                   Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG,
                                   Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG))) {
            Zend_Cache::throwException('Invalid cleaning mode');
        }
        self::_validateTagsArray($tags);

        return $this->_backend->clean($mode, $tags);
    }

    /**
     * Return an array of stored cache ids which match given tags
     *
     * In case of multiple tags, a logical AND is made between tags
     *
     * @param array $tags array of tags
     * @return array array of matching cache ids (string)
     */
    public function getIdsMatchingTags($tags = array())
    {
        if (!$this->_extendedBackend) {
            Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
        }
        if (!($this->_backendCapabilities['tags'])) {
            Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
        }

        $ids = $this->_backend->getIdsMatchingTags($tags);

        // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
        if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
            $prefix    = & $this->_options['cache_id_prefix'];
            $prefixLen = strlen($prefix);
            foreach ($ids as &$id) {
                if (strpos($id, $prefix) === 0) {
                    $id = substr($id, $prefixLen);
                }
            }
        }

        return $ids;
    }

    /**
     * Return an array of stored cache ids which don't match given tags
     *
     * In case of multiple tags, a logical OR is made between tags
     *
     * @param array $tags array of tags
     * @return array array of not matching cache ids (string)
     */
    public function getIdsNotMatchingTags($tags = array())
    {
        if (!$this->_extendedBackend) {
            Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
        }
        if (!($this->_backendCapabilities['tags'])) {
            Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
        }

        $ids = $this->_backend->getIdsNotMatchingTags($tags);

        // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
        if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
            $prefix    = & $this->_options['cache_id_prefix'];
            $prefixLen = strlen($prefix);
            foreach ($ids as &$id) {
                if (strpos($id, $prefix) === 0) {
                    $id = substr($id, $prefixLen);
                }
            }
        }

        return $ids;
    }

    /**
     * Return an array of stored cache ids which match any given tags
     *
     * In case of multiple tags, a logical OR is made between tags
     *
     * @param array $tags array of tags
     * @return array array of matching any cache ids (string)
     */
    public function getIdsMatchingAnyTags($tags = array())
    {
        if (!$this->_extendedBackend) {
            Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
        }
        if (!($this->_backendCapabilities['tags'])) {
            Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
        }

        $ids = $this->_backend->getIdsMatchingAnyTags($tags);

        // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
        if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
            $prefix    = & $this->_options['cache_id_prefix'];
            $prefixLen = strlen($prefix);
            foreach ($ids as &$id) {
                if (strpos($id, $prefix) === 0) {
                    $id = substr($id, $prefixLen);
                }
            }
        }

        return $ids;
    }

    /**
     * Return an array of stored cache ids
     *
     * @return array array of stored cache ids (string)
     */
    public function getIds()
    {
        if (!$this->_extendedBackend) {
            Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
        }

        $ids = $this->_backend->getIds();

        // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
        if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
            $prefix    = & $this->_options['cache_id_prefix'];
            $prefixLen = strlen($prefix);
            foreach ($ids as &$id) {
                if (strpos($id, $prefix) === 0) {
                    $id = substr($id, $prefixLen);
                }
            }
        }

        return $ids;
    }

    /**
     * Return an array of stored tags
     *
     * @return array array of stored tags (string)
     */
    public function getTags()
    {
        if (!$this->_extendedBackend) {
            Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
        }
        if (!($this->_backendCapabilities['tags'])) {
            Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
        }
        return $this->_backend->getTags();
    }

    /**
     * Return the filling percentage of the backend storage
     *
     * @return int integer between 0 and 100
     */
    public function getFillingPercentage()
    {
        if (!$this->_extendedBackend) {
            Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
        }
        return $this->_backend->getFillingPercentage();
    }

    /**
     * Return an array of metadatas for the given cache id
     *
     * The array will include these keys :
     * - expire : the expire timestamp
     * - tags : a string array of tags
     * - mtime : timestamp of last modification time
     *
     * @param string $id cache id
     * @return array array of metadatas (false if the cache id is not found)
     */
    public function getMetadatas($id)
    {
        if (!$this->_extendedBackend) {
            Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
        }
        $id = $this->_id($id); // cache id may need prefix
        return $this->_backend->getMetadatas($id);
    }

    /**
     * Give (if possible) an extra lifetime to the given cache id
     *
     * @param string $id cache id
     * @param int $extraLifetime
     * @return boolean true if ok
     */
    public function touch($id, $extraLifetime)
    {
        if (!$this->_extendedBackend) {
            Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
        }
        $id = $this->_id($id); // cache id may need prefix

        $this->_log("Zend_Cache_Core: touch item '{$id}'", 7);
        return $this->_backend->touch($id, $extraLifetime);
    }

    /**
     * Validate a cache id or a tag (security, reliable filenames, reserved prefixes...)
     *
     * Throw an exception if a problem is found
     *
     * @param  string $string Cache id or tag
     * @throws Zend_Cache_Exception
     * @return void
     */
    protected static function _validateIdOrTag($string)
    {
        if (!is_string($string)) {
            Zend_Cache::throwException('Invalid id or tag : must be a string');
        }
        if (substr($string, 0, 9) == 'internal-') {
            Zend_Cache::throwException('"internal-*" ids or tags are reserved');
        }
        if (!preg_match('~^[a-zA-Z0-9_]+$~D', $string)) {
            Zend_Cache::throwException("Invalid id or tag '$string' : must use only [a-zA-Z0-9_]");
        }
    }

    /**
     * Validate a tags array (security, reliable filenames, reserved prefixes...)
     *
     * Throw an exception if a problem is found
     *
     * @param  array $tags Array of tags
     * @throws Zend_Cache_Exception
     * @return void
     */
    protected static function _validateTagsArray($tags)
    {
        if (!is_array($tags)) {
            Zend_Cache::throwException('Invalid tags array : must be an array');
        }
        foreach($tags as $tag) {
            self::_validateIdOrTag($tag);
        }
        reset($tags);
    }

    /**
     * Make sure if we enable logging that the Zend_Log class
     * is available.
     * Create a default log object if none is set.
     *
     * @throws Zend_Cache_Exception
     * @return void
     */
    protected function _loggerSanity()
    {
        if (!isset($this->_options['logging']) || !$this->_options['logging']) {
            return;
        }

        if (isset($this->_options['logger']) && $this->_options['logger'] instanceof Zend_Log) {
            return;
        }

        // Create a default logger to the standard output stream

        $logger = new Zend_Log(new Zend_Log_Writer_Stream('php://output'));
        $logger->addFilter(new Zend_Log_Filter_Priority(Zend_Log::WARN, '<='));
        $this->_options['logger'] = $logger;
    }

    /**
     * Log a message at the WARN (4) priority.
     *
     * @param string $message
     * @throws Zend_Cache_Exception
     * @return void
     */
    protected function _log($message, $priority = 4)
    {
        if (!$this->_options['logging']) {
            return;
        }
        if (!(isset($this->_options['logger']) || $this->_options['logger'] instanceof Zend_Log)) {
            Zend_Cache::throwException('Logging is enabled but logger is not set');
        }
        $logger = $this->_options['logger'];
        $logger->log($message, $priority);
    }

    /**
     * Make and return a cache id
     *
     * Checks 'cache_id_prefix' and returns new id with prefix or simply the id if null
     *
     * @param  string $id Cache id
     * @return string Cache id (with or without prefix)
     */
    protected function _id($id)
    {
        if (($id !== null) && isset($this->_options['cache_id_prefix'])) {
            return $this->_options['cache_id_prefix'] . $id; // return with prefix
        }
        return $id; // no prefix, just return the $id passed
    }

}


<?php

namespace Reaction\Helpers\Request;

/**
 * Class Inflector. Proxy to \Reaction\Helpers\Inflector
 * @package Reaction\Helpers\Request
 */
class Inflector extends RequestAppHelperProxy
{
    public $helperClass = 'Reaction\Helpers\Inflector';

    /**
     * Converts a word to its plural form.
     * Note that this is for English only!
     * For example, 'apple' will become 'apples', and 'child' will become 'children'.
     * @param string $word the word to be pluralized
     * @return string the pluralized word
     * @see \Reaction\Helpers\Inflector::pluralize()
     */
    public function pluralize($word)
    {
        return $this->proxy(__FUNCTION__, [$word]);
    }

    /**
     * Returns the singular of the $word.
     * @param string $word the english word to singularize
     * @return string Singular noun.
     * @see \Reaction\Helpers\Inflector::singularize()
     */
    public function singularize($word)
    {
        return $this->proxy(__FUNCTION__, [$word]);
    }

    /**
     * Converts an underscored or CamelCase word into a English
     * sentence.
     * @param string $words
     * @param bool $ucAll whether to set all words to uppercase
     * @return string
     * @see \Reaction\Helpers\Inflector::titleize()
     */
    public function titleize($words, $ucAll = false)
    {
        return $this->proxy(__FUNCTION__, [$words, $ucAll]);
    }

    /**
     * Returns given word as CamelCased.
     *
     * Converts a word like "send_email" to "SendEmail". It
     * will remove non alphanumeric character from the word, so
     * "who's online" will be converted to "WhoSOnline".
     * @see variablize()
     * @param string $word the word to CamelCase
     * @return string
     * @see \Reaction\Helpers\Inflector::camelize()
     */
    public function camelize($word)
    {
        return $this->proxy(__FUNCTION__, [$word]);
    }

    /**
     * Converts a CamelCase name into space-separated words.
     * For example, 'PostTag' will be converted to 'Post Tag'.
     * @param string $name the string to be converted
     * @param bool $ucwords whether to capitalize the first letter in each word
     * @return string the resulting words
     * @see \Reaction\Helpers\Inflector::camel2words()
     */
    public function camel2words($name, $ucwords = true)
    {
        return $this->proxy(__FUNCTION__, [$name, $ucwords]);
    }

    /**
     * Converts a CamelCase name into an ID in lowercase.
     * Words in the ID may be concatenated using the specified character (defaults to '-').
     * For example, 'PostTag' will be converted to 'post-tag'.
     * @param string $name the string to be converted
     * @param string $separator the character used to concatenate the words in the ID
     * @param bool|string $strict whether to insert a separator between two consecutive uppercase chars, defaults to false
     * @return string the resulting ID
     * @see \Reaction\Helpers\Inflector::camel2id()
     */
    public function camel2id($name, $separator = '-', $strict = false)
    {
        return $this->proxy(__FUNCTION__, [$name, $separator, $strict]);
    }

    /**
     * Converts an ID into a CamelCase name.
     * Words in the ID separated by `$separator` (defaults to '-') will be concatenated into a CamelCase name.
     * For example, 'post-tag' is converted to 'PostTag'.
     * @param string $id the ID to be converted
     * @param string $separator the character used to separate the words in the ID
     * @return string the resulting CamelCase name
     * @see \Reaction\Helpers\Inflector::id2camel()
     */
    public function id2camel($id, $separator = '-')
    {
        return $this->proxy(__FUNCTION__, [$id, $separator]);
    }

    /**
     * Converts any "CamelCased" into an "underscored_word".
     * @param string $words the word(s) to underscore
     * @return string
     * @see \Reaction\Helpers\Inflector::underscore()
     */
    public function underscore($words)
    {
        return $this->proxy(__FUNCTION__, [$words]);
    }

    /**
     * Returns a human-readable string from $word.
     * @param string $word the string to humanize
     * @param bool $ucAll whether to set all words to uppercase or not
     * @return string
     * @see \Reaction\Helpers\Inflector::humanize()
     */
    public function humanize($word, $ucAll = false)
    {
        return $this->proxy(__FUNCTION__, [$word, $ucAll]);
    }

    /**
     * Same as camelize but first char is in lowercase.
     *
     * Converts a word like "send_email" to "sendEmail". It
     * will remove non alphanumeric character from the word, so
     * "who's online" will be converted to "whoSOnline".
     * @param string $word to lowerCamelCase
     * @return string
     * @see \Reaction\Helpers\Inflector::variablize()
     */
    public function variablize($word)
    {
        return $this->proxy(__FUNCTION__, [$word]);
    }

    /**
     * Converts a class name to its table name (pluralized) naming conventions.
     *
     * For example, converts "Person" to "people".
     * @param string $className the class name for getting related table_name
     * @return string
     * @see \Reaction\Helpers\Inflector::tableize()
     */
    public function tableize($className)
    {
        return $this->proxy(__FUNCTION__, [$className]);
    }

    /**
     * Returns a string with all spaces converted to given replacement,
     * non word characters removed and the rest of characters transliterated.
     *
     * If intl extension isn't available uses fallback that converts latin characters only
     * and removes the rest. You may customize characters map via $transliteration property
     * of the helper.
     *
     * @param string $string An arbitrary string to convert
     * @param string $replacement The replacement to use for spaces
     * @param bool $lowercase whether to return the string in lowercase or not. Defaults to `true`.
     * @return string The converted string.
     * @see \Reaction\Helpers\Inflector::slug()
     */
    public function slug($string, $replacement = '-', $lowercase = true)
    {
        return $this->proxy(__FUNCTION__, [$string, $replacement, $lowercase]);
    }

    /**
     * Returns transliterated version of a string.
     *
     * If intl extension isn't available uses fallback that converts latin characters only
     * and removes the rest. You may customize characters map via $transliteration property
     * of the helper.
     *
     * @param string $string input string
     * @param string|\Transliterator $transliterator either a [[\Transliterator]] or a string
     * from which a [[\Transliterator]] can be built.
     * @return string
     * @see \Reaction\Helpers\Inflector::transliterate()
     */
    public function transliterate($string, $transliterator = null)
    {
        return $this->proxy(__FUNCTION__, [$string, $transliterator]);
    }

    /**
     * @return bool if intl extension is loaded
     */
    protected function hasIntl()
    {
        return $this->proxy(__FUNCTION__);
    }

    /**
     * Converts a table name to its class name.
     *
     * For example, converts "people" to "Person".
     * @param string $tableName
     * @return string
     * @see \Reaction\Helpers\Inflector::classify()
     */
    public function classify($tableName)
    {
        return $this->proxy(__FUNCTION__, [$tableName]);
    }

    /**
     * Converts number to its ordinal English form. For example, converts 13 to 13th, 2 to 2nd ...
     * @param int $number the number to get its ordinal value
     * @return string
     * @see \Reaction\Helpers\Inflector::ordinalize()
     */
    public function ordinalize($number)
    {
        return $this->proxy(__FUNCTION__, [$number]);
    }

    /**
     * Converts a list of words into a sentence.
     *
     * Special treatment is done for the last few words. For example,
     *
     * ```php
     * $words = ['Spain', 'France'];
     * echo Inflector::sentence($words);
     * // output: Spain and France
     *
     * $words = ['Spain', 'France', 'Italy'];
     * echo Inflector::sentence($words);
     * // output: Spain, France and Italy
     *
     * $words = ['Spain', 'France', 'Italy'];
     * echo Inflector::sentence($words, ' & ');
     * // output: Spain, France & Italy
     * ```
     *
     * @param array $words the words to be converted into an string
     * @param string $twoWordsConnector the string connecting words when there are only two
     * @param string $lastWordConnector the string connecting the last two words. If this is null, it will
     * take the value of `$twoWordsConnector`.
     * @param string $connector the string connecting words other than those connected by
     * $lastWordConnector and $twoWordsConnector
     * @return string the generated sentence
     * @see \Reaction\Helpers\Inflector::sentence()
     */
    public function sentence(array $words, $twoWordsConnector = null, $lastWordConnector = null, $connector = ', ')
    {
        return $this->proxy(__FUNCTION__, [$words, $twoWordsConnector, $lastWordConnector, $connector]);
    }
}


<?php namespace RestGalleries\APIs\Flickr;

use RestGalleries\APIs\ApiGallery;

/**
 * Specific implementation of the GalleryAdapter interface, makes requests and normalizes the given data for return them.
 */
class Gallery extends ApiGallery
{
    protected $endPoint = 'http://api.flickr.com/services/rest/';

    /**
     * Common query values for all the requests.
     *
     * @var array
     */
    private $defaultQuery = [
        'format'         => 'json',
        'nojsoncallback' => 1
    ];

    protected function fetchIds()
    {
        $ids     = [];
        $query   = array_merge(
            $this->defaultQuery,
            [
                'method'               => 'flickr.photosets.getList',
                'page'                 => 1,
                'per_page'             => 50,
                'primary_photo_extras' => ''
            ]
        );

        $request = $this->newRequest();

        do {
            $body = $request->setQuery($query)
                ->GET()
                ->getBody('array');

            if ($body['stat'] == 'fail') {
                return;
            }

            $photosets = &$body['photosets'];
            $newIds    = $this->extractIdsArray($photosets);
            $ids       = $this->addIds($ids, $newIds);

        } while (++$query['page'] <= $photosets['pages']);

        return $ids;

    }

    /**
     * Extracts from the given array data, the identifiers of galleries.
     *
     * @param  array $data
     * @return array
     */
    private function extractIdsArray($data)
    {
        return array_pluck($data['photoset'], 'id');
    }

    /**
     * Adds to the identifiers array, a new array of identifiers.
     *
     * @param  array $ids
     * @param  array $newIds
     * @return array|null
     */
    private function addIds(array $ids, array $newIds)
    {
        if (! empty($newIds)) {
            return array_merge($ids, $newIds);
        }
    }

    protected function fetchGallery($id)
    {
        $query = array_merge(
            $this->defaultQuery,
            [
                'method'      => 'flickr.photosets.getInfo',
                'photoset_id' => $id,
            ]
        );

        $body = $this->newRequest()
            ->setQuery($query)
            ->GET()
            ->getBody();

        return $this->extractGalleryArray($body);

    }

    /**
     * Extracts the data for the object gallery from the array data given, normalizes them and returns them.
     *
     * @param  object $data
     * @return array|null
     */
    private function extractGalleryArray($data)
    {
        if ($data->stat == 'fail') {
            return;
        }

        $photoset = &$data->photoset;
        $photo    = $this->newPhoto();

        $gallery                = [];
        $gallery['id']          = $photoset->id;
        $gallery['title']       = $photoset->title->_content;
        $gallery['description'] = $photoset->description->_content;
        $gallery['photos']      = $photo->all($photoset->id);
        $gallery['created']     = (string) $photoset->date_create;
        $gallery['url']         = 'https://www.flickr.com/photos/';
        $gallery['url']         .= $photoset->owner;
        $gallery['url']         .= '/sets/';
        $gallery['url']         .= $photoset->id;
        $gallery['size']        = $photoset->count_photos;
        $gallery['user_id']     = $photoset->owner;
        $gallery['thumbnail']   = $photoset->primary;
        $gallery['views']       = $photoset->count_views;

        return $gallery;

    }

}


<?php
namespace gossi\trixionary\client\action;

use keeko\framework\foundation\AbstractAction;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use gossi\trixionary\model\SportQuery;
use Symfony\Component\OptionsResolver\OptionsResolver;
use keeko\framework\domain\payload\Blank;

/**
 * Manage Objects
 *
 * This code is automatically created. Modifications will probably be overwritten.
 *
 * @author Thomas Gossmann
 */
class ObjectsAction extends AbstractAction {

	protected function configureParams(OptionsResolver $resolver) {
		$resolver->setRequired(['id']);
	}

	/**
	 * Automatically generated run method
	 *
	 * @param Request $request
	 * @return Response
	 */
	public function run(Request $request) {
		$sport = SportQuery::create()
			->leftJoinObject()
			->findOneById($this->getParam('id'));

		return $this->responder->run($request, new Blank(['sport' => $sport]));
	}
}


<?php
declare(strict_types=1);

namespace icePHP;

/**
 * 访问请求类
 */
final class Request extends ArrayObject
{
    //由于SArrayObject要求必须允许外部访问
    //但本类 不希望 外部实例化
    public function __construct()
    {
        // 防入侵处理
        self::antiHtml();

        // 处理编码过的中文GET参数
        self::urlDecode();

        // 构造成为ArrayObject
        parent::__construct($_REQUEST);

        // 数据
        $this->_datas = $_REQUEST;
    }

    //单例句柄
    private static $instance;

    // 获取单例实例
    public static function instance(): Request
    {
        if (!static::$instance) {
            static::$instance = new static();
        }
        return static::$instance;
    }

    // 数据
    protected $_datas;

    /**
     * 获取请求体
     * @return string
     */
    public function body(): string
    {
        if (!$this->isPost()) {
            return '';
        }
        return @file_get_contents('php://input');
    }

    /**
     * 获取代理信息
     * @return string
     */
    public function agent(): string
    {
        return $_SERVER['HTTP_USER_AGENT'] ?? '';
    }

    /**
     * 获取当前的域名
     * @return string
     */
    public function host(): string
    {
        if (isCliMode()) {
            return $_SERVER['HTTP_HOST'];
        }
        return parse_url($this->url(), PHP_URL_HOST);
    }

    /**
     * 判断是否是POST提交
     * @return bool
     */
    public function isPost(): bool
    {
        return (!empty($_POST) or !empty($_FILES));
    }

    /**
     * 检查GET参数中的经过URLEncode过后汉字
     * 修改$_REQUEST
     */
    private function urlDecode(): void
    {
        foreach ($_GET as $k => $v) {
            if (is_array($v)) {
                foreach ($v as $k2 => $v2) {
                    if (preg_match('#%[0-9A-Z]{2}#isU', $v2) > 0) {
                        $v[$k2] = urldecode($v2);
                        $_REQUEST[$k] = $_GET[$k] = $v;
                    }
                }
            } else {
                if (preg_match('#%[0-9A-Z]{2}#isU', $v) > 0) {
                    $_GET[$k] = $_REQUEST[$k] = urldecode($v);
                }
            }
        }
    }

    /**
     * 魔术方法,获取属性,不存在时,返回Null,而不报错
     * @param $name string
     * @return mixed
     */
    public function __get($name)
    {
        if (isset($this->_datas[$name])) {
            return $this->_datas[$name];
        }
        return null;
    }

    /**
     * 设置属性时,保存到私有数据中,以便下次获取
     * @param string $name
     * @param mixed $value
     */
    public function __set(string $name, $value): void
    {
        // 保存到私有数据中
        $this->_data[$name] = $value;
    }

    /**
     * 删除一个属性
     * @param string $name
     */
    public function __unset(string $name): void
    {
        // 从私有数据中清除
        unset($this->_data[$name]);
    }

    /**
     * 获取域名中的分段,可以指定下标
     * @param $offset int 下标
     * @return string|array
     */
    public function domains(int $offset = null)
    {
        // 先去除端口部分
        $d = explode(':', $_SERVER['HTTP_HOST']);

        // 分段
        $d = explode('.', $d[0]);

        // 个数
        $l = count($d);

        // CN的时候,最后一段合起来
        if ($d[$l - 1] == 'cn') {
            $d[$l - 2] .= '.cn';
            unset($d[$l - 1]);
        }

        // 反转 , 0表示 .com/.edu.cn/.net之类,1表示站点名
        $domains = array_reverse($d);

        // 不足三段,补充www
        if (!isset($domains[2])) {
            $domains[2] = 'www';
        }

        // 后面没有的补充空字符串,以免以后访问时出错
        for ($i = 3; $i < 10; $i++) {
            if (!isset($domains[$i])) {
                $domains[$i] = '';
            }
        }

        //如果要求只返回指定部分
        if (!is_null($offset)) {
            return $domains[$offset];
        }

        //返回全部
        return $domains;
    }

    /**
     * 判断浏览器语言
     * @return string
     */
    public function language(): string
    {
        if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            return "简体中文";
        }

        // 只取前4位，这样只判断最优先的语言。如果取前5位，可能出现en,zh的情况，影响判断。
        $lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 4);

        if (preg_match("/zh-c/i", $lang)) {
            return "简体中文";
        }

        // 这个你认识吧?
        if (preg_match("/zh/i", $lang)) {
            return "繁體中文";
        }

        // 英文
        if (preg_match("/en/i", $lang)) {
            return "English";
        }

        // 法文
        if (preg_match("/fr/i", $lang)) {
            return "French";
        }

        // 德文
        if (preg_match("/de/i", $lang)) {
            return "German";
        }

        // 日语
        if (preg_match("/jp/i", $lang)) {
            return "Japanese";
        }

        // 韩文
        if (preg_match("/ko/i", $lang)) {
            return "Korean";
        }

        // 西班牙文
        if (preg_match("/es/i", $lang)) {
            return "Spanish";
        }

        // 瑞典语也是芬兰的官方语言
        if (preg_match("/sv/i", $lang)) {
            return "Swedish";
        }

        return $_SERVER["HTTP_ACCEPT_LANGUAGE"];
    }

    /**
     * 获取域名 http://xxx.yyy.zzz:ii
     * @return string
     */
    public function domain(): string
    {
        /* 协议 */
        $protocol = $this->protocol();

        /* 域名或IP地址 */
        if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
            $host = $_SERVER['HTTP_X_FORWARDED_HOST'];
            return $protocol . $host;
        }

        if (isset($_SERVER['HTTP_HOST'])) {
            $host = $_SERVER['HTTP_HOST'];
            return $protocol . $host;
        }

        /* 端口 */
        if (isset($_SERVER['SERVER_PORT'])) {
            $port = ':' . $_SERVER['SERVER_PORT'];

            if ((':80' == $port && 'http://' == $protocol) || (':443' == $port && 'https://' == $protocol)) {
                $port = '';
            }
        } else {
            $port = '';
        }

        if (isset($_SERVER['SERVER_NAME'])) {
            $host = $_SERVER['SERVER_NAME'] . $port;
        } elseif (isset($_SERVER['SERVER_ADDR'])) {
            $host = $_SERVER['SERVER_ADDR'] . $port;
        } else {
            $host = '';
        }

        return $protocol . $host;
    }

    /**
     * 获取请求中的原始POST数据
     * @return string
     */
    public function rawPost(): string
    {
        return file_get_contents('php://input');
    }

    /**
     * 获取请示中的原始IP信息,如果通过CDN,可能是多段的
     * @return string
     */
    public function rawIp(): string
    {
        //依序检查
        foreach (['REMOTE_ADDR', 'HTTP_X_FORWARDED_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP'] as $k) {
            if (isset($_SERVER[$k]) and $_SERVER[$k]) return $_SERVER[$k];
        }

        return '0.0.0.0';
    }

    /**
     * 获取用户IP,如果通过CDN,也能获取到
     * @return string
     */
    public function ip(): string
    {
        $ip = $this->rawIp();

        $parts = explode(',', $ip);
        if ($parts) {
            return $parts[0];
        }
        return $ip;
    }

    /**
     * 获得当前的页面文件的url 不带参数
     * @return string
     */
    public function curUrl(): string
    {
        if (!empty($_SERVER['REQUEST_URI'])) {
            $nowUrls = explode('?', $_SERVER['REQUEST_URI']);
            return $nowUrls[0];
        }
        return $_SERVER['PHP_SELF'];
    }

    /**
     * 强制指定当前请求是Ajax请求,通常用在接口开发中
     */
    public function forceAjax(): void
    {
        isAjax(true);
    }

    /**
     * 判断是否由Pjax请求而来
     * @return boolean
     */
    public function isPjax(): bool
    {
        return (isset($_SERVER['HTTP_X_PJAX']) and $_SERVER['HTTP_X_PJAX']);
    }

    /**
     * 判断请求是否来自手机端
     * @return boolean
     */
    public function isMobile(): bool
    {
        // 如果有HTTP_X_WAP_PROFILE则一定是移动设备
        if (isset($_SERVER['HTTP_X_WAP_PROFILE'])) {
            return true;
        }

        // 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
        if (isset($_SERVER['HTTP_VIA'])) {
            // 找不到为false,否则为true
            return stristr($_SERVER['HTTP_VIA'], "wap") ? true : false;
        }
        // 脑残法，判断手机发送的客户端标志,兼容性有待提高
        if (isset($_SERVER['HTTP_USER_AGENT'])) {
            $keywords = ['android', 'wap', 'mobile', 'iphone', 'ipad', 'samsung', 'htc', 'nokia', 'sony', 'lenovo', 'sgh', 'lg', 'ericsson', 'mot', 'sharp', 'sie-', 'philips', 'panasonic', 'alcatel', 'blackberry', 'meizu', 'netfront', 'symbian', 'ucweb', 'windowsce', 'palm', 'operamini', 'operamobi', 'openwave', 'nexusone', 'cldc', 'midp'];

            // 从HTTP_USER_AGENT中查找手机浏览器的关键字
            if (preg_match("/(" . implode('|', $keywords) . ")/i", strtolower($_SERVER['HTTP_USER_AGENT']))) {
                return true;
            }
        }

        // 协议法，因为有可能不准确，放到最后判断
        if (isset($_SERVER['HTTP_ACCEPT'])) {
            // 如果只支持wml并且不支持html那一定是移动设备
            // 如果支持wml和html但是wml在html之前则是移动设备
            $accept = $_SERVER['HTTP_ACCEPT'] ?: '';
            if ((strpos($accept, 'vnd.wap.wml') !== false) && (strpos($accept, 'text/html') === false || (strpos($accept, 'vnd.wap.wml') < strpos($accept, 'text/html')))) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否IE浏览器
     * @return boolean
     */
    public function isIe(): bool
    {
        return strpos($this->agent(), 'MSIE') > 0;
    }

    /**
     * 判断用户是否使用IE6/7
     * @return boolean
     */
    public function isIe67(): bool
    {
        return strpos($this->agent(), 'MSIE 6.0') > 0 or strpos($this->agent(), 'MSIE 7.0') > 0;
    }

    /**
     * 判断 用户是否使用IE8
     * @return bool
     */
    public function isIe8(): bool
    {
        return strpos($this->agent(), 'MSIE 8.0') > 0;
    }

    /**
     * 递归检查一个数组中是否有禁止词
     * @param array $arr 要检查的数组
     * @param array $config 配置信息
     */
    private function antiHtmlArray(array $arr, array $config): void
    {
        foreach ($arr as $k => $v) {
            if (is_array($v)) {
                $this->antiHtmlArray($v, $config);
                continue;
            }

            // 如果此参数在忽略列表中,放过检查
            if (in_array($k, $config['ignoreParams'])) {
                continue;
            }

            //优先处理轻度安全参数
            if (isset($config['lightParams']) and in_array($k, $config['lightParams'])) {
                if (preg_match('/' . $config['lightDisabled'] . '/i', $v)) {
                    FileLog::instance()->antiLight($_REQUEST);
                    exit();
                }
                continue;
            }

            //其它参数,按高安全级检查
            if ($config['disabled'] and preg_match('/' . $config['disabled'] . '/i', $v)) {
                FileLog::instance()->antiHigh($_REQUEST);
                exit();
            }

            //如果参数名称非法
            if (isset($config['antiName']) and in_array($k, $config['antiName'])) {
                FileLog::instance()->antiName($_REQUEST);
                exit();
            }
        }
    }

    /**
     * 防入侵,包括IP黑名单,禁止字符,禁止词
     */
    private function antiHtml(): void
    {
        // 取防入侵配置
        $config = configDefault(null, 'anti');
        if (!$config) {
            return;
        }

        // 如果来访IP在黑名单 中
        if (in_array($this->rawIp(), $config['blackip']) or $this->rawIp() == '-' and in_array($this->ip(), $config['blackip'])) {
            FileLog::instance()->blackIp($this->rawIp(), $this->ip(), $_REQUEST);
            exit();
        }

        if (isset($_REQUEST)) {
            $this->antiHtmlArray($_REQUEST, $config);
        }

        // 不允许 FLASH访问
        if (isset($_SERVER['HTTP_X_FLASH_VERSION']) and $_SERVER['HTTP_X_FLASH_VERSION'] and $this->ip() === $this->rawIp()) {
            FileLog::instance()->flash($this->ip(), $_REQUEST, $_SERVER);
            exit();
        }
    }

    /**
     * 获取POST参数数组
     * @return ArrayObject
     */
    public function posts(): ArrayObject
    {
        return new ArrayObject($_POST);
    }

    /**
     * 获取GET参数数组
     * @return ArrayObject
     */
    public function gets(): ArrayObject
    {
        return new ArrayObject($_GET);
    }

    /**
     * 获取Request参数数组
     * @return ArrayObject
     */
    public function requests(): ArrayObject
    {
        return new ArrayObject($_REQUEST);
    }

    /**
     * 获取请求时的URL
     * @return string
     */
    public function url(): string
    {
        return 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    }

    /**
     * 获取引用地址
     * @return string
     */
    public function referrer(): string
    {
        return isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
    }

    /**
     * 返回协议名称  HTTP:// 或 HTTPS://
     * @return string
     */
    public function protocol(): string
    {
        return (isset($_SERVER['HTTPS']) and (strtolower($_SERVER['HTTPS']) != 'off')) ? 'https://' : 'http://';
    }

    /**
     * 获取当前请求的端口
     * @return string
     */
    public function port(): string
    {
        $d = explode(':', $_SERVER['HTTP_HOST']);
        return isset($d[1]) ? $d[1] : '80';
    }
}


<?php

declare(strict_types=1);

/**
 * States.
 *
 * LICENSE
 *
 * This source file is subject to the MIT license and the version 3 of the GPL3
 * license that are bundled with this package in the folder licences
 * 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 richarddeloge@gmail.com so we can send you a copy immediately.
 *
 *
 * @copyright   Copyright (c) 2009-2017 Richard Déloge (richarddeloge@gmail.com)
 *
 * @link        http://teknoo.software/states Project website
 *
 * @license     http://teknoo.software/license/mit         MIT License
 * @author      Richard Déloge <richarddeloge@gmail.com>
 */

namespace Teknoo\States\LifeCycle\Scenario;

use Teknoo\States\LifeCycle\Event\EventDispatcherBridgeInterface;

/**
 * Class Manager
 * Interface to manage to store all enabled scenarii and register them into the event dispatcher.
 *
 * @copyright   Copyright (c) 2009-2017 Richard Déloge (richarddeloge@gmail.com)
 *
 * @link        http://teknoo.software/states Project website
 *
 * @license     http://teknoo.software/license/mit         MIT License
 * @author      Richard Déloge <richarddeloge@gmail.com>
 */
class Manager implements ManagerInterface
{
    /**
     * Current enabled scenari.
     *
     * @var ScenarioInterface[]
     */
    private $scenariiList = [];

    /**
     * Dispatcher used to register scenarii.
     *
     * @var EventDispatcherBridgeInterface
     */
    private $dispatcher;

    /**
     * @param EventDispatcherBridgeInterface $dispatcher
     */
    public function __construct(EventDispatcherBridgeInterface $dispatcher)
    {
        $this->setDispatcher($dispatcher);
    }

    /**
     * {@inheritdoc}
     */
    public function getDispatcher()
    {
        return $this->dispatcher;
    }

    /**
     * {@inheritdoc}
     */
    public function setDispatcher(EventDispatcherBridgeInterface $dispatcher): ManagerInterface
    {
        $this->dispatcher = $dispatcher;

        return $this;
    }

    /**
     * @param ScenarioInterface $scenario
     *
     * @return $this
     */
    private function addEventsAboutScenario(ScenarioInterface $scenario)
    {
        $dispatcher = $this->getDispatcher();
        foreach ($scenario->getEventsNamesList() as $eventName) {
            $dispatcher->addListener($eventName, $scenario);
        }

        return $this;
    }

    /**
     * @param ScenarioInterface $scenario
     *
     * @return $this
     */
    private function removeEventsAboutScenario(ScenarioInterface $scenario)
    {
        $dispatcher = $this->getDispatcher();
        foreach ($scenario->getEventsNamesList() as $eventName) {
            $dispatcher->removeListener($eventName, $scenario);
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function registerScenario(ScenarioInterface $scenario): ManagerInterface
    {
        $scenarioHash = \spl_object_hash($scenario);

        if (!isset($this->scenariiList[$scenarioHash])) {
            $this->scenariiList[$scenarioHash] = $scenario;
            $this->addEventsAboutScenario($scenario);
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function unregisterScenario(ScenarioInterface $scenario): ManagerInterface
    {
        $scenarioHash = \spl_object_hash($scenario);

        if (isset($this->scenariiList[$scenarioHash])) {
            $this->removeEventsAboutScenario($scenario);
            unset($this->scenariiList[$scenarioHash]);
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function listScenarii(): array
    {
        return $this->scenariiList;
    }
}


<?php
/**
 * This source file is part of Xloit project.
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the MIT License that is bundled with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * <http://www.opensource.org/licenses/mit-license.php>
 * 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 <license@xloit.com> so we can send you a copy immediately.
 *
 * @license   MIT
 * @link      http://xloit.com
 * @copyright Copyright (c) 2016, Xloit. All rights reserved.
 */

namespace Xloit\Bridge\Zend\Session\SaveHandler\Database\Adapter;

use Xloit\Bridge\Zend\Session\SaveHandler\Database\DatabaseOptions;

/**
 * An {@link AbstractAdapter} abstract class.
 *
 * @abstract
 * @package Xloit\Bridge\Zend\Session\SaveHandler\Database\Adapter
 */
abstract class AbstractAdapter implements AdapterInterface
{
    /**
     * Session save handler options.
     *
     * @var DatabaseOptions
     */
    protected $options;

    /**
     *
     *
     * @var mixed
     */
    protected $storageManager;

    /**
     * Constructor to prevent {@link AbstractAdapter} from being loaded more than once.
     *
     * @param mixed           $storageManager
     * @param DatabaseOptions $options
     */
    public function __construct($storageManager, DatabaseOptions $options)
    {
        $this->setStorageManager($storageManager);
        $this->setOptions($options);
    }

    /**
     * Returns the Storage Manager value.
     *
     * @return mixed
     */
    public function getStorageManager()
    {
        return $this->storageManager;
    }

    /**
     * Sets the Storage Manager value.
     *
     * @param mixed $storageManager
     *
     * @return $this
     */
    public function setStorageManager($storageManager)
    {
        $this->storageManager = $storageManager;

        return $this;
    }

    /**
     * Returns the Options value.
     *
     * @return DatabaseOptions
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * Sets the Options value.
     *
     * @param DatabaseOptions $options
     *
     * @return $this
     */
    public function setOptions(DatabaseOptions $options)
    {
        $this->options = $options;

        return $this;
    }

    /**
     * Indicates whether the session is valid.
     *
     * @param mixed $session
     *
     * @return bool
     */
    public function isValid($session)
    {
        return ($this->getModifiedValue($session)->getTimestamp() + $this->getLifetimeValue($session)) > time();
    }
}


<?php

namespace Versatile\Html\Bootstrap;

use Illuminate\Contracts\Config\Repository as Config;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\HtmlString;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Macroable;
use Versatile\Html\FormBuilder;
use Versatile\Html\HtmlBuilder;

class Bootstrap
{
    use Macroable;

    /**
     * Illuminate HtmlBuilder instance.
     *
     * @var \Versatile\Html\HtmlBuilder
     */
    protected $html;

    /**
     * Illuminate FormBuilder instance.
     *
     * @var \Versatile\Html\FormBuilder
     */
    protected $form;

    /**
     * Illuminate Repository instance.
     *
     * @var \Illuminate\Config\Repository
     */
    protected $config;

    /**
     * Bootstrap form type class.
     *
     * @var string
     */
    protected $type;

    /**
     * Bootstrap form left column class.
     *
     * @var string
     */
    protected $leftColumnClass;

    /**
     * Bootstrap form left column offset class.
     *
     * @var string
     */
    protected $leftColumnOffsetClass;

    /**
     * Bootstrap form right column class.
     *
     * @var string
     */
    protected $rightColumnClass;

    /**
     * The icon prefix.
     *
     * @var string
     */
    protected $iconPrefix;

    /**
     * The errorbag that is used for validation (multiple forms).
     *
     * @var string
     */
    protected $errorBag;

    /**
     * The error class.
     *
     * @var string
     */
    protected $errorClass;

    /**
     * Construct the class.
     *
     * @param  \Versatile\Html\HtmlBuilder $html
     * @param  \Versatile\Html\FormBuilder $form
     * @param  \Illuminate\Contracts\Config\Repository $config
     * @return void
     */
    public function __construct(HtmlBuilder $html, FormBuilder $form, Config $config)
    {
        $this->html = $html;
        $this->form = $form;
        $this->config = $config;
    }

    /**
     * Open a form while passing a model and the routes for storing or updating
     * the model. This will set the correct route along with the correct
     * method.
     *
     * @param  array $options
     * @return string
     */
    public function open(array $options = [])
    {
        // Set the HTML5 role.
        $options['role'] = 'form';

        // Set the class for the form type.
        if (!array_key_exists('class', $options)) {
            $options['class'] = $this->getType();
        }

        if (array_key_exists('left_column_class', $options)) {
            $this->setLeftColumnClass($options['left_column_class']);
        }

        if (array_key_exists('left_column_offset_class', $options)) {
            $this->setLeftColumnOffsetClass($options['left_column_offset_class']);
        }

        if (array_key_exists('right_column_class', $options)) {
            $this->setRightColumnClass($options['right_column_class']);
        }

        array_forget($options, [
            'left_column_class',
            'left_column_offset_class',
            'right_column_class'
        ]);

        if (array_key_exists('model', $options)) {
            return $this->model($options);
        }

        if (array_key_exists('error_bag', $options)) {
            $this->setErrorBag($options['error_bag']);
        }

        return $this->form->open($options);
    }

    /**
     * Reset and close the form.
     *
     * @return string
     */
    public function close()
    {
        $this->type = null;

        $this->leftColumnClass = $this->rightColumnClass = null;

        return $this->form->close();
    }

    /**
     * Open a form configured for model binding.
     *
     * @param  array $options
     * @return string
     */
    protected function model($options)
    {
        $model = $options['model'];

        if (isset($options['url'])) {
            // If we're explicity passed a URL, we'll use that.
            array_forget($options, ['model', 'update', 'store']);
            $options['method'] = isset($options['method']) ? $options['method'] : 'GET';

            return $this->form->model($model, $options);
        }

        // If we're not provided store/update actions then let the form submit to itself.
        if (!isset($options['store']) && !isset($options['update'])) {
            array_forget($options, 'model');
            return $this->form->model($model, $options);
        }

        if (!is_null($options['model']) && $options['model']->exists) {
            // If the form is passed a model, we'll use the update route to update
            // the model using the PUT method.
            $name = is_array($options['update']) ? array_first($options['update']) : $options['update'];
            $route = Str::contains($name, '@') ? 'action' : 'route';

            $options[$route] = array_merge((array)$options['update'], [$options['model']->getRouteKey()]);
            $options['method'] = 'PUT';
        } else {
            // Otherwise, we're storing a brand new model using the POST method.
            $name = is_array($options['store']) ? array_first($options['store']) : $options['store'];
            $route = Str::contains($name, '@') ? 'action' : 'route';

            $options[$route] = $options['store'];
            $options['method'] = 'POST';
        }

        // Forget the routes provided to the input.
        array_forget($options, ['model', 'update', 'store']);

        return $this->form->model($model, $options);
    }

    /**
     * Open a vertical (standard) Bootstrap form.
     *
     * @param  array $options
     * @return string
     */
    public function vertical(array $options = [])
    {
        $this->setType(Type::VERTICAL);

        return $this->open($options);
    }

    /**
     * Open an inline Bootstrap form.
     *
     * @param  array $options
     * @return string
     */
    public function inline(array $options = [])
    {
        $this->setType(Type::INLINE);

        return $this->open($options);
    }

    /**
     * Open a horizontal Bootstrap form.
     *
     * @param  array $options
     * @return string
     */
    public function horizontal(array $options = [])
    {
        $this->setType(Type::HORIZONTAL);

        return $this->open($options);
    }

    /**
     * Create a Bootstrap static field.
     *
     * @param  string $name
     * @param  string $label
     * @param  string $value
     * @param  array $options
     * @return string
     */
    public function staticField($name, $label = null, $value = null, array $options = [])
    {
        $options = array_merge(['class' => 'form-control-static'], $options);

        if (is_array($value) and isset($value['html'])) {
            $value = $value['html'];
        } else {
            $value = e($value);
        }

        $label = $this->getLabelTitle($label, $name);
        $inputElement = '
html->attributes($options) . '>' . $value . '
';

        $wrapperOptions = $this->isHorizontal() ? ['class' => $this->getRightColumnClass()] : [];
        $wrapperElement = '
html->attributes($wrapperOptions) . '>' . $inputElement . $this->getFieldError($name) . $this->getHelpText($name, $options) . '
';

        return $this->getFormGroup($name, $label, $wrapperElement);
    }

    /**
     * Create a Bootstrap text field input.
     *
     * @param  string $name
     * @param  string $label
     * @param  string $value
     * @param  array $options
     * @return string
     */
    public function text($name, $label = null, $value = null, array $options = [])
    {
        return $this->input('text', $name, $label, $value, $options);
    }

    /**
     * Create a Bootstrap email field input.
     *
     * @param  string $name
     * @param  string $label
     * @param  string $value
     * @param  array $options
     * @return string
     */
    public function email($name = 'email', $label = null, $value = null, array $options = [])
    {
        return $this->input('email', $name, $label, $value, $options);
    }

    /**
     * Create a Bootstrap URL field input.
     *
     * @param  string $name
     * @param  string $label
     * @param  string $value
     * @param  array $options
     * @return string
     */
    public function url($name, $label = null, $value = null, array $options = [])
    {
        return $this->input('url', $name, $label, $value, $options);
    }

    /**
     * Create a Bootstrap tel field input.
     *
     * @param  string $name
     * @param  string $label
     * @param  string $value
     * @param  array $options
     * @return string
     */
    public function tel($name, $label = null, $value = null, array $options = [])
    {
        return $this->input('tel', $name, $label, $value, $options);
    }

    /**
     * Create a Bootstrap number field input.
     *
     * @param  string $name
     * @param  string $label
     * @param  string $value
     * @param  array $options
     * @return string
     */
    public function number($name, $label = null, $value = null, array $options = [])
    {
        return $this->input('number', $name, $label, $value, $options);
    }

    /**
     * Create a Bootstrap date field input.
     *
     * @param  string $name
     * @param  string $label
     * @param  string $value
     * @param  array $options
     * @return string
     */
    public function date($name, $label = null, $value = null, array $options = [])
    {
        return $this->input('date', $name, $label, $value, $options);
    }

    /**
     * Create a Bootstrap email time input.
     *
     * @param  string $name
     * @param  string $label
     * @param  string $value
     * @param  array $options
     * @return string
     */
    public function time($name, $label = null, $value = null, array $options = [])
    {
        return $this->input('time', $name, $label, $value, $options);
    }

    /**
     * Create a Bootstrap textarea field input.
     *
     * @param  string $name
     * @param  string $label
     * @param  string $value
     * @param  array $options
     * @return string
     */
    public function textarea($name, $label = null, $value = null, array $options = [])
    {
        return $this->input('textarea', $name, $label, $value, $options);
    }

    /**
     * Create a Bootstrap password field input.
     *
     * @param  string $name
     * @param  string $label
     * @param  array $options
     * @return string
     */
    public function password($name = 'password', $label = null, array $options = [])
    {
        return $this->input('password', $name, $label, null, $options);
    }

    /**
     * Create a Bootstrap checkbox input.
     *
     * @param  string $name
     * @param  string $label
     * @param  string $value
     * @param  bool $checked
     * @param  array $options
     * @return string
     */
    public function checkbox($name, $label = null, $value = 1, $checked = null, array $options = [])
    {
        $inputElement = $this->checkboxElement($name, $label, $value, $checked, false, $options);

        $wrapperOptions = $this->isHorizontal() ? ['class' => implode(' ', [$this->getLeftColumnOffsetClass(), $this->getRightColumnClass()])] : [];
        $wrapperElement = '
html->attributes($wrapperOptions) . '>' . $inputElement . $this->getFieldError($name) . $this->getHelpText($name, $options) . '
';

        return $this->getFormGroup($name, null, $wrapperElement);
    }

    /**
     * Create a single Bootstrap checkbox element.
     *
     * @param  string $name
     * @param  string $label
     * @param  string $value
     * @param  bool $checked
     * @param  bool $inline
     * @param  array $options
     * @return string
     */
    public function checkboxElement($name, $label = null, $value = 1, $checked = null, $inline = false, array $options = [])
    {
        $label = $label === false ? null : $this->getLabelTitle($label, $name);

        $labelOptions = $inline ? ['class' => 'checkbox-inline'] : [];
        $inputElement = $this->form->checkbox($name, $value, $checked, $options);
        $labelElement = 'html->attributes($labelOptions) . '>' . $inputElement . $label . '';

        return $inline ? $labelElement : '
' . $labelElement . '
';
    }

    /**
     * Create a collection of Bootstrap checkboxes.
     *
     * @param  string $name
     * @param  string $label
     * @param  array $choices
     * @param  array $checkedValues
     * @param  bool $inline
     * @param  array $options
     * @return string
     */
    public function checkboxes($name, $label = null, $choices = [], $checkedValues = [], $inline = false, array $options = [])
    {
        $elements = '';

        foreach ($choices as $value => $choiceLabel) {
            $checked = in_array($value, (array)$checkedValues);

            $elements .= $this->checkboxElement($name, $choiceLabel, $value, $checked, $inline, $options);
        }

        $wrapperOptions = $this->isHorizontal() ? ['class' => $this->getRightColumnClass()] : [];
        $wrapperElement = '
html->attributes($wrapperOptions) . '>' . $elements . $this->getFieldError($name) . $this->getHelpText($name, $options) . '
';

        return $this->getFormGroup($name, $label, $wrapperElement);
    }

    /**
     * Create a Bootstrap radio input.
     *
     * @param  string $name
     * @param  string $label
     * @param  string $value
     * @param  bool $checked
     * @param  array $options
     * @return string
     */
    public function radio($name, $label = null, $value = null, $checked = null, array $options = [])
    {
        $inputElement = $this->radioElement($name, $label, $value, $checked, false, $options);

        $wrapperOptions = $this->isHorizontal() ? ['class' => implode(' ', [$this->getLeftColumnOffsetClass(), $this->getRightColumnClass()])] : [];
        $wrapperElement = '
html->attributes($wrapperOptions) . '>' . $inputElement . '
';

        return $this->getFormGroup(null, $label, $wrapperElement);
    }

    /**
     * Create a single Bootstrap radio input.
     *
     * @param  string $name
     * @param  string $label
     * @param  string $value
     * @param  bool $checked
     * @param  bool $inline
     * @param  array $options
     * @return string
     */
    public function radioElement($name, $label = null, $value = null, $checked = null, $inline = false, array $options = [])
    {
        $label = $label === false ? null : $this->getLabelTitle($label, $name);

        $value = is_null($value) ? $label : $value;

        $labelOptions = $inline ? ['class' => 'radio-inline'] : [];

        $inputElement = $this->form->radio($name, $value, $checked, $options);
        $labelElement = 'html->attributes($labelOptions) . '>' . $inputElement . $label . '';

        return $inline ? $labelElement : '
' . $labelElement . '
';
    }

    /**
     * Create a collection of Bootstrap radio inputs.
     *
     * @param  string $name
     * @param  string $label
     * @param  array $choices
     * @param  string $checkedValue
     * @param  bool $inline
     * @param  array $options
     * @return string
     */
    public function radios($name, $label = null, $choices = [], $checkedValue = null, $inline = false, array $options = [])
    {
        $elements = '';

        foreach ($choices as $value => $choiceLabel) {
            $checked = $value === $checkedValue;

            $elements .= $this->radioElement($name, $choiceLabel, $value, $checked, $inline, $options);
        }

        $wrapperOptions = $this->isHorizontal() ? ['class' => $this->getRightColumnClass()] : [];
        $wrapperElement = '
html->attributes($wrapperOptions) . '>' . $elements . $this->getFieldError($name) . $this->getHelpText($name, $options) . '
';

        return $this->getFormGroup($name, $label, $wrapperElement);
    }

    /**
     * Create a Bootstrap label.
     *
     * @param  string $name
     * @param  string $value
     * @param  array $options
     * @return string
     */
    public function label($name, $value = null, array $options = [])
    {
        $options = $this->getLabelOptions($options);

        $escapeHtml = false;

        if (is_array($value) and isset($value['html'])) {
            $value = $value['html'];
        } elseif ($value instanceof HtmlString) {
            $value = $value->toHtml();
        } else {
            $escapeHtml = true;
        }

        return $this->form->label($name, $value, $options, $escapeHtml);
    }

    /**
     * Create a Boostrap submit button.
     *
     * @param  string $value
     * @param  array $options
     * @return string
     */
    public function submit($value = null, array $options = [])
    {
        $options = array_merge(['class' => 'btn btn-primary'], $options);

        $inputElement = $this->form->submit($value, $options);

        $wrapperOptions = $this->isHorizontal() ? ['class' => implode(' ', [$this->getLeftColumnOffsetClass(), $this->getRightColumnClass()])] : [];
        $wrapperElement = '
html->attributes($wrapperOptions) . '>' . $inputElement . '
';

        return $this->getFormGroup(null, null, $wrapperElement);
    }

    /**
     * Create a Boostrap button.
     *
     * @param  string $value
     * @param  array $options
     * @return string
     */
    public function button($value = null, array $options = [])
    {
        $options = array_merge(['class' => 'btn btn-primary'], $options);

        $inputElement = $this->form->button($value, $options);

        $wrapperOptions = $this->isHorizontal() ? ['class' => implode(' ', [$this->getLeftColumnOffsetClass(), $this->getRightColumnClass()])] : [];
        $wrapperElement = '
html->attributes($wrapperOptions) . '>' . $inputElement . '
';

        return $this->getFormGroup(null, null, $wrapperElement);
    }

    /**
     * Create a Boostrap file upload button.
     *
     * @param  string $name
     * @param  string $label
     * @param  array $options
     * @return string
     */
    public function file($name, $label = null, array $options = [])
    {
        $label = $this->getLabelTitle($label, $name);

        $options = array_merge(['class' => 'filestyle', 'data-buttonBefore' => 'true'], $options);

        $options = $this->getFieldOptions($options, $name);
        $inputElement = $this->form->input('file', $name, null, $options);

        $wrapperOptions = $this->isHorizontal() ? ['class' => $this->getRightColumnClass()] : [];
        $wrapperElement = '
html->attributes($wrapperOptions) . '>' . $inputElement . $this->getFieldError($name) . $this->getHelpText($name, $options) . '
';

        return $this->getFormGroup($name, $label, $wrapperElement);
    }

    /**
     * Create the input group for an element with the correct classes for errors.
     *
     * @param  string $type
     * @param  string $name
     * @param  string $label
     * @param  string $value
     * @param  array $options
     * @return string
     */
    public function input($type, $name, $label = null, $value = null, array $options = [])
    {
        $label = $this->getLabelTitle($label, $name);

        $optionsField = $this->getFieldOptions(array_except($options, ['suffix', 'prefix']), $name);

        $inputElement = '';

        if (isset($options['prefix'])) {
            $inputElement = $options['prefix'];
        }

        $inputElement .= $type === 'password' ? $this->form->password($name, $optionsField) : $this->form->{$type}($name, $value, $optionsField);

        if (isset($options['suffix'])) {
            $inputElement .= $options['suffix'];
        }

        if (isset($options['prefix']) || isset($options['suffix'])) {
            $inputElement = '
' . $inputElement . '
';
        }

        $wrapperOptions = $this->isHorizontal() ? ['class' => $this->getRightColumnClass()] : [];
        $wrapperElement = '
html->attributes($wrapperOptions) . '>' . $inputElement . $this->getFieldError($name) . $this->getHelpText($name, $optionsField) . '
';

        return $this->getFormGroup($name, $label, $wrapperElement);
    }

    /**
     * Create an addon button element.
     *
     * @param  string $label
     * @param  array $options
     * @return string
     */
    public function addonButton($label, $options = [])
    {
        $attributes = array_merge(['class' => 'btn', 'type' => 'button'], $options);

        if (isset($options['class'])) {
            $attributes['class'] .= ' btn';
        }

        return '
<button ' . $this->html->attributes($attributes) . '>' . $label . '</button>
';
    }

    /**
     * Create an addon text element.
     *
     * @param  string $text
     * @param  array $options
     * @return string
     */
    public function addonText($text, $options = [])
    {
        return '
html->attributes($options) . '>' . $text . '
';
    }

    /**
     * Create an addon icon element.
     *
     * @param  string $icon
     * @param  array $options
     * @return string
     */
    public function addonIcon($icon, $options = [])
    {
        $prefix = array_get($options, 'prefix', $this->getIconPrefix());

        return '
html->attributes($options) . '>
';
    }

    /**
     * Create a hidden field.
     *
     * @param  string $name
     * @param  string $value
     * @param  array $options
     * @return string
     */
    public function hidden($name, $value = null, $options = [])
    {
        return $this->form->hidden($name, $value, $options);
    }

    /**
     * Create a select box field.
     *
     * @param  string $name
     * @param  string $label
     * @param  array $list
     * @param  string $selected
     * @param  array $options
     * @return string
     */
    public function select($name, $label = null, $list = [], $selected = null, array $options = [])
    {
        $label = $this->getLabelTitle($label, $name);

        $options = $this->getFieldOptions($options, $name);
        $inputElement = $this->form->select($name, $list, $selected, $options);

        $wrapperOptions = $this->isHorizontal() ? ['class' => $this->getRightColumnClass()] : [];
        $wrapperElement = '
html->attributes($wrapperOptions) . '>' . $inputElement . $this->getFieldError($name) . $this->getHelpText($name, $options) . '
';

        return $this->getFormGroup($name, $label, $wrapperElement);
    }

    /**
     * Get the label title for a form field, first by using the provided one
     * or titleizing the field name.
     *
     * @param  string $label
     * @param  string $name
     * @return mixed
     */
    protected function getLabelTitle($label, $name)
    {
        if ($label === false) {
            return null;
        }

        if (is_null($label) && Lang::has("forms.{$name}")) {
            return Lang::get("forms.{$name}");
        }

        return $label ?: str_replace('_', ' ', Str::title($name));
    }

    /**
     * Get a form group comprised of a form element and errors.
     *
     * @param  string $name
     * @param  string $element
     * @return string
     */
    protected function getFormGroupWithoutLabel($name, $element)
    {
        $options = $this->getFormGroupOptions($name);

        return '
html->attributes($options) . '>' . $element . '
';
    }

    /**
     * Get a form group comprised of a label, form element and errors.
     *
     * @param  string $name
     * @param  string $value
     * @param  string $element
     * @return string
     */
    protected function getFormGroupWithLabel($name, $value, $element)
    {
        $options = $this->getFormGroupOptions($name);

        return '
html->attributes($options) . '>' . $this->label($name, $value) . $element . '
';
    }

    /**
     * Get a form group with or without a label.
     *
     * @param  string $name
     * @param  string $label
     * @param  string $element
     * @return string
     */
    public function getFormGroup($name = null, $label = null, $wrapperElement)
    {
        if (is_null($label)) {
            return $this->getFormGroupWithoutLabel($name, $wrapperElement);
        }
        return $this->getFormGroupWithLabel($name, $label, $wrapperElement);
    }

    /**
     * Merge the options provided for a form group with the default options
     * required for Bootstrap styling.
     *
     * @param  string $name
     * @param  array $options
     * @return array
     */
    protected function getFormGroupOptions($name = null, array $options = [])
    {
        $class = 'form-group';

        if ($name) {
            $class .= ' ' . $this->getFieldErrorClass($name);
        }

        return array_merge(['class' => $class], $options);
    }

    /**
     * Merge the options provided for a field with the default options
     * required for Bootstrap styling.
     *
     * @param  array $options
     * @param  string $name
     * @return array
     */
    protected function getFieldOptions(array $options = [], $name = null)
    {
        $options['class'] = trim('form-control ' . $this->getFieldOptionsClass($options));

        // If we've been provided the input name and the ID has not been set in the options,
        // we'll use the name as the ID to hook it up with the label.
        if ($name && !array_key_exists('id', $options)) {
            $options['id'] = $name;
        }

        return $options;
    }

    /**
     * Returns the class property from the options, or the empty string
     *
     * @param   array $options
     * @return  string
     */
    protected function getFieldOptionsClass(array $options = [])
    {
        return array_get($options, 'class');
    }

    /**
     * Merge the options provided for a label with the default options
     * required for Bootstrap styling.
     *
     * @param  array $options
     * @return array
     */
    protected function getLabelOptions(array $options = [])
    {
        $class = 'control-label';
        if ($this->isHorizontal()) {
            $class .= ' ' . $this->getLeftColumnClass();
        }

        return array_merge(['class' => trim($class)], $options);
    }

    /**
     * Get the form type.
     *
     * @return string
     */
    public function getType()
    {
        return isset($this->type) ? $this->type : $this->config->get('html.bootstrap.type');
    }

    /**
     * Determine if the form is of a horizontal type.
     *
     * @return bool
     */
    public function isHorizontal()
    {
        return $this->getType() === Type::HORIZONTAL;
    }

    /**
     * Set the form type.
     *
     * @param  string $type
     * @return void
     */
    public function setType($type)
    {
        $this->type = $type;
    }

    /**
     * Get the column class for the left column of a horizontal form.
     *
     * @return string
     */
    public function getLeftColumnClass()
    {
        return $this->leftColumnClass ?: $this->config->get('html.bootstrap.left_column_class');
    }

    /**
     * Set the column class for the left column of a horizontal form.
     *
     * @param  string $class
     * @return void
     */
    public function setLeftColumnClass($class)
    {
        $this->leftColumnClass = $class;
    }

    /**
     * Get the column class for the left column offset of a horizontal form.
     *
     * @return string
     */
    public function getLeftColumnOffsetClass()
    {
        return $this->leftColumnOffsetClass ?: $this->config->get('html.bootstrap.left_column_offset_class');
    }

    /**
     * Set the column class for the left column offset of a horizontal form.
     *
     * @param  string $class
     * @return void
     */
    public function setLeftColumnOffsetClass($class)
    {
        $this->leftColumnOffsetClass = $class;
    }

    /**
     * Get the column class for the right column of a horizontal form.
     *
     * @return string
     */
    public function getRightColumnClass()
    {
        return $this->rightColumnClass ?: $this->config->get('html.bootstrap.right_column_class');
    }

    /**
     * Set the column class for the right column of a horizontal form.
     *
     * @param  string $class
     * @return void
     */
    public function setRightColumnClass($class)
    {
        $this->rightColumnClass = $class;
    }

    /**
     * Get the icon prefix.
     *
     * @return string
     */
    public function getIconPrefix()
    {
        return $this->iconPrefix ?: $this->config->get('html.bootstrap.icon_prefix');
    }

    /**
     * Get the error class.
     *
     * @return string
     */
    public function getErrorClass()
    {
        return $this->errorClass ?: $this->config->get('html.bootstrap.error_class');
    }

    /**
     * Get the error bag.
     *
     * @return string
     */
    protected function getErrorBag()
    {
        return $this->errorBag ?: $this->config->get('html.bootstrap.error_bag');
    }

    /**
     * Set the error bag.
     *
     * @param  $errorBag  string
     * @return void
     */
    protected function setErrorBag($errorBag)
    {
        $this->errorBag = $errorBag;
    }

    /**
     * Flatten arrayed field names to work with the validator, including removing "[]",
     * and converting nested arrays like "foo[bar][baz]" to "foo.bar.baz".
     *
     * @param  string $field
     * @return string
     */
    public function flattenFieldName($field)
    {
        return preg_replace_callback("/\[(.*)\\]/U", function ($matches) {
            if (!empty($matches[1]) || $matches[1] === '0') {
                return "." . $matches[1];
            }
        }, $field);
    }

    /**
     * Get the MessageBag of errors that is populated by the
     * validator.
     *
     * @return \Illuminate\Support\MessageBag
     */
    protected function getErrors()
    {
        return $this->form->getSessionStore()->get('errors');
    }

    /**
     * Get the first error for a given field, using the provided
     * format, defaulting to the normal Bootstrap 3 format.
     *
     * @param  string $field
     * @param  string $format
     * @return mixed
     */
    protected function getFieldError($field, $format = ':message')
    {
        $field = $this->flattenFieldName($field);

        if ($this->getErrors()) {
            $allErrors = $this->config->get('html.bootstrap.show_all_errors');

            if ($this->getErrorBag()) {
                $errorBag = $this->getErrors()->{$this->getErrorBag()};
            } else {
                $errorBag = $this->getErrors();
            }

            if ($allErrors) {
                return implode('', $errorBag->get($field, $format));
            }

            return $errorBag->first($field, $format);
        }
    }

    /**
     * Return the error class if the given field has associated
     * errors, defaulting to the normal Bootstrap 3 error class.
     *
     * @param  string $field
     * @param  string $class
     * @return string
     */
    protected function getFieldErrorClass($field)
    {
        return $this->getFieldError($field) ? $this->getErrorClass() : null;
    }

    /**
     * Get the help text for the given field.
     *
     * @param  string $field
     * @param  array $options
     * @return string
     */
    protected function getHelpText($field, array $options = [])
    {
        if (array_key_exists('help_text', $options)) {
            return '' . e($options['help_text']) . '';
        }

        return '';
    }
}


<?php

/**
 * @copyright  Copyright (c) Flipbox Digital Limited
 * @license    https://github.com/flipboxfactory/craft-sortable-associations/blob/master/LICENSE
 * @link       https://github.com/flipboxfactory/craft-sortable-associations
 */

namespace flipbox\craft\sortable\associations\services\traits;

use craft\helpers\ArrayHelper;

/**
 * @author Flipbox Factory <hello@flipboxfactory.com>
 * @since 1.0.1
 */
trait SequentialOrderTrait
{
    /**
     * @param array $sourceArray The source array which the target is to be inserted into.  The
     * key represents a unique identifier, while the value is the sort order.
     *
     * As an example if this is the $sourceArray
     *
     * ```
     * [
     *      111 => 1,
     *      343 => 2,
     *      545 => 3,
     *      'foo' => 4,
     *      'bar' => 5
     * ]
     * ```
     *
     * And your $targetKey is 'fooBar' with a $targetOrder of 4, the result would be
     *
     * ```
     * [
     *      111 => 1,
     *      343 => 2,
     *      545 => 3,
     *      'fooBar' => 4,
     *      'foo' => 5,
     *      'bar' => 6
     * ]
     * ```
     *
     * @param string|int $targetKey
     * @param int $targetOrder
     * @return array|bool
     */
    protected function insertSequential(array $sourceArray, $targetKey, int $targetOrder)
    {
        $this->ensureSequential($sourceArray);

        // Append exiting types after position
        if (false === ($indexPosition = array_search($targetKey, array_keys($sourceArray)))) {
            return false;
        }

        // Determine the furthest affected index
        $affectedIndex = $indexPosition >= $targetOrder ? ($targetOrder - 1) : $indexPosition;

        // All that are affected by re-ordering
        $affectedTypes = array_slice($sourceArray, $affectedIndex, null, true);

        // Remove the current (we're going to put it back in later)
        $currentPosition = (int)ArrayHelper::remove($affectedTypes, $targetKey);

        // Already in that position?
        if ($currentPosition === $targetOrder) {
            return true;
        }

        $startingSortOrder = $targetOrder;
        if ($affectedIndex++ < $targetOrder) {
            $startingSortOrder = $affectedIndex;
        }

        // Prepend current type
        $order = [$targetKey => $targetOrder];

        // Assemble order
        if (false !== ($position = array_search($targetOrder, array_values($affectedTypes)))) {
            if ($indexPosition < $targetOrder) {
                $position++;
            }

            if ($position > 0) {
                $order = array_slice($affectedTypes, 0, $position, true) + $order;
            }

            $order += array_slice($affectedTypes, $position, null, true);
        }

        return array_flip(array_combine(
            range($startingSortOrder, count($order)),
            array_keys($order)
        ));
    }

    /**
     * @param array $sourceArray
     */
    private function ensureSequential(array &$sourceArray)
    {
        $ct = 1;
        foreach ($sourceArray as $key => &$sortOrder) {
            $sortOrder = (int) $sortOrder ?: $ct++;

            if ($sortOrder > $ct) {
                $ct = $sortOrder + 1;
            }
        }
    }
}