<?php

namespace Kurenai;

class Document implements Contracts\Document
{
    /**
     * The document body in Markdown format.
     *
     * @var string
     */
    protected $content;

    /**
     * An array of document metadata.
     *
     * @var array
     */
    protected $metadata = [];

    /**
     * A Kurenai\MarkdownParserInterface implementation.
     *
     * @var \Kurenai\Contracts\MarkdownParser
     */
    protected $markdownParser;

    /**
     * Instantiate an instance optionally injecting a markdown parser implementation.
     *
     * @param  \Kurenai\Contracts\MarkdownParser  $markdownParser
     */
    public function __construct(Contracts\MarkdownParser $markdownParser)
    {
        $this->markdownParser = $markdownParser;
    }

    /**
     * Set the document content in Markdown format.
     *
     * @param  string  $content
     *
     * @return $this
     */
    public function setContent(string $content)
    {
        $this->content = $content;

        return $this;
    }

    /**
     * Get the document content in Markdown format.
     *
     * @return string
     */
    public function getContent(): string
    {
        return $this->content;
    }

    /**
     * Get the document content in HTML format.
     *
     * @return string
     */
    public function getHtmlContent(): string
    {
        return $this->markdownParser->render($this->content);
    }

    /**
     * Set the document metadata using an array.
     *
     * @param  array  $metadata
     *
     * @return $this
     */
    public function set(array $metadata)
    {
        $this->metadata = $metadata;

        return $this;
    }

    /**
     * Add a piece of metadata to the document.
     *
     * @param  string  $key
     * @param  mixed   $value
     *
     * @return $this
     */
    public function add(string $key, $value)
    {
        $this->metadata[$key] = $value;

        return $this;
    }

    /**
     * Get metadata from the document.
     *
     * @param  string|null  $key
     *
     * @return mixed
     */
    public function get(?string $key = null)
    {
        if (is_null($key)) {
            return $this->metadata;
        }

        if (! array_key_exists($key, $this->metadata)) {
            return null;
        }

        return $this->metadata[$key];
    }
}


<?php
/**
 * OAuth server utils.
 *
 * @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;
#
use Slim\Http\Body;
#
use WebSharks\Core\Classes\Core\Request;
use WebSharks\Core\Classes\Core\Response;
#
use League\OAuth2\Server\ResourceServer;
use League\OAuth2\Server\AuthorizationServer;
#
use League\OAuth2\Server\Grant\AuthCodeGrant;
use League\OAuth2\Server\Grant\PasswordGrant;
use League\OAuth2\Server\Grant\RefreshTokenGrant;
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
use League\OAuth2\Server\Grant\ImplicitGrant;
#
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
#
use WebSharks\Core\Classes\Core\OAuth\Server\Entities\AccessTokenEntity;
use WebSharks\Core\Classes\Core\OAuth\Server\Entities\AuthCodeEntity;
use WebSharks\Core\Classes\Core\OAuth\Server\Entities\ClientEntity;
use WebSharks\Core\Classes\Core\OAuth\Server\Entities\RefreshTokenEntity;
use WebSharks\Core\Classes\Core\OAuth\Server\Entities\ScopeEntity;
use WebSharks\Core\Classes\Core\OAuth\Server\Entities\UserEntity;
#
use WebSharks\Core\Classes\Core\OAuth\Server\Repositories\AccessTokenRepository;
use WebSharks\Core\Classes\Core\OAuth\Server\Repositories\AuthCodeRepository;
use WebSharks\Core\Classes\Core\OAuth\Server\Repositories\ClientRepository;
use WebSharks\Core\Classes\Core\OAuth\Server\Repositories\RefreshTokenRepository;
use WebSharks\Core\Classes\Core\OAuth\Server\Repositories\ScopeRepository;
use WebSharks\Core\Classes\Core\OAuth\Server\Repositories\UserRepository;

/**
 * OAuth server utils.
 *
 * @since 17xxxx
 */
class OAuthServer extends Classes\Core\Base\Core
{
    /**
     * Authorization server.
     *
     * @since 17xxxx
     *
     * @type AuthorizationServer|null
     */
    protected $AuthorizationServer;

    /**
     * Resource server.
     *
     * @since 17xxxx
     *
     * @type ResourceServer|null
     */
    protected $ResourceServer;

    /**
     * Access token repo.
     *
     * @since 17xxxx
     *
     * @type AccessTokenRepository|null
     */
    protected $AccessTokenRepository;

    /**
     * Authorize.
     *
     * @since 17xxxx OAuth utils.
     *
     * @param Request  $Request  Request.
     * @param Response $Response Response.
     * @param callable $callback Callback.
     */
    public function authorize(Request $Request, Response $Response, callable $callback)
    {
        try { // Handles `/authorize` endpoint.
            if (!$this->c::isActiveSession()) {
                throw $this->c::issue('Requires active session.');
            }
            $_SESSION['oauth']   = $_SESSION['oauth'] ?? [];
            $AuthorizationServer = $this->authorizationServer();

            $_nonce_action       = 'oauth-authorize';
            $_nonce              = (string) $Request->getParam('_nonce');

            if ($_nonce && !$this->c::verifyNonce($_nonce_action, $_nonce)) {
                throw $this->c::issue('Unable to verify `_nonce`.');
            }
            if ($_nonce && !empty($_SESSION['oauth'][$_nonce]['AuthorizationRequest'])) {
                if (!($AuthorizationRequest = unserialize($_SESSION['oauth'][$_nonce]['AuthorizationRequest']))) {
                    throw $this->c::issue('Unable to unserialize `AuthorizationRequest`.');
                }
                $user_id  = (string) ($_SESSION['oauth']['user_id'] ?? '');
                $approved = $this->c::isTruthy($Request->getParam('approved'));

                if ($user_id && $approved) {
                    $UserEntity = $this->App->Di->get(UserEntity::class);
                    $UserEntity->setIdentifier($user_id);

                    $AuthorizationRequest->setUser($UserEntity);
                    $AuthorizationRequest->setAuthorizationApproved(true);

                    unset($_SESSION['oauth']);
                    $Response = $AuthorizationServer->completeAuthorizationRequest($AuthorizationRequest, $Response);
                    $Response->output(['exit' => true]);
                } else {
                    $_nonce                                             = $this->c::createNonce($_nonce_action);
                    $_SESSION['oauth'][$_nonce]['AuthorizationRequest'] = serialize($AuthorizationRequest);

                    $callback($AuthorizationRequest, $_nonce);
                    exit; // Stop here.
                }
            } else {
                $AuthorizationRequest                               = $AuthorizationServer->validateAuthorizationRequest($Request);
                $_nonce                                             = $this->c::createNonce($_nonce_action);
                $_SESSION['oauth'][$_nonce]['AuthorizationRequest'] = serialize($AuthorizationRequest);

                $callback($AuthorizationRequest, $_nonce);
                exit; // Stop here.
            }
        } catch (OAuthServerException $Exception) {
            unset($_SESSION['oauth']);
            $Response = $Exception->generateHttpResponse($Response);
            $Response->output(['exit' => true]);
            //
        } catch (\Throwable $Exception) {
            unset($_SESSION['oauth']);
            $Exception = new OAuthServerException($Exception->getMessage(), 0, 'unknown', 500);
            $Response  = $Exception->generateHttpResponse($Response);
            $Response->output(['exit' => true]);
        }
    }

    /**
     * Issue token.
     *
     * @since 17xxxx OAuth utils.
     *
     * @param Request  $Request  Request.
     * @param Response $Response Response.
     */
    public function issueToken(Request $Request, Response $Response)
    {
        try { // Handles `/(access|refresh)-token` endpoints.
            $AuthorizationServer = $this->authorizationServer();
            $Response            = $AuthorizationServer->respondToAccessTokenRequest($Request, $Response);
            $Response->output(['exit' => true]);
            //
        } catch (OAuthServerException $Exception) {
            $Response = $Exception->generateHttpResponse($Response);
            $Response->output(['exit' => true]);
            //
        } catch (\Throwable $Exception) {
            $Exception = new OAuthServerException($Exception->getMessage(), 0, 'unknown', 500);
            $Response  = $Exception->generateHttpResponse($Response);
            $Response->output(['exit' => true]);
        }
    }

    /**
     * Filter resource request.
     *
     * @since 17xxxx OAuth utils.
     *
     * @param Request  $Request  Request.
     * @param Response $Response Response.
     *
     * @return Request w/ `oauth_` attributes.
     *
     * If access token is valid the following attributes are set:
     * - `oauth_access_token_id` the access token identifier.
     * - `oauth_client_id` the client identifier.
     * - `oauth_user_id` the user identifier represented by the access token.
     * - `oauth_scopes` an array of string scope identifiers.
     */
    public function resourceRequest(Request $Request, Response $Response): Request
    {
        try { // Authenticate resource request.
            $ResourceServer = $this->resourceServer();
            return $Request = $ResourceServer->validateAuthenticatedRequest($Request);
            //
        } catch (OAuthServerException $Exception) {
            $Response = $Exception->generateHttpResponse($Response);
            $Response->output(['exit' => true]);
            //
        } catch (\Throwable $Exception) {
            $Exception = new OAuthServerException($Exception->getMessage(), 0, 'unknown', 500);
            $Response  = $Exception->generateHttpResponse($Response);
            $Response->output(['exit' => true]);
        }
    }

    /**
     * Access token repository.
     *
     * @since 17xxxx OAuth utils.
     *
     * @return AccessTokenRepository Repo.
     */
    protected function accessTokenRepository(): AccessTokenRepository
    {
        if ($this->AccessTokenRepository) {
            return $this->AccessTokenRepository;
        }
        return $this->AccessTokenRepository = $this->App->Di->get(AccessTokenRepository::class);
    }

    /**
     * Get resource server.
     *
     * @since 17xxxx OAuth utils.
     *
     * @return ResourceServer Server.
     */
    protected function resourceServer(): ResourceServer
    {
        if ($this->ResourceServer) {
            return $this->ResourceServer;
        }
        return $this->ResourceServer = new ResourceServer($this->accessTokenRepository(), $this->App->Config->©oauth['©public_key']);
    }

    /**
     * Get authorization server.
     *
     * @since 17xxxx OAuth utils.
     *
     * @return AuthorizationServer Server.
     */
    protected function authorizationServer(): AuthorizationServer
    {
        if ($this->AuthorizationServer) {
            return $this->AuthorizationServer;
        }
        $_one_month  = new \DateInterval('P1M');
        $_one_day    = new \DateInterval('P1D');
        $_10_minutes = new \DateInterval('PT10M');

        $AuthCodeRepository     = $this->App->Di->get(AuthCodeRepository::class);
        $ClientRepository       = $this->App->Di->get(ClientRepository::class);
        $RefreshTokenRepository = $this->App->Di->get(RefreshTokenRepository::class);
        $ScopeRepository        = $this->App->Di->get(ScopeRepository::class);
        $UserRepository         = $this->App->Di->get(UserRepository::class);

        $this->AuthorizationServer = new AuthorizationServer(
            $ClientRepository,
            $this->accessTokenRepository(),
            $ScopeRepository,
            $this->App->Config->©oauth['©private_key'],
            $this->App->Config->©oauth['©encryption_key']
        );
        $AuthCodeGrant          = new AuthCodeGrant($AuthCodeRepository, $RefreshTokenRepository, $_10_minutes);
        $PasswordGrant          = new PasswordGrant($UserRepository, $RefreshTokenRepository);
        $RefreshTokenGrant      = new RefreshTokenGrant($RefreshTokenRepository);
        $ClientCredentialsGrant = new ClientCredentialsGrant();
        $ImplicitGrant          = new ImplicitGrant($_one_day);

        $AuthCodeGrant->setRefreshTokenTTL($_one_month);
        $PasswordGrant->setRefreshTokenTTL($_one_month);
        $RefreshTokenGrant->setRefreshTokenTTL($_one_month);

        $this->AuthorizationServer->enableGrantType($AuthCodeGrant, $_one_day);
        $this->AuthorizationServer->enableGrantType($PasswordGrant, $_one_day);
        $this->AuthorizationServer->enableGrantType($RefreshTokenGrant, $_one_day);
        $this->AuthorizationServer->enableGrantType($ClientCredentialsGrant, $_one_day);
        $this->AuthorizationServer->enableGrantType($ImplicitGrant, $_one_day);

        return $this->AuthorizationServer;
    }
}


<?php
/**
 * Slashes.
 *
 * @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;

/**
 * Slashes.
 *
 * @since 150424 Initial release.
 */
class Slashes extends Classes\Core\Base\Core
{
    /**
     * Add slashes deeply.
     *
     * @since 150424 Initial release.
     *
     * @param mixed $value Any input value.
     *
     * @return string|array|object Slashed value.
     */
    public function add($value)
    {
        if (is_array($value) || is_object($value)) {
            foreach ($value as $_key => &$_value) {
                $_value = $this->add($_value);
            } // unset($_key, $_value);
            return $value;
        }
        $string = (string) $value;

        return addslashes($string);
    }

    /**
     * Remove slashes deeply.
     *
     * @since 150424 Initial release.
     *
     * @param mixed $value Any input value.
     *
     * @return string|array|object Unslashed value.
     */
    public function remove($value)
    {
        if (is_array($value) || is_object($value)) {
            foreach ($value as $_key => &$_value) {
                $_value = $this->remove($_value);
            } // unset($_key, $_value);
            return $value;
        }
        $string = (string) $value;

        return stripslashes($string);
    }
}


<?php
/**
 * Iterator 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;

/**
 * Iterator utilities.
 *
 * @since 150424 Initial release.
 */
class Iterators extends Classes\Core\Base\Core
{
    /**
     * Recursive array iterator.
     *
     * @since 150424 Array utils.
     *
     * @param array $array Input array.
     *
     * @return \RecursiveIteratorIterator Recursive iterator.
     */
    public function arrayRecursive(array $array): \RecursiveIteratorIterator
    {
        return new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array));
    }

    /**
     * Recursive dir/regex iterator.
     *
     * @since 150424 Initial release.
     *
     * @param string $dir   Directory to scan.
     * @param string $regex Regular expression.
     *
     * @throws Exception If either of the input parameters are empty.
     *
     * @return \RegexIterator Recursive dir/regex iterator.
     */
    public function dirRecursiveRegex(string $dir, string $regex = '/.+/u'): \RegexIterator
    {
        if (!$dir || !$regex) {
            throw $this->c::issue('Missing required `$dir` and/or `$regex` parameters.');
        }
        $DirIterator      = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_SELF | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS);
        $IteratorIterator = new \RecursiveIteratorIterator($DirIterator, \RecursiveIteratorIterator::CHILD_FIRST);
        $RegexIterator    = new \RegexIterator($IteratorIterator, $regex, \RegexIterator::MATCH, \RegexIterator::USE_KEY);

        return $RegexIterator;
    }
}


<?php
/**
 * Fatalities.
 *
 * @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;

/**
 * Fatalities.
 *
 * @since 170824.30708 Fatalities.
 */
class Fatalities extends Classes\Core\Base\Core
{
    /**
     * Die (general).
     *
     * @since 170824.30708 Fatalities.
     *
     * @param string|int $message Message (or code).
     * @param string     $slug    Custom error slug.
     * @param string     $code    Custom error status code.
     */
    public function die($message = '', string $slug = '', int $code = 500)
    {
        if (is_int($message)) {
            $code    = $message;
            $message = '';
        }
        $code    = $code ?: 500; // Default code.
        $slug    = $slug ?: $this->c::statusHeaderSlug($code);
        $message = $message ?: $this->c::statusHeaderMessage($code);

        $this->c::obEndCleanAll();
        $this->c::noCacheFlags();
        $this->c::noCacheHeaders();
        $this->c::statusHeader($code);

        if ($this->c::isAjax() || $this->c::isApi()) {
            header('content-type: application/json; charset=utf-8');
            exit(json_encode([
                'success' => false,
                'error'   => [
                    'code'    => $code,
                    'slug'    => $slug,
                    'message' => $message,
                ],
            ]));
        } else {
            header('content-type: text/html; charset=utf-8');
            exit($this->c::getTemplate('http/html/utils/fatality.php')->parse(compact('message')));
        }
    }

    /**
     * Die (invalid).
     *
     * @since 170824.30708 Fatalities.
     *
     * @param string $message Custom message.
     * @param string $slug    Custom error slug.
     * @param string $code    Custom error status code.
     */
    public function dieInvalid(string $message = '', string $slug = '', int $code = 400)
    {
        $this->die($message, $slug, $code);
    }

    /**
     * Die (forbidden).
     *
     * @since 170824.30708 Fatalities.
     *
     * @param string $message Custom message.
     * @param string $slug    Custom error slug.
     * @param string $code    Custom error status code.
     */
    public function dieForbidden(string $message = '', string $slug = '', int $code = 403)
    {
        $this->die($message, $slug, $code);
    }

    /**
     * Die (as an echo).
     *
     * @since 170824.30708 Fatalities.
     */
    public function dieEcho()
    {
        $url     = $this->c::currentUrl();
        $method  = $this->c::currentMethod();
        $headers = $_h = $this->c::currentHeaders();

        $query = $this->c::unslash($_GET);
        $vars  = $_r  = $this->c::unslash($_REQUEST);

        $raw = (string) file_get_contents('php://input');

        if (mb_stripos($_h['content-type'] ?? '', '/json')) {
            $json = json_decode($raw) ?: (object) [];
        }
        $code    = (int) ($_r['status'] ?? 200);
        $slug    = $this->c::statusHeaderSlug($code);
        $message = $this->c::statusHeaderMessage($code);

        $response = json_encode($this->c::removeNulls([
            'success' => $code < 400,

            'error'   => $code >= 400 ? [
                'code'    => $code,
                'slug'    => $slug,
                'message' => $message,
            ] : null,

            'echo'    => [
                'time' => time(),

                'method' => $method,
                'url'    => $url,
                'query'  => $query,

                'headers' => $headers,
                'data'    => [
                    'vars' => $vars,
                    'json' => $json ?? null,
                    'raw'  => $raw,
                ],
                'status' => [
                    'code'    => $code,
                    'slug'    => $slug,
                    'message' => $message,
                ],
            ],
        ]), JSON_PRETTY_PRINT);

        $this->c::obEndCleanAll();
        $this->c::noCacheFlags();
        $this->c::noCacheHeaders();
        $this->c::statusHeader($code);

        if ($this->c::isAjax() || $this->c::isApi()) {
            header('content-type: application/json; charset=utf-8');
            exit($response);
        } else {
            header('content-type: text/plain; charset=utf-8');
            exit($response);
        }
    }
}


<?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;

/**
 * Google Map API helper.
 *
 * @author GeLo <geloen.eric@gmail.com>
 */
class ApiHelper extends AbstractHelper
{
    /** @var boolean */
    protected $loaded;

    /**
     * Creates a Google Map API helper.
     */
    public function __construct()
    {
        parent::__construct();

        $this->loaded = false;
    }

    /**
     * Checks/Sets if the API is already loaded.
     *
     * @param boolean $loaded TRUE if the API is already loaded else FALSE.
     *
     * @return boolean TRUE if the API is already loaded else FALSE.
     */
    public function isLoaded($loaded = null)
    {
        if ($loaded !== null) {
            $this->loaded = (bool) $loaded;
        }

        return $this->loaded;
    }

    /**
     * Renders the API.
     *
     * @param string  $language  The language.
     * @param array   $libraries Additionnal libraries.
     * @param string  $callback  A JS callback.
     * @param string  $api_key   Google Browser API key for the map
     *
     * @return string The HTML output.
     */
    public function render(
        $language = 'en',
        array $libraries = array(),
        $callback = null,
        $api_key = null
    )
    {
        $otherParameters = array();

        if (!empty($libraries)) {
            $otherParameters['libraries'] = implode(',', $libraries);
        }

        $otherParameters['language'] = $language;

        // Google Maps API warning: NoApiKeys https://developers.google.com/maps/documentation/javascript/error-messages#no-api-keys
        if (!is_null($api_key)) {
            $otherParameters['key'] = $api_key;
        }

        $this->jsonBuilder
            ->reset()
            ->setValue('[other_params]', urldecode(http_build_query($otherParameters)));

        if ($callback !== null) {
            $this->jsonBuilder->setValue('[callback]', $callback, false);
        }

        $callbackFunction = 'load_ivory_google_map_api';
        $url = sprintf('//www.google.com/jsapi?callback=%s', $callbackFunction);
        $loader = sprintf('google.load("maps", "3", %s);', $this->jsonBuilder->build());

        $output = array();
        $output[] = '<script type="text/javascript">'.PHP_EOL;
        $output[] = sprintf('function %s () { %s };'.PHP_EOL, $callbackFunction, $loader);
        $output[] = '</script>'.PHP_EOL;
        $output[] = sprintf('<script type="text/javascript" src="%s"></script>'.PHP_EOL, $url);

        $this->loaded = true;

        return implode('', $output);
    }
}


<?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\DistanceMatrix;

/**
 * A distance matrix response wraps the distance results & the response status.
 *
 * @author GeLo <geloen.eric@gmail.com>
 * @author Tyler Sommer <sommertm@gmail.com>
 */
class DistanceMatrixResponseRow
{
    /** @var array */
    protected $elements;

    /**
     * Create a distance matrix response row.
     *
     * @param array $elements The row elements.
     */
    public function __construct(array $elements)
    {
        $this->setElements($elements);
    }

    /**
     * Gets the distance matrix row elements.
     *
     * @return array The row elements.
     */
    public function getElements()
    {
        return $this->elements;
    }

    /**
     * Sets the distance matrix row elements.
     *
     * @param array $elements The row elements.
     */
    public function setElements(array $elements)
    {
        $this->elements = array();

        foreach ($elements as $element) {
            $this->addElement($element);
        }
    }

    /**
     * Add a distance matrix element.
     *
     * @param \Ivory\GoogleMap\Services\DistanceMatrix\DistanceMatrixResponseElement $element The element to add.
     */
    public function addElement(DistanceMatrixResponseElement $element)
    {
        $this->elements[] = $element;
    }
}


<?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\ControlPosition;
use Ivory\GoogleMap\Exception\HelperException;

/**
 * Control position helper.
 *
 * @author GeLo <geloen.eric@gmail.com>
 */
class ControlPositionHelper
{
    /**
     * Renders a control position.
     *
     * @param string $controlPosition The control position.
     *
     * @throws \Ivory\GoogleMap\Exception\HelperException If the control position is not valid.
     *
     * @return The JS output.
     */
    public function render($controlPosition)
    {
        switch ($controlPosition) {
            case ControlPosition::BOTTOM_CENTER:
            case ControlPosition::BOTTOM_LEFT:
            case ControlPosition::BOTTOM_RIGHT:
            case ControlPosition::LEFT_BOTTOM:
            case ControlPosition::LEFT_CENTER:
            case ControlPosition::LEFT_TOP:
            case ControlPosition::RIGHT_BOTTOM:
            case ControlPosition::RIGHT_CENTER:
            case ControlPosition::RIGHT_TOP:
            case ControlPosition::TOP_CENTER:
            case ControlPosition::TOP_LEFT:
            case ControlPosition::TOP_RIGHT:
                return sprintf('google.maps.ControlPosition.%s', strtoupper($controlPosition));
            default:
                throw HelperException::invalidControlPosition();
        }
    }
}


<?php
/**
 * IP address.
 *
 * @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;

/**
 * IP address utils.
 *
 * @since 150424 Initial release.
 */
class Ip extends Classes\Core\Base\Core
{
    /**
     * Current visitor's IP address.
     *
     * @since 150424 Initial release.
     *
     * @return string Current visitor's IP address.
     *
     * @internal Supports both IPv4 & IPv6 addresses.
     */
    public function current(): string
    {
        if (($ip = &$this->cacheKey(__FUNCTION__)) !== null) {
            return $ip; // Already cached this.
        }
        if ($this->c::isCli()) {
            throw $this->c::issue('Not possible in CLI mode.');
        } // @TODO Try detecting IP address in CLI mode.

        $sources = [
            'HTTP_CF_CONNECTING_IP',
            'HTTP_CLIENT_IP',
            'HTTP_X_FORWARDED_FOR',
            'HTTP_X_FORWARDED',
            'HTTP_X_CLUSTER_CLIENT_IP',
            'HTTP_FORWARDED_FOR',
            'HTTP_FORWARDED',
            'HTTP_VIA',
            'REMOTE_ADDR',
        ];
        foreach ($sources as $_source) {
            if (!empty($_SERVER[$_source]) && is_string($_SERVER[$_source])) {
                if (($_valid_public_ip = $this->getValidPublicFrom($_SERVER[$_source]))) {
                    return $ip = $_valid_public_ip; // IPv4 or IPv6 address.
                }
            } // unset($_valid_public_ip); // Housekeeping.
        } // unset($_source); // Housekeeping.

        if (!empty($_SERVER['REMOTE_ADDR'])) {
            return $ip = mb_strtolower((string) $_SERVER['REMOTE_ADDR']);
        }
        return $ip = 'unknown'; // Not possible.
    }

    /**
     * Region code for IP address.
     *
     * @since 150424 Initial release.
     *
     * @param string $ip An IP address.
     *
     * @return string Region code for IP address.
     */
    public function region(string $ip): string
    {
        if (($geo = $this->geoData($ip))) {
            return $geo->region;
        }
        return ''; // Failure.
    }

    /**
     * Country code for IP address.
     *
     * @since 150424 Initial release.
     *
     * @param string $ip An IP address.
     *
     * @return string Country code for IP address.
     */
    public function country(string $ip): string
    {
        if (!empty($_SERVER['HTTP_CF_IPCOUNTRY']) // Save time.
            && $ip === $_SERVER['HTTP_CF_CONNECTING_IP'] ?? '' && $ip === $this->current()
            && mb_strlen((string) $_SERVER['HTTP_CF_IPCOUNTRY']) === 2) {
            return (string) $_SERVER['HTTP_CF_IPCOUNTRY'];
        } // This saves time by using CloudFlare.

        if (($geo = $this->geoData($ip))) {
            return $geo->country;
        }
        return ''; // Failure.
    }

    /**
     * Geo data for IP address.
     *
     * @since 150424 Initial release.
     *
     * @param string $ip An IP address.
     *
     * @return \StdClass|bool Geo data for IP.
     */
    protected function geoData(string $ip)
    {
        # Valid IP. Do we have one?

        if (!($ip = $this->c::mbTrim(mb_strtolower($ip)))) {
            return false; // Not possible.
        }
        # Check object cache. Did we already do this?

        if (($geo = &$this->cacheKey(__FUNCTION__, $ip)) !== null) {
            return $geo; // Already cached this.
        }
        # Check filesystem cache. Did we already do this?

        if (!$this->App->Config->©fs_paths['©cache_dir']) {
            throw $this->c::issue('Missing cache directory.');
        }
        $ip_sha1               = sha1($ip); // Needed below.
        $cache_dir             = $this->App->Config->©fs_paths['©cache_dir'].'/ip-geo-data/'.$this->c::sha1ModShardId($ip_sha1, true);
        $cache_dir_permissions = $this->App->Config->©fs_permissions['©transient_dirs'];
        $cache_file            = $cache_dir.'/'.$ip_sha1.'.json';

        if (is_file($cache_file) && filemtime($cache_file) >= strtotime('-30 days')) {
            $cache      = json_decode(file_get_contents($cache_file));
            return $geo = is_object($cache) ? $cache : false;
        }
        # Initialize request-related variables.

        $region = $country = ''; // Initialize.

        # Query geoPlugin service.

        $response = $this->c::remoteRequest(
            'GET::http://www.geoplugin.net/json.gp?ip='.urlencode($ip),
            ['max_con_secs' => 5, 'max_stream_secs' => 5]
        );
        if (!$response || !is_object($json = json_decode($response))) {
            // Fill object cache, but do not create a cache file.
            return $geo = false; // Connection failure.
        }
        # Parse response; i.e., try to fill geo data.

        if (!empty($json->geoplugin_regionCode)) {
            $region = (string) $json->geoplugin_regionCode;
            $region = $this->c::mbStrPad($region, 2, '0', STR_PAD_LEFT);
            $region = mb_strtoupper($region);
        }
        if (!empty($json->geoplugin_countryCode)) {
            $country = (string) $json->geoplugin_countryCode;
            $country = mb_strtoupper($country);
        }
        # Fill object cache; i.e., `\StdClass` or `false`.

        $geo = (object) compact('region', 'country');

        if (mb_strlen($geo->region) !== 2 || mb_strlen($geo->country) !== 2) {
            $geo = false; // Invalid (or insufficient) data.
        }
        # Cache geo data; i.e., `\StdClass` or `false`.

        if (!is_dir($cache_dir)) {
            mkdir($cache_dir, $cache_dir_permissions, true);
        }
        file_put_contents($cache_file, json_encode($geo));

        # Return, else `false` on failure.

        return $geo; // `\StdClass` or `false`.
    }

    /**
     * Looks for a valid/public IP address.
     *
     * @since 150424 Initial release.
     *
     * @param string $list_of_possible_ips A single IP, or a comma-delimited list of IPs.
     *
     * @return string A valid/public IP address (if one is found); else an empty string.
     */
    protected function getValidPublicFrom(string $list_of_possible_ips): string
    {
        if (!($list_of_possible_ips = $this->c::mbTrim($list_of_possible_ips))) {
            return ''; // Not possible; i.e., empty string.
        }
        foreach (preg_split('/[\s;,]+/u', $list_of_possible_ips, -1, PREG_SPLIT_NO_EMPTY) as $_possible_ip) {
            if (($_valid_public_ip = filter_var(mb_strtolower($_possible_ip), FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))) {
                return $_valid_public_ip; // A valid public IPv4 or IPv6 address.
            }
        } // unset($_possible_ip, $_valid_public_ip); // Housekeeping.

        return ''; // Default return value.
    }
}


<?php
/**
 * Request types.
 *
 * @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;

/**
 * Request types.
 *
 * @since 160531 Request types.
 */
class RequestType extends Classes\Core\Base\Core
{
    /**
     * Is AJAX?
     *
     * @since 160531 Request types.
     *
     * @param bool|null Is AJAX?
     */
    protected $is_ajax;

    /**
     * Is API?
     *
     * @since 160531 Request types.
     *
     * @param bool|null Is API?
     */
    protected $is_api;

    /**
     * REsT action.
     *
     * @since 160531 Request types.
     *
     * @param string|null REsT action.
     */
    protected $doing_rest_action;

    /**
     * Doing actions.
     *
     * @since 170623.50532 Action utils.
     *
     * @param array Actions.
     */
    protected $doing_actions;

    /**
     * Completed actions & data.
     *
     * @since 170623.50532 Action utils.
     *
     * @param array Actions & data.
     */
    protected $did_actions;

    /**
     * Is WordPress?
     *
     * @since 160606 Request types.
     *
     * @param bool Is WordPress?
     */
    protected $is_wordpress;

    /**
     * Class constructor.
     *
     * @since 160606 Request types.
     *
     * @param Classes\App $App Instance of App.
     */
    public function __construct(Classes\App $App)
    {
        parent::__construct($App);

        $this->doing_actions = [];
        $this->did_actions   = [];
        $this->is_wordpress  = $this->c::isWordPress();
    }

    /**
     * An AJAX request?
     *
     * @since 160531 Request types.
     *
     * @param bool|null $flag If setting flag.
     *
     * @return bool Is an AJAX request?
     */
    public function isAjax(bool $flag = null): bool
    {
        if (isset($flag)) {
            $this->is_ajax = $flag;
        }
        if (!isset($this->is_ajax) && $this->is_wordpress) {
            if (defined('DOING_AJAX') && DOING_AJAX) {
                $this->is_ajax = true; // Via WP constants.
            }
        }
        return (bool) $this->is_ajax;
    }

    /**
     * An API request?
     *
     * @since 160605 Request types.
     *
     * @param bool|null $flag If setting flag.
     *
     * @return bool Is an API request?
     */
    public function isApi(bool $flag = null): bool
    {
        if (isset($flag)) {
            $this->is_api = $flag;
        }
        if (!isset($this->is_api) && $this->is_wordpress) {
            if ((defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) || (defined('REST_REQUEST') && REST_REQUEST)) {
                $this->is_api = true; // Via WP constants.
            }
        }
        return (bool) $this->is_api;
    }

    /**
     * Doing a REsT action?
     *
     * @since 160531 Request types.
     *
     * @param string|null $action Only to set action.
     *
     * @return string The REsT action being done.
     */
    public function doingRestAction(string $action = null): string
    {
        if (isset($action)) {
            $this->doing_rest_action = $action;
            $this->doingAction($action, true);
        }
        return (string) $this->doing_rest_action;
    }

    /**
     * Doing an action?
     *
     * @since 170623.50532 Action utils.
     *
     * @param string     $action Action to add or check.
     * @param mixed|null $data   If adding action data.
     *
     * @return bool True if doing the action.
     */
    public function doingAction(string $action = '', bool $flag = null): bool
    {
        if ($action && isset($flag)) {
            $this->doing_actions[$action] = $flag;
        }
        if ($action) {
            return $this->doing_actions[$action] ?? false;
        } else {
            return !empty($this->doing_actions);
        }
    }

    /**
     * Did (completed) an action?
     *
     * @since 170623.50532 Action utils.
     *
     * @param string     $action Action to add or check.
     * @param mixed|null $data   If adding truthy action data.
     *
     * @return mixed|null Action data, else `null`.
     */
    public function didAction(string $action = '', $data = null)
    {
        if ($action && isset($data)) {
            if (!$data) { // Empty/falsey?
                throw $this->c::issue('Data must be truthy.');
            } // Avoid falsey data, which leads to confusion.
            $this->did_actions[$action] = $data;
        }
        if ($action) {
            return $this->did_actions[$action] ?? null;
        } else {
            return !empty($this->did_actions);
        }
    }
}


<?php

namespace Groovel\MailChimp\services;

use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Psr7\Request;

/**
 * Class MailChimpClient
 * @package Groovel\MailChimp\services
 *
 * Simple php client to request mailchimp API 3.0.
 */
class MailChimpClient
{
    /**
     * @var \GuzzleHttp\Client
     */
    private static $client;

    private static $api_version;

    private static $listid;

    /**
     * @param $api_token
     * @param $base_uri
     * @param array $options Accept same options as Guzzle constructor
     */
    public function __construct($api_token,$base_uri,$version,$list_id ,array $config = [])
    {
        self::$api_version=$version;
    	$config = array_merge($config, [
            'base_uri' => $base_uri,
            'auth'     => ['apikey', $api_token],
        ]);

    	self::$listid=$list_id;
        self::$client = new \GuzzleHttp\Client($config);
    }

    /**
     * Shortcut.
     *
     * @param $uri
     * @param array $options
     * @return mixed
     */
    private static function getData($uri, $options = [])
    {
         return json_decode(self::$client->get(self::$api_version.'/'.$uri, $options)->getBody()->getContents());
    }

  private static function send($action, $method = 'POST', $data = [], array $options = [])
	{
		$json = null;
		if (!empty($data))
		{
			$json = json_encode($data);
		}

		$headers = [];
		$request = new Request($method, $action, $headers, $json);
		$response = self::$client->send($request, $options);

		$body = $response->getBody();
		return json_decode($body->getContents());

	}

    /**
     * get list for a given list id
     * @param $listid
     * @return mixed
     */
    public static function getList($listid){
    	return self::getData('/lists/'.$listid);
    }

    /**
     * get list for a given list id
     * @param $listid
     * @return mixed
     */
    public static function getList(){
    	return self::getData('/lists/'.self::$listid);
    }

    /**
     * get list of emails members for a given list id
     * @param $listid
     * @return mixed
     */
    public static function getMembersEmail($listid){
    	return self::getData('/lists/'.$listid.'/members');
    }

    public static function getMembersEmail(){
    	return self::getData('/lists/'.self::$listid.'/members');
    }

    /**
     * subscribe new user to a given listid
     * @param $email
     * @param $listid
     * @return mixed
     */
    public static function subscribeNewUser($email,$listid,$confirm = false){
    	$data = [
    			'email_address' => $email,
    			'status' => ($confirm ? 'pending' : 'subscribed'),
    	];
    	if (!empty($merge_fields)) $data['merge_fields'] = $merge_fields;
    	$action = self::$api_version.'/lists/'.$listid.'/members/';

    	return self::send($action, 'POST', $data);

    }

    public static function subscribeNewUser($email,$confirm = false){
    	$data = [
    			'email_address' => $email,
    			'status' => ($confirm ? 'pending' : 'subscribed'),
    	];
    	if (!empty($merge_fields)) $data['merge_fields'] = $merge_fields;
    	$action = self::$api_version.'/lists/'.self::$listid.'/members/';

    	return self::send($action, 'POST', $data);

    }

    /**
     * get a user from a given listid with a given email
     * @param $email
     * @param $listid
     * @return mixed
     */

    public function getMember($email,$listid)
    {
    	$action =  "lists/".$listid."/members/" . md5($email);
   		return self::getData($action);
    }

    public function getMember($email)
    {
    	$action =  "lists/".self::$listid."/members/" . md5($email);
    	return self::getData($action);
    }

    /**
     * unsubscribe new user to a given listid
     * @param $email
     * @param $listid
     * @return mixed
     */
    public static function unsubscribeUser($email,$listid){
    	$data = [
    			'status' => 'unsubscribed'
    	];
    	if (!empty($merge_fields)) $data['merge_fields'] = $merge_fields;
    	$action = self::$api_version.'/lists/'.$listid.'/members/'. md5($email);

    	return self::send($action, 'PATCH', $data);

    }

    public static function unsubscribeUser($email){
    	$data = [
    			'status' => 'unsubscribed'
    	];
    	if (!empty($merge_fields)) $data['merge_fields'] = $merge_fields;
    	$action = self::$api_version.'/lists/'.self::$listid.'/members/'. md5($email);

    	return self::send($action, 'PATCH', $data);

    }

    /**
     * unsubscribe new user to a given listid
     * @param $email
     * @param $listid
     * @return mixed
     */
    public static function cleanUser($email,$listid){
    	$data = [
     			'status' => 'cleaned'
    	];
    	if (!empty($merge_fields)) $data['merge_fields'] = $merge_fields;
    	$action = self::$api_version.'/lists/'.$listid.'/members/'. md5($email);

    	return self::send($action, 'PATCH', $data);

    }

    public static function cleanUser($email){
    	$data = [
    			'status' => 'cleaned'
    	];
    	if (!empty($merge_fields)) $data['merge_fields'] = $merge_fields;
    	$action = self::$api_version.'/lists/'.self::$listid.'/members/'. md5($email);

    	return self::send($action, 'PATCH', $data);

    }

    /**
     * update status user to a given listid to a given email
     * @param $email
     * @param $listid
     * @param $status
     * @return mixed
     */
    public static function updateUser($listid,$email,$status)
    {
    	$data = [
    			'status' => $status
    	];
    	if (!empty($merge_fields)) $data['merge_fields'] = $merge_fields;
    	$action = self::$api_version.'/lists/'.$listid.'/members/'. md5($email);

    	return self::send($action, 'PATCH', $data);
    }

    public static function updateUser($email,$status)
    {
    	$data = [
    			'status' => $status
    	];
    	if (!empty($merge_fields)) $data['merge_fields'] = $merge_fields;
    	$action = self::$api_version.'/lists/'.self::$listid.'/members/'. md5($email);

    	return self::send($action, 'PATCH', $data);
    }

    /**
     * delete a user to a given listid to a given email
     * @param $email
     * @param $listid
     * @return mixed
     */
    public static function deleteUser($listid,$email)
    {
     	$email=md5($email);
    	$action = self::$api_version.'/lists/'.$listid.'/members/'. $email;
    	return self::send($action, 'DELETE', []);
    }

    public static function deleteUser($email)
    {
    	$email=md5($email);
    	$action = self::$api_version.'/lists/'.self::$listid.'/members/'. $email;
    	return self::send($action, 'DELETE', []);
    }

}


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

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;

/**
 * Tokenizer.
 *
 * @since 150424 Initial release.
 */
class Tokenizer extends Classes\Core\Base\Core implements Interfaces\UrlConstants
{
    /**
     * ID.
     *
     * @since 150424
     *
     * @type string
     */
    protected $id;

    /**
     * String.
     *
     * @since 150424
     *
     * @type string
     */
    protected $string;

    /**
     * Tokenize what?
     *
     * @since 150424
     *
     * @type array
     */
    protected $tokenize;

    /**
     * Behavioral args.
     *
     * @since 160720
     *
     * @type array
     */
    protected $args;

    /**
     * Tokens.
     *
     * @since 150424
     *
     * @type array
     */
    protected $tokens;

    /**
     * A shortcode tag name.
     *
     * @since 160720
     *
     * @type string|null
     */
    protected $shortcode_unautop_compat_tag_name;

    /**
     * Escape char patterns.
     *
     * @since 170211.63148
     *
     * @type array|null
     */
    protected static $m0_esc_no_vws;

    /**
     * Tokenize specific elements.
     *
     * @since 150424 Initial release.
     *
     * @param Classes\App $App      Instance of App.
     * @param string      $string   Input string.
     * @param array       $tokenize Specific elements.
     * @param array       $args     Any behavioral args.
     *
     * @return string The tokenized string.
     */
    public function __construct(Classes\App $App, string $string, array $tokenize, array $args = [])
    {
        parent::__construct($App);

        $this->id       = $this->c::uniqueId();
        $this->string   = $string; // String to tokenize.
        $this->tokenize = $tokenize; // What to tokenize.

        $default_args = [
            'shortcode_tag_names'               => [],
            'exclude_escaped_shortcode'         => false,
            'shortcode_unautop_compat'          => false,
            'shortcode_unautop_compat_tag_name' => '',
        ]; // Establishes argument defaults.

        $this->args                                      = $args + $default_args;
        $this->args['shortcode_tag_names']               = (array) $this->args['shortcode_tag_names'];
        $this->args['exclude_escaped_shortcode']         = (bool) $this->args['exclude_escaped_shortcode'];
        $this->args['shortcode_unautop_compat']          = (bool) $this->args['shortcode_unautop_compat'];
        $this->args['shortcode_unautop_compat_tag_name'] = (string) $this->args['shortcode_unautop_compat_tag_name'];

        $this->tokens = []; // Initialize tokens.

        if (!isset(static::$m0_esc_no_vws)) {
            static::$m0_esc_no_vws['[]'] = $this->c::regexM0EscNoVws('[]');
            static::$m0_esc_no_vws['()'] = $this->c::regexM0EscNoVws('()');
            static::$m0_esc_no_vws['{}'] = $this->c::regexM0EscNoVws('{}');
        }
        if (!$this->string || !$this->tokenize) {
            return; // Nothing to do.
        }
        $this->maybeTokenizeShortcodes();
        $this->maybeTokenizeIfCondTags();

        $this->maybeTokenizePreTags();
        $this->maybeTokenizeCodeTags();
        $this->maybeTokenizeSampTags();

        $this->maybeTokenizeStyleTags();
        $this->maybeTokenizeScriptTags();

        $this->maybeTokenizeTextareaTags();
        $this->maybeTokenizeAnchorTags();

        $this->maybeTokenizeAllTags();
        $this->maybeTokenizeSpecialAttrs();

        $this->maybeTokenizeMdFences();
        $this->maybeTokenizeMdLinks();
    }

    /**
     * Get tokenizer ID.
     *
     * @since 160720 Initial release.
     *
     * @return string Tokenizer ID.
     */
    public function getId(): string
    {
        return $this->id;
    }

    /**
     * Get tokenized string.
     *
     * @since 150424 Initial release.
     *
     * @return string Tokenized string (by ref).
     */
    public function &getString(): string
    {
        return $this->string;
    }

    /**
     * Set tokenized string.
     *
     * @since 150424 Initial release.
     *
     * @param string Set the tokenized string.
     */
    public function setString(string $string)
    {
        $this->string = $string;
    }

    /**
     * Maybe tokenize shortcodes.
     *
     * @since 150424 Initial release.
     */
    protected function maybeTokenizeShortcodes()
    {
        if (!in_array('shortcodes', $this->tokenize, true)) {
            return; // Not tokenizing these.
        } elseif (mb_strpos($this->string, '[') === false) {
            return; // No `[` shortcodes.
        } elseif (mb_strpos($this->string, ']') === false) {
            return; // No `[` shortcodes.
        }
        if ($this->args['shortcode_tag_names']) {
            $shortcode_tag_names = $this->args['shortcode_tag_names'];
        } elseif (!empty($GLOBALS['shortcode_tags']) && is_array($GLOBALS['shortcode_tags'])) {
            $shortcode_tag_names = array_keys($GLOBALS['shortcode_tags']);
        }
        if (empty($shortcode_tag_names) || !$this->c::isWordPress() || !$this->c::canCallFunc('get_shortcode_regex')) {
            return; // Not possible at this time.
        }
        if ($this->args['shortcode_unautop_compat_tag_name']) {
            $this->shortcode_unautop_compat_tag_name = $this->args['shortcode_unautop_compat_tag_name'];
        } else { // Any registered shortcode tag name; for `shortcode_unautop_compat` mode.
            $this->shortcode_unautop_compat_tag_name = $shortcode_tag_names[0];
        } // This prevents WordPress from wrapping a stand-alone token with `<p></p>`.

        $regex = '/'.get_shortcode_regex($shortcode_tag_names).'/us'; // Dot matches new line.
        // See: <https://developer.wordpress.org/reference/functions/get_shortcode_regex/>

        $this->string = preg_replace_callback($regex, function ($m) {
            // If excluding escaped shortcodes, check here before we continue.
            if ($this->args['exclude_escaped_shortcode'] && $m[1] === '[' && $m[6] === ']') {
                return $m[0]; // Exclude from tokenization.
            }
            $this->tokens[] = $m[0]; // Original data for token.
            // i.e., The entire shortcode (w/ possible escape brackets).

            if ($this->args['shortcode_unautop_compat']) {
                return $token = '['.$this->shortcode_unautop_compat_tag_name.' _is_token=⁅⒯⁆]'.
                                    '⁅⒯'.$this->id.'⒯'.(count($this->tokens) - 1).'⒯⁆'.
                                '[/'.$this->shortcode_unautop_compat_tag_name.']';
            } else { // Default behavior.
                return $token = '⁅⒯'.$this->id.'⒯'.(count($this->tokens) - 1).'⒯⁆';
            }
        }, $this->string); // Shortcodes replaced by tokens.
    }

    /**
     * Maybe tokenize `<!--[if`, `<![if` tags.
     *
     * @since 170824.30708 Initial release.
     */
    protected function maybeTokenizeIfCondTags()
    {
        if (!in_array('if-conds', $this->tokenize, true)) {
            return; // Not tokenizing these.
        } elseif (mb_stripos($this->string, '<!--[') === false && mb_stripos($this->string, '<![') === false) {
            return; // Nothing to tokenize here.
        }
        $regex = '/\<\![^[>]*?\[if\W[^\]]*?\][^>]*?\>(?s:.*?)\<\![^[>]*?\[endif\][^>]*?\>/ui';

        $this->string = preg_replace_callback($regex, function ($m) {
            $this->tokens[] = $m[0];  // Original data for token.
            return $token = '⁅⒯'.$this->id.'⒯'.(count($this->tokens) - 1).'⒯⁆';
        }, $this->string); // Tags replaced by tokens.
    }

    /**
     * Maybe tokenize `<pre>` tags.
     *
     * @since 150424 Initial release.
     */
    protected function maybeTokenizePreTags()
    {
        if (!in_array('pre', $this->tokenize, true)) {
            return; // Not tokenizing these.
        } elseif (mb_stripos($this->string, '<pre') === false) {
            return; // Nothing to tokenize here.
        }
        $regex = '/\<(pre)(?:\>|\s[^>]*\>)(?:(?s:[^<>]+|(?!\<\\1[\s>]).)+?|(?R))*?\<\/\\1\>/ui';

        $this->string = preg_replace_callback($regex, function ($m) {
            $this->tokens[] = $m[0]; // Original data for token.
            return $token = '⁅⒯'.$this->id.'⒯'.(count($this->tokens) - 1).'⒯⁆';
        }, $this->string); // Tags replaced by tokens.
    }

    /**
     * Maybe tokenize `<code>` tags.
     *
     * @since 150424 Initial release.
     */
    protected function maybeTokenizeCodeTags()
    {
        if (!in_array('code', $this->tokenize, true)) {
            return; // Not tokenizing these.
        } elseif (mb_stripos($this->string, '<code') === false) {
            return; // Nothing to tokenize here.
        }
        $regex = '/\<(code)(?:\>|\s[^>]*\>)(?:(?s:[^<>]+|(?!\<\\1[\s>]).)+?|(?R))*?\<\/\\1\>/ui';

        $this->string = preg_replace_callback($regex, function ($m) {
            $this->tokens[] = $m[0];  // Original data for token.
            return $token = '⁅⒯'.$this->id.'⒯'.(count($this->tokens) - 1).'⒯⁆';
        }, $this->string); // Tags replaced by tokens.
    }

    /**
     * Maybe tokenize `<samp>` tags.
     *
     * @since 150424 Initial release.
     */
    protected function maybeTokenizeSampTags()
    {
        if (!in_array('samp', $this->tokenize, true)) {
            return; // Not tokenizing these.
        } elseif (mb_stripos($this->string, '<samp') === false) {
            return; // Nothing to tokenize here.
        }
        $regex = '/\<(samp)(?:\>|\s[^>]*\>)(?:(?s:[^<>]+|(?!\<\\1[\s>]).)+?|(?R))*?\<\/\\1\>/ui';

        $this->string = preg_replace_callback($regex, function ($m) {
            $this->tokens[] = $m[0];  // Original data for token.
            return $token = '⁅⒯'.$this->id.'⒯'.(count($this->tokens) - 1).'⒯⁆';
        }, $this->string); // Tags replaced by tokens.
    }

    /**
     * Maybe tokenize `<style>` tags.
     *
     * @since 170824.30708 Initial release.
     */
    protected function maybeTokenizeStyleTags()
    {
        if (!in_array('style', $this->tokenize, true)) {
            return; // Not tokenizing these.
        } elseif (mb_stripos($this->string, '<style') === false) {
            return; // Nothing to tokenize here.
        }
        $regex = '/\<(style)(?:\>|\s[^>]*\>)(?:(?s:[^<>]+|(?!\<\\1[\s>]).)+?|(?R))*?\<\/\\1\>/ui';

        $this->string = preg_replace_callback($regex, function ($m) {
            $this->tokens[] = $m[0];  // Original data for token.
            return $token = '⁅⒯'.$this->id.'⒯'.(count($this->tokens) - 1).'⒯⁆';
        }, $this->string); // Tags replaced by tokens.
    }

    /**
     * Maybe tokenize `<script>` tags.
     *
     * @since 170824.30708 Initial release.
     */
    protected function maybeTokenizeScriptTags()
    {
        if (!in_array('script', $this->tokenize, true)) {
            return; // Not tokenizing these.
        } elseif (mb_stripos($this->string, '<script') === false) {
            return; // Nothing to tokenize here.
        }
        $regex = '/\<(script)(?:\>|\s[^>]*\>)(?:(?s:[^<>]+|(?!\<\\1[\s>]).)+?|(?R))*?\<\/\\1\>/ui';

        $this->string = preg_replace_callback($regex, function ($m) {
            $this->tokens[] = $m[0];  // Original data for token.
            return $token = '⁅⒯'.$this->id.'⒯'.(count($this->tokens) - 1).'⒯⁆';
        }, $this->string); // Tags replaced by tokens.
    }

    /**
     * Maybe tokenize `<textarea>` tags.
     *
     * @since 170824.30708 Initial release.
     */
    protected function maybeTokenizeTextareaTags()
    {
        if (!in_array('textarea', $this->tokenize, true)) {
            return; // Not tokenizing these.
        } elseif (mb_stripos($this->string, '<textarea') === false) {
            return; // Nothing to tokenize here.
        }
        $regex = '/\<(textarea)(?:\>|\s[^>]*\>)(?:(?s:[^<>]+|(?!\<\\1[\s>]).)+?|(?R))*?\<\/\\1\>/ui';

        $this->string = preg_replace_callback($regex, function ($m) {
            $this->tokens[] = $m[0];  // Original data for token.
            return $token = '⁅⒯'.$this->id.'⒯'.(count($this->tokens) - 1).'⒯⁆';
        }, $this->string); // Tags replaced by tokens.
    }

    /**
     * Maybe tokenize `<a>` tags.
     *
     * @since 150424 Initial release.
     */
    protected function maybeTokenizeAnchorTags()
    {
        if (!in_array('anchors', $this->tokenize, true)) {
            return; // Not tokenizing these.
        } elseif (mb_stripos($this->string, '<a') === false) {
            return; // Nothing to tokenize here.
        }
        $regex = '/\<(a)(?:\>|\s[^>]*\>)(?:(?s:[^<>]+|(?!\<\\1[\s>]).)+?|(?R))*?\<\/\\1\>/ui';

        $this->string = preg_replace_callback($regex, function ($m) {
            $this->tokens[] = $m[0];  // Original data for token.
            return $token = '⁅⒯'.$this->id.'⒯'.(count($this->tokens) - 1).'⒯⁆';
        }, $this->string); // Tags replaced by tokens.
    }

    /**
     * Maybe tokenize all `<tags>`.
     *
     * @since 150424 Initial release.
     */
    protected function maybeTokenizeAllTags()
    {
        if (!in_array('tags', $this->tokenize, true)) {
            return; // Not tokenizing these.
        } elseif (mb_stripos($this->string, '<') === false) {
            return; // Nothing to tokenize here.
        }
        $regex = '/\<\/?[\w\-]+(?:\>|\s[^>]*\>)/ui';

        $this->string = preg_replace_callback($regex, function ($m) {
            $this->tokens[] = $m[0];  // Original data for token.
            return $token = '⁅⒯'.$this->id.'⒯'.(count($this->tokens) - 1).'⒯⁆';
        }, $this->string); // Tags replaced by tokens.
    }

    /**
     * Maybe tokenize `<x style|on*|data-*="">` attrs.
     *
     * @since 170824.30708 Initial release.
     */
    protected function maybeTokenizeSpecialAttrs()
    {
        if (!in_array('s-attrs', $this->tokenize, true)) {
            return; // Not tokenizing these.
        } elseif (mb_stripos($this->string, '<') === false) {
            return; // Nothing to tokenize here.
        }
        $regex = '/(?<=\s)(?:style|on[a-z]+|data\-[a-z\-]+)\s*\=\s*(["\'])(?s:.*?)\\1/ui';

        $this->string = preg_replace_callback($regex, function ($m) {
            $this->tokens[] = $m[0];  // Original data for token.
            return $token = '⁅⒯'.$this->id.'⒯'.(count($this->tokens) - 1).'⒯⁆';
        }, $this->string); // Attributes replaced by tokens.
    }

    /**
     * Maybe tokenize MD fences.
     *
     * @since 150424 Initial release.
     */
    protected function maybeTokenizeMdFences()
    {
        if (!in_array('md-fences', $this->tokenize, true)) {
            return; // Not tokenizing these.
        } elseif (mb_strpos($this->string, '~') === false && mb_strpos($this->string, '`') === false) {
            return; // Nothing to tokenize here.
        }
        $regex = '/(~{3,}|`{3,}|``|`)(?s:.*?)[^`]\\1/ui';
        // Note: this covers ``a literal `tick inside`` also.
        // See: <https://daringfireball.net/projects/markdown/syntax#code>
        // This also picks up ```lang {attributes} in fenced code blocks.

        $this->string = preg_replace_callback($regex, function ($m) {
            $this->tokens[] = $m[0];  // Original data for token.
            return $token = '⁅⒯'.$this->id.'⒯'.(count($this->tokens) - 1).'⒯⁆';
        }, $this->string); // Fences replaced by tokens.
    }

    /**
     * Maybe tokenize `[md](links)`, et al.
     *
     * @since 150424 Initial release.
     */
    protected function maybeTokenizeMdLinks()
    {
        if (!in_array('md-links', $this->tokenize, true)) {
            return; // Not tokenizing these.
        }
        $_ = static::$m0_esc_no_vws; // Shorter reference.

        $this->string = preg_replace_callback(
            [
                // This one covers MD [links](), [link][references], and ![](images).
                // The negative lookahed for `(?!\/)` is to avoid catching a tokenized shortcode.
                // e.g., Whenever the `shortcode_unautop_compat` configuration option has been enabled.
                '/\!?\[(?:'.$_['[]'].'|(?R))\](?:\h*\[(?!\/)'.$_['[]'].'\]|\('.$_['()'].'\))(?:\h*\{'.$_['{}'].'\})?/ui',

                // In these, we should only tokenize the definition markers and the `<link>` in link definitions.
                '/^\h{0,3}(?:\['.$_['[]'].'\])+\h*\:\h*(?:\<)?[^\s<>]*(?:\>)?/uim', // Link definition markers.
                '/^\h{0,3}(?:\*\['.$_['[]'].'\])+\h*\:/uim', // Abbreviation definition markers.
                '/^\h{0,3}(?:\[\^[0-9]+\])+\h*\:/uim', // Footnote definition markers.

                // Also tokenize MD `<urls>` in brackets.
                '/\<((?:[a-zA-Z][a-zA-Z0-9+.\-]*:)?\/{2}[^\v<>]*)\>/ui',

                // Also tokenize checkbox markers.
                '/^\h*(?:[*+\-]|[0-9]+\.)\h+\[[\sx]\]/uim',
            ],
            function ($m) {
                $this->tokens[] = $m[0];  // Original data for token.
                return $token = '⁅⒯'.$this->id.'⒯'.(count($this->tokens) - 1).'⒯⁆';
            },
            $this->string // Shortcodes replaced by tokens.
        );
    }

    /**
     * Restore token originals.
     *
     * @since 150424 Initial release.
     *
     * @return string After restoring tokens.
     */
    public function &restoreGetString(): string
    {
        if (!$this->tokens || mb_strpos($this->string, '⒯') === false) {
            return $this->string; // Nothing to restore in this case.
        }
        $restore_shortcode_tokens = $this->args['shortcode_unautop_compat']
            && $this->shortcode_unautop_compat_tag_name && in_array('shortcodes', $this->tokenize, true);

        foreach (array_reverse($this->tokens, true) as $_token_id => $_original) {
            // Must go in reverse order so nested tokens unfold properly.
            // If `$restore_shortcode_tokens`, first replace shortcode tokens.

            $_token = '⁅⒯'.$this->id.'⒯'.$_token_id.'⒯⁆'; // Placeholder.

            if ($restore_shortcode_tokens) { // Restoring shortcode tokens in this class instance?
                $this->string = str_replace('['.$this->shortcode_unautop_compat_tag_name.' _is_token=⁅⒯⁆]'.
                                                $_token.// Token inside the shortcode token.
                                            '[/'.$this->shortcode_unautop_compat_tag_name.']', $_original, $this->string);
            }
            $this->string = str_replace($_token, $_original, $this->string); // Always.
        } // unset($_token_id, $_token, $_original); // Housekeeping.

        return $this->string;
    }
}


<?php
/**
 * Color 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;

/**
 * Color utilities.
 *
 * @since 161010 Color utilities.
 */
class Color extends Classes\Core\Base\Core
{
    /**
     * SHA-1 color generation.
     *
     * @since 161012 Color utilities.
     *
     * @param string $string String to hash.
     * @param float  $adjust See {@link adjusLuminosity()}.
     *
     * @return string Auto-generated color with X luminosity.
     */
    public function sha1(string $string, float $adjust = 0): string
    {
        $hex        = '#'.mb_substr(sha1($string), 0, 6);
        return $hex = $adjust ? $this->adjustLuminosity($hex, $adjust) : $hex;
    }

    /**
     * Adjust luminosity.
     *
     * @since 161010 Color utilities.
     *
     * @param string $hex    Hex color code to adjust.
     * @param float  $adjust `-0.5` (50% darker), `0.5` (50% ligher).
     *
     * @return string Adjusted hex color, +- luminosity.
     *
     * @see https://www.sitepoint.com/javascript-generate-lighter-darker-color/
     */
    public function adjustLuminosity(string $hex, float $adjust): string
    {
        $hex = $this->cleanHex($hex);

        for ($adjusted_hex = '', $_dec, $_hex, $_i = 0; $_i < 3; ++$_i) {
            $_dec = hexdec(mb_substr($hex, $_i * 2, 2));
            $_hex = dechex(round(min(max(0, $_dec + ($_dec * $adjust)), 255)));
            $adjusted_hex .= $this->c::mbStrPad($_hex, 2, '0', STR_PAD_LEFT);
        } // unset($_dec, $_hex, $_i); // Housekeeping.

        return '#'.$adjusted_hex;
    }

    /**
     * Contrasting B/W color.
     *
     * @since 161010 Color utilities.
     *
     * @param string $hex Hex color code.
     *
     * @return string Suggested B/W contrasting color.
     *
     * @see https://24ways.org/2010/calculating-color-contrast/
     */
    public function contrastingBw(string $hex): string
    {
        $hex = $this->cleanHex($hex);

        $r = hexdec(substr($hex, 0, 2));
        $g = hexdec(substr($hex, 2, 2));
        $b = hexdec(substr($hex, 4, 2));

        $yiq = (($r * 299) + ($g * 587) + ($b * 114)) / 1000;

        return $yiq >= 128 ? '#000000' : '#ffffff';
    }

    /**
     * Cleans a hex color.
     *
     * @since 161010 Color utilities.
     *
     * @param string $hex Hex color.
     *
     * @return string Clean hex color.
     */
    public function cleanHex(string $hex): string
    {
        $hex = ltrim($hex, '#');

        if (!isset($hex[5])) {
            $hex = preg_replace('/(.)/u', '$1$1', $hex);
            $hex = $this->c::mbStrPad($hex, 6, '0');
        }
        return $hex;
    }
}


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

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;

/**
 * Application.
 *
 * @since 150424 Initial release.
 */
class App extends Classes\Core\Base\Core
{
    /**
     * Parent app.
     *
     * @since 160224
     *
     * @type Classes\App|null
     */
    public $Parent;

    /**
     * Reflection.
     *
     * @since 160228
     *
     * @type \ReflectionClass
     */
    public $Reflection;

    /**
     * Class.
     *
     * @since 160223
     *
     * @type string
     */
    public $class;

    /**
     * Namespace.
     *
     * @since 150424
     *
     * @type string
     */
    public $namespace;

    /**
     * Namespace SHA-1.
     *
     * @since 150424
     *
     * @type string
     */
    public $namespace_sha1;

    /**
     * File.
     *
     * @since 160225
     *
     * @type string
     */
    public $file;

    /**
     * Dir.
     *
     * @since 150424
     *
     * @type string
     */
    public $dir;

    /**
     * Dir basename.
     *
     * @since 160223
     *
     * @type string
     */
    public $dir_basename;

    /**
     * Base dir.
     *
     * @since 160227
     *
     * @type string
     */
    public $base_dir;

    /**
     * Base dir basename.
     *
     * @since 160227
     *
     * @type string
     */
    public $base_dir_basename;

    /**
     * WSC dir.
     *
     * @since 150424
     *
     * @type string
     */
    public $ws_core_dir;

    /**
     * Is WSC?
     *
     * @since 150424
     *
     * @type bool
     */
    public $is_ws_core;

    /**
     * Is a core?
     *
     * @since 150424
     *
     * @type bool
     */
    public $is_core;

    /**
     * Facades.
     *
     * @since 160227
     *
     * @type \StdClass
     */
    public $Facades;

    /**
     * Config.
     *
     * @since 150424
     *
     * @type Classes\Core\Base\Config
     */
    public $Config;

    /**
     * Dicer.
     *
     * @since 150424
     *
     * @type Classes\Core\Base\Di
     */
    public $Di;

    /**
     * Utilities.
     *
     * @since 150424
     *
     * @type Classes\Core\Base\Utils
     */
    public $Utils;

    /**
     * Version.
     *
     * @since 150424
     *
     * @type string Version.
     */
    const VERSION = '171120.30641'; //v//

    /**
     * Constructor.
     *
     * @since 150424 Initial release.
     *
     * @param array            $instance_base Instance base.
     * @param array            $instance      Instance args.
     * @param Classes\App|null $Parent        Parent app (optional).
     */
    public function __construct(array $instance_base = [], array $instance = [], Classes\App $Parent = null)
    {
        parent::__construct($this);

        # Establish debug mode early-on.

        if (isset($instance['©debug']['©enable'])) {
            $debug = (bool) $instance['©debug']['©enable'];
        } elseif (isset($instance_base['©debug']['©enable'])) {
            $debug = (bool) $instance_base['©debug']['©enable'];
        } else { // Based on server CFGs (if enabled).
            $use_server_cfgs = (bool) ($instance['©use_server_cfgs']
                ?? $instance_base['©use_server_cfgs'] ?? !empty($_SERVER['CFG_HOST']));
            $debug = $use_server_cfgs && !empty($_SERVER['CFG_DEBUG']);
        }
        # Define a possible parent app.

        $this->Parent = $Parent;

        # Define reflection-based properties.
        // If not already obtained by an extender.

        $this->Reflection = $this->Reflection ?: new \ReflectionClass($this);

        $this->class          = $this->class ?: $this->Reflection->getName();
        $this->namespace      = $this->namespace ?: $this->Reflection->getNamespaceName();
        $this->namespace_sha1 = $this->namespace_sha1 ?: sha1($this->namespace);

        $this->file         = $this->file ?: $this->Reflection->getFileName();
        $this->dir          = $this->dir ?: dirname($this->file);
        $this->dir_basename = $this->dir_basename ?: basename($this->dir);

        $this->base_dir          = $this->base_dir ?: dirname($this->dir, 3);
        $this->base_dir_basename = $this->base_dir_basename ?: basename($this->base_dir);

        $this->ws_core_dir = $this->ws_core_dir ?: dirname(__FILE__, 4);

        $this->is_ws_core = $this->class === self::class;
        $this->is_core    = $this->is_ws_core || !$this->Parent;

        # Validate app instance (if applicable).

        if ($debug) { // Only in debug mode.
            if (isset($GLOBALS[$this->class])) {
                throw new Exception(sprintf('One instance of `%1$s` only please.', $this->class));
            } elseif (mb_substr($this->namespace, -8) !== '\\Classes') {
                throw new Exception(sprintf('Invalid namespace: `%1$s`. Expecting: `...\\Classes`.', $this->namespace));
            } elseif (mb_substr($this->dir, -21) !== '/src/includes/classes') {
                throw new Exception(sprintf('Invalid dir: `%1$s`. Expecting: `.../src/includes/classes`.', $this->dir));
            }
        }
        # Instantiate/configure child class instances; e.g., Config, Di, Utils.

        $this->Config = new Classes\Core\Base\Config($this, $instance_base, $instance);
        $this->Di     = new Classes\Core\Base\Di($this, $this->Config->©di['©default_rule']);
        $this->Utils  = new Classes\Core\Base\Utils($this);

        $this->Di->addInstances([$this, $this->Config, $this->Utils]);

        # Setup global references & facades.
        # http://docstore.mik.ua/orelly/webprog/pcook/ch07_13.htm

        $GLOBALS[$this->class] = $this; // Global access variable.

        $GLOBALS[$this->Facades->c = $this->namespace.'\\CoreFacades'] = $this;
        $_facades_base_class                                           = $this->getClass(Classes\Core\Base\Facades::class);
        eval('namespace '.$this->namespace.' { class CoreFacades extends \\'.$_facades_base_class.' {} }');

        foreach ($this->Config->©sub_namespace_map as $_sub_namespace => $_identifiers) {
            if (class_exists($_facades_base_class = $this->getClass($this->namespace.'\\'.$_sub_namespace.'\\Base\\Facades'))) {
                $GLOBALS[$this->Facades->{$_identifiers['©facades']} = $this->namespace.'\\'.$_sub_namespace.'Facades'] = $this;
                eval('namespace '.$this->namespace.' { class '.$_sub_namespace.'Facades extends \\'.$_facades_base_class.' {} }');
            }
        } // unset($_sub_namespace, $_identifiers, $_facades_base_class);

        $GLOBALS[$this->Facades->a = $this->namespace.'\\AppFacades'] = $this;
        $_facades_base_class                                          = $this->namespace.'\\Base\\Facades';
        $_facades_base_class                                          = class_exists($_facades_base_class) ? $_facades_base_class : 'StdClass';
        eval('namespace '.$this->namespace.' { class AppFacades extends \\'.$_facades_base_class.' {} }');

        # Post-construct sub-routines.

        $this->maybeDebug();
        $this->maybeSetLocales();
        $this->maybeHandleThrowables();
    }

    /**
     * Maybe setup debug error reporting.
     *
     * @since 150424 Initial release.
     */
    protected function maybeDebug()
    {
        if (!$this->Config->©debug['©enable']) {
            return; // Nothing to do here.
        } elseif (!$this->Config->©debug['©er_enable']) {
            return; // Nothing to do here.
        }
        error_reporting(E_ALL); // Enable error reporting.

        if ($this->Config->©debug['©er_display']) {
            ini_set('display_errors', 'yes'); // Display.
        }
        if ($this->Config->©debug['©er_assertions']) {
            // Fail softly, because it can only go from `0` to `1`.
            // If the current value is `-1` this will trigger a warning.
            @ini_set('zend.assertions', '1');
            ini_set('assert.exception', 'yes');
        }
    }

    /**
     * Maybe handle throwables.
     *
     * @since 160711 Initial release.
     */
    protected function maybeHandleThrowables()
    {
        if (!$this->Config->©debug['©enable'] && $this->Config->©handle_throwables) {
            // Handle throwables w/ a custom error page.
            $this->c::setupThrowableHandler();
        }
    }

    /**
     * Maybe setup locales.
     *
     * @since 150424 Initial release.
     */
    protected function maybeSetLocales()
    {
        if ($this->Config->©locales) {
            // Try locale codes in a specific order.
            // See: <http://php.net/manual/en/function.setlocale.php>
            setlocale(LC_ALL, $this->Config->©locales);
        }
    }

    /**
     * Get class (in order of precedence).
     *
     * @since 160224 Support ancestors.
     *
     * @param string $class The class path.
     *
     * @return string Class w/ highest precedence.
     */
    public function getClass(string $class): string
    {
        if (!($classes_position = mb_strripos($class, '\\Classes\\'))) {
            return $class; // Not possible; return as-is.
        } elseif (!($sub_class = mb_substr($class, $classes_position + 9))) {
            return $class; // Not possible; return as-is.
        }
        if (class_exists($this->namespace.'\\'.$sub_class)) {
            return $class = $this->namespace.'\\'.$sub_class;
        } elseif (($_Parent = $this->Parent)) {
            do {
                if (class_exists($_Parent->namespace.'\\'.$sub_class)) {
                    return $class = $_Parent->namespace.'\\'.$sub_class;
                }
            } while ($_Parent->Parent); // unset($_Parent);
        }
        return $class; // Return as-is (nothing more we can do).
    }

    /**
     * Merge config arrays.
     *
     * @since 160227 Config utils.
     *
     * @param array $base  Base array.
     * @param array $merge Array to merge.
     *
     * @return array The resuling array after merging.
     *
     * @internal This method must be capable of running w/o a constructed app.
     */
    public function mergeConfig(array $base, array $merge): array
    {
        if (isset($base['©di']['©default_rule']['new_instances'])) {
            $base_di_default_rule_new_instances = $base['©di']['©default_rule']['new_instances'];
        } // Save new instances before emptying numeric arrays.

        if (isset($merge['©mysql_db']['©hosts'])) {
            unset($base['©mysql_db']['©hosts']);
        } // Override base array. Replace w/ new hosts.

        $base = $this->maybeEmptyNumericConfigArrays($base, $merge);

        if (isset($base_di_default_rule_new_instances, $merge['©di']['©default_rule']['new_instances'])) {
            $merge['©di']['©default_rule']['new_instances'] = array_merge($base_di_default_rule_new_instances, $merge['©di']['©default_rule']['new_instances']);
        }
        return $merged = array_replace_recursive($base, $merge);
    }

    /**
     * Empty numeric config arrays.
     *
     * @since 160227 Config utils.
     *
     * @param array $base  Base array.
     * @param array $merge Array to merge.
     *
     * @return array The `$base` w/ possibly-empty numeric arrays.
     *
     * @internal This method must be capable of running w/o a constructed app.
     */
    public function maybeEmptyNumericConfigArrays(array $base, array $merge): array
    {
        if (!$merge) { // Save time. Merge is empty?
            return $base; // Nothing to do here.
        }
        foreach ($base as $_key => &$_value) {
            if (is_array($_value) && array_key_exists($_key, $merge)) {
                if (!$_value || $_value === array_values($_value)) {
                    $_value = []; // Empty numeric arrays.
                } elseif ($merge[$_key] && is_array($merge[$_key])) {
                    $_value = $this->maybeEmptyNumericConfigArrays($_value, $merge[$_key]);
                }
            }
        } // unset($_key, $_value); // Housekeeping.
        return $base; // Return possibly-modified base.
    }

    /**
     * Fill replacement codes.
     *
     * @since 150424 Config utils.
     *
     * @param mixed      $value     Input value.
     * @param array|null $___config Internal use only.
     *
     * @return mixed string|array|object Output value.
     *
     * @internal This method must be capable of running w/o a fully-constructed app.
     *  Only dependency is the initial reflection-based properties.
     */
    public function fillConfigReplacementCodes($value, array $___config = null)
    {
        if (is_array($value) || is_object($value)) {
            if (!isset($___config)) {
                $___config = &$value; // Reference.
            }
            foreach ($value as $_key => &$_value) {
                $_value = $this->fillConfigReplacementCodes($_value, $___config);
            } // unset($_key, $_value);
            return $value;
        }
        if ($value && is_string($value) && mb_strpos($value, '%%') !== false) {
            $value = str_replace(
                [
                    '%%app_class%%',
                    '%%app_namespace%%',
                    '%%app_namespace_sha1%%',

                    '%%app_file%%',
                    '%%app_dir%%',
                    '%%app_dir_basename%%',

                    '%%app_base_dir%%',
                    '%%app_base_dir_basename%%',

                    '%%ws_core_dir%%',

                    '%%home_dir%%',

                    '%%app_slug%%',
                    '%%app_var%%',
                ],
                [
                    $this->class,
                    $this->namespace,
                    $this->namespace_sha1,

                    $this->file,
                    $this->dir,
                    $this->dir_basename,

                    $this->base_dir,
                    $this->base_dir_basename,

                    $this->ws_core_dir,

                    (string) ($_SERVER['HOME'] ?? $_SERVER['WEBSHARK_HOME'] ?? ''),

                    !empty($___config['©brand']['©slug']) ? (string) $___config['©brand']['©slug'] : '',
                    !empty($___config['©brand']['©var']) ? (string) $___config['©brand']['©var'] : '',
                ],
                $value
            );
        }
        return $value;
    }
}


<?php
/**
 * Simple expression utils.
 *
 * @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;

/**
 * Simple expression utils.
 *
 * @since 160708 Simple expression utils.
 */
class SimpleExpression extends Classes\Core\Base\Core implements Interfaces\SimpleExpressionConstants
{
    /**
     * Builds a PHP expression from a simple expression string.
     *
     * @since 160708 Simple expression utils.
     *
     * @param string   $expr     A simple expression.
     * @param callable $callback A callback template handler.
     *
     * @internal The `$callback` receives one argument, a raw test-fragment (string, not quoted).
     *  The callback should always return a single PHP function call (as a string), using the test-fragment.
     *  e.g., `'check_something("'".$test_fragment."'")'`, `'get_something("'".$test_fragment."'")'`, etc.
     *
     * @return string PHP expression from a simple expression string.
     *
     * @note The following reserved characters should NOT be used as a part of any test-fragment or comparison-value.
     *  Reserved chars are: `(`, `)`, and all whitespace. Brackets & whitespace are delimiters in the expression.
     *
     * @note Another special char is `!`, which can appear before any test-fragment to negate the test.
     *  This is not considered a reserved char though, because it is only valid as the first char in a test-fragment.
     *  Also, it is only valid when the test-fragment contains a total of 2+ chars (counting the `!` symbol itself).
     *  It's also worth noting that the `!` symbol is not passed to the `$callback` when it's a valid negation.
     *
     * @note There are also reserved sequences that form logical-operators and/or comparison-operators that should NOT be
     *  used as any stand-alone test-fragment or comparison-value. They can be part of a test-fragment or comparison-value,
     *  but not stand-alone; i.e., any of these preceded by `^|[\s()]` and followed by `[\s()]|$` form an operator.
     *  Reserved sequences include: `AND`, `OR`, `&&`, `||`, `===`, `!==`, `==`, `!=`, `<=`, `>=`, `<>`, `>`, `<`.
     *
     * @note Other special chars can be implemented by the caller as a part of the test-fragment.
     *  For instance, it might be desirable to allow for a test-fragment that starts with a function name.
     *  e.g., `function:arg`, `function:arg,arg`, etc. So long as it doesn't use reserved chars you're fine.
     *  Just be sure to document special chars. In this example, `[:,]` would be reserved in test-fragments.
     */
    public function toPhp(string $expr, callable $callback): string
    {
        if (!$expr) { // Expression empty?
            return ''; // Nothing to do in this case.
        } elseif (!preg_match($this::SIMPLE_EXPRESSION_REGEX_VALID, '('.$expr.')')) {
            return ''; // Not possible; i.e., not a valid simple expression.
            // NOTE: This validation is important. If it fails, the sub-routines below could
            // produce a PHP expression that contains syntax errors. Or worse, a security exploit.
            // In short, this validation allows us to bypass expensive, individual, token-based validation.
            // Once this validation passes, sub-routines below make several assumptions about the expression.
        }
        $delimiters_regex     = '/([()])|\s+/u';
        $logical_operators    = ['AND', 'OR', '&&', '||'];
        $comparison_operators = ['===', '!==', '==', '!=', '<=', '>=', '<>', '>', '<'];

        $conditions      = ''; // Initialize conditions.
        $_previous_token = $_previous_token_type = ''; // Initialize previous token.
        $_tokens         = preg_split($delimiters_regex, $expr, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);

        for ($_i = 0; $_i < count($_tokens); ++$_i) {
            $_token = $_tokens[$_i]; // Current token.

            if ($_token === '(' || $_token === ')') {
                $_space = $_token === '(' && $_previous_token_type === 'logical-operator' ? ' ' : '';
                $conditions .= $_space.$_token;
                $_previous_token      = $_token;
                $_previous_token_type = 'bracket';
                //
            } elseif (in_array(mb_strtoupper($_token), $logical_operators, true)) {
                $_space = ' '; // Always.
                $conditions .= $_space.$_token;
                $_previous_token      = $_token;
                $_previous_token_type = 'logical-operator';
                //
            } else { // Append a new condition.
                $_space = $conditions !== '' && $_previous_token !== '(' ? ' ' : '';

                if ($_token[0] === '!' && isset($_token[1])) {
                    $_negating      = '!'; // Negating.
                    $_test_fragment = mb_substr($_token, 1);
                } else {
                    $_negating      = ''; // Not negating.
                    $_test_fragment = $_token;
                }
                if (isset($_tokens[$_i + 1], $_tokens[$_i + 2]) && in_array($_tokens[$_i + 1], $comparison_operators, true)) {
                    $_comparison_operator = $_tokens[$_i + 1]; // e.g., `===`, `!==`, `>=`, `>`, etc.
                    $_comparison_value    = $_tokens[$_i + 2]; // i.e., A value to compare.

                    if (isset($_comparison_value[0]) && is_numeric($_comparison_value)) {
                        // NOTE: To test for a numeric string literal, wrap it in quotes; e.g., '123', '45.06', etc.
                    } elseif ($_comparison_value && in_array(mb_strtolower($_comparison_value), ['true', 'false', 'null'], true)) {
                        // NOTE: To test for a string literal, wrap it in quotes; e.g., 'true', 'false', 'null'.
                    } else { // Anything else becomes a quoted string literal.
                        $_comparison_value = $this->quoteStrLiteral($_comparison_value);
                    }
                    if ($_negating) { // If negating, must wrap the expression in brackets.
                        $conditions .= $_space.$_negating.'('.$callback($_test_fragment).' '.$_comparison_operator.' '.$_comparison_value.')';
                    } else {
                        $conditions .= $_space.$callback($_test_fragment).' '.$_comparison_operator.' '.$_comparison_value;
                    }
                    $_i                   = $_i + 2;
                    $_previous_token      = $_token;
                    $_previous_token_type = 'test-fragment+comparison';
                    //
                } else { // If no operator, use a boolean test only.
                    $conditions .= $_space.$_negating.$callback($_test_fragment);
                    $_previous_token      = $_token;
                    $_previous_token_type = 'test-fragment';
                }
            }
        } // unset($_tokens, $_token, $_previous_token, $_previous_token_type);
        // unset($_space, $_negating, $_test_fragment, $_comparison_operator, $_comparison_value);

        return $conditions; // NOTE: Caller should wrap this in brackets if it's being concatenated with others.
    }

    /**
     * Quote string literal.
     *
     * @since 160708 Simple expression utils.
     *
     * @param string $string String to quote.
     *
     * @return string Quoted string literal. If empty, returns `''`.
     *
     * @note Please see {@link c::sQuote()} for further details.
     *
     * @note The simple expression syntax does NOT allow for spaces (or brackets) in any individual token.
     *  i.e., It is NOT possible to quote a string containing a space (or brackets) at this time.
     */
    protected function quoteStrLiteral(string $string): string
    {
        return $this->c::sQuote($string, true);
    }
}


<?php
namespace Perry\Representation\Eve\v1;

use \Perry\Representation\Reference as Reference;
use \Perry\Representation\Uri as Uri;
use \Perry\Representation\Base as Base;

class Corporation extends Base
{
    public $taxRate;

    public $alliance;

    public $founder;

    public $membersRoles;

    public $private;

    public $href;

    public $accounts;

    public $logo;

    public $id;

    public $acceptApplications;

    public $shares;

    public $homepage;

    public $description;

    public $faction;

    public $ceo;

    public $isNpcCorporation;

    public $applications;

    public $members;

    public $conflicts;

    public $ticker;

    public $displayName;

    public $name;

    public $stations = [];

    public $headquarters;

    public $districts;

    public $offices = [];

    public $deposit;

    // by Warringer\Types\Base
    public function setTaxRate($taxRate)
    {
        $this->taxRate = $taxRate;
    }

    // by Warringer\Types\Reference
    public function setAlliance($alliance)
    {
        $this->alliance = new Reference($alliance);
    }

    // by Warringer\Types\Reference
    public function setFounder($founder)
    {
        $this->founder = new Reference($founder);
    }

    // by Warringer\Types\Reference
    public function setMembersRoles($membersRoles)
    {
        $this->membersRoles = new Reference($membersRoles);
    }

    // by Warringer\Types\Reference
    public function setPrivate($private)
    {
        $this->private = new Reference($private);
    }

    // by Warringer\Types\Uri
    public function setHref($href)
    {
        $this->href = new Uri($href);
    }

    // by Warringer\Types\Reference
    public function setAccounts($accounts)
    {
        $this->accounts = new Reference($accounts);
    }

    // by Warringer\Types\Dict
    public function setLogo($logo)
    {
        // by Warringer\Types\Dict
        $converters = [];
        $converters['32x32'] = function ($value) { return new Reference($value); };
        $converters['logo'] = function ($value) { return $value; };
        $converters['128x128'] = function ($value) { return new Reference($value); };
        $converters['256x256'] = function ($value) { return new Reference($value); };
        $converters['64x64'] = function ($value) { return new Reference($value); };

        $func = function ($value) use($converters) {
            $return = new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS);
            $return['32x32'] = isset($value->{'32x32'}) ? $converters['32x32']($value->{'32x32'}) : null;
            $return['logo'] = isset($value->{'logo'}) ? $converters['logo']($value->{'logo'}) : null;
            $return['128x128'] = isset($value->{'128x128'}) ? $converters['128x128']($value->{'128x128'}) : null;
            $return['256x256'] = isset($value->{'256x256'}) ? $converters['256x256']($value->{'256x256'}) : null;
            $return['64x64'] = isset($value->{'64x64'}) ? $converters['64x64']($value->{'64x64'}) : null;
            return $return;
        };
        $this->logo = $func($logo);
    }

    // by Warringer\Types\Long
    public function setId($id)
    {
        $this->id = $id;
    }

    // by Warringer\Types\Base
    public function setAcceptApplications($acceptApplications)
    {
        $this->acceptApplications = $acceptApplications;
    }

    // by Warringer\Types\Long
    public function setShares($shares)
    {
        $this->shares = $shares;
    }

    // by Warringer\Types\String
    public function setHomepage($homepage)
    {
        $this->homepage = $homepage;
    }

    // by Warringer\Types\String
    public function setDescription($description)
    {
        $this->description = $description;
    }

    // by Warringer\Types\Reference
    public function setFaction($faction)
    {
        $this->faction = new Reference($faction);
    }

    // by Warringer\Types\Reference
    public function setCeo($ceo)
    {
        $this->ceo = new Reference($ceo);
    }

    // by Warringer\Types\Base
    public function setIsNpcCorporation($isNpcCorporation)
    {
        $this->isNpcCorporation = $isNpcCorporation;
    }

    // by Warringer\Types\Reference
    public function setApplications($applications)
    {
        $this->applications = new Reference($applications);
    }

    // by Warringer\Types\Reference
    public function setMembers($members)
    {
        $this->members = new Reference($members);
    }

    // by Warringer\Types\Reference
    public function setConflicts($conflicts)
    {
        $this->conflicts = new Reference($conflicts);
    }

    // by Warringer\Types\String
    public function setTicker($ticker)
    {
        $this->ticker = $ticker;
    }

    // by Warringer\Types\String
    public function setDisplayName($displayName)
    {
        $this->displayName = $displayName;
    }

    // by Warringer\Types\String
    public function setName($name)
    {
        $this->name = $name;
    }

    // by Warringer\Types\ArrayType
    public function setStations($stations)
    {
        // by Warringer\Types\Dict
        $converters = [];

        $func = function ($value) use($converters) {
            $return = new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS);
            return $return;
        };

        foreach ($stations as $key => $value) {
            $this->stations[$key] = $func($value);
        }
    }

    // by Warringer\Types\Reference
    public function setHeadquarters($headquarters)
    {
        $this->headquarters = new Reference($headquarters);
    }

    // by Warringer\Types\Reference
    public function setDistricts($districts)
    {
        $this->districts = new Reference($districts);
    }

    // by Warringer\Types\ArrayType
    public function setOffices($offices)
    {
        // by Warringer\Types\Dict
        $converters = [];

        $func = function ($value) use($converters) {
            $return = new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS);
            return $return;
        };

        foreach ($offices as $key => $value) {
            $this->offices[$key] = $func($value);
        }
    }

    // by Warringer\Types\Reference
    public function setDeposit($deposit)
    {
        $this->deposit = new Reference($deposit);
    }

}


<?php
/**
 * Request utils.
 *
 * @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;

/**
 * Request utils.
 *
 * @since 17xxxx
 */
class Request extends Classes\Core\Base\Core
{
    /**
     * Class constructor.
     *
     * @since 17xxxx Request utils.
     *
     * @param Classes\App $App Instance of App.
     */
    public function __construct(Classes\App $App)
    {
        parent::__construct($App);

        if ($this->c::isCli()) {
            throw $this->c::issue('Not possible in CLI mode.');
        }
    }

    /**
     * Current request.
     *
     * @since 17xxxx Request utilities.
     *
     * @return Classes\Core\Request Instance.
     */
    public function current(): Classes\Core\Request
    {
        return Classes\Core\Request::createFromGlobals($_SERVER);
    }

    /**
     * Create request.
     *
     * @since 17xxxx Request utilities.
     *
     * @return Classes\Core\Request Instance.
     */
    public function create(array $args = []): Classes\Core\Request
    {
        return $this->App->Di->get(Classes\Core\Request::class, $args);
    }

    /**
     * Create request body.
     *
     * @since 17xxxx Request utilities.
     *
     * @param string|null $content Body content.
     *
     * @return Classes\Core\RequestBody Instance.
     */
    public function createBody(string $content = null): Classes\Core\RequestBody
    {
        return $this->App->Di->get(Classes\Core\RequestBody::class, compact('content'));
    }
}