<?php

/*
 * This file is part of the Ivory Google Map package.
 *
 * (c) Eric GELOEN <geloen.eric@gmail.com>
 *
 * For the full copyright and license information, please read the LICENSE
 * file that was distributed with this source code.
 */

namespace Ivory\GoogleMap\Services\Geocoding;

use Geocoder\Geocoder as BaseGeocoder;

/**
 * Geocoder which describes a google map geocoder.
 *
 * @see http://code.google.com/apis/maps/documentation/javascript/reference.html#Geocoder
 * @author GeLo <geloen.eric@gmail.com>
 */
class Geocoder extends BaseGeocoder
{
    /**
     * {@inheritdoc}
     */
    public function geocode($request)
    {
        if ($this->getProvider() instanceof GeocoderProvider) {
            return $this->getProvider()->getGeocodedData($request);
        }

        return parent::geocode($request);
    }

    /**
     * {@inheritdoc}
     */
    public function reverse($latitude, $longitude)
    {
        if ($this->getProvider() instanceof GeocoderProvider) {
            return $this->getProvider()->getReversedData(array($latitude, $longitude));
        }

        return parent::reverse($latitude, $longitude);
    }
}


<?php
/**
 * Slack.
 *
 * @author @jaswrks
 * @copyright WebSharks™
 */
declare(strict_types=1);
namespace WebSharks\Core\Classes\Core\Utils;

use WebSharks\Core\Classes;
use WebSharks\Core\Interfaces;
use WebSharks\Core\Traits;
#
use WebSharks\Core\Classes\Core\Error;
use WebSharks\Core\Classes\Core\Base\Exception;
#
use function assert as debug;
use function get_defined_vars as vars;

/**
 * Slack.
 *
 * @since 161009 Slack utils.
 */
class Slack extends Classes\Core\Base\Core
{
    /**
     * Cache directory.
     *
     * @since 161009
     *
     * @type string
     */
    protected $cache_dir;

    /**
     * Class constructor.
     *
     * @since 161009 Slack utils.
     *
     * @param Classes\App $App Instance of App.
     */
    public function __construct(Classes\App $App)
    {
        parent::__construct($App);

        if (!$this->App->Config->©fs_paths['©cache_dir']) {
            throw $this->c::issue('Missing cache directory.');
        }
        $this->cache_dir = $this->App->Config->©fs_paths['©cache_dir'].'/slack';
    }

    /**
     * Notify a Slack channel.
     *
     * @since 161009 Slack utils.
     *
     * @param array $args Notification args.
     *
     * @return bool True if notification sent successfully.
     */
    public function notify(array $args): bool
    {
        # Establish arguments.

        $default_args = [
            'channel' => '',

            'username' => '',
            'icon_url' => '',

            'mrkdwn'     => true,
            'link_names' => true,
            'text'       => '',

            'attachment_color' => '#e8e8e8',

            'attachment_pretext' => '',

            'attachment_author_name' => '',
            'attachment_author_link' => '',
            'attachment_author_icon' => '',

            'attachment_title'      => '',
            'attachment_title_link' => '',
            'attachment_text'       => '',

            'attachment_image_url' => '',
            'attachment_thumb_url' => '',

            'attachment_footer_icon' => '',
            'attachment_footer'      => '',

            'attachments' => [], // Custom set of attachments.

            'api_webhook_url' => $this->App->Config->©slack['©api_webhook_url'],
        ];
        $args = array_merge($default_args, $args);
        $args = array_intersect_key($args, $default_args);

        foreach ($args as $_key => &$_value) {
            if (in_array($_key, ['mrkdwn', 'link_names'], true)) {
                $_value = (bool) $_value;
            } elseif (in_array($_key, ['attachments'], true)) {
                $_value = (array) $_value;
            } else {
                $_value = (string) $_value;
            }
        } // unset($_key, $_value);

        # General information.

        $data = [
            'channel' => $args['channel'],

            'username' => $args['username'],
            'icon_url' => $args['icon_url'],

            'mrkdwn'     => $args['mrkdwn'],
            'link_names' => $args['link_names'],
            'text'       => $args['text'],
        ];
        # Attachment (if applicable).

        if ($args['attachments']) {
            $data['attachments'] = $args['attachments'];
            //
        } elseif ($args['attachment_color'] && (0
                || $args['attachment_pretext']
                || $args['attachment_author_name']
                || $args['attachment_title']
                || $args['attachment_text']
                || $args['attachment_image_url']
                || $args['attachment_thumb_url']
                || $args['attachment_footer']
            )
        ) { // Add attachment.
            $data['attachments'] = [
                [
                    'mrkdwn_in' => ['pretext', 'text'],
                    'color'     => $args['attachment_color'],

                    'pretext' => $args['attachment_pretext'],

                    'author_name' => $args['attachment_author_name'],
                    'author_link' => $args['attachment_author_link'],
                    'author_icon' => $args['attachment_author_icon'],

                    'title'      => $args['attachment_title'],
                    'title_link' => $args['attachment_title_link'],

                    'text' => $args['attachment_text'],

                    'image_url' => $args['attachment_image_url'],
                    'thumb_url' => $args['attachment_thumb_url'],

                    'footer_icon' => $args['attachment_footer_icon'],
                    'footer'      => $args['attachment_footer'],
                ],
            ];
        }
        # Validate & finalize data elements.

        if (!$data['channel'] || !$data['username'] || !$data['icon_url'] || !$args['api_webhook_url']
            || (!$data['text'] && empty($data['attachments']))) {
            return false; // Not possible.
        }
        # POST, validate, and collect API response.

        $request_args = [
            'headers' => [
                'content-type: application/json',
            ],
            'body'         => json_encode($data),
            'return_array' => true,
        ];
        $response = $this->c::remoteRequest('POST::'.$args['api_webhook_url'], $request_args);

        # Validate response.

        if ($response['code'] !== 200) {
            debug(0, $this->c::issue(vars(), 'Bad response code.'));
            return false;
        } elseif ($response['body'] !== 'ok') {
            debug(0, $this->c::issue(vars(), 'Response body not `ok`.'));
            return false;
        }
        # Return success.

        return true;
    }

    /**
     * Slack mrkdwn.
     *
     * @since 170124.74961 Initial release.
     *
     * @param mixed $value Input value.
     * @param array $args  Additional args.
     *
     * @return string|array|object Mrkdwn'd.
     */
    public function mrkdwn($value, array $args = [])
    {
        if (is_array($value) || is_object($value)) {
            foreach ($value as $_key => &$_value) {
                $_value = $this->mrkdwn($_value, $args);
            } // unset($_key, $_value);
            return $value;
        }
        $markdown = (string) $value;

        if (!isset($markdown[0])) {
            return $markdown;
        }
        $default_args = [
            'current_gfm_owner' => '',
            'current_gfm_repo'  => '',
            'is_gfm'            => false,
        ];
        $args += $default_args; // Merge defaults.

        $is_gfm            = (bool) $args['is_gfm'];
        $current_gfm_owner = (string) $args['current_gfm_owner'];
        $current_gfm_repo  = (string) $args['current_gfm_repo'];

        // GitHub flavored markdown?
        if ($is_gfm) { // If so, convert issue references.
            $markdown = $this->c::mdGitHubIssueRefs($markdown, [
                'current_owner' => $current_gfm_owner,
                'current_repo'  => $current_gfm_repo,
            ]); // Keeps MD formatting.
        } // Converts MD issue references.

        // Begin conversions.
        $mrkdwn = $markdown; // Converting.

        // Normalize breaks for `/m` mode below.
        $mrkdwn = $this->c::normalizeEols($mrkdwn);

        // Remove language specifier from MD fences.
        $mrkdwn = preg_replace('/^(\h*)(~{3,}|`{3,})[\w\-]+(\v)/uim', '$1$2$3', $mrkdwn);

        // Tokenize; i.e., strip MD fences.
        $Tokenizer = $this->c::tokenize($mrkdwn, ['md-fences']);
        $mrkdwn    = &$Tokenizer->getString(); // By reference.

        // Convert markdown `*italic*` into Slack `_italic_`.
        $mrkdwn = preg_replace('/(?<=^|[\s;,(])\*{1}([^*\v]+)\*{1}(?=$|[\s.!?;,)])/ui', '_$1_', $mrkdwn);

        // Convert markdown `**bold**` into Slack `*bold*`.
        $mrkdwn = preg_replace('/(?<=^|[\s;,(])\*{2}([^*\v]+)\*{2}(?=$|[\s.!?;,)])/ui', '*$1*', $mrkdwn);

        // Convert markdown `~~strike~~` into Slack `~strike~`.
        $mrkdwn = preg_replace('/(?<=^|[\s;,(])~{2}([^~\v]+)~{2}(?=$|[\s.!?;,)])/ui', '~$1~', $mrkdwn);

        // Convert markdown headings into Slack `*bold*`.
        $mrkdwn = preg_replace('/^([ ]*)(#+)[ ]+([^#\v]+?)(?:[ ]+\\2)?(?:[ ]+\{[^{}]*\})?$/uim', '$1*$3*', $mrkdwn);

        // Convert markdown list items into Slack `:li:`.
        $mrkdwn = preg_replace_callback('/^([ ]*)[*\-][ ]+(?!\[)/uim', function ($m) {
            return str_replace(' ', '   ', $m[1]).':li:'; // Via emoji code.
        }, $mrkdwn); // This turns each space into 3 for nested list items.

        // Convert markdown list items into Slack `- :cbc:` or `- :cb:`.
        $mrkdwn = preg_replace_callback('/^([ ]*)[*\-][ ]+\[([x| ])\](?=[ ]+)/uim', function ($m) {
            return str_replace(' ', '   ', $m[1]).($m['2'] === 'x' ? ':cbc:' : ':cb:');
        }, $mrkdwn); // This turns each space into 3 for nested list items.

        // Tokenize  markdown `<>` links because `<>` are escaped below. Note UTF-8 `⟨` and `⟩` in place of `<` and `>`.
        $mrkdwn = preg_replace('/(?<=^|[\s;,(])\<([a-z][a-z0-9+.\-]*\:[^\s<>]+)\>(?=$|[\s.!?;,)])/ui', '⟨$1⟩', $mrkdwn);

        // Escape special HTML chars. Converts `<>&` into plain text; i.e., disallows raw HTML code.
        $mrkdwn = $this->c::escHtmlChars($mrkdwn); // After `<>` links & issue references.

        // Restore  markdown `<>` links. Note UTF-8 `⟨` and `⟩` in place of `<` and `>`.
        $mrkdwn = preg_replace('/(?<=^|[\s;,(])[⟨]([a-z][a-z0-9+.\-]*\:[^\s<>]+)[⟩](?=$|[\s.!?;,)])/ui', '<$1>', $mrkdwn);

        // Replace images (not Slack compatible) with clickable links.
        $mrkdwn = preg_replace_callback('/\!?\[(?:[^[\]]*|(?R))\]\([a-z][a-z0-9+.\-]*\:[^\s()]+\)(?:[ ]+\{[^{}]*\})?/ui', function ($m) {
            if (mb_stripos($m[0], '![') === false) {
                return $m[0]; // Not an image.
            } else {
                return preg_replace_callback('/\!\[(?<alt_text>[^[\]]*)\]\((?<url>[a-z][a-z0-9+.\-]*\:[^\s()]+)\)/ui', function ($m) {
                    return '<'.$m['url'].'|'.$this->c::escHtmlChars($m['alt_text'] ? $m['alt_text'].' '.__('(image)') : $this->c::midClip(basename($m['url']), 15)).'>';
                }, $m[0]); // Strip away images.
            }
        }, $mrkdwn); // Strip away images.

        // Convert links for Slack compatibility.
        $mrkdwn = preg_replace_callback('/(?<=^|[\s;,(])\[(?<text>[^|[\]]*)\]\((?<url>[a-z][a-z0-9+.\-]*\:[^|\s()]+)\)(?=$|[\s.!?;,)])/ui', function ($m) {
            if (isset($m['text'][0])) {
                return '<'.$m['url'].'|'.$this->c::escHtmlChars($m['text']).'>';
            } else {
                return '<'.$m['url'].'>';
            }
        }, $mrkdwn); // `<url|text>`

        // No more than two consecutive breaks.
        $mrkdwn = $this->c::mbTrim($this->c::normalizeEols($mrkdwn, true));

        return $mrkdwn = &$Tokenizer->restoreGetString();
    }
}


<?php
/**
 * CDN utilities.
 *
 * @author @jaswrks
 * @copyright WebSharks™
 */
declare (strict_types = 1);
namespace WebSharks\Core\Classes\Core\Utils;

use WebSharks\Core\Classes;
use WebSharks\Core\Interfaces;
use WebSharks\Core\Traits;
#
use WebSharks\Core\Classes\Core\Error;
use WebSharks\Core\Classes\Core\Base\Exception;
#
use function assert as debug;
use function get_defined_vars as vars;

/**
 * CDN utilities.
 *
 * @since 150424 Initial release.
 */
class Cdn extends Classes\Core\Base\Core implements Interfaces\MimeConstants
{
    /**
     * Extensions.
     *
     * @since 150424
     *
     * @var string[]
     */
    protected $static_exts;

    /**
     * Class constructor.
     *
     * @since 150424 Initial release.
     *
     * @param Classes\App $App Instance of App.
     */
    public function __construct(Classes\App $App)
    {
        parent::__construct($App);

        $this->static_exts = array_keys($this::MIME_TYPES);
        $this->static_exts = array_diff($this->static_exts, ['php']);
    }

    /**
     * CDN URL generator.
     *
     * @since 150424 Initial release.
     *
     * @param string $uri    URI to serve via CDN.
     * @param string $scheme Specific scheme?
     *
     * @return string Output URL.
     */
    public function url(string $uri, string $scheme = ''): string
    {
        if (!($host = $this->App->Config->©urls['©hosts']['©cdn'])) {
            throw $this->c::issue('Missing CDN host name.');
        }
        $uri = $uri ? $this->c::mbLTrim($uri, '/') : '';
        $uri = $uri ? '/'.$uri : ''; // Force leading slash.

        if ($uri && defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) {
            $uri = preg_replace('/\.min\.js($|[?])/u', '.js${1}', $uri);
        } // This allows developers to debug uncompressed JS files.

        $base_path = $this->App->Config->©urls['©base_paths']['©cdn'];
        $base_path = $base_path && $uri ? $this->c::mbRTrim($base_path, '/') : $base_path;

        return $url = $this->c::setScheme('//'.$host.$base_path.$uri, $scheme);
    }

    /**
     * CDN URL filter.
     *
     * @since 150424 Initial release.
     *
     * @param string $url_uri_qsl Input URL, URI, or query string w/ a leading `?`.
     *
     * @return string Output URL, URI, or query string w/ a leading `?`.
     */
    public function filter(string $url_uri_qsl): string
    {
        if (!$this->App->Config->©urls['©cdn_filter_enable']) {
            return $url_uri_qsl; // Nothing to do.
        }
        if (!$this->App->Config->©urls['©hosts']['©cdn']) {
            return $url_uri_qsl; // Nothing to do.
        }
        if (!($parts = $this->c::parseUrl($url_uri_qsl))) {
            return $url_uri_qsl; // Not possible.
        }
        if (empty($parts['path'])) {
            return $url_uri_qsl; // Not applicable.
        }
        if (!empty($parts['user']) || !empty($parts['pass'])) {
            return $url_uri_qsl; // Not applicable.
        }
        if (!($ext = $this->c::fileExt($parts['path']))) {
            return $url_uri_qsl; // No extension.
        }
        if (!in_array($ext, $this->static_exts, true)) {
            return $url_uri_qsl; // Not applicable.
        }
        if (!empty($parts['host'])) { // Has a host name?
            $host_root = $this->c::parseUrlHost($parts['host'])['root'];
            $app_root  = $this->App->Config->©urls['©hosts']['©roots']['©app'];
            if (!$host_root || $this->c::mbStrCaseCmp($host_root, $app_root) !== 0) {
                return $url_uri_qsl; // Not applicable.
            } // Also check for an empty/invalid root here.
        } // If there is a host part, it must have an app root!

        $uri = $parts; // w/ URI parts only.
        unset($uri['scheme'], $uri['host'], $uri['port']);
        $uri = $this->c::unparseUrl($uri); // The URI only.

        return $this->url($uri, $parts['scheme'] ?? '');
    }
}


<?php

/*
 * This file is part of the Ivory Google Map package.
 *
 * (c) Eric GELOEN <geloen.eric@gmail.com>
 *
 * For the full copyright and license information, please read the LICENSE
 * file that was distributed with this source code.
 */

namespace Ivory\GoogleMap\Helper\Events;

use Ivory\GoogleMap\Events\Event;

/**
 * Event manager helper.
 *
 * @author GeLo <geloen.eric@gmail.com>
 */
class EventManagerHelper
{
    /**
     * Renders a dom event.
     *
     * @param \Ivory\GoogleMap\Events\Event $domEvent The dom event.
     *
     * @return string The JS output.
     */
    public function renderDomEvent(Event $domEvent)
    {
        return sprintf(
            '%s = google.maps.event.addDomListener(%s, "%s", %s, %s);'.PHP_EOL,
            $domEvent->getJavascriptVariable(),
            $domEvent->getInstance(),
            $domEvent->getEventName(),
            $domEvent->getHandle(),
            json_encode($domEvent->isCapture())
        );
    }

    /**
     * Renders a dom event once.
     *
     * @param \Ivory\GoogleMap\Events\Event $domEventOnce The dom event once.
     *
     * @return string The JS output.
     */
    public function renderDomEventOnce(Event $domEventOnce)
    {
        return sprintf(
            '%s = google.maps.event.addDomListenerOnce(%s, "%s", %s, %s);'.PHP_EOL,
            $domEventOnce->getJavascriptVariable(),
            $domEventOnce->getInstance(),
            $domEventOnce->getEventName(),
            $domEventOnce->getHandle(),
            json_encode($domEventOnce->isCapture())
        );
    }

    /**
     * Renders an event.
     *
     * @param \Ivory\GoogleMap\Events\Event $event The event.
     *
     * @return string The JS output.
     */
    public function renderEvent(Event $event)
    {
        return sprintf(
            '%s = google.maps.event.addListener(%s, "%s", %s);'.PHP_EOL,
            $event->getJavascriptVariable(),
            $event->getInstance(),
            $event->getEventName(),
            $event->getHandle()
        );
    }

    /**
     * Renders an event once.
     *
     * @param \Ivory\GoogleMap\Events\Event $eventOnce The event once.
     *
     * @return string The JS output.
     */
    public function renderEventOnce(Event $eventOnce)
    {
        return sprintf(
            '%s = google.maps.event.addListenerOnce(%s, "%s", %s);'.PHP_EOL,
            $eventOnce->getJavascriptVariable(),
            $eventOnce->getInstance(),
            $eventOnce->getEventName(),
            $eventOnce->getHandle()
        );
    }
}


<?php

/*
 * This file is part of the Ivory Google Map package.
 *
 * (c) Eric GELOEN <geloen.eric@gmail.com>
 *
 * For the full copyright and license information, please read the LICENSE
 * file that was distributed with this source code.
 */

namespace Ivory\GoogleMap\Helper\Overlays;

use Ivory\GoogleMap\Exception\HelperException;
use Ivory\GoogleMap\Overlays\Animation;

/**
 * Animation helper.
 *
 * @author GeLo <geloen.eric@gmail.com>
 */
class AnimationHelper
{
    /**
     * Renders an animation.
     *
     * @param string $animation The animation.
     *
     * @throws \Ivory\GoogleMap\Exception\HelperException If the animation is not valid.
     *
     * @return string The JS output.
     */
    public function render($animation)
    {
        switch ($animation) {
            case Animation::BOUNCE:
            case Animation::DROP:
                return sprintf('google.maps.Animation.%s', strtoupper($animation));
            default:
                throw HelperException::invalidAnimation();
        }
    }
}


<?php

/*
 * This file is part of the Ivory Google Map package.
 *
 * (c) Eric GELOEN <geloen.eric@gmail.com>
 *
 * For the full copyright and license information, please read the LICENSE
 * file that was distributed with this source code.
 */

namespace Ivory\GoogleMap\Helper\Controls;

use Ivory\GoogleMap\Controls\ZoomControl;
use Ivory\GoogleMap\Helper\AbstractHelper;

/**
 * Zoom control helper.
 *
 * @author GeLo <geloen.eric@gmail.com>
 */
class ZoomControlHelper extends AbstractHelper
{
    /** @var \Ivory\GoogleMap\Helper\Controls\ControlPositionHelper */
    protected $controlPositionHelper;

    /** @var \vory\GoogleMap\Templating\Helper\Controls\ZoomControlStyleHelper */
    protected $zoomControlStyleHelper;

    /**
     * Create a zoom control helper.
     *
     * @param \Ivory\GoogleMap\Helper\Controls\ControlPositionHelper  $controlPositionHelper  The control position
     *                                                                                helper.
     * @param \Ivory\GoogleMap\Helper\Controls\ZoomControlStyleHelper $zoomControlStyleHelper The zoom control style
     *                                                                                helper.
     */
    public function __construct(
        ControlPositionHelper $controlPositionHelper = null,
        ZoomControlStyleHelper $zoomControlStyleHelper = null
    ) {
        parent::__construct();

        if ($controlPositionHelper === null) {
            $controlPositionHelper = new ControlPositionHelper();
        }

        if ($zoomControlStyleHelper === null) {
            $zoomControlStyleHelper = new ZoomControlStyleHelper();
        }

        $this->setControlPositionHelper($controlPositionHelper);
        $this->setZoomControlStyleHelper($zoomControlStyleHelper);
    }

    /**
     * Gets the control position helper.
     *
     * @return \Ivory\GoogleMap\Helper\Controls\ControlPositionHelper The control position helper.
     */
    public function getControlPositionHelper()
    {
        return $this->controlPositionHelper;
    }

    /**
     * Sets the control position helper.
     *
     * @param \Ivory\GoogleMap\Helper\Controls\ControlPositionHelper $controlPositionHelper The control position helper.
     */
    public function setControlPositionHelper(ControlPositionHelper $controlPositionHelper)
    {
        $this->controlPositionHelper = $controlPositionHelper;
    }

    /**
     * Gets the zoom control style helper.
     *
     * @return \Ivory\GoogleMap\Helper\Controls\ZoomControlStyleHelper The zoom control style helper.
     */
    public function getZoomControlStyleHelper()
    {
        return $this->zoomControlStyleHelper;
    }

    /**
     * Sets the zoom control style helper.
     *
     * @param \Ivory\GoogleMap\Helper\Controls\ZoomControlStyleHelper $zoomControlStyleHelper The zoom control style
     *                                                                                helper.
     */
    public function setZoomControlStyleHelper(ZoomControlStyleHelper $zoomControlStyleHelper)
    {
        $this->zoomControlStyleHelper = $zoomControlStyleHelper;
    }

    /**
     * Renders a zoom control.
     *
     * @param \Ivory\GoogleMap\Controls\ZoomControl $zoomControl The zoom control.
     *
     * @return string The JS output.
     */
    public function render(ZoomControl $zoomControl)
    {
        return $this->jsonBuilder
            ->reset()
            ->setValue('[position]', $this->controlPositionHelper->render($zoomControl->getControlPosition()), false)
            ->setValue('[style]', $this->zoomControlStyleHelper->render($zoomControl->getZoomControlStyle()), false)
            ->build();
    }
}


<?php

namespace webignition\InternetMediaType\Parameter\Parser;

use webignition\StringParser\StringParser;
use webignition\QuotedString\Parser as QuotedStringParser;

/**
 * Parses out the value from an internet media type parameter string
 */
class ValueParser extends StringParser
{
    const ATTRIBUTE_VALUE_SEPARATOR = '=';
    const QUOTED_STRING_DELIMITER = '"';

    const STATE_IN_NON_QUOTED_VALUE = 1;
    const STATE_IN_QUOTED_VALUE = 2;

    /**
     * Attribute part of the attribute=value parameter string
     *
     * @var string
     */
    private $attribute = '';

    public function setAttribute(string $attribute)
    {
        $this->attribute = $attribute;
    }

    /**
     * @param string $inputString
     *
     * @return string
     */
    public function parse($inputString): ?string
    {
        $output = parent::parse($this->getNonAttributePart($inputString));

        if ($this->getCurrentState() == self::STATE_IN_NON_QUOTED_VALUE) {
            return $output;
        }

        if ($output == '') {
            return null;
        }

        $quotedStringParser = new QuotedStringParser();
        $quotedString = $quotedStringParser->parse($output);

        return $quotedString->getValue();
    }

    private function getNonAttributePart(string $inputString): string
    {
        return trim(substr(
            $inputString,
            strlen($this->attribute) + strlen(self::ATTRIBUTE_VALUE_SEPARATOR)
        ));
    }

    protected function parseCurrentCharacter()
    {
        switch ($this->getCurrentState()) {
            case self::STATE_UNKNOWN:
                $this->deriveState();
                break;

            default:
                $this->appendOutputString();
                $this->incrementCurrentCharacterPointer();
                break;
        }
    }

    private function deriveState()
    {
        if ($this->isCurrentCharacterQuotedStringDelimiter()) {
            $this->setCurrentState(self::STATE_IN_QUOTED_VALUE);
        } else {
            $this->setCurrentState(self::STATE_IN_NON_QUOTED_VALUE);
        }
    }

    private function isCurrentCharacterQuotedStringDelimiter(): bool
    {
        return $this->getCurrentCharacter() == self::QUOTED_STRING_DELIMITER;
    }
}


<?php
/**
 * Exec time utilities.
 *
 * @author @jaswrks
 * @copyright WebSharks™
 */
declare (strict_types = 1);
namespace WebSharks\Core\Classes\Core\Utils;

use WebSharks\Core\Classes;
use WebSharks\Core\Interfaces;
use WebSharks\Core\Traits;
#
use WebSharks\Core\Classes\Core\Error;
use WebSharks\Core\Classes\Core\Base\Exception;
#
use function assert as debug;
use function get_defined_vars as vars;

/**
 * Exec time utilities.
 *
 * @since 150424 Initial release.
 */
class ExecTime extends Classes\Core\Base\Core
{
    /**
     * Max execution time.
     *
     * @since 150424 Initial release.
     *
     * @param int|null $max Max execution time.
     *
     * @return int Max execution time; in seconds.
     */
    public function max(int $max = null): int
    {
        if (isset($max) && $max >= 0) {
            @set_time_limit($max);
        }
        return (int) ini_get('max_execution_time');
    }
}


<?php
/**
 * WebPurify.
 *
 * @author @jaswrks
 * @copyright WebSharks™
 */
declare (strict_types = 1);
namespace WebSharks\Core\Classes\Core\Utils;

use WebSharks\Core\Classes;
use WebSharks\Core\Interfaces;
use WebSharks\Core\Traits;
#
use WebSharks\Core\Classes\Core\Error;
use WebSharks\Core\Classes\Core\Base\Exception;
#
use function assert as debug;
use function get_defined_vars as vars;

/**
 * WebPurify.
 *
 * @since 150424 Badwords checker.
 */
class WebPurify extends Classes\Core\Base\Core
{
    /**
     * Cache directory.
     *
     * @since 150424
     *
     * @var string
     */
    protected $cache_dir;

    /**
     * Class constructor.
     *
     * @since 150424 Badwords checker.
     *
     * @param Classes\App $App Instance of App.
     */
    public function __construct(Classes\App $App)
    {
        parent::__construct($App);

        if (!$this->App->Config->©fs_paths['©cache_dir']) {
            throw $this->c::issue('Missing cache directory.');
        }
        $this->cache_dir = $this->App->Config->©fs_paths['©cache_dir'].'/webpurify';
    }

    /**
     * Check a slug for bad words.
     *
     * @since 150424 Badwords checker.
     *
     * @param string $slug Input slug to check.
     * @param array  $args Any additional args.
     *
     * @return bool True if slug contains bad words.
     */
    public function checkSlug(string $slug, array $args = []): bool
    {
        $text = mb_strtolower($slug);
        $text = preg_replace('/[^\p{L}\p{N}]/ui', ' ', $text);
        $text = preg_replace('/\s+/u', ' ', $text);

        return $this->check($text, $args);
    }

    /**
     * Check a string for bad words.
     *
     * @since 150424 Badwords checker.
     *
     * @param string $text Input text to check.
     * @param array  $args Any additional args.
     *
     * @return bool True if string contains bad words.
     *
     * @link https://www.webpurify.com/documentation/methods/check/
     * @TODO Test this with a POST request instead of GET for longer `text`.
     */
    public function check(string $text, array $args = []): bool
    {
        # The text is empty?

        if (!($text = $this->c::mbTrim($text))) {
            return false; // Nothing to do.
        }
        $default_args = [
            'api_key' => $this->App->Config->©webpurify['©api_key'],
            'text'    => $text, // From above.
        ];
        $args = array_merge($default_args, $args);

        # Already cached this in memory?

        if (!is_null($check = &$this->cacheKey(__FUNCTION__, $args))) {
            return $check; // Cached this already.
        }
        # Prepare endpoint/request args.

        $endpoint_args = array_merge($args, [
            'method' => 'webpurify.live.check',
            'format' => 'json',
            'lang'   => 'en',
            'rsp'    => '0',
        ]);
        $request_args = [
            'max_con_secs'    => 5,
            'max_stream_secs' => 5,
            'return_array'    => true,
        ];
        $endpoint = 'http://api1.webpurify.com/services/rest/';
        $endpoint = $this->c::addUrlQueryArgs($endpoint_args, $endpoint);

        # Determine sharded cache directory and file.

        $endpoint_sha1         = sha1($endpoint);
        $cache_dir             = $this->cache_dir.'/'.$this->c::sha1ModShardId($endpoint_sha1, true);
        $cache_dir_permissions = $this->App->Config->©fs_permissions['©transient_dirs'];
        $cache_file            = $cache_dir.'/'.$endpoint_sha1;

        # Use cached response if at all possible.

        if (is_file($cache_file)) { // File exists?
            return $check = (bool) file_get_contents($cache_file);
        }
        # Query for remote response via API endpoint URL.

        $response = $this->c::remoteRequest('GET::'.$endpoint, $request_args);

        # Validate response; false (and no cache) on any error.

        if ($response['code'] === 104) {
            return false; // Do not cache.
        }
        if (!is_object($json = json_decode($response['body']))) {
            return false; // Do not cache.
        }
        if (!isset($json->rsp->{'@attributes'}->stat)
            || $json->rsp->{'@attributes'}->stat !== 'ok') {
            return false; // Do not cache.
        }
        if (!isset($json->rsp->found)) {
            return false; // Do not cache.
        }
        # Cache and return response.

        if (!is_dir($cache_dir)) {
            mkdir($cache_dir, $cache_dir_permissions, true);
        }
        $check = (bool) $json->rsp->found; // `> 0` = `true`.
        file_put_contents($cache_file, (string) (int) $check);

        return $check; // True or false.
    }
}


<?php

/*
 * This file is part of the Ivory Google Map package.
 *
 * (c) Eric GELOEN <geloen.eric@gmail.com>
 *
 * For the full copyright and license information, please read the LICENSE
 * file that was distributed with this source code.
 */

namespace Ivory\GoogleMap\Services\Geocoding\Result;

use Ivory\GoogleMap\Exception\GeocodingException;

/**
 * Geocoder result which describes a google map geocoder result.
 *
 * @see http://code.google.com/apis/maps/documentation/javascript/reference.html#GeocoderResult
 * @author GeLo <geloen.eric@gmail.com>
 */
class GeocoderResult
{
    /** @var array */
    protected $addressComponents;

    /** @var string */
    protected $formattedAddress;

    /** @var \Ivory\GoogleMap\Services\Geocoding\Result\GeocoderGeometry */
    protected $geometry;

    /** @var boolean */
    protected $partialMatch;

    /** @var array */
    protected $types;

    /**
     * Create a gecoder result.
     *
     * @param array                                                       $addressComponents The address components.
     * @param string                                                      $formattedAddress  The formatted address.
     * @param \Ivory\GoogleMap\Services\Geocoding\Result\GeocoderGeometry $geometry          The geometry.
     * @param array                                                       $types             The types.
     * @param boolean                                                     $partialMatch      The partial match flag.
     */
    public function __construct(
        array $addressComponents,
        $formattedAddress,
        GeocoderGeometry $geometry,
        array $types,
        $partialMatch = null
    ) {
        $this->setAddressComponents($addressComponents);
        $this->setFormattedAddress($formattedAddress);
        $this->setGeometry($geometry);
        $this->setTypes($types);
        $this->setPartialMatch($partialMatch);
    }

    /**
     * Gets the address components.
     *
     * @param string|null The type of the address components.
     *
     * @return array The address components.
     */
    public function getAddressComponents($type = null)
    {
        if ($type === null) {
            return $this->addressComponents;
        }

        $addressComponents = array();

        foreach ($this->addressComponents as $addressComponent) {
            if (in_array($type, $addressComponent->getTypes())) {
                $addressComponents[] = $addressComponent;
            }
        }

        return $addressComponents;
    }

    /**
     * Sets address components.
     *
     * @param array $addressComponents The address components.
     */
    public function setAddressComponents(array $addressComponents)
    {
        $this->addressComponents = array();

        foreach ($addressComponents as $addressComponent) {
            $this->addAddressComponent($addressComponent);
        }
    }

    /**
     * Adds an address component to the geocoder result.
     *
     * @param \Ivory\GoogleMapBundle\Model\Services\Result\GeocoderAddressComponent $addressComponent The address
     *                                                                                component to add.
     */
    public function addAddressComponent(GeocoderAddressComponent $addressComponent)
    {
        $this->addressComponents[] = $addressComponent;
    }

    /**
     * Gets the formatted address.
     *
     * @return string The formatted address.
     */
    public function getFormattedAddress()
    {
        return $this->formattedAddress;
    }

    /**
     * Sets the formatted address.
     *
     * @param string $formattedAddress The formatted address.
     *
     * @throws \Ivory\GoogleMap\Exception\GeocodingException If the formatted address is not valid.
     */
    public function setFormattedAddress($formattedAddress)
    {
        if (!is_string($formattedAddress)) {
            throw GeocodingException::invalidGeocoderResultFormattedAddress();
        }

        $this->formattedAddress = $formattedAddress;
    }

    /**
     * Gets the geocoder result geometry.
     *
     * @return \Ivory\GoogleMap\Services\Geocoding\Result\GeocoderGeometry The geocoder result geometry.
     */
    public function getGeometry()
    {
        return $this->geometry;
    }

    /**
     * Sets the geocoder result geometry.
     *
     * @param \Ivory\GoogleMap\Services\Geocoding\Result\GeocoderGeometry $geometry The geocoder result geometry.
     */
    public function setGeometry(GeocoderGeometry $geometry)
    {
        $this->geometry = $geometry;
    }

    /**
     * Checks if the geocoder result is a partial match.
     *
     * @return boolean TRUE if the geocoder result is a partial match else FALSE.
     */
    public function isPartialMatch()
    {
        return $this->partialMatch;
    }

    /**
     * Sets the geocoder result partial match flag.
     *
     * @param boolean $partialMatch TRUE if the geocoder result is a partial match else FALSE.
     *
     * @throws \Ivory\GoogleMap\Exception\GeocodingException If the partial match flag is not valid.
     */
    public function setPartialMatch($partialMatch = null)
    {
        if (!is_bool($partialMatch) && ($partialMatch !== null)) {
            throw GeocodingException::invalidGeocoderResultPartialMatch();
        }

        $this->partialMatch = $partialMatch;
    }

    /**
     * Gets the geocoder result types.
     *
     * @return array The geocoder result types.
     */
    public function getTypes()
    {
        return $this->types;
    }

    /**
     * Sets the geocoder result types.
     *
     * @param array $types The geocoder result types.
     */
    public function setTypes(array $types)
    {
        $this->types = array();

        foreach ($types as $type) {
            $this->addType($type);
        }
    }

    /**
     * Adds a type to the geocoder result.
     *
     * @param string $type The type to add.
     *
     * @throws \Ivory\GoogleMap\Exception\GeocodingException If the type is not valid.
     */
    public function addType($type)
    {
        if (!is_string($type)) {
            throw GeocodingException::invalidGeocoderResultType();
        }

        $this->types[] = $type;
    }
}


<?php
//------------------------
//· mysql模块 已弃用
//---------------------
namespace denha;

use denha;

class Mysqli
{

    private static $instance;
    public static $options; //记录参数信息

    public static $link; //mysql链接信息

    public $dbConfig; //数据库连接信息
    public $tablepre; //表前缀
    public $build; //保存未解析Sql信息
    public $sqlInfo; //执行sql记录

    public $linkId;
    public $result;
    public $querystring;
    public $isclose;
    public $safecheck;

    public $table;
    public $join;
    public $field;
    public $where;
    public $order;
    public $limit;
    public $group;
    public $total;
    public $excID; //插入ID
    public $_sql; //最后执行sql
    public $chilidSql; //子查询

    private function __construct($dbConfig = '')
    {
        if ($dbConfig) {
            $this->dbConfig = $dbConfig;
        } else {
            $this->dbConfig = config();
        }

        $dbConfig = $this->dbConfig['db_config'];

        foreach ($dbConfig as $key => $value) {
            if ($value['db_host'] == '' || $value['db_user'] == '' || $value['db_name'] == '') {
                throw new Exception('接数据库信息有误！请查看是否配置正确与完整');
            }

            $res[$key] = $this->openMysql($value);

            Mysqli::$options['dbConfig'][$key] = $value;

        }

        Mysqli::$link = $res;

    }

    //单例实例化 避免重复New暂用资源
    public static function getInstance($dbConfig = '')
    {
        if (is_null(self::$instance)) {
            self::$instance = new Mysqli($dbConfig);
        }

        return self::$instance;

    }

    /**
     * 连接数据库
     * @date   2017-03-19T16:18:28+0800
     * @author ChenMingjiang
     */
    public function openMysql($dbConfig = array())
    {
        try {
            $res = mysqli_connect($dbConfig['db_host'], $dbConfig['db_user'], $dbConfig['db_pwd'], $dbConfig['db_name']);

            mysqli_query($res, 'set names utf8mb4');
            mysqli_query($res, 'SET sql_mode =\'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION\'');

        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }

        if (!$res) {
            throw new Exception('连接数据库失败，可能数据库密码不对或数据库服务器出错！');
        }

        return $res;
    }

    public function getSql()
    {
        return $this->_sql;
    }

    /** 链接 */
    public function connect($id = '')
    {

        if (empty(Mysqli::$options['dbConfig'][$id]) && $id) {
            throw new Exception('connect:id:' . $id . ' 数据库配置信息不存在');
        }

        $this->linkId   = !$id ? reset(Mysqli::$link) : Mysqli::$link[$id];
        $dbConfig       = !$id ? reset(Mysqli::$options['dbConfig']) : Mysqli::$options['dbConfig'][$id];
        $this->tablepre = $dbConfig['db_prefix'];

        if (!$this->linkId) {
            throw new Exception('链接信息异常');
        }

        $this->build['database'] = $dbConfig['db_name'];
        $this->build['content']  = !$id ? key(Mysqli::$link) : $id;

        return $this;
    }

    /** 构造Sql初始化 */
    public function init()
    {

        $this->where     = '';
        $this->field     = '*';
        $this->limit     = '';
        $this->group     = '';
        $this->order     = '';
        $this->join      = '';
        $this->chilidSql = false;
        $this->having    = '';
        $this->connect();
    }

    /**
     * 数据表
     * @date   2017-03-19T16:18:23+0800
     * @author ChenMingjiang
     * @param  [type]                   $table  [description]
     * @param  string                   $table2 [description]
     * @return [type]                           [description]
     */
    public function table($table, $options = array())
    {

        $isTablepre = isset($options['is_tablepre']) ? $options['is_tablepre'] : true;
        $link       = isset($options['link']) ? $options['link'] : '';

        $this->init();

        if ($link) {
            $this->connect($link);
        }

        $this->table = parseName($table);
        if ($isTablepre) {
            $this->table = $this->tablepre != '' ? $this->table = $this->tablepre . $this->table : '';
        }

        $this->build['table'] = array('table' => $this->table, 'is_tablepre' => $isTablepre);

        return $this;
    }

    /**
     * 获取表名称
     * @date   2017-06-10T22:56:01+0800
     * @author ChenMingjiang
     * @param  [type]                   $table [description]
     * @return [type]                          [description]
     */
    public function tableName()
    {
        return $this->table;
    }

    /**
     * 获取表名称
     * @date   2017-06-10T22:56:01+0800
     * @author ChenMingjiang
     * @param  [type]                   $table [description]
     * @return [type]                          [description]
     */
    public function getTableName()
    {
        return $this->table;
    }

    /**
     * 判断是否存在该表
     * @date   2017-09-20T09:57:21+0800
     * @author ChenMingjiang
     * @return boolean                  [description]
     */
    public function isTable()
    {
        $this->_sql = 'SHOW TABLES LIKE \'dh_banner\'';
        $result     = (bool) mysqli_num_rows($this->query());
        return $result;
    }

    private function addFieldTag($field)
    {
        if (strripos($field, '`') === false && $field != '_string') {
            $field = strripos($field, '.') !== false ? str_replace('.', '.`', $field) . '`' : '`' . $field . '`';
        }

        return $field;
    }

    /**
     * 查询条件
     * @date   2018-06-29T15:50:51+0800
     * @author ChenMingjiang
     * @param  [type]                   $where [description]
     * @param  [type]                   $value [description]
     * @param  [type]                   $exp   [description]
     * @return [type]                          [description]
     */
    public function where($where, $value = null, $exp = null)
    {

        $expRule[0] = ['>', '<', '>=', '<=', '!=', 'like', '<>'];
        $expRule[1] = ['in', 'not in', 'IN', 'NOT IN'];
        $expRule[2] = ['instr', 'INSTR'];
        $expRule[3] = ['between', 'BETWEEN'];
        $expRule[4] = ['or', 'OR'];

        if (!$where) {
            return $this;
        }

        $newWhere = '';

        if ($value !== null && $exp !== null) {
            $map[$where] = [$value, $exp];
            $where       = $map;
        } elseif ($value !== null && $exp === null) {
            $map[$where] = $value;
            $where       = $map;
        }

        if (is_array($where)) {
            foreach ($where as $mapField => $v) {

                $mapField = $this->addFieldTag($mapField);

                if (is_array($v)) {

                    list($mapExp, $mapValue) = $v;

                    if (in_array($mapExp, $expRule[0])) {
                        $newWhere .= $mapField . '  ' . $mapExp . ' \'' . $mapValue . '\' AND ';
                    } elseif (in_array($mapExp, $expRule[1])) {
                        if (!$mapValue) {
                            $newWhere .= $mapField . '  ' . $mapExp . ' (\'\') AND ';
                        } else {
                            if (!is_array($mapValue) && stripos($mapValue, ',') !== false) {
                                $mapValue = explode(',', $mapValue);
                            }
                            $mapValue      = is_array($mapValue) ? $mapValue : (array) $mapValue;
                            $commonInValue = '';
                            foreach ($mapValue as $inValue) {
                                $commonInValue .= '\'' . $inValue . '\',';
                            }
                            $commonInValue = substr($commonInValue, 0, -1);
                            $newWhere .= $mapField . '  ' . $mapExp . ' (' . $commonInValue . ') AND ';
                        }
                    } elseif (in_array($mapExp, $expRule[2])) {
                        $newWhere .= $mapExp . '(' . $mapField . ',\'' . $mapValue . '\') AND ';
                    } elseif (in_array($mapExp, $expRule[3])) {

                        $newWhere .= $mapField . '  ' . $mapExp . ' \'' . $mapValue . '\' AND \'' . $v[2] . '\' AND ';
                    } elseif (in_array($mapExp, $expRule[4])) {
                        $newWhere .= $mapField . ' = \'' . $mapValue . '\' OR ';
                    }
                } elseif ($mapField == '_string') {
                    $newWhere .= $v . ' AND ';
                } else {
                    $newWhere .= $mapField . ' = \'' . $v . '\' AND ';
                }
            }
        } else {
            $newWhere = $where;
        }

        if (stripos($this->where, 'WHERE') === false) {
            $this->where = ' WHERE ' . substr($newWhere, 0, -4);
        } else {
            $this->where .= ' AND ' . substr($newWhere, 0, -4);
        }

        $this->build['where'] = $where;
        return $this;
    }

    /**
     * 关联查询
     * @date   2017-06-10T22:52:34+0800
     * @author ChenMingjiang
     * @param  [type]                   $table [description]
     * @param  [type]                   $where [description]
     * @param  string                   $float [description]
     * @return [type]                          [description]
     */
    public function join($table, $where = '', $float = 'left')
    {
        if ($table == $this->table) {
            abort('表与关联表名字相同');
        }

        $where ?: $where = $this->table . '.id =' . $table . '.id';

        $this->join .= ' ' . $float . ' JOIN ' . $table . ' ON ' . $where;

        $this->build['join'] = array('table' => $table, 'where' => $where, 'float' => $float);

        return $this;
    }

    /**
     * 查询数量
     * @date   2017-03-19T16:18:13+0800
     * @author ChenMingjiang
     * @param  [type]                   $limit [description]
     * @return [type]                          [description]
     */
    public function limit($limit = 0, $pageSize = '')
    {
        $this->limit = ' LIMIT ' . $limit;
        if ($pageSize) {
            $this->limit = ' LIMIT ' . $limit . ',' . $pageSize;
        }

        $this->build['limit'] = array('limit' => $limit, 'pageSize' => $pageSize);

        return $this;
    }

    /**
     * 查询字段
     * @date   2017-03-19T16:18:09+0800
     * @author ChenMingjiang
     * @param  [type]                   $field [description]
     * @return [type]                          [description]
     */
    public function field($field = '*')
    {
        if (!$field) {
            $this->field = '*';
            return $this;
        }

        $newField = '';
        $field    = is_array($field) ? $field : explode(',', $field);
        foreach ($field as $k => $v) {
            if (stripos($v, 'as') === false && stripos($v, '*') === false && stripos($v, '`') === false && stripos($v, '.') === false && stripos($v, 'concat') === false) {
                $newField .= '`' . $v . '`,';
            } else {
                $newField .= $v . ',';
            }

        }

        $newField = substr($newField, 0, -1);

        $this->field = $newField;

        $this->build['field'] = $field;

        return $this;
    }

    public function group($value = '')
    {
        if (is_array($value)) {
            $i = 0;
            foreach ($value as $k => $v) {
                if ($i == 0) {
                    $newGroup .= $v;
                } else {
                    $newGroup .= "," . $v;
                }

                $i++;
            }
        } else {
            $newGroup = $value;
        }
        $this->group = ' GROUP BY ' . $newGroup;

        $this->build['group'] = $group;

        return $this;
    }

    public function order($value)
    {
        if (is_array($value)) {
            $i = 0;
            foreach ($value as $k => $v) {
                if ($i == 0) {
                    $newValue .= $v;
                } else {
                    $newValue .= "," . $v;
                }

                $i++;
            }
        } else {
            $newValue = $value;
        }

        if ($newValue) {
            $this->order = ' ORDER BY ' . $newValue;
        }

        $this->build['order'] = $this->order;

        return $this;
    }

    /**
     * 判断是否存在该数据表
     * @date   2017-03-19T16:18:01+0800
     * @author ChenMingjiang
     * @param  string                   $table [description]
     * @return [type]                          [description]
     */
    public function existsTbale($table = '')
    {
        if ($table == '') {$table = $this->table;}
        $sql = "SELECT COUNT(*) as total  FROM information_schema.TABLES WHERE TABLE_NAME='$table'";
        $t   = mysqli_fetch_array(mysqli_query($this->linkId, $sql));
        if ($t['total'] == 0) {return false;}
        return true;
    }

    /** 查询数据表信息 */
    public function fieldStatus($field)
    {

        $fieldArray = stripos($field, ',') !== false ? explode(',', $field) : (array) $field;

        $this->_sql = "SHOW TABLE STATUS WHERE NAME = '{$this->table}'";
        $result     = $this->query();

        $this->total = mysqli_num_rows($result);
        if ($this->total == 0) {return false;}

        while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
            $dataRow = $row;
        }

        if ($fieldArray) {
            foreach ($fieldArray as $key => $value) {
                if (isset($dataRow[$value])) {
                    $data[$value] = $dataRow[$value];
                }
            }
        } else {
            $data = $dataRow;
        }

        return $data;
    }

    /**
     * 查询表字段名
     * @date   2017-03-19T16:14:45+0800
     * @author ChenMingjiang
     * @return [type]                   [description]
     */
    public function getField($field = 'column_name')
    {
        $this->where = ' where table_name = ' . "'" . $this->table . "'";
        $this->field = $field;
        $this->table = 'information_schema.columns';

        $this->_sql = "select " . $this->field . " from " . $this->table . $this->where;
        $result     = $this->query();

        while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
            if (stripos($field, ',') === false) {
                $data[] = $row['column_name'];
            } else {
                $data[] = $row;
            }

        }

        return $data;
    }

    /**
     * 统计总数
     * @date   2017-06-14T11:09:55+0800
     * @author ChenMingjiang
     * @return [type]                   [description]
     */
    public function count($field = '')
    {

        $this->limit(1);
        if ($field) {
            $sql = 'SELECT   ' . $field . '  FROM ' . $this->table;
        } else {
            $sql = 'SELECT  COUNT(*) AS  t  FROM ' . $this->table;
        }

        if ($this->join) {
            $sql .= $this->join;
        }

        if ($this->where != '') {
            $sql .= $this->where;
        }
        if ($this->group != '') {
            $sql .= $this->group;
        }

        $result = $this->query($sql);

        $this->total = $result ? mysqli_num_rows($result) : 0;

        if ($field) {
            return (int) $this->total;
        }

        $data = mysqli_fetch_array($result, MYSQLI_NUM);

        return $data[0];
    }

    /**
     * 子查询 如果开启 则直接返回sql
     * @date   2017-11-22T00:38:42+0800
     * @author ChenMingjiang
     * @param  boolean                  $value [description]
     * @return [type]                          [description]
     */
    public function childSql($value = false)
    {
        $this->chilidSql = $value;

        return $this;
    }

    /**
     * 子查询table
     * @date   2017-11-22T00:45:38+0800
     * @author ChenMingjiang
     * @param  [type]                   $table [description]
     * @return [type]                          [description]
     */
    public function childSqlQuery($table)
    {
        $this->table = '(' . $table . ') as child';

        return $this;
    }

    /**
     * hvaing
     * @date   2017-11-22T01:18:55+0800
     * @author ChenMingjiang
     * @param  [type]                   $field [description]
     * @return [type]                          [description]
     */
    public function having($field)
    {
        $this->having = ' HAVING ' . $field;
        return $this;
    }

    /**
     * 获取单个字段内容
     * @date   2018-04-06T21:35:17+0800
     * @author ChenMingjiang
     * @param  [type]                   $field [description]
     * @return [type]                          [description]
     */
    public function value($field)
    {
        if (!$this->table) {
            throw new Exception('请选择数据表');
        }

        $this->limit(1);

        $this->field($field);

        $this->_sql = 'SELECT ' . $this->field . ' FROM ' . $this->table;

        empty($this->join) ?: $this->_sql .= $this->join;
        empty($this->where) ?: $this->_sql .= $this->where;
        empty($this->group) ?: $this->_sql .= $this->group;
        empty($this->having) ?: $this->_sql .= $this->having;
        empty($this->order) ?: $this->_sql .= $this->order;
        empty($this->limit) ?: $this->_sql .= $this->limit;

        $result = $this->query();
        if (!$result) {
            throw new Exception('查询sql错误:' . $this->_sql);
        }

        //获取记录条数
        $this->total = mysqli_num_rows($result);
        if ($this->total == 0) {return false;}

        while ($row = mysqli_fetch_array($result, MYSQLI_NUM)) {
            $this->field = str_replace('`', '', $this->field);

            $data = $row[0];
        }

        if (empty($data)) {
            return false;
        }

        return $data;
    }

    /**
     * 查询单条/多条信息
     * @date   2017-11-22T00:35:19+0800
     * @author ChenMingjiang
     * @param  string                   $value   [array:查询数据 one:查询单条单个字段内容]
     * @param  boolean                  $isArray [单字段 数组模式]
     * @param  boolean                  $chilid  [子查询]
     * @return [type]                            [description]
     */
    public function find($value = '', $isArray = false)
    {

        if (!$this->table) {
            throw new Exception('请选择数据表');
        }

        if (!$this->limit && $value != 'array' && !$isArray) {
            $this->limit(1);
        }

        $this->_sql = 'SELECT ' . $this->field . ' FROM ' . $this->table;

        empty($this->join) ?: $this->_sql .= $this->join;
        empty($this->where) ?: $this->_sql .= $this->where;
        empty($this->group) ?: $this->_sql .= $this->group;
        empty($this->having) ?: $this->_sql .= $this->having;
        empty($this->order) ?: $this->_sql .= $this->order;
        empty($this->limit) ?: $this->_sql .= $this->limit;

        //开启子查询直接返回sql
        if ($this->chilidSql) {
            return $this->_sql;
        }

        $result = $this->query();
        if (!$result) {
            throw new Exception('查询sql错误:' . $this->_sql);
        }

        //获取记录条数
        $this->total = mysqli_num_rows($result);
        if ($this->total == 0) {
            if ($value == 'array') {
                return array();
            } elseif ($value == 'one' && !$isArray) {
                return null;
            } elseif ($value == 'one' && $isArray) {
                return array();
            } else {
                return '';
            }
        }

        //单个字段模式
        if ($value == 'one' && !$isArray) {
            $row = mysqli_fetch_array($result, MYSQLI_NUM);
            if (empty($row)) {
                return null;
            }

            return $row[0];
        }
        //单字段数组模式
        elseif ($value == 'one' && $isArray) {
            while ($row = mysqli_fetch_array($result, MYSQLI_NUM)) {
                $this->field = str_replace('`', '', $this->field);

                $isArray !== true ? $data[$row[1]] = $row[0] : $data[] = $row[0];
            }

            if (empty($data)) {
                return array();
            }

            return $data;
        }
        //三维数组模式
        elseif ($this->total > 1 || $value == 'array') {

            while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
                $data[] = $row;
            }

            for ($i = 0, $n = count($data); $i < $n; $i++) {
                if (is_array($data[$i])) {
                    foreach ($data[$i] as $key => $value) {
                        $datas[$i][$key] = $value;
                    }
                }
            }

            return $datas;
        }
        //二维数组模式
        else {
            $row = mysqli_fetch_array($result, MYSQLI_ASSOC);

            foreach ($row as $key => $value) {
                $data[$key] = $value;
            }

            return $data;
        }
    }

    public function select()
    {

    }

    /**
     * 添加
     * @date   2017-03-19T16:19:43+0800
     * @author ChenMingjiang
     * @param  string                   $data [description]
     */
    public function add($data = '')
    {
        $newField = '';
        $data     = is_array($data) ? $data : explode(',', $data);

        foreach ($data as $k => $v) {
            $v = str_replace('\'', '\\\'', $v);
            $v = str_replace('\\', '\\/\\', $v);
            $newField .= $this->addFieldTag($k) . ' = \'' . $v . '\',';
        }

        $newField    = substr($newField, 0, -1);
        $this->field = $newField;

        $this->_sql = 'INSERT INTO `' . $this->table . '` SET ' . $this->field;
        $result     = $this->query();
        if ($result) {
            $result = max(mysqli_insert_id($this->linkId), 1);
        }
        return $result;
    }

    /**
     * 添加多条信息
     * @date   2017-09-19T15:45:40+0800
     * @author ChenMingjiang
     */
    public function addAll($data = array())
    {
        foreach ($data as $key => $value) {
            $result = $this->add($value);
        }
        return $result;
    }

    /**
     * 修改保存
     * @date   2017-03-19T16:20:24+0800
     * @author ChenMingjiang
     * @param  string                   $data [description]
     * @return [type]                         [description]
     */
    public function save($data = '', $value = null)
    {
        if (!$this->where) {
            return false;
        }

        $newField = '';
        if ($value !== null && !is_array($data)) {
            $value    = str_replace('\\', '\\\\', $value);
            $value    = str_replace('\'', '\\\'', $value);
            $newField = '`' . $data . '`=\'' . $value . '\'';
        } else {
            if (is_array($data)) {
                foreach ($data as $k => $v) {
                    if (is_array($v)) {
                        $v[0] = strtolower($v[0]);
                        if ($v[0] == 'add') {
                            $newField .= '`' . $k . '`  = `' . $k . '` + ' . $v[1] . ',';
                        } elseif ($v[0] == 'less') {
                            $newField .= '`' . $k . '`  = `' . $k . '` - ' . $v[1] . ',';
                        } elseif ($v[0] == 'concat') {
                            $newField .= '`' . $k . '`  = CONCAT(`' . $k . '`,\'\',\'' . str_replace('\'', '\\\'', $v[1]) . '\'),';
                        }
                    } else {
                        $v = str_replace('\\', '\\\\', $v);
                        $v = str_replace('\'', '\\\'', $v);
                        $newField .= '`' . $k . '`=\'' . $v . '\',';
                    }
                }
                $newField = substr($newField, 0, -1);
            } else {
                $newField = $field;
            }
        }

        $this->field = $newField;

        $this->_sql = 'UPDATE ' . $this->table . ' SET ' . $this->field;
        $this->_sql .= $this->where ? $this->where : '';

        $result = $this->query();
        return $result;
    }

    /**
     * 删除数据
     * @date   2017-03-19T16:20:32+0800
     * @author ChenMingjiang
     * @return [type]                   [description]
     */
    public function delete()
    {
        if (!$this->where) {
            return false;
        }

        $this->_sql = 'DELETE FROM ' . $this->table . $this->where;
        $result     = $this->query();
        return $result;
    }

    //开启事务
    public function startTrans()
    {
        mysqli_query($this->linkId, 'begin');
        /*$this->query('begin');*/
        return true;
    }

    //回滚事务
    public function rollback()
    {
        mysqli_query($this->linkId, 'rollback');
        /*$this->query('rollback');*/
        return true;
    }

    //提交事务
    public function commit()
    {
        mysqli_query($this->linkId, 'commit');
        /* $this->query('commit');*/
        return true;
    }

    /**
     * 执行
     * @date   2017-03-19T16:20:36+0800
     * @author ChenMingjiang
     * @param  [type]                   $sql [description]
     * @return [type]                        [description]
     */
    public function query($sql = '')
    {
        !$sql ?: $this->_sql = $sql;
        $_beginTime          = microtime(true);
        $result              = mysqli_query($this->linkId, $this->_sql);
        $_endTime            = microtime(true);

        $this->sqlInfo['time'] = $_endTime - $_beginTime; //获取执行时间
        $this->sqlInfo['sql']  = $this->_sql;

        if ($result) {
            Trace::addSqlInfo($this->sqlInfo); //存入调试信息中
            $this->addSqlLog(); //存入文件中
            return $result;
        } else {

            //存入文件
            if ($this->dbConfig['db_save_log']) {
                $this->addErrorSqlLog();
            }

            if (config('debug')) {
                throw new Exception('SQL ERROR : ' . $this->_sql);
            }

            if (config('trace')) {
                Trace::addErrorInfo('[SQL ERROR] ' . $this->_sql);
            }

            return false;

        }

    }

    public function addErrorSqlLog()
    {
        //如果没有写入权限尝试修改权限 如果修改后还是失败 则跳过
        if (isWritable(DATA_PATH)) {
            $path = DATA_PATH . 'sql_log' . DS . $this->build['database'] . DS;
            is_dir($path) ? '' : mkdir($path, 0755, true);
            $path .= 'error_' . date('Y_m_d_H', TIME) . '.text';

            $time = &$this->sqlInfo['time'];
            $info = '------ ' . $time . ' | ' . date('Y-m-d H:i:s', TIME) . ' | ip:' . getIP() . ' | ';
            $info .= 'Url:' . URL;
            //$info .= ' | Controller:' . CONTROLLER;
            //$info .= ' | Action:' . ACTION;
            $info .= PHP_EOL;

            $content = $this->sqlInfo['sql'] . ';' . PHP_EOL . '来源：' . $_SERVER['HTTP_USER_AGENT'] . PHP_EOL . '--------------' . PHP_EOL;
            $file    = fopen($path, 'a');
            fwrite($file, $content . $info . PHP_EOL);
            fclose($file);
        }
    }

    /**
     * 保存sql记录
     * @date   2017-10-18T13:45:16+0800
     * @author ChenMingjiang
     * @return [type]                   [description]
     */
    public function addSqlLog()
    {
        //如果没有写入权限尝试修改权限 如果修改后还是失败 则跳过
        if (!isWritable(DATA_PATH)) {
            return false;
        }

        // define('CONTROLLER', '未知'); //文件后缀
        // define('ACTION', '未知'); //文件后缀

        //创建文件夹
        is_dir(DATA_PATH . 'sql_log') ? '' : mkdir(DATA_PATH . 'sql_log', 0755, true);

        $time = &$this->sqlInfo['time'];
        $info = '------ ' . $time . ' | ' . date('Y-m-d H:i:s', TIME) . ' | ip:' . getIP() . ' | ';
        $info .= 'Url:' . URL;
        //$info .= ' | Controller:' . CONTROLLER;
        //$info .= ' | Action:' . ACTION;
        $info .= PHP_EOL;

        //记录sql
        if ($this->sqlInfo && $this->dbConfig['db_save_log']) {
            $path = DATA_PATH . 'sql_log' . DS . $this->build['database'] . DS;
            is_dir($path) ? '' : mkdir($path, 0755, true);
            if (stripos($this->sqlInfo['sql'], 'select') === 0) {

                $path .= 'select_' . date('Y_m_d_H', TIME) . '.text';
                $content = $this->sqlInfo['sql'] . PHP_EOL;

            } elseif (stripos($this->sqlInfo['sql'], 'update') === 0) {

                $path .= 'update_' . date('Y_m_d_H', TIME) . '.text';
                $content = $this->sqlInfo['sql'] . ';' . PHP_EOL;

            } elseif (stripos($this->sqlInfo['sql'], 'delete') === 0) {

                $path .= 'delete_' . date('Y_m_d_H', TIME) . '.text';
                $content = $this->sqlInfo['sql'] . ';' . PHP_EOL;

            } elseif (stripos($this->sqlInfo['sql'], 'insert') === 0) {

                $path .= 'add_' . date('Y_m_d_H', TIME) . '.text';
                $content = $this->sqlInfo['sql'] . ';' . PHP_EOL;

            } else {

                $path .= 'other_' . date('Y_m_d_H', TIME) . '.text';
                $content = $this->sqlInfo['sql'] . ';' . PHP_EOL;
            }

            //记录慢sql
            if ($this->dbConfig['db_slow_save_log']) {
                if ($this->sqlInfo['time'] > $this->dbConfig['db_slow_time']) {
                    $path .= 'slow_' . date('Y_m_d_H', TIME) . '.text';
                    $content = $this->sqlInfo['sql'] . PHP_EOL;
                }
            }

            $file = fopen($path, 'a');
            fwrite($file, $content . $info . PHP_EOL);
            fclose($file);
        }
    }
}


<?php
/**
 * Backtrace.
 *
 * @author @jaswrks
 * @copyright WebSharks™
 */
declare(strict_types=1);
namespace WebSharks\Core\Traits\Facades;

use WebSharks\Core\Classes;
use WebSharks\Core\Interfaces;
use WebSharks\Core\Traits;
#
use WebSharks\Core\Classes\Core\Error;
use WebSharks\Core\Classes\Core\Base\Exception;
#
use function assert as debug;
use function get_defined_vars as vars;

/**
 * Backtrace.
 *
 * @since 170126.77069
 */
trait Backtrace
{
    /**
     * @since 170126.77069 Backtrace utils.
     *
     * @param int|null $at  Starting at position.
     * @param bool     $all Include magic middle-men?
     *
     * @see Classes\Core\Utils\Backtrace::callers()
     */
    public static function backtraceCallers(int $at = null, bool $all = false)
    {
        return $GLOBALS[static::class]->Utils->©Backtrace->callers($at, $all, 1);
    }

    /**
     * @since 170126.77069 Backtrace utils.
     *
     * @param string|array $caller Caller(s).
     * @param int|null     $at     Starting at position.
     * @param bool         $all    Include magic middle-men?
     *
     * @see Classes\Core\Utils\Backtrace::hasCaller()
     */
    public static function hasBacktraceCaller($caller, int $at = null, bool $all = false)
    {
        return $GLOBALS[static::class]->Utils->©Backtrace->hasCaller($caller, $at, $all, 1);
    }
}


<?php
/**
 * Gravatar utilities.
 *
 * @author @jaswrks
 * @copyright WebSharks™
 */
declare(strict_types=1);
namespace WebSharks\Core\Classes\Core\Utils;

use WebSharks\Core\Classes;
use WebSharks\Core\Interfaces;
use WebSharks\Core\Traits;
#
use WebSharks\Core\Classes\Core\Error;
use WebSharks\Core\Classes\Core\Base\Exception;
#
use function assert as debug;
use function get_defined_vars as vars;

/**
 * Gravatar utilities.
 *
 * @since 160114 Initial release.
 */
class Gravatar extends Classes\Core\Base\Core
{
    /**
     * Get a user gravatar.
     *
     * @since 160114 Initial release.
     *
     * @param string $email  Email address.
     * @param int    $size   A valid gravatar size.
     * @param string $scheme Specific scheme?
     *
     * @return string Gravatar URL.
     */
    public function url(string $email, int $size = 64, string $scheme = ''): string
    {
        $md5 = md5(mb_strtolower($this->c::mbTrim($email)));
        return $this->c::setScheme('https://www.gravatar.com/avatar/'.$md5.'?s='.$size, $scheme);
    }
}