<?php

/**
 * @author    Sorin Badea <sorin.badea91@gmail.com>
 * @license   MIT license (see the license file in the root directory)
 */

namespace ThinFrame\Foundation\Helper;

/**
 * ShortCodesProcessor - shortcodes processor
 *
 * Migrated from WordPress's ShortCodes library
 *
 * @package ThinFrame\Foundation\ShortCodes
 * @since   0.2
 */
class ShortCodesProcessor
{
    /**
     * @var array registered shortcodes
     */
    protected $shortCodesTags = array();

    /**
     * Register short code
     *
     * @param string   $tag      tag name
     * @param callable $callback callback that will be triggered when tag will be processed
     *
     * @return $this
     */
    public function registerShortCode($tag, $callback)
    {
        if (is_callable($callback)) {
            $this->shortCodesTags[$tag] = $callback;
        }

        return $this;
    }

    /**
     * Remove short code
     *
     * @param string $tag tag to be removed
     *
     * @return $this
     */
    public function removeShortCode($tag)
    {
        if (isset($this->shortCodesTags[$tag])) {
            unset($this->shortCodesTags[$tag]);
        }

        return $this;
    }

    /**
     * Parse content
     *
     * @param string $content content to be parsed
     *
     * @return string
     */
    public function parseContent($content)
    {
        if (count($this->shortCodesTags) == 0) {
            return $content;
        }

        $context = $this;

        return preg_replace_callback(
            '/' . $this->generateShortCodesRegex() . '/s',
            function ($matches) use ($context) {
                return $context->parseShortCodeTag($matches);
            },
            $content
        );
    }

    /**
     * Generates shortcodes regex
     *
     * @return string
     */
    protected function generateShortCodesRegex()
    {
        $tags                  = array_keys($this->shortCodesTags);
        $regularExpression = join('|', array_map('preg_quote', $tags));

        // WARNING! Do not change this regex without changing do_shortcode_tag() and strip_shortcode_tag()
        // Also, see shortcode_unautop() and shortcode.js.
        return
            '\\[' // Opening bracket
            . '(\\[?)' // 1: Optional second opening bracket for escaping shortcodes: [[tag]]
            . "($regularExpression)" // 2: Shortcode name
            . '(?![\\w-])' // Not followed by word character or hyphen
            . '(' // 3: Unroll the loop: Inside the opening shortcode tag
            . '[^\\]\\/]*' // Not a closing bracket or forward slash
            . '(?:'
            . '\\/(?!\\])' // A forward slash not followed by a closing bracket
            . '[^\\]\\/]*' // Not a closing bracket or forward slash
            . ')*?'
            . ')'
            . '(?:'
            . '(\\/)' // 4: Self closing tag ...
            . '\\]' // ... and closing bracket
            . '|'
            . '\\]' // Closing bracket
            . '(?:'
            . '(' // 5: Unroll the loop: Optionally, anything between the opening and closing shortcode tags
            . '[^\\[]*+' // Not an opening bracket
            . '(?:'
            . '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag
            . '[^\\[]*+' // Not an opening bracket
            . ')*+'
            . ')'
            . '\\[\\/\\2\\]' // Closing shortcode tag
            . ')?'
            . ')'
            . '(\\]?)'; // 6: Optional second closing brocket for escaping shortcodes: [[tag]]
    }

    /**
     * Parse short code tag
     *
     * @param array $matches
     *
     * @return string
     */
    public function parseShortCodeTag($matches)
    {
        // allow [[foo]] syntax for escaping a tag
        if ($matches[1] == '[' && $matches[6] == ']') {
            return substr($matches[0], 1, -1);
        }

        $tag        = $matches[2];
        $attributes = $this->parseShortCodeAttributes($matches[3]);

        if (isset($matches[5])) {
            // enclosing tag - extra parameter
            return $matches[1] . call_user_func(
                $this->shortCodesTags[$tag],
                $attributes,
                $matches[5],
                $tag,
                $this
            ) . $matches[6];
        } else {
            // self-closing tag
            return $matches[1] . call_user_func(
                $this->shortCodesTags[$tag],
                $attributes,
                null,
                $tag,
                $this
            ) . $matches[6];
        }
    }

    /**
     * Parse short code attributes
     *
     * @param string $text short code attributes
     *
     * @return array|string
     */
    protected function parseShortCodeAttributes($text)
    {
        $attributes = array();
        $pattern    =
            '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s' .
            '|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/';
        $text       = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text);
        if (preg_match_all($pattern, $text, $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                if (!empty($match[1])) {
                    $attributes[strtolower($match[1])] = stripcslashes($match[2]);
                } elseif (!empty($match[3])) {
                    $attributes[strtolower($match[3])] = stripcslashes($match[4]);
                } elseif (!empty($match[5])) {
                    $attributes[strtolower($match[5])] = stripcslashes($match[6]);
                } elseif (isset($match[7]) and strlen($match[7])) {
                    $attributes[] = stripcslashes($match[7]);
                } elseif (isset($match[8])) {
                    $attributes[] = stripcslashes($match[8]);
                }
            }
        } else {
            $attributes = ltrim($text);
        }

        return $attributes;
    }
}


<?php
/**
 * PEAR_REST_10
 *
 * PHP versions 4 and 5
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2009 The Authors
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 * @version    CVS: $Id: 10.php 313023 2011-07-06 19:17:11Z dufuz $
 * @link       http://pear.php.net/package/PEAR
 * @since      File available since Release 1.4.0a12
 */

/**
 * For downloading REST xml/txt files
 */
require_once 'PEAR/REST.php';

/**
 * Implement REST 1.0
 *
 * @category   pear
 * @package    PEAR
 * @author     Greg Beaver <cellog@php.net>
 * @copyright  1997-2009 The Authors
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 * @version    Release: 1.9.4
 * @link       http://pear.php.net/package/PEAR
 * @since      Class available since Release 1.4.0a12
 */
class PEAR_REST_10
{
    /**
     * @var PEAR_REST
     */
    var $_rest;
    function PEAR_REST_10($config, $options = array())
    {
        $this->_rest = &new PEAR_REST($config, $options);
    }

    /**
     * Retrieve information about a remote package to be downloaded from a REST server
     *
     * @param string $base The uri to prepend to all REST calls
     * @param array $packageinfo an array of format:
     * <pre>
     *  array(
     *   'package' => 'packagename',
     *   'channel' => 'channelname',
     *  ['state' => 'alpha' (or valid state),]
     *  -or-
     *  ['version' => '1.whatever']
     * </pre>
     * @param string $prefstate Current preferred_state config variable value
     * @param bool $installed the installed version of this package to compare against
     * @return array|false|PEAR_Error see {@link _returnDownloadURL()}
     */
    function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false)
    {
        $states = $this->betterStates($prefstate, true);
        if (!$states) {
            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
        }

        $channel  = $packageinfo['channel'];
        $package  = $packageinfo['package'];
        $state    = isset($packageinfo['state'])   ? $packageinfo['state']   : null;
        $version  = isset($packageinfo['version']) ? $packageinfo['version'] : null;
        $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml';

        $info = $this->_rest->retrieveData($restFile, false, false, $channel);
        if (PEAR::isError($info)) {
            return PEAR::raiseError('No releases available for package "' .
                $channel . '/' . $package . '"');
        }

        if (!isset($info['r'])) {
            return false;
        }

        $release = $found = false;
        if (!is_array($info['r']) || !isset($info['r'][0])) {
            $info['r'] = array($info['r']);
        }

        foreach ($info['r'] as $release) {
            if (!isset($this->_rest->_options['force']) && ($installed &&
                  version_compare($release['v'], $installed, '<'))) {
                continue;
            }

            if (isset($state)) {
                // try our preferred state first
                if ($release['s'] == $state) {
                    $found = true;
                    break;
                }
                // see if there is something newer and more stable
                // bug #7221
                if (in_array($release['s'], $this->betterStates($state), true)) {
                    $found = true;
                    break;
                }
            } elseif (isset($version)) {
                if ($release['v'] == $version) {
                    $found = true;
                    break;
                }
            } else {
                if (in_array($release['s'], $states)) {
                    $found = true;
                    break;
                }
            }
        }

        return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
    }

    function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage,
                               $prefstate = 'stable', $installed = false, $channel = false)
    {
        $states = $this->betterStates($prefstate, true);
        if (!$states) {
            return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
        }

        $channel  = $dependency['channel'];
        $package  = $dependency['name'];
        $state    = isset($dependency['state'])   ? $dependency['state']   : null;
        $version  = isset($dependency['version']) ? $dependency['version'] : null;
        $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml';

        $info = $this->_rest->retrieveData($restFile, false, false, $channel);
        if (PEAR::isError($info)) {
            return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package']
                . '" dependency "' . $channel . '/' . $package . '" has no releases');
        }

        if (!is_array($info) || !isset($info['r'])) {
            return false;
        }

        $exclude = array();
        $min = $max = $recommended = false;
        if ($xsdversion == '1.0') {
            switch ($dependency['rel']) {
                case 'ge' :
                    $min = $dependency['version'];
                break;
                case 'gt' :
                    $min = $dependency['version'];
                    $exclude = array($dependency['version']);
                break;
                case 'eq' :
                    $recommended = $dependency['version'];
                break;
                case 'lt' :
                    $max = $dependency['version'];
                    $exclude = array($dependency['version']);
                break;
                case 'le' :
                    $max = $dependency['version'];
                break;
                case 'ne' :
                    $exclude = array($dependency['version']);
                break;
            }
        } else {
            $min = isset($dependency['min']) ? $dependency['min'] : false;
            $max = isset($dependency['max']) ? $dependency['max'] : false;
            $recommended = isset($dependency['recommended']) ?
                $dependency['recommended'] : false;
            if (isset($dependency['exclude'])) {
                if (!isset($dependency['exclude'][0])) {
                    $exclude = array($dependency['exclude']);
                }
            }
        }
        $release = $found = false;
        if (!is_array($info['r']) || !isset($info['r'][0])) {
            $info['r'] = array($info['r']);
        }
        foreach ($info['r'] as $release) {
            if (!isset($this->_rest->_options['force']) && ($installed &&
                  version_compare($release['v'], $installed, '<'))) {
                continue;
            }
            if (in_array($release['v'], $exclude)) { // skip excluded versions
                continue;
            }
            // allow newer releases to say "I'm OK with the dependent package"
            if ($xsdversion == '2.0' && isset($release['co'])) {
                if (!is_array($release['co']) || !isset($release['co'][0])) {
                    $release['co'] = array($release['co']);
                }
                foreach ($release['co'] as $entry) {
                    if (isset($entry['x']) && !is_array($entry['x'])) {
                        $entry['x'] = array($entry['x']);
                    } elseif (!isset($entry['x'])) {
                        $entry['x'] = array();
                    }
                    if ($entry['c'] == $deppackage['channel'] &&
                          strtolower($entry['p']) == strtolower($deppackage['package']) &&
                          version_compare($deppackage['version'], $entry['min'], '>=') &&
                          version_compare($deppackage['version'], $entry['max'], '<=') &&
                          !in_array($release['v'], $entry['x'])) {
                        $recommended = $release['v'];
                        break;
                    }
                }
            }
            if ($recommended) {
                if ($release['v'] != $recommended) { // if we want a specific
                    // version, then skip all others
                    continue;
                } else {
                    if (!in_array($release['s'], $states)) {
                        // the stability is too low, but we must return the
                        // recommended version if possible
                        return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel);
                    }
                }
            }
            if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions
                continue;
            }
            if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions
                continue;
            }
            if ($installed && version_compare($release['v'], $installed, '<')) {
                continue;
            }
            if (in_array($release['s'], $states)) { // if in the preferred state...
                $found = true; // ... then use it
                break;
            }
        }
        return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
    }

    /**
     * Take raw data and return the array needed for processing a download URL
     *
     * @param string $base REST base uri
     * @param string $package Package name
     * @param array $release an array of format array('v' => version, 's' => state)
     *                       describing the release to download
     * @param array $info list of all releases as defined by allreleases.xml
     * @param bool|null $found determines whether the release was found or this is the next
     *                    best alternative.  If null, then versions were skipped because
     *                    of PHP dependency
     * @return array|PEAR_Error
     * @access private
     */
    function _returnDownloadURL($base, $package, $release, $info, $found, $phpversion = false, $channel = false)
    {
        if (!$found) {
            $release = $info['r'][0];
        }

        $packageLower = strtolower($package);
        $pinfo = $this->_rest->retrieveCacheFirst($base . 'p/' . $packageLower . '/' .
            'info.xml', false, false, $channel);
        if (PEAR::isError($pinfo)) {
            return PEAR::raiseError('Package "' . $package .
                '" does not have REST info xml available');
        }

        $releaseinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' .
            $release['v'] . '.xml', false, false, $channel);
        if (PEAR::isError($releaseinfo)) {
            return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
                '" does not have REST xml available');
        }

        $packagexml = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' .
            'deps.' . $release['v'] . '.txt', false, true, $channel);
        if (PEAR::isError($packagexml)) {
            return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
                '" does not have REST dependency information available');
        }

        $packagexml = unserialize($packagexml);
        if (!$packagexml) {
            $packagexml = array();
        }

        $allinfo = $this->_rest->retrieveData($base . 'r/' . $packageLower .
            '/allreleases.xml', false, false, $channel);
        if (PEAR::isError($allinfo)) {
            return $allinfo;
        }

        if (!is_array($allinfo['r']) || !isset($allinfo['r'][0])) {
            $allinfo['r'] = array($allinfo['r']);
        }

        $compatible = false;
        foreach ($allinfo['r'] as $release) {
            if ($release['v'] != $releaseinfo['v']) {
                continue;
            }

            if (!isset($release['co'])) {
                break;
            }

            $compatible = array();
            if (!is_array($release['co']) || !isset($release['co'][0])) {
                $release['co'] = array($release['co']);
            }

            foreach ($release['co'] as $entry) {
                $comp = array();
                $comp['name']    = $entry['p'];
                $comp['channel'] = $entry['c'];
                $comp['min']     = $entry['min'];
                $comp['max']     = $entry['max'];
                if (isset($entry['x']) && !is_array($entry['x'])) {
                    $comp['exclude'] = $entry['x'];
                }

                $compatible[] = $comp;
            }

            if (count($compatible) == 1) {
                $compatible = $compatible[0];
            }

            break;
        }

        $deprecated = false;
        if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
            if (is_array($pinfo['dp'])) {
                $deprecated = array('channel' => (string) $pinfo['dc'],
                                    'package' => trim($pinfo['dp']['_content']));
            } else {
                $deprecated = array('channel' => (string) $pinfo['dc'],
                                    'package' => trim($pinfo['dp']));
            }
        }

        $return = array(
            'version'    => $releaseinfo['v'],
            'info'       => $packagexml,
            'package'    => $releaseinfo['p']['_content'],
            'stability'  => $releaseinfo['st'],
            'compatible' => $compatible,
            'deprecated' => $deprecated,
        );

        if ($found) {
            $return['url'] = $releaseinfo['g'];
            return $return;
        }

        $return['php'] = $phpversion;
        return $return;
    }

    function listPackages($base, $channel = false)
    {
        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }

        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            return array();
        }

        if (!is_array($packagelist['p'])) {
            $packagelist['p'] = array($packagelist['p']);
        }

        return $packagelist['p'];
    }

    /**
     * List all categories of a REST server
     *
     * @param string $base base URL of the server
     * @return array of categorynames
     */
    function listCategories($base, $channel = false)
    {
        $categories = array();

        // c/categories.xml does not exist;
        // check for every package its category manually
        // This is SLOOOWWWW : ///
        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }

        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            $ret = array();
            return $ret;
        }

        if (!is_array($packagelist['p'])) {
            $packagelist['p'] = array($packagelist['p']);
        }

        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        foreach ($packagelist['p'] as $package) {
                $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
                if (PEAR::isError($inf)) {
                    PEAR::popErrorHandling();
                    return $inf;
                }
                $cat = $inf['ca']['_content'];
                if (!isset($categories[$cat])) {
                    $categories[$cat] = $inf['ca'];
                }
        }

        return array_values($categories);
    }

    /**
     * List a category of a REST server
     *
     * @param string $base base URL of the server
     * @param string $category name of the category
     * @param boolean $info also download full package info
     * @return array of packagenames
     */
    function listCategory($base, $category, $info = false, $channel = false)
    {
        // gives '404 Not Found' error when category doesn't exist
        $packagelist = $this->_rest->retrieveData($base.'c/'.urlencode($category).'/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }

        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            return array();
        }

        if (!is_array($packagelist['p']) ||
            !isset($packagelist['p'][0])) { // only 1 pkg
            $packagelist = array($packagelist['p']);
        } else {
            $packagelist = $packagelist['p'];
        }

        if ($info == true) {
            // get individual package info
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            foreach ($packagelist as $i => $packageitem) {
                $url = sprintf('%s'.'r/%s/latest.txt',
                        $base,
                        strtolower($packageitem['_content']));
                $version = $this->_rest->retrieveData($url, false, false, $channel);
                if (PEAR::isError($version)) {
                    break; // skipit
                }
                $url = sprintf('%s'.'r/%s/%s.xml',
                        $base,
                        strtolower($packageitem['_content']),
                        $version);
                $info = $this->_rest->retrieveData($url, false, false, $channel);
                if (PEAR::isError($info)) {
                    break; // skipit
                }
                $packagelist[$i]['info'] = $info;
            }
            PEAR::popErrorHandling();
        }

        return $packagelist;
    }

    function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false)
    {
        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }
        if ($this->_rest->config->get('verbose') > 0) {
            $ui = &PEAR_Frontend::singleton();
            $ui->log('Retrieving data...0%', true);
        }
        $ret = array();
        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            return $ret;
        }
        if (!is_array($packagelist['p'])) {
            $packagelist['p'] = array($packagelist['p']);
        }

        // only search-packagename = quicksearch !
        if ($searchpackage && (!$searchsummary || empty($searchpackage))) {
            $newpackagelist = array();
            foreach ($packagelist['p'] as $package) {
                if (!empty($searchpackage) && stristr($package, $searchpackage) !== false) {
                    $newpackagelist[] = $package;
                }
            }
            $packagelist['p'] = $newpackagelist;
        }
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $next = .1;
        foreach ($packagelist['p'] as $progress => $package) {
            if ($this->_rest->config->get('verbose') > 0) {
                if ($progress / count($packagelist['p']) >= $next) {
                    if ($next == .5) {
                        $ui->log('50%', false);
                    } else {
                        $ui->log('.', false);
                    }
                    $next += .1;
                }
            }

            if ($basic) { // remote-list command
                if ($dostable) {
                    $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
                        '/stable.txt', false, false, $channel);
                } else {
                    $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
                        '/latest.txt', false, false, $channel);
                }
                if (PEAR::isError($latest)) {
                    $latest = false;
                }
                $info = array('stable' => $latest);
            } else { // list-all command
                $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
                if (PEAR::isError($inf)) {
                    PEAR::popErrorHandling();
                    return $inf;
                }
                if ($searchpackage) {
                    $found = (!empty($searchpackage) && stristr($package, $searchpackage) !== false);
                    if (!$found && !(isset($searchsummary) && !empty($searchsummary)
                        && (stristr($inf['s'], $searchsummary) !== false
                            || stristr($inf['d'], $searchsummary) !== false)))
                    {
                        continue;
                    };
                }
                $releases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
                    '/allreleases.xml', false, false, $channel);
                if (PEAR::isError($releases)) {
                    continue;
                }
                if (!isset($releases['r'][0])) {
                    $releases['r'] = array($releases['r']);
                }
                unset($latest);
                unset($unstable);
                unset($stable);
                unset($state);
                foreach ($releases['r'] as $release) {
                    if (!isset($latest)) {
                        if ($dostable && $release['s'] == 'stable') {
                            $latest = $release['v'];
                            $state = 'stable';
                        }
                        if (!$dostable) {
                            $latest = $release['v'];
                            $state = $release['s'];
                        }
                    }
                    if (!isset($stable) && $release['s'] == 'stable') {
                        $stable = $release['v'];
                        if (!isset($unstable)) {
                            $unstable = $stable;
                        }
                    }
                    if (!isset($unstable) && $release['s'] != 'stable') {
                        $latest = $unstable = $release['v'];
                        $state = $release['s'];
                    }
                    if (isset($latest) && !isset($state)) {
                        $state = $release['s'];
                    }
                    if (isset($latest) && isset($stable) && isset($unstable)) {
                        break;
                    }
                }
                $deps = array();
                if (!isset($unstable)) {
                    $unstable = false;
                    $state = 'stable';
                    if (isset($stable)) {
                        $latest = $unstable = $stable;
                    }
                } else {
                    $latest = $unstable;
                }
                if (!isset($latest)) {
                    $latest = false;
                }
                if ($latest) {
                    $d = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
                        $latest . '.txt', false, false, $channel);
                    if (!PEAR::isError($d)) {
                        $d = unserialize($d);
                        if ($d) {
                            if (isset($d['required'])) {
                                if (!class_exists('PEAR_PackageFile_v2')) {
                                    require_once 'PEAR/PackageFile/v2.php';
                                }
                                if (!isset($pf)) {
                                    $pf = new PEAR_PackageFile_v2;
                                }
                                $pf->setDeps($d);
                                $tdeps = $pf->getDeps();
                            } else {
                                $tdeps = $d;
                            }
                            foreach ($tdeps as $dep) {
                                if ($dep['type'] !== 'pkg') {
                                    continue;
                                }
                                $deps[] = $dep;
                            }
                        }
                    }
                }
                if (!isset($stable)) {
                    $stable = '-n/a-';
                }
                if (!$searchpackage) {
                    $info = array('stable' => $latest, 'summary' => $inf['s'], 'description' =>
                        $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
                        'unstable' => $unstable, 'state' => $state);
                } else {
                    $info = array('stable' => $stable, 'summary' => $inf['s'], 'description' =>
                        $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
                        'unstable' => $unstable, 'state' => $state);
                }
            }
            $ret[$package] = $info;
        }
        PEAR::popErrorHandling();
        return $ret;
    }

    function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg)
    {
        $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
        if (PEAR::isError($packagelist)) {
            return $packagelist;
        }

        $ret = array();
        if (!is_array($packagelist) || !isset($packagelist['p'])) {
            return $ret;
        }

        if (!is_array($packagelist['p'])) {
            $packagelist['p'] = array($packagelist['p']);
        }

        foreach ($packagelist['p'] as $package) {
            if (!isset($installed[strtolower($package)])) {
                continue;
            }

            $inst_version = $reg->packageInfo($package, 'version', $channel);
            $inst_state   = $reg->packageInfo($package, 'release_state', $channel);
            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
            $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
                '/allreleases.xml', false, false, $channel);
            PEAR::popErrorHandling();
            if (PEAR::isError($info)) {
                continue; // no remote releases
            }

            if (!isset($info['r'])) {
                continue;
            }

            $release = $found = false;
            if (!is_array($info['r']) || !isset($info['r'][0])) {
                $info['r'] = array($info['r']);
            }

            // $info['r'] is sorted by version number
            usort($info['r'], array($this, '_sortReleasesByVersionNumber'));
            foreach ($info['r'] as $release) {
                if ($inst_version && version_compare($release['v'], $inst_version, '<=')) {
                    // not newer than the one installed
                    break;
                }

                // new version > installed version
                if (!$pref_state) {
                    // every state is a good state
                    $found = true;
                    break;
                } else {
                    $new_state = $release['s'];
                    // if new state >= installed state: go
                    if (in_array($new_state, $this->betterStates($inst_state, true))) {
                        $found = true;
                        break;
                    } else {
                        // only allow to lower the state of package,
                        // if new state >= preferred state: go
                        if (in_array($new_state, $this->betterStates($pref_state, true))) {
                            $found = true;
                            break;
                        }
                    }
                }
            }

            if (!$found) {
                continue;
            }

            $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' .
                $release['v'] . '.xml', false, false, $channel);
            if (PEAR::isError($relinfo)) {
                return $relinfo;
            }

            $ret[$package] = array(
                'version'  => $release['v'],
                'state'    => $release['s'],
                'filesize' => $relinfo['f'],
            );
        }

        return $ret;
    }

    function packageInfo($base, $package, $channel = false)
    {
        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
        $pinfo = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
        if (PEAR::isError($pinfo)) {
            PEAR::popErrorHandling();
            return PEAR::raiseError('Unknown package: "' . $package . '" in channel "' . $channel . '"' . "\n". 'Debug: ' .
                $pinfo->getMessage());
        }

        $releases = array();
        $allreleases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
            '/allreleases.xml', false, false, $channel);
        if (!PEAR::isError($allreleases)) {
            if (!class_exists('PEAR_PackageFile_v2')) {
                require_once 'PEAR/PackageFile/v2.php';
            }

            if (!is_array($allreleases['r']) || !isset($allreleases['r'][0])) {
                $allreleases['r'] = array($allreleases['r']);
            }

            $pf = new PEAR_PackageFile_v2;
            foreach ($allreleases['r'] as $release) {
                $ds = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
                    $release['v'] . '.txt', false, false, $channel);
                if (PEAR::isError($ds)) {
                    continue;
                }

                if (!isset($latest)) {
                    $latest = $release['v'];
                }

                $pf->setDeps(unserialize($ds));
                $ds = $pf->getDeps();
                $info = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package)
                    . '/' . $release['v'] . '.xml', false, false, $channel);

                if (PEAR::isError($info)) {
                    continue;
                }

                $releases[$release['v']] = array(
                    'doneby' => $info['m'],
                    'license' => $info['l'],
                    'summary' => $info['s'],
                    'description' => $info['d'],
                    'releasedate' => $info['da'],
                    'releasenotes' => $info['n'],
                    'state' => $release['s'],
                    'deps' => $ds ? $ds : array(),
                );
            }
        } else {
            $latest = '';
        }

        PEAR::popErrorHandling();
        if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
            if (is_array($pinfo['dp'])) {
                $deprecated = array('channel' => (string) $pinfo['dc'],
                                    'package' => trim($pinfo['dp']['_content']));
            } else {
                $deprecated = array('channel' => (string) $pinfo['dc'],
                                    'package' => trim($pinfo['dp']));
            }
        } else {
            $deprecated = false;
        }

        if (!isset($latest)) {
            $latest = '';
        }

        return array(
            'name' => $pinfo['n'],
            'channel' => $pinfo['c'],
            'category' => $pinfo['ca']['_content'],
            'stable' => $latest,
            'license' => $pinfo['l'],
            'summary' => $pinfo['s'],
            'description' => $pinfo['d'],
            'releases' => $releases,
            'deprecated' => $deprecated,
            );
    }

    /**
     * Return an array containing all of the states that are more stable than
     * or equal to the passed in state
     *
     * @param string Release state
     * @param boolean Determines whether to include $state in the list
     * @return false|array False if $state is not a valid release state
     */
    function betterStates($state, $include = false)
    {
        static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
        $i = array_search($state, $states);
        if ($i === false) {
            return false;
        }

        if ($include) {
            $i--;
        }

        return array_slice($states, $i + 1);
    }

    /**
     * Sort releases by version number
     *
     * @access private
     */
    function _sortReleasesByVersionNumber($a, $b)
    {
        if (version_compare($a['v'], $b['v'], '=')) {
            return 0;
        }

        if (version_compare($a['v'], $b['v'], '>')) {
            return -1;
        }

        if (version_compare($a['v'], $b['v'], '<')) {
            return 1;
        }
    }
}


<?php
/*
* Copyright (c) 2013 GNKW & Kamsoft.fr
*
* This file is part of Gnkam Autobus.
*
* Gnkam Autobus is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Gnkam Autobus is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Gnkam Autobus.  If not, see <http://www.gnu.org/licenses/>.
*/
namespace Gnkam\Autobus\Stac;
use Gnkw\Http\Rest\Client;
use Gnkw\Http\Uri;
use DateTime;
use DomDocument;

/**
 * ScheduleReceiver class
 * @author Anthony
 * @since 05/10/2013
 */
class ScheduleReceiver
{
	private $client;
	private $date;

	/**
	 * ScheduleReceiver constructor
	 */
	public function __construct()
	{
		$this->client = new Client('http://www.bus-stac.fr');
	}

	/**
	* Get array for an id
	* @param integer $id Line id
	* @param integer $sens 1 or 2
	* @param DateTime $date Schedule date
	* @return array Schedule in array representation
	*/
	public function getArrayData($id, $sens = 1, $date)
	{
		$this->date = $date;

		$this->uri =  new Uri('/horaires_ligne/index.asp');
		$this->uri->addParam('rub_code', 6);
		$this->uri->addParam('thm_id', 2);
		$this->uri->addParam('gpl_id', 0);
		$this->uri->addParam('lign_id', $id);
		$this->uri->addParam('sens', $sens);
		$this->uri->addParam('date', $this->date->format('d/m/Y'));

		return $this->allHours();
	}

	/**
	* Get all hours in an array
	* @return array Schedules line informations
	*/
	private function allHours()
	{
		$index = 1;
		$same = false;
		$hours = array();
		while(!$same)
		{
			# Get the page
			$this->uri->addParam('index', $index);
			$request = $this->client->get($this->uri);
			$resource = $request->getResource();
			if(!$resource->code(200))
			{
				return array('error' => 'fail to call Stac website');
			}
			$page = $resource->getContent();

			# Get page array representation
			$array = $this->parser($page);
			if(isset($previousArray))
			{
				# End condition when same page is called
				$compareId = 0;
				do
				{
					$comparePrevious = $previousArray[$compareId]['hours'][0];
					$compareCurrent = $array[$compareId]['hours'][0];
					$compareId++;
				}
				while($comparePrevious === '-');

				if($comparePrevious === $compareCurrent)
				{
					$same = true;
				}
				else
				{
					# Add new array informations
					$previousArray = $array;
					foreach($array as $pKey => $place)
					{
						foreach($place['hours'] as $hour){
							$hours[$pKey]['hours'][] = $hour;
						}
					}
				}
			}
			else
			{
				# First save of informations
				$previousArray = $array;
				$hours = $array;
			}
			$index+=6;
		}
		return $hours;
	}

	/**
	* Parse a page
	* @param string $page Page to parse
	* @return array Schedule line informations for a page
	*/
	private function parser($page)
	{
		$templateTime = clone $this->date;
		$hours = array();
		$array = array();

		# Get only table
		$page =  preg_replace('#(.+)\<tbody\>(.+)\<\/tbody\>(.+)#siU', '<table>$2</table>', $page);

		# save to DomDocument
		$dom = new DomDocument();
		libxml_use_internal_errors(true);

		# Load and save informations
		$dom->loadHTML($page);
		$raws = $dom->getElementsByTagName('tr');
		foreach($raws as $cKey => $raw)
		{
			if($cKey > 1)
			{
				$cells = $raw->getElementsByTagName('td');
				foreach($cells as $rKey => $cell)
				{
					$hours[$rKey][] = mb_convert_encoding($cell->nodeValue, mb_internal_encoding(), 'ISO-8859-1');
				}
			}
		}

		# Informations restructuration
		foreach($hours[0] as $placeKey => $place)
		{
			$size = count($hours);
			$array[$placeKey] =
			array(
				'place' => $place,
				'hours' => array()
			);
			for($i = 1; $i<$size; $i++)
			{
				$hourValue = $hours[$i][$placeKey];
				$hourValue = ($hourValue == '-') ? null : $hourValue;
				if(null !== $hourValue)
				{
					$cellHour = explode(':', $hourValue);
					$templateTime->setTime(intval($cellHour[0]), intval($cellHour[1]));
					$hourValue = $templateTime->getTimestamp();
				}
				$array[$placeKey]['hours'][] = $hourValue;
			}
		}
		return $array;
	}
}


<?php

namespace Behavior;

use Think\Behavior;
use Think\Storage;
use Think\View;

/**
 * 静态缓存读取
 */
final class ReadHtmlCacheBehavior extends Behavior
{
    /**
     * 执行入口
     *
     * @param mixed $params
     */
    public function run(&$params)
    {
        // 开启静态缓存
        if (IS_GET && C('HTML_CACHE_ON')) {
            $cacheTime = $this->requireHtmlCache();
            //静态页面有效
            if (false !== $cacheTime && $this->checkHTMLCache(HTML_FILE_NAME, $cacheTime)) {
                // 读取静态页面输出
                echo Storage::read(HTML_FILE_NAME);
                return;
            }
        }
    }

    /**
     * 判断是否需要静态缓存
     * @return bool|mixed
     */
    private static function requireHtmlCache()
    {
        // 读取静态规则
        $htmls = C('HTML_CACHE_RULES');
        if (!empty($htmls)) {
            $htmls = array_change_key_case($htmls);
            // 静态规则文件定义格式 actionName=>array('静态规则','缓存时间','附加规则')
            // 'read'=>array('{id},{name}',60,'md5') 必须保证静态规则的唯一性 和 可判断性
            // 检测静态规则
            $controllerName = strtolower(CONTROLLER_NAME);
            $actionName = strtolower(ACTION_NAME);
            if (isset($htmls[$controllerName.':'.$actionName])) {
                // 某个控制器的操作的静态规则
                $html = $htmls[$controllerName.':'.$actionName];
            } elseif (isset($htmls[$controllerName.':'])) {
                // 某个控制器的静态规则
                $html = $htmls[$controllerName.':'];
            } elseif (isset($htmls[$actionName])) {
                // 所有操作的静态规则
                $html = $htmls[$actionName];
            } elseif (isset($htmls['*'])) {
                // 全局静态规则
                $html = $htmls['*'];
            }
            if (!empty($html)) {
                // 解读静态规则
                $rule = is_array($html) ? $html[0] : $html;
                // 以$_开头的系统变量
                $callback = function ($match) {
                    switch ($match[1]) {
                        case '_GET':
                            $var = $_GET[$match[2]];
                            break;
                        case '_POST':
                            $var = $_POST[$match[2]];
                            break;
                        case '_REQUEST':
                            $var = $_REQUEST[$match[2]];
                            break;
                        case '_SERVER':
                            $var = $_SERVER[$match[2]];
                            break;
                        case '_SESSION':
                            $var = $_SESSION[$match[2]];
                            break;
                        case '_COOKIE':
                            $var = $_COOKIE[$match[2]];
                            break;
                        default:
                            break;
                    }

                    return (count($match) == 4) ? $match[3]($var) : $var;
                };
                $rule = preg_replace_callback('/{\$(_\w+)\.(\w+)(?:\|(\w+))?}/', $callback, $rule);
                // {ID|FUN} GET变量的简写
                $rule = preg_replace_callback(
                    '/{(\w+)\|(\w+)}/',
                    function ($match) {
                        return $match[2]($_GET[$match[1]]);
                    },
                    $rule
                );
                $rule = preg_replace_callback(
                    '/{(\w+)}/',
                    function ($match) {
                        return $_GET[$match[1]];
                    },
                    $rule
                );
                // 特殊系统变量
                $rule = str_ireplace(
                    ['{:controller}', '{:action}', '{:module}'],
                    [CONTROLLER_NAME, ACTION_NAME, MODULE_NAME],
                    $rule
                );
                // {|FUN} 单独使用函数
                $rule = preg_replace_callback(
                    '/{|(\w+)}/',
                    function ($match) {
                        return $match[1]();
                    },
                    $rule
                );
                $cacheTime = C('HTML_CACHE_TIME', null, 60);
                if (is_array($html)) {
                    // 应用附加函数
                    if (!empty($html[2])) {
                        $rule = $html[2]($rule);
                    }
                    // 缓存有效期
                    $cacheTime = isset($html[1]) ? $html[1] : $cacheTime;
                }

                // 当前缓存文件
                define('HTML_FILE_NAME', HTML_PATH.$rule.C('HTML_FILE_SUFFIX', null, '.html'));

                return $cacheTime;
            }
        }

        // 无需缓存
        return false;
    }

    /**
     * 检查静态HTML文件是否有效
     * 如果无效需要重新更新

     *
     * @param string $cacheFile 静态文件名
     * @param mixed $cacheTime 缓存有效期
     *
     * @return bool
     */
    public static function checkHTMLCache($cacheFile = '', $cacheTime = '')
    {
        if (!is_file($cacheFile)) {
            return false;
        } elseif (filemtime((new View)->parseTemplate()) > Storage::get($cacheFile, 'mtime', 'html')) {
            // 模板文件如果更新静态文件需要更新
            return false;
        } elseif (!is_numeric($cacheTime) && function_exists($cacheTime)) {
            return $cacheTime($cacheFile);
        } elseif ($cacheTime != 0 && NOW_TIME > Storage::get($cacheFile, 'mtime', 'html') + $cacheTime) {
            // 文件是否在有效期
            return false;
        }

        //静态文件有效
        return true;
    }

}


<?php

namespace EdwinLuijten\Ekko\Broadcast\Broadcasters;

use EdwinLuijten\Ekko\Broadcast\Identity;
use EdwinLuijten\Ekko\Broadcast\StrUtil;
use Pusher;

class PusherBroadcaster extends AbstractBroadcaster implements BroadcasterInterface
{
    /**
     * @var Pusher
     */
    private $pusher;

    /**
     * PusherBroadcaster constructor.
     * @param Pusher $pusher
     */
    public function __construct(Pusher $pusher)
    {
        $this->pusher = $pusher;
    }

    /**
     * @param Identity $identity
     * @return mixed
     * @throws \Exception
     */
    public function auth(Identity $identity)
    {
        if (StrUtil::startsWith($identity->channel, ['private-', 'presence-']) && empty($identity->identifier)) {
            throw new \Exception('Unauthorized', 403);
        }

        return parent::verifyThatIdentityCanAccessChannel(
            $identity,
            str_replace(['private-', 'presence-'], '', $identity->channel)
        );
    }

    /**
     * @param Identity $identity
     * @param $response
     * @return string signature
     */
    public function validAuthenticationResponse(Identity $identity, $response)
    {
        if (StrUtil::startsWith($identity->channel, 'private')) {
            return $this->pusher->socket_auth($identity->channel, $identity->socketId);
        } else {
            return $this->pusher->presence_auth(
                $identity->channel,
                $identity->socketId,
                $identity->identifier,
                $response
            );
        }
    }

    /**
     * Broadcast the given event.
     *
     * @param  array $channels
     * @param  string $event
     * @param  array $payload
     * @return void
     */
    public function broadcast(array $channels, $event, array $payload = [])
    {
        $socket = isset($payload['socket']) ? $payload['socket'] : null;

        $this->pusher->trigger($this->formatChannels($channels), $event, $payload, $socket);
    }

    /**
     * Get the Pusher instance.
     *
     * @return Pusher
     */
    public function getPusher()
    {
        return $this->pusher;
    }
}


<?php

/*
 * This file is part of the Valkyrja framework.
 *
 * (c) Melech Mizrachi
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Valkyrja\Console;

use Valkyrja\Application;
use Valkyrja\Console\Enums\ArgumentMode;
use Valkyrja\Console\Input\Argument;
use Valkyrja\Console\Input\Option;

/**
 * Abstract Class CommandHandler.
 *
 * @author Melech Mizrachi
 */
abstract class CommandHandler
{
    /**
     * The command.
     */
    public const COMMAND           = '';
    public const PATH              = '';
    public const SHORT_DESCRIPTION = '';
    public const DESCRIPTION       = '';

    /**
     * Tabbing structure to use.
     */
    protected const TAB        = '  ';
    protected const DOUBLE_TAB = self::TAB . self::TAB;

    /**
     * Help docs for this command.
     *
     * @return int
     */
    public function help(): int
    {
        $this->usageMessage();
        $this->argumentsSection();
        $this->optionsSection();

        return 0;
    }

    /**
     * Get the command version.
     *
     * @return int
     */
    public function version(): int
    {
        $this->applicationMessage();

        return 0;
    }

    /**
     * Get the valid arguments.
     *
     * @return \Valkyrja\Console\Input\Argument[]
     */
    protected function getArguments(): array
    {
        return [];
    }

    /**
     * Get the valid options.
     *
     * @return \Valkyrja\Console\Input\Option[]
     */
    protected function getOptions(): array
    {
        return [];
    }

    /**
     * The sections divider.
     *
     * @return void
     */
    protected function sectionDivider(): void
    {
        output()->writeMessage('', PHP_EOL);
    }

    /**
     * The application message.
     *
     * @return void
     */
    protected function applicationMessage(): void
    {
        output()->formatter()->magenta();
        output()->writeMessage('Valkyrja Application');
        output()->formatter()->resetColor();
        output()->writeMessage(' version ');
        output()->formatter()->cyan();
        output()->writeMessage(Application::VERSION, true);
        output()->formatter()->resetColor();
    }

    /**
     * The section message.
     *
     * @param string $sectionName
     *
     * @return void
     */
    protected function sectionTitleMessage(string $sectionName): void
    {
        output()->formatter()->underscore();
        output()->writeMessage($sectionName . ':', true);
        output()->formatter()->resetOptions();
    }

    /**
     * The usage message and description.
     *
     * @param string $message [optional] The usage to use instead of default
     *
     * @return void
     */
    protected function usageMessage(string $message = null): void
    {
        $message = $message ?? $this->usagePath();

        $this->sectionTitleMessage('Usage');
        output()->writeMessage(static::TAB);
        output()->writeMessage($message, true);
    }

    /**
     * Get the usage path.
     *
     * @return string
     */
    protected function usagePath(): string
    {
        $message = static::COMMAND;

        if ($this->getOptions()) {
            $message .= ' [options]';
        }

        foreach ($this->getArguments() as $argument) {
            $message .= ' '
                . ($argument->getMode() === ArgumentMode::OPTIONAL ? '[' : '')
                . '<'
                . $argument->getName()
                . '>'
                . ($argument->getMode() === ArgumentMode::OPTIONAL ? ']' : '');
        }

        return $message;
    }

    /**
     * The arguments section.
     *
     * @param Argument[] ...$arguments The argument
     *
     * @return void
     */
    protected function argumentsSection(Argument ...$arguments): void
    {
        if (! $arguments) {
            $arguments = $this->getArguments();
        }

        if (! $arguments) {
            return;
        }

        $longestLength = 0;

        $this->sectionDivider();
        $this->sectionTitleMessage('Arguments');

        foreach ($arguments as $argument) {
            $longestLength = max(\strlen($argument->getName()), $longestLength);
        }

        foreach ($arguments as $argument) {
            $this->sectionMessage(
                static::TAB . $argument->getName(),
                $argument->getDescription(),
                $longestLength
            );
        }
    }

    /**
     * The options section.
     *
     * @param Option[] ...$options The options
     *
     * @return void
     */
    protected function optionsSection(Option ...$options): void
    {
        if (! $options) {
            $options = $this->getOptions();
        }

        if (! $options) {
            return;
        }

        $longestLength = 0;

        $this->sectionDivider();
        $this->sectionTitleMessage('Options');

        foreach ($options as $option) {
            $longestLength =
                max(\strlen($this->getOptionName($option)), $longestLength);
        }

        foreach ($options as $option) {
            $this->sectionMessage(
                $this->getOptionName($option),
                $option->getDescription(),
                $longestLength
            );
        }
    }

    /**
     * Get an options name for the options section.
     *
     * @param Option $option The option
     *
     * @return string
     */
    protected function getOptionName(Option $option): string
    {
        $name = '';

        if ($option->getShortcut()) {
            $name .= '-' . $option->getShortcut() . ', ';
        } else {
            $name .= static::DOUBLE_TAB;
        }

        $name .= '--' . $option->getName();

        return $name;
    }

    /**
     * The section message.
     *
     * @param string $name          The name
     * @param string $description   The description
     * @param int    $longestLength The longest item length
     *
     * @return void
     */
    protected function sectionMessage(string $name, string $description, int $longestLength = null): void
    {
        $longestLength = $longestLength ?? 0;
        $spacesToAdd   = $longestLength - \strlen($name);

        output()->formatter()->green();
        output()->writeMessage(static::TAB . $name);
        output()->formatter()->resetColor();
        output()->writeMessage(
            $spacesToAdd > 0 ? str_repeat('.', $spacesToAdd) : ''
        );
        output()->writeMessage(str_repeat('.', 8));
        output()->writeMessage($description, true);
    }
}


<?php namespace Ordercloud\Entities\Products;

use JsonSerializable;

abstract class AbstractProductAddonSet implements JsonSerializable
{
    /** @var int */
    private $id;
    /** @var string */
    private $name;
    /**
     * @var string
     */
    private $displayName;
    /**
     * @var string
     */
    private $description;
    /**
     * @var boolean
     */
    private $enabled;

    /**
     * @param int                  $id
     * @param string               $name
     * @param string               $displayName
     * @param string               $description
     * @param boolean              $enabled
     */
    public function __construct($id, $name, $displayName, $description, $enabled)
    {
        $this->id = $id;
        $this->name = $name;
        $this->displayName = $displayName;
        $this->description = $description;
        $this->enabled = $enabled;
    }

    /**
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @return string
     */
    public function getDisplayName()
    {
        return $this->displayName;
    }

    /**
     * @return string
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * @return boolean
     */
    public function isEnabled()
    {
        return $this->enabled;
    }

    /**
     * Specify data which should be serialized to JSON
     */
    public function jsonSerialize()
    {
        return [
            'id' => $this->getId(),
            'name' => $this->getName(),
            'displayName' => $this->getDisplayName(),
            'description' => $this->getDescription(),
            'enabled' => $this->isEnabled(),
        ];
    }
}


<?php
declare(strict_types=1);

namespace Granam\DiceRolls\Templates\Rollers;

use Granam\DiceRolls\DiceRoll;
use Granam\DiceRolls\Roll;
use Granam\DiceRolls\Roller;
use Granam\DiceRolls\Templates\DiceRolls\Dice1d6DrdPlusBonusRoll;
use Granam\DiceRolls\Templates\DiceRolls\Dice1d6DrdPlusMalusRoll;
use Granam\DiceRolls\Templates\DiceRolls\Dice1d6Roll;
use Granam\DiceRolls\Templates\Numbers\Two;
use Granam\DiceRolls\Templates\Dices\Dice1d6;
use Granam\DiceRolls\Templates\Evaluators\OneToOneEvaluator;
use Granam\DiceRolls\Templates\Rolls\Roll2d6DrdPlus;
use Granam\DiceRolls\Templates\RollOn\RollOn12;
use Granam\DiceRolls\Templates\RollOn\RollOn2;
use Granam\Integer\IntegerInterface;
use Granam\Integer\Tools\ToInteger;

/**
 * 2x1d6; 12 = bonus roll by 1x1d6 => 1-3 = 0, 4-6 = +1 and rolls again; 2 = malus roll by 1x1d6 => 1-3 = -1 and rolls again, 4-6 = 0
 * @method Roll2d6DrdPlus|Roll roll(int $sequenceStartNumber = 1): Roll
 */
class Roller2d6DrdPlus extends Roller
{

    private static $roller2d6DrdPlus;

    /**
     * @return Roller2d6DrdPlus
     */
    public static function getIt(): Roller2d6DrdPlus
    {
        if (self::$roller2d6DrdPlus === null) {
            self::$roller2d6DrdPlus = new static();
        }

        return self::$roller2d6DrdPlus;
    }

    public function __construct()
    {
        parent::__construct(
            Dice1d6::getIt(),
            Two::getIt(), // number of rolls = 2
            OneToOneEvaluator::getIt(), // rolled value remains untouched
            new RollOn12( // bonus happens on sum roll value of 12 (both standard rolls summarized)
                Roller1d6DrdPlusBonus::getIt() // bonus roll by 1d6; 1-3 = +0; 4-6 = +1; repeatedly in case of bonus
            ),
            new RollOn2( // malus happens on sum roll of 2 (both standard rolls summarized)
                Roller1d6DrdPlusMalus::getIt() // malus roll by 1d6; 1-3 = -1; 4-6 = 0; repeatedly in case of malus
            )
        );
    }

    /**
     * @param array|DiceRoll[] $standardDiceRolls
     * @param array|DiceRoll[] $bonusDiceRolls
     * @param array|DiceRoll[] $malusDiceRolls
     * @return Roll2d6DrdPlus|Roll
     */
    protected function createRoll(array $standardDiceRolls, array $bonusDiceRolls, array $malusDiceRolls): Roll
    {
        /** @noinspection ExceptionsAnnotatingAndHandlingInspection */
        return new Roll2d6DrdPlus($standardDiceRolls, $bonusDiceRolls, $malusDiceRolls);
    }

    /**
     * Warning! This will generate roll history randomly
     *
     * @param int|IntegerInterface $rollSummary
     * @return Roll2d6DrdPlus
     * @throws \Granam\Integer\Tools\Exceptions\WrongParameterType
     * @throws \Granam\Integer\Tools\Exceptions\ValueLostOnCast
     */
    public function generateRoll($rollSummary): Roll2d6DrdPlus
    {
        $rollSummary = ToInteger::toInteger($rollSummary);
        $bonusDiceRolls = [];
        $malusDiceRolls = [];
        if ($rollSummary <= 2) { // two ones = malus rolls and one "not valid" malus roll
            $standardDiceRolls = [new Dice1d6Roll(1, 1), new Dice1d6Roll(1, 2)]; // two ones = malus rolls
            $sequenceNumber = 3;
            for ($malusRollsCount = 2 - $rollSummary, $malusRollNumber = 1; $malusRollNumber <= $malusRollsCount; $malusRollNumber++) {
                /** @noinspection PhpUnhandledExceptionInspection */
                $malusDiceRolls[] = new Dice1d6DrdPlusMalusRoll(\random_int(1, 3), $sequenceNumber); // malus roll is valid only in range 1..3
                $sequenceNumber++;
            }
            /** @noinspection PhpUnhandledExceptionInspection */
            $malusDiceRolls[] = new Dice1d6DrdPlusMalusRoll(\random_int(4, 6), $sequenceNumber); // last malus roll was not "valid" - broke the chain
        } elseif ($rollSummary < 12) {
            $randomRange = 12 - $rollSummary; // 1..11
            $firstRandomMinimum = 6 - $randomRange;
            if ($firstRandomMinimum < 1) {
                $firstRandomMinimum = 1;
            }
            $firstRandomMaximum = $rollSummary - $firstRandomMinimum;
            /** @noinspection PhpUnhandledExceptionInspection */
            $firstRoll = \random_int($firstRandomMinimum, $firstRandomMaximum);
            $secondRoll = $rollSummary - $firstRoll;
            $firstDiceRoll = new Dice1d6Roll($firstRoll, 1);
            $secondDiceRoll = new Dice1d6Roll($secondRoll, 2);
            $standardDiceRolls = [$firstDiceRoll, $secondDiceRoll];
        } else { // two sixes = bonus rolls and one "not valid" bonus roll
            $standardDiceRolls = [new Dice1d6Roll(6, 1), new Dice1d6Roll(6, 2)];
            $sequenceNumber = 3;
            for ($bonusRollsCount = $rollSummary - 12, $bonusRollNumber = 1; $bonusRollNumber <= $bonusRollsCount; $bonusRollNumber++) {
                /** @noinspection PhpUnhandledExceptionInspection */
                $bonusDiceRolls[] = new Dice1d6DrdPlusBonusRoll(\random_int(4, 6), $sequenceNumber); // bonus roll is valid only in range 4..6
                $sequenceNumber++;
            }
            /** @noinspection PhpUnhandledExceptionInspection */
            $bonusDiceRolls[] = new Dice1d6DrdPlusBonusRoll(\random_int(1, 3), $sequenceNumber); // last bonus roll was not "valid" - broke the chain
        }

        return $this->createRoll($standardDiceRolls, $bonusDiceRolls, $malusDiceRolls);
    }

}


<?php

/*
 * This file is part of the SmarTwig (Twig Extension).
 *
 * (c) Omar Yepez <omar.yepez@yepsua.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Yepsua\SmarTwig\Twig\Node;

use \Twig_Compiler;

class SimpleNode extends CommonNode{

  public function __construct($names, $values, $lineno, $tag = null) {
    parent::__construct(array('values' => $values), array('names' => $names), $lineno, $tag);
  }

  /**
   * Compiles the node to PHP.
   *
   * @param Twig_Compiler A Twig_Compiler instance
   */
  public function compileWidget(Twig_Compiler $compiler){
    $compiler->addDebugInfo($this);
    $this->compileOptions($compiler);
    $this->compileExtension($compiler);
    if(!$this->isOnlyJsS()){
      $this->compileInitWidget($compiler);
      $this->compileHtmlProperties($compiler);
      $this->compileWidgetContents($compiler);
      $this->compileEndWidget($compiler);
    }
    if(!$this->isOnlyHTML()){
      if($this->isBuiltByListener()){
        $this->compileListener($compiler);
      }
      $this->compileBuilder($compiler);
      if($this->isExec()){
        $this->compileCallableMethods($compiler);
        $this->compileExecute($compiler);
      }
    }
  }

  public function compileWidgetContents(Twig_Compiler $compiler){
    if($this->getContent() !== null){
      $compiler->indent();
        $compiler->subcompile($this->getContent(),true);
      $compiler->outdent();
    }
  }
}


<?php
/**
 * Keytype Model
 *
 * @author Del
 */

namespace Delatbabel\Keylists\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

/**
 * Keytype Model
 *
 * Contains the list of key lists used throughout the application.
 *
 * @see database/migrations/2012_01_01_000001_create_keylists_tables.php
 */
class Keytype extends Model
{
    use SoftDeletes, Cacheable;

    /** @var array */
    protected $fillable = ['name', 'description', 'extended_data',
        'created_by', 'updated_by'];

    protected $casts = [
        'extended_data'     => 'array',
    ];

    /**
     * Return the cache key for a model.
     *
     * @return string
     */
    protected function cacheKey()
    {
        return 'table.to.array.keytypes';
    }

    /**
     * Return the cache index key for a model.
     *
     * @return string
     */
    protected function getIndexKey()
    {
        return $this->name;
    }

    /**
     * Many:1 relationship with Keytype
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function keyvalues()
    {
        return $this->hasMany('Delatbabel\Keylists\Models\Keyvalue');
    }

    /**
     * return an array of name/description pairs from the cache
     *
     * @return array
     */
    public static function getKeytypes()
    {
        $tableCache = static::tableToArray();

        // Reformat the table cache to name => description.
        $result = [];
        foreach ($tableCache as $key => $value) {
            $result[$value->name] = $value->description;
        }

        return $result;
    }
}


<?php

namespace tourze\Http\Request\Client;

use Doctrine\Common\Inflector\Inflector;
use ReflectionClass;
use tourze\Base\Base;
use tourze\Base\Exception\BaseException;
use tourze\Http\Exception\HttpException;
use tourze\Http\Http;
use tourze\Http\Response;
use tourze\Http\Request;
use tourze\Http\Request\RequestClient;

/**
 * 内部执行的请求
 *
 * @package tourze\Http\Request\Client
 */
class InternalClient extends RequestClient
{

    /**
     * 处理请求
     *
     *     $request->execute();
     *
     * @param   Request  $request
     * @param   Response $response
     * @return \tourze\Http\Response
     * @throws \tourze\Base\Exception\BaseException
     */
    public function executeRequest(Request $request, Response $response)
    {
        $className = 'Controller';

        // 控制器
        $controller = $request->controller;
        $className = Inflector::classify($controller) . $className;

        // 目录
        $directory = $request->directory;
        if ($directory)
        {
            $directory = str_replace('/', '\\', $directory);
            $className = $directory . $className;
        }

        // 保存请求状态
        $previous = Request::$current;
        Request::$current = $request;

        Base::getLog()->info(__METHOD__ . ' controller class', [
            'class' => $className,
        ]);

        try
        {
            if ( ! class_exists($className))
            {
                Base::getLog()->debug(__METHOD__ . ' class not found', [
                    'class' => $className,
                ]);
                throw HttpException::factory(Http::NOT_FOUND, 'The requested URL :uri was not found on this server.', [
                    ':uri' => $request->uri,
                ])->request($request);
            }

            $class = new ReflectionClass($className);

            if ($class->isAbstract())
            {
                Base::getLog()->error(__METHOD__ . ' calling abstract controller class', [
                    'class' => $className,
                ]);
                throw new BaseException('Cannot create instances of abstract :controller', [
                    ':controller' => $className,
                ]);
            }

            $controller = $class->newInstance([
                'request'  => $request,
                'response' => $response,
            ]);
            $response = $class->getMethod('execute')->invoke($controller);

            if ( ! $response instanceof Response)
            {
                Base::getLog()->error(__METHOD__ . ' unknown response type');
                throw new BaseException('Controller failed to return a Response');
            }
        }
        catch (HttpException $e)
        {
            if (null === $e->request())
            {
                $e->request($request);
            }
            $response = $e->getResponse();
        }

        Request::$current = $previous;
        return $response;
    }

}


<?php
namespace Soluble\Normalist\Synthetic;

use Soluble\Normalist\Synthetic\ResultSet\ResultSet; use Soluble\Db\Sql\Select;
use Zend\Db\Sql\Sql;
use Zend\Db\Sql\Where;
use Zend\Db\Sql\Predicate;

class TableSearch
{
    /**
     * @var Table
     */
    protected $table;

    /**
     *
     * @var Select
     */
    protected $select;

    /**
     * @var boolean
     */
    protected $has_modified_columns = false;

    /**
     *
     * @var array|string
     */
    protected $tableIdentifier;

    /**
     *
     * @param Select $select table name
     * @param Table $table
     */
    public function __construct(Select $select, Table $table)
    {
        $this->select = $select;
        $this->table = $table;
        $this->tableIdentifier = $this->select->getRawState(Select::TABLE);
    }

    /**
     * Limit the number of results
     *
     * @param int $limit
     * @return TableSearch
     */
    public function limit($limit)
    {
        $this->select->limit($limit);
        return $this;
    }

    /**
     * Set offset to use when a limit has been set.
     *
     * @param int $offset
     * @return TableSearch
     */
    public function offset($offset)
    {
        $this->select->offset($offset);
        return $this;
    }

    /**
     * Set the table columns to retrieve
     *
     * @param array $columns array list of columns, key are used as aliases
     * @param boolean $prefixColumnsWithTable
     * @return TableSearch
     */
    public function columns(array $columns, $prefixColumnsWithTable = true)
    {
        $this->has_modified_columns = true;
        $this->select->columns($columns, $prefixColumnsWithTable);
        return $this;
    }

    /**
     * Add table prefixed columns, will automatically
     * quote table parts identifiers found in the column name.
     * It provides an alternative for defining columns from multiple/joined
     * table in one go.
     *
     * @param array $columns
     */
    public function prefixedColumns(array $columns)
    {
        $this->has_modified_columns = true;
        $this->select->prefixedColumns($columns);
        return $this;
    }

    /**
     * Set a group option
     *
     * @param array|string $group a column or an array of columns to group by
     * @return TableSearch
     */
    public function group($group)
    {
        $this->select->group($group);
        return $this;
    }

    /**
     * Set a habing option
     *
     * @param  Where|\Closure|string|array $predicate
     * @param  string $combination One of the OP_* constants from Predicate\PredicateSet
     * @return Select

     * @return TableSearch
     */
    public function having($predicate, $combination = Predicate\PredicateSet::OP_AND)
    {
        $this->select->having($predicate, $combination);
        return $this;
    }

    /**
     * Set an order by clause
     *
     * @param string|array $order a columns or an array definition of columns
     * @return TableSearch
     */
    public function order($order)
    {
        $this->select->order($order);
        return $this;
    }

    /**
     * Add a where condition
     *
     * @param  Where|\Closure|string|array|Predicate\PredicateInterface $predicate
     * @param  string $combination One of the OP_* constants from Zend\Db\Sql\Predicate\PredicateSet
     * @throws Zend\Db\Sql\Exception\InvalidArgumentException
     * @return TableSearch
     */
    public function where($predicate, $combination = null)
    {
        $this->select->where($predicate, $combination);
        return $this;
    }

    /**
     * Add an orWhere condition to the search
     *
     * @param  Where|\Closure|string|array|Predicate\PredicateInterface $predicate
     * @throws Zend\Db\Sql\Exception\InvalidArgumentException
     * @return TableSearch
     */
    public function orWhere($predicate)
    {
        $this->select->orWhere($predicate);
        return $this;
    }

    /**
     * Add an inner table join to the search
     *
     * @param  string|array $table
     * @param  string $on
     * @param  string|array $columns by default won't retrieve any column from the joined table
     * @return TableSearch
     */
    public function join($table, $on, $columns = [])
    {
        $prefixed_table = $this->prefixTableJoinCondition($table);

        //$this->columns($this->getPrefixedColumns());

        $this->select->join($prefixed_table, $on, $columns, Select::JOIN_INNER);
        return $this;
    }

    /**
     * Add an left outer table join to the search
     *
     * @param  string|array $table
     * @param  string $on
     * @param  string|array $columns by default won't retrieve any column from the joined table
     * @return TableSearch
     */
    public function joinLeft($table, $on, $columns = [])
    {
        $prefixed_table = $this->prefixTableJoinCondition($table);
        $this->select->join($prefixed_table, $on, $columns, Select::JOIN_LEFT);
        return $this;
    }

    /**
     * Add an right outer table join to the search
     *
     * @param  string|array $table
     * @param  string $on
     * @param  string|array $columns by default won't retrieve any column from the joined table
     * @return TableSearch
     */
    public function joinRight($table, $on, $columns = [])
    {
        $prefixed_table = $this->prefixTableJoinCondition($table);
        $this->select->join($prefixed_table, $on, $columns, Select::JOIN_RIGHT);
        return $this;
    }

    /**
     * Return the underlying select
     *
     * @return Select
     */
    public function getSelect()
    {
        return $this->select;
    }

    /**
     * Return SQL string
     *
     * @return string
     */
    public function getSql()
    {
        $adapterPlatform = $this->table->getTableManager()->getDbAdapter()->getPlatform();
        return $this->select->getSqlString($adapterPlatform);
    }

    /**
     * Return a json version of the results
     *
     * @return string Json encoded
     */
    public function toJson()
    {
        return json_encode($this->select->execute()->toArray());
    }

    /**
     * Return an array version of the results
     *
     * @return array
     */
    public function toArray()
    {
        return $this->select->execute()->toArray();
    }

    /**
     * Return record as an array
     *
     * @return ResultSet
     */
    public function execute()
    {
        $rs = new ResultSet($this->select, $this->table, !$this->has_modified_columns);
        return $rs;
    }

    /**
     * Return an array indexed by $indexKey
     * useful for comboboxes...
     *
     * @param string $columnKey
     * @param string $indexKey
     * @return array
     */
    public function toArrayColumn($columnKey, $indexKey)
    {
        $select = clone $this->select;
        $select->reset($select::COLUMNS)->columns([$columnKey, $indexKey]);
        return array_column($select->execute()->toArray(), $columnKey, $indexKey);
    }

    /**
     *
     * @param array|string $tableSpec

    protected function getPrefixedColumns($tableSpec=null)
    {

        if ($tableSpec === null) $tableSpec = $this->tableIdentifier;

        if (is_array($tableSpec)) {
            $alias = key($this->tableIdentifier);
            $table = $this->tableIdentifier[$alias];
        } else {
            $alias = $this->tableIdentifier;
            $table = $alias;
        }

        $columns = array();
        $pf = $this->table->getTableManager()->getDbAdapter()->getPlatform();
        $cols = array_keys($this->table->getColumnsInformation());
        foreach($cols as $column) {
            //$columns["$alias.$column"] = $column;
            //$columns[$column] = new \Zend\Db\Sql\TableIdentifier($pf->quoteIdentifier($alias) . $pf->getIdentifierSeparator() . $pf->quoteIdentifier($column));
            //$columns[$column] =
            $columns[$column] = new Predicate\Expression($pf->quoteIdentifier($alias) . $pf->getIdentifierSeparator() . $pf->quoteIdentifier($column));
        }
        return $columns;

    }
    */
    /**
     * Prefix table join condition
     *
     * @param string|array $table
     * @return array|string
     */
    protected function prefixTableJoinCondition($table)
    {
        $tm = $this->table->getTableManager();
        if (is_array($table)) {
            $alias = key($table);
            $prefixed_table = $tm->getPrefixedTable($table[$alias]);
            $table = [$alias => $prefixed_table];
        } elseif (is_string($table)) {
            $prefixed_table = $tm->getPrefixedTable($table);
            $table = $prefixed_table;
        }
        return $table;
    }
}


<?php

/*
 * This file is part of the Valkyrja framework.
 *
 * (c) Melech Mizrachi
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Valkyrja\Support;

/**
 * Class Collection.
 *
 * @author Melech Mizrachi
 */
class Collection
{
    /**
     * The collection of items.
     *
     * @var array
     */
    protected $collection;

    /**
     * Collection constructor.
     *
     * @param array $collection
     */
    public function __construct(array $collection = [])
    {
        $this->setAll($collection);
    }

    /**
     * Get a single item from the collection.
     *
     * @param string $key     The key to get
     * @param mixed  $default [optional] The default value
     *
     * @return mixed
     */
    public function get(string $key, $default = null) // : mixed
    {
        return $this->has($key)
            ? $this->collection[$key]
            : $default;
    }

    /**
     * Determine if an item is in the collection.
     *
     * @param string $key The key
     *
     * @return bool
     */
    public function has(string $key): bool
    {
        return isset($this->collection[$key]);
    }

    /**
     * Determine if a value exists in the collection.
     *
     * @param mixed $value The value
     *
     * @return bool
     */
    public function exists($value): bool
    {
        return \in_array($value, $this->collection, true);
    }

    /**
     * Set a new item into the collection.
     *
     * @param string $key   The key
     * @param mixed  $value The value
     *
     * @return self
     */
    public function set(string $key, $value): self
    {
        $this->collection[$key] = $value;

        return $this;
    }

    /**
     * Remove an item from the collection.
     *
     * @param string $key The key
     *
     * @return self
     */
    public function remove(string $key): self
    {
        if (! $this->has($key)) {
            return $this;
        }

        unset($this->collection[$key]);

        return $this;
    }

    /**
     * Get all the items in the collection.
     *
     * @return array
     */
    public function all(): array
    {
        return $this->collection;
    }

    /**
     * Set the collection.
     *
     * @param array $collection The collection
     *
     * @return self
     */
    public function setAll(array $collection): self
    {
        $this->collection = $collection;

        return $this;
    }

    /**
     * Get all the keys in the collection.
     *
     * @return array
     */
    public function keys(): array
    {
        return array_keys($this->collection);
    }

    /**
     * Get the total count of items in the collection.
     *
     * @return int
     */
    public function count(): int
    {
        return \count($this->collection);
    }

    /**
     * Determine if the collection is empty.
     *
     * @return bool
     */
    public function isEmpty(): bool
    {
        return empty($this->collection);
    }

    /**
     * Get a single item from the collection.
     *
     * @param string $key The key
     *
     * @return mixed
     */
    public function __get(string $key) // : mixed
    {
        return $this->get($key);
    }

    /**
     * Determine if an item is in the collection.
     *
     * @param string $key The key
     *
     * @return bool
     */
    public function __isset(string $key): bool
    {
        return $this->has($key);
    }

    /**
     * Set a new item into the collection.
     *
     * @param string $key   The key
     * @param mixed  $value The value
     *
     * @return mixed
     */
    public function __set(string $key, $value)
    {
        return $this->set($key, $value);
    }

    /**
     * Remove an item from the collection.
     *
     * @param string $key The key
     *
     * @return void
     */
    public function __unset(string $key): void
    {
        $this->remove($key);
    }

    /**
     * Convert the collection to a string.
     *
     * @return string
     */
    public function __toString(): string
    {
        return json_encode($this->collection);
    }
}


<?php

namespace Colorium\Orm;

abstract class SQL
{

    /**
     * Compile SELECT
     *
     * @param string $table
     * @param array $fields
     * @param array $where
     * @param array $sort
     * @param array $limit
     * @return array
     */
    public static function select($table, array $fields, array $where = [], array $sort = [], array $limit = [])
    {
        $sql = $values = [];
        $sql[] = 'SELECT ' . implode(', ', $fields);
        $sql[] = 'FROM ' . static::e($table);

        if($where) {
            $items = [];
            foreach($where as $exp => $data) {
                $items[] = $exp;
                if(is_array($data)) {
                    $values = array_merge($values, $data);
                    continue;
                }
                $values[] = $data;
            }
            $sql[] = 'WHERE ' . implode(' AND ', $items);
        }

        if($sort) {
            $items = [];
            foreach($sort as $field => $direction) {
                $items[] = static::e($field) . ' ' . ($direction == SORT_DESC ? 'DESC' : 'ASC');
            }
            $sql[] = 'ORDER BY ' .implode(', ', $items);
        }

        if($limit) {
            $sql[] = 'LIMIT ' . $limit[0] . ', ' . $limit[1];
        }

        $sql = implode("\n", $sql);
        return [$sql, $values];
    }

    /**
     * Compile INSERT INTO
     *
     * @param string $table
     * @param array $set
     * @return array
     */
    public static function insert($table, array $set)
    {
        $sql = $values = $fields = $holders = [];
        $sql[] = 'INSERT INTO ' . static::e($table);

        foreach($set as $field => $value) {
            $fields[] = static::e($field);
            $holders[] = '?';
            $values[] = $value;
        }

        $sql[] = '(' . implode(', ', $fields) . ')';
        $sql[] = 'VALUES (' . implode(', ', $holders) . ')';

        $sql = implode("\n", $sql);
        return [$sql, $values];
    }

    /**
     * Compile UPDATE
     *
     * @param string $table
     * @param array $set
     * @param array $where
     * @return array
     */
    public static function update($table, array $set, array $where = [])
    {
        $sql = $values = $fields = [];
        $sql[] = 'UPDATE ' . static::e($table);

        foreach($set as $field => $value) {
            $fields[] = static::e($field) . ' = ?';
            $values[] = $value;
        }

        $sql[] = 'SET ' . implode(', ', $fields);

        if($where) {
            $clause = [];
            foreach($where as $exp => $data) {
                $clause[] = $exp;
                if(is_array($data)) {
                    $values = array_merge($values, $data);
                    continue;
                }
                $values[] = $data;
            }
            $sql[] = 'WHERE ' . implode(' AND ', $clause);
        }

        $sql = implode("\n", $sql);
        return [$sql, $values];
    }

    /**
     * Compile DELETE
     *
     * @param string $table
     * @param array $where
     * @return array
     */
    public static function delete($table, array $where = [])
    {
        $sql = $values = [];
        $sql[] = 'DELETE FROM ' . static::e($table);

        if($where) {
            $where = [];
            foreach($where as $exp => $data) {
                $where[] = $exp;
                if(is_array($data)) {
                    $values = array_merge($values, $data);
                    continue;
                }
                $values[] = $data;
            }
            $sql[] = 'WHERE ' . implode(' AND ', $where);
        }

        $sql = implode("\n", $sql);
        return [$sql, $values];
    }

    /**
     * Compile TABLE EXISTS
     *
     * @param string $table
     * @return bool
     */
    public static function tableExists($table)
    {
        return 'SELECT 1 FROM ' . static::e($table);
    }

    /**
     * Compile CREATE TABLE
     *
     * @param string $table
     * @param array $specs
     * @return bool
     */
    public static function createTable($table, array $specs)
    {
        $sql = 'CREATE TABLE IF NOT EXISTS ' . static::e($table) . ' (';
        foreach($specs as $field => $opts) {
            $sql .= "\n" .  static::e($field) . ' ' . $opts['type'];
            if($opts['primary'] == true) {
                $opts['nullable'] = false;
                $opts['default'] = null;
                $sql .= ' PRIMARY KEY AUTO_INCREMENT';
            }
            if(!$opts['nullable']) {
                $sql .= ' NOT NULL';
            }
            if($opts['default']) {
                $sql .= ' DEFAULT ' . $opts['default'];
            }
            $sql .= ',';
        }

        $sql = trim($sql, ',') . "\n" . ');';
        return $sql;
    }

    /**
     * Compile DROP TABLE
     *
     * @param string $table
     * @return bool
     */
    public static function dropTable($table)
    {
        return 'DROP TABLE IF EXISTS ' . static::e($table);
    }

    /**
     * Compile TRUNCATE TABLE
     *
     * @param string $table
     * @return bool
     */
    public static function truncateTable($table)
    {
        return 'TRUNCATE TABLE ' . static::e($table);
    }

    /**
     * Protect string
     *
     * @param $field
     * @return string
     */
    protected static function e($field)
    {
        return '`' . trim($field, '`') . '`';
    }

}