<?php
/**
 * Image 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;
//
use RedeyeVentures\GeoPattern\GeoPattern as Identipattern;

/**
 * Image utilities.
 *
 * @since 161011 Compression.
 */
class Image extends Classes\Core\Base\Core
{
    /**
     * Identipattern generator.
     *
     * @since 17xxxx Identipatterns.
     *
     * @param array $args Named arguments.
     *
     * @return bool True on success.
     */
    public function identipattern(array $args): bool
    {
        if (!class_exists('Imagick')) {
            return false;
        }
        $default_args = [
            'string' => '',
            'for'    => '',

            'color'      => '',
            'base_color' => '',

            'output_file'   => '',
            'output_format' => '',
        ];
        $args += $default_args; // Defaults.
        $args = $this->parseFormatArgs($args);

        if (!$args['output_file'] || !$args['output_format']) {
            return false; // Required arguments.
        }
        $args['string'] = (string) $args['string'];
        $args['string'] = $args['string'] ?: (string) $args['for'];

        $args['color']      = (string) $args['color'];
        $args['base_color'] = (string) $args['base_color'];

        $output_file_existed_prior = is_file($args['output_file']);

        try { // Catch exceptions.
            $svg = (new Identipattern([
                'string'    => $args['string'] ?: null,
                'baseColor' => $args['base_color'] ?: null,
                'color'     => $args['color'] ?: null,
            ]))->toSVG(); // SVG format initially.

            if (file_put_contents($args['output_file'], $svg) === false) {
                throw $this->c::issue('Storage failure.');
            }
            if ($args['output_format'] !== 'svg') {
                if (!$this->convert([
                        'format' => 'svg',
                        'file'   => $args['output_file'],

                        'output_file'   => $args['output_file'],
                        'output_format' => $args['output_format'],
                    ])) {
                    throw $this->c::issue('Conversion failure.');
                }
            }
            return true; // Success.
            //
        } catch (\Throwable $Exception) {
            if (!$output_file_existed_prior && is_file($args['output_file'])) {
                unlink($args['output_file']);
            }
            return false;
        }
    }

    /**
     * Convert image format.
     *
     * @since 17xxxx Imagick utils.
     *
     * @param array $args Named arguments.
     *
     * @return bool True on success.
     */
    public function convert(array $args): bool
    {
        if (!class_exists('Imagick')) {
            return false;
        }
        $default_args = [
            'file'   => '',
            'format' => '',

            'output_file'   => '',
            'output_format' => '',
        ];
        $args += $default_args; // Defaults.
        $args = $this->parseFormatArgs($args);

        if (!is_file($args['file']) || !$args['format']) {
            return false; // Not possible.
        } elseif (!$args['output_file'] || !$args['output_format']) {
            return false; // Not possible.
        }
        $output_file_existed_prior = is_file($args['output_file']);

        try { // Catch exceptions.
            $image = new \Imagick();
            $image->setBackgroundColor('transparent');
            $image->readImage($args['format'].':'.$args['file']);

            $image->writeImage($args['output_format'].':'.$args['output_file']);
            return true; // Success.
            //
        } catch (\Throwable $Exception) {
            if (!$output_file_existed_prior && is_file($args['output_file'])) {
                unlink($args['output_file']);
            }
            return false;
        }
    }

    /**
     * Resize image.
     *
     * @since 17xxxx Imagick utils.
     *
     * @param array $args Named arguments.
     *
     * @return bool True on success.
     */
    public function resize(array $args): bool
    {
        if (!class_exists('Imagick')) {
            return false;
        }
        $default_args = [
            'file'   => '',
            'format' => '',

            'size'   => 0,
            'width'  => 0,
            'height' => 0,
            'crop'   => '',

            'filter'  => \Imagick::FILTER_LANCZOS,
            'blur'    => 1,
            'bestfit' => false,

            'output_file'   => '',
            'output_format' => '',
        ];
        $args += $default_args; // Defaults.
        $args = $this->parseFormatArgs($args);

        if (!is_file($args['file']) || !$args['format']) {
            return false; // Not possible.
        } elseif (!$args['output_file'] || !$args['output_format']) {
            return false; // Not possible.
        }
        $args['size']   = max(0, (int) $args['size']);
        $args['width']  = max(1, (int) ($args['width'] ?: $args['size']));
        $args['height'] = max(1, (int) ($args['height'] ?: $args['size']));
        $args['crop']   = mb_strtolower((string) $args['crop']);

        $args['filter']  = (int) $args['filter'];
        $args['blur']    = (int) $args['blur'];
        $args['bestfit'] = (bool) $args['bestfit'];

        $output_file_existed_prior = is_file($args['output_file']);

        try { // Catch exceptions.
            $image = new \Imagick();
            $image->setBackgroundColor('transparent');
            $image->readImage($args['format'].':'.$args['file']);

            if ($args['crop'] === 'thumbnail') {
                $image->cropThumbnailImage($args['width'], $args['height']);
                //
            } elseif ($args['format'] === 'svg') {
                $svg   = $image;
                $image = new \Imagick();

                $res_x = ceil(300 * ($args['width'] / $svg->getImageWidth()));
                $res_y = ceil(300 * ($args['height'] / $svg->getImageHeight()));
                $image->setResolution($res_x, $res_y); // Before filling.

                $image->setBackgroundColor('transparent');
                $image->readImage($args['format'].':'.$args['file']);
                $image->scaleImage($args['width'], $args['height'], $args['bestfit']);
            } else {
                $image->resizeImage($args['width'], $args['height'], $args['filter'], $args['blur'], $args['bestfit']);
            }
            $image->writeImage($args['output_format'].':'.$args['output_file']);

            return true; // Success.
            //
        } catch (\Throwable $Exception) {
            echo $Exception->getMessage()."\n";
            if (!$output_file_existed_prior && is_file($args['output_file'])) {
                unlink($args['output_file']);
            }
            return false;
        }
    }

    /**
     * Texturize image.
     *
     * @since 17xxxx Imagick utils.
     *
     * @param array $args Named arguments.
     *
     * @return bool True on success.
     */
    public function texturize(array $args): bool
    {
        if (!class_exists('Imagick')) {
            return false;
        }
        $default_args = [
            'file'   => '',
            'format' => '',

            'size'   => 0,
            'width'  => 0,
            'height' => 0,

            'output_file'   => '',
            'output_format' => '',
        ];
        $args += $default_args; // Defaults.
        $args = $this->parseFormatArgs($args);

        if (!is_file($args['file']) || !$args['format']) {
            return false; // Not possible.
        } elseif (!$args['output_file'] || !$args['output_format']) {
            return false; // Not possible.
        }
        $args['size']   = max(1, (int) $args['size']);
        $args['width']  = max(1, (int) ($args['width'] ?: $args['size']));
        $args['height'] = max(1, (int) ($args['height'] ?: $args['size']));

        $output_file_existed_prior = is_file($args['output_file']);

        try { // Catch exceptions.
            if ($args['format'] === 'svg' && $args['output_format'] === 'svg') {
                $xml = new \DOMDocument('1.0', 'utf-8');
                $svg = ['texture_nodes' => []];

                if (!$xml->load($args['file'])) {
                    throw $this->c::issue('SVG load failure.');
                } elseif (!($svg['tag'] = $xml->getElementsByTagName('svg')->item(0))) {
                    throw $this->c::issue('SVG tag parse failure.');
                }
                while ($svg['tag']->hasChildNodes()) {
                    $svg['texture_nodes'][] = $svg['tag']->firstChild;
                    $svg['tag']->removeChild($svg['tag']->firstChild);
                }
                $svg['defs']    = $xml->createElement('defs');
                $svg['texture'] = $xml->createElement('pattern');
                $svg['rect']    = $xml->createElement('rect');

                $svg['tag']->appendChild($svg['defs']);
                $svg['defs']->appendChild($svg['texture']);
                $svg['tag']->appendChild($svg['rect']);

                $svg['texture']->setAttribute('id', 'texture');
                $svg['texture']->setAttribute('patternUnits', 'userSpaceOnUse');
                $svg['texture']->setAttribute('width', $svg['tag']->getAttribute('width'));
                $svg['texture']->setAttribute('height', $svg['tag']->getAttribute('height'));

                foreach ($svg['texture_nodes'] as $_texture_node) {
                    $svg['texture']->appendChild($_texture_node);
                } // unset($_texture_node); // Housekeeping.

                $svg['rect']->setAttribute('width', '100%');
                $svg['rect']->setAttribute('height', '100%');
                $svg['rect']->setAttribute('fill', 'url(#texture)');

                $svg['tag']->setAttribute('width', (string) $args['width']);
                $svg['tag']->setAttribute('height', (string) $args['height']);

                if (file_put_contents($args['output_file'], $xml->saveXML()) === false) {
                    throw $this->c::issue('SVG save failure.');
                }
                return true; // Success.
            } else {
                $image = new \Imagick();
                $image->setBackgroundColor('transparent');
                $image->readImage($args['format'].':'.$args['file']);

                $canvas = new \Imagick(); // To hold texturization.
                $canvas->newImage($args['width'], $args['height'], 'transparent', $args['output_format']);

                $image = $canvas->textureImage($image);
                $image->writeImage($args['output_format'].':'.$args['output_file']);

                return true; // Success.
            }
        } catch (\Throwable $Exception) {
            if (!$output_file_existed_prior && is_file($args['output_file'])) {
                unlink($args['output_file']);
            }
            return false;
        }
    }

    /**
     * Compress image.
     *
     * @since 17xxxx Imagick utils.
     *
     * @param array $args Named arguments.
     *
     * @return bool True on success.
     */
    public function compress(array $args): bool
    {
        if (!class_exists('Imagick')) {
            return false; // Not possible.
        }
        $default_args = [
            'file'   => '',
            'format' => '',

            // For SVGs (1-8).
            'precision' => 0,

            // For PNGs (1-100).
            'min_quality' => 0,
            'max_quality' => 0,

            // For JPGs (1-100).
            'quality' => 0,

            'output_file'   => '',
            'output_format' => '',
        ];
        $args += $default_args; // Defaults.
        $args = $this->parseFormatArgs($args);

        if (!is_file($args['file']) || !$args['format']) {
            return false; // Not possible.
        } elseif (!$args['output_file'] || !$args['output_format']) {
            return false; // Not possible.
        }
        if ($args['output_format'] === 'svg') {
            return $this->compressSvg($args['file'], $args);
        } elseif (mb_stripos($args['output_format'], 'png') === 0) {
            return $this->compressPng($args['file'], $args);
        }
        $args['quality'] = max(0, min(100, (int) $args['quality']));

        $output_file_existed_prior = is_file($args['output_file']);
        $compression_type          = $this->formatToCompressionType($args['output_format']);
        $compression_quality       = $args['quality'] ?: $this->formatToCompressionQuality($args['output_format']);

        try { // Catch exceptions.
            $image = new \Imagick();
            $image->setBackgroundColor('transparent');
            $image->readImage($args['format'].':'.$args['file']);

            $image->stripImage(); // Profiles/comments.
            $image->setImageCompression($compression_type);
            $image->setImageCompressionQuality($compression_quality);
            $image->writeImage($args['output_format'].':'.$args['output_file']);

            return true; // Success.
            //
        } catch (\Throwable $Exception) {
            if (!$output_file_existed_prior && is_file($args['output_file'])) {
                unlink($args['output_file']);
            }
            return false;
        }
    }

    /**
     * Compress SVG image.
     *
     * @since 17xxxx Initial release.
     *
     * @param string $file SVG file path.
     * @param array  $args Behavioral args.
     *
     * @return bool True on success.
     *
     * @see https://github.com/svg/svgo
     */
    protected function compressSvg(string $file, array $args = []): bool
    {
        if (!$this->c::canCallFunc('exec')) {
            return false;
        }
        $default_args = [
            'precision'   => 0,
            'output_file' => '',
        ];
        $args += $default_args; // Defaults.

        if (!$file || !is_file($file)) {
            return false; // Not possible.
        }
        $args['precision'] = (int) ($args['precision'] ?: 4);
        $args['precision'] = max(1, min(8, $args['precision']));

        $args['output_file'] = (string) $args['output_file'];
        $args['output_file'] = $args['output_file'] ?: $file;

        $output_file_existed_prior = is_file($args['output_file']);

        $esc_file        = $this->c::escShellArg($file);
        $esc_precision   = $this->c::escShellArg($args['precision']);
        $esc_output_file = $this->c::escShellArg($args['output_file']);
        exec('svgo --quiet --precision='.$esc_precision.' --output='.$esc_output_file.' '.$esc_file, $_, $status);

        if ($status === 0) {
            return true; // Success.
        } else {
            if (!$output_file_existed_prior && is_file($args['output_file'])) {
                unlink($args['output_file']);
            }
            return false;
        }
    }

    /**
     * Compress PNG image.
     *
     * @since 161011 Initial release.
     *
     * @param string $file PNG file path.
     * @param array  $args Behavioral args.
     *
     * @return bool True on success.
     *
     * @see https://pngquant.org/php.html
     */
    protected function compressPng(string $file, array $args = []): bool
    {
        if (!$this->c::canCallFunc('exec')) {
            return false;
        }
        $default_args = [
            'min_quality' => 0,
            'max_quality' => 0,
            'output_file' => '',
        ];
        $args += $default_args; // Defaults.

        if (!$file || !is_file($file)) {
            return false; // Not possible.
        }
        $args['min_quality'] = (int) ($args['min_quality'] ?: 60);
        $args['min_quality'] = max(0, min(100, $args['min_quality']));

        $args['max_quality'] = (int) ($args['max_quality'] ?: 90);
        $args['max_quality'] = max($args['min_quality'], min(100, $args['max_quality']));

        $args['output_file'] = (string) $args['output_file'];
        $args['output_file'] = $args['output_file'] ?: $file;

        $output_file_existed_prior = is_file($args['output_file']);

        $esc_file        = $this->c::escShellArg($file);
        $esc_output_file = $this->c::escShellArg($args['output_file']);
        $esc_quality     = $this->c::escShellArg($args['min_quality'].'-'.$args['max_quality']);
        exec('pngquant --skip-if-larger --quality='.$esc_quality.' --force --output='.$esc_output_file.' '.$esc_file, $_, $status);

        if ($status === 0) {
            return true; // Success.
            //
        } elseif (in_array($status, [98, 99], true)) {
            if ($args['output_file'] !== $file) {
                return copy($file, $args['output_file']);
            } else {
                return true;
            } // See `$ man pngquant` for further details.
            // `98` = --skip-if-larger, `99` = --quality min-max.
        } else {
            if (!$output_file_existed_prior && is_file($args['output_file'])) {
                unlink($args['output_file']);
            }
            return false;
        }
    }

    /**
     * Decode image data URL.
     *
     * @since 161011 Initial release.
     *
     * @param string $url Data URL.
     *
     * @return array `[raw_image_data, extension]`.
     */
    public function decodeDataUrl(string $url): array
    {
        if (mb_stripos($url, 'data:') !== 0) {
            return []; // Not applicable.
        }
        $data     = str_replace(' ', '+', $url);
        $data_100 = mb_substr($data, 0, 100);

        if (mb_stripos($data_100, 'data:image/x-icon;base64,') === 0) {
            $extension = 'ico';
            //
        } elseif (mb_stripos($data_100, 'data:image/icon;base64,') === 0) {
            $extension = 'ico';
            //
        } elseif (mb_stripos($data_100, 'data:image/ico;base64,') === 0) {
            $extension = 'ico';
            //
        } elseif (mb_stripos($data_100, 'data:image/gif;base64,') === 0) {
            $extension = 'gif';
            //
        } elseif (mb_stripos($data_100, 'data:image/jpg;base64,') === 0) {
            $extension = 'jpg';
            //
        } elseif (mb_stripos($data_100, 'data:image/jpeg;base64,') === 0) {
            $extension = 'jpg';
            //
        } elseif (mb_stripos($data_100, 'data:image/png;base64,') === 0) {
            $extension = 'png';
            //
        } elseif (mb_stripos($data_100, 'data:image/svg+xml;base64,') === 0) {
            $extension = 'svg';
            //
        } elseif (mb_stripos($data_100, 'data:image/webp;base64,') === 0) {
            $extension = 'webp';
        }
        if (empty($extension)) {
            return []; // Not possible.
            //
        } elseif (!($raw_image_data = mb_substr($data, mb_strpos($data, ',') + 1))) {
            return []; // Decode failure.
            //
        } elseif (!($raw_image_data = base64_decode($raw_image_data, true))) {
            return []; // Decode failure.
        }
        return compact('raw_image_data', 'extension');
    }

    /**
     * One pixel image.
     *
     * @since 17xxxx Imagick utils.
     *
     * @param string $format Format.
     *
     * @return string One pixel image.
     */
    public function onePx(string $format): string
    {
        switch ($this->formatToExt($format)) {
            case 'svg':
                $_1px = '<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"/>';
                break;

            case 'png':
                $_1px = base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=');
                break;

            case 'jpg': // Not transparent.
                $_1px = base64_decode('/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAP//////////////////////////////////////////////////////////////////////////////////////2wBDAf//////////////////////////////////////////////////////////////////////////////////////wgARCAABAAEDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAAA//EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAADP/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/9oACAEBAAE/AH//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAECAQE/AH//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==');
                break;

            case 'gif':
                $_1px = base64_decode('R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==');
                break;

            default:
                $_1px = '';
        }
        return $_1px;
    }

    /**
     * Extension to format.
     *
     * @since 17xxxx Imagick utils.
     *
     * @param string $ext_file Extension (or file).
     *
     * @return string Extension format.
     */
    public function extToFormat(string $ext_file): string
    {
        if ($ext_file && !preg_match('/^[a-z0-9_\-]+$/ui', $ext_file)) {
            $ext = $this->c::fileExt($ext_file);
        } else {
            $ext = $ext_file;
        }
        switch (($ext = mb_strtolower($ext))) {
            case 'jpg':
                $format = 'jpeg';
                break;

            case 'png':
                $format = 'png32';
                break;

            default:
                $format = $ext;
        }
        return $format;
    }

    /**
     * Format to extension.
     *
     * @since 17xxxx Imagick utils.
     *
     * @param string $format Image format.
     *
     * @return string Format extension.
     */
    public function formatToExt(string $format): string
    {
        switch (($format = mb_strtolower($format))) {
            case 'jpeg':
                $ext = 'jpg';
                break;

            case 'png8':
            case 'png16':
            case 'png24':
            case 'png32':
            case 'png64':
                $ext = 'png';
                break;

            default:
                $ext = $format;
        }
        return $ext;
    }

    /**
     * Extension to MIME type.
     *
     * @since 17xxxx Imagick utils.
     *
     * @param string $ext_file Extension (or file).
     *
     * @return string Extension MIME type.
     */
    public function extToMimeType(string $ext_file): string
    {
        if ($ext_file && !preg_match('/^[a-z0-9_\-]+$/ui', $ext_file)) {
            $ext = $this->c::fileExt($ext_file);
        } else {
            $ext = $ext_file;
        }
        switch (($ext = mb_strtolower($ext))) {
            case 'svg':
                $type = 'image/svg+xml; charset=utf-8';
                break;

            case 'png':
                $type = 'image/png';
                break;

            case 'jpg':
                $type = 'image/jpeg';
                break;

            case 'gif':
                $type = 'image/gif';
                break;

            default:
                $type = $ext ? 'image/'.$ext : '';
        }
        return $type;
    }

    /**
     * Format to MIME type.
     *
     * @since 17xxxx Imagick utils.
     *
     * @param string $format Image format.
     *
     * @return string Format MIME type.
     */
    public function formatToMimeType(string $format): string
    {
        return $this->extToMimeType($this->formatToExt($format));
    }

    /**
     * Format to compression type.
     *
     * @since 17xxxx Imagick utils.
     *
     * @param string $format Image format.
     *
     * @return int Format compression type.
     */
    protected function formatToCompressionType(string $format): int
    {
        if (!class_exists('Imagick')) {
            return 0; // Not possible.
        }
        switch (($ext = $this->formatToExt($format))) {
            case 'jpg':
                $type = \Imagick::COMPRESSION_JPEG;
                break;

            default:
                $type = \Imagick::COMPRESSION_UNDEFINED;
        }
        return $type;
    }

    /**
     * Format to compression quality.
     *
     * @since 17xxxx Imagick utils.
     *
     * @param string $format Image format.
     *
     * @return int Format compression quality.
     */
    protected function formatToCompressionQuality(string $format): int
    {
        switch (($ext = $this->formatToExt($format))) {
            case 'jpg':
                $quality = 85;
                break;

            default:
                $quality = 92;
        }
        return $quality;
    }

    /**
     * Set format extension.
     *
     * @since 17xxxx Imagick utils.
     *
     * @param string $file   Image file.
     * @param string $format Image format.
     *
     * @return string File w/ format extension.
     */
    protected function setFormatExt(string $file, string $format): string
    {
        return $this->c::setFileExt($file, $this->formatToExt($format));
    }

    /**
     * Change format extension.
     *
     * @since 17xxxx Imagick utils.
     *
     * @param string $file   Image file.
     * @param string $format Image format.
     *
     * @return string File w/ possible format extension.
     */
    protected function changeFormatExt(string $file, string $format): string
    {
        return $this->c::changeFileExt($file, $this->formatToExt($format));
    }

    /**
     * Parse format args.
     *
     * @since 17xxxx Imagick utils.
     *
     * @param array $args Named arguments.
     *
     * @return array Parsed arguments.
     */
    protected function parseFormatArgs(array $args): array
    {
        $default_args = [
            'file'   => '',
            'format' => '',

            'output_file'   => '',
            'output_format' => '',
        ];
        $args += $default_args; // Defaults.

        $args['file']          = (string) $args['file'];
        $args['output_file']   = (string) $args['output_file'];

        $args['format']        = mb_strtolower((string) $args['format']);
        $args['output_format'] = mb_strtolower((string) $args['output_format']);

        $args['format']        = $args['format'] === 'jpg' ? 'jpeg' : $args['format'];
        $args['output_format'] = $args['output_format'] === 'jpg' ? 'jpeg' : $args['output_format'];

        $args['format']        = $args['format'] ?: $this->extToFormat($args['file']);
        $args['output_file']   = $args['output_file'] ?: $this->changeFormatExt($args['file'], $args['output_format']);
        $args['output_format'] = $args['output_format'] ?: $this->extToFormat($args['output_file']);

        return $args;
    }
}


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

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

class Capsuleer extends Base
{
    public $portrait;

    public $private;

    // by Warringer\Types\Dict
    public function setPortrait($portrait)
    {
        // by Warringer\Types\Dict
        $converters = [];
        $converters['pattern'] = function ($value) { return $value; };

        $func = function ($value) use($converters) {
            $return = new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS);
            $return['pattern'] = isset($value->{'pattern'}) ? $converters['pattern']($value->{'pattern'}) : null;
            return $return;
        };
        $this->portrait = $func($portrait);
    }

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

}


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

use Ivory\GoogleMap\Exception\ServiceException;
use Ivory\GoogleMap\Services\Utils\XmlParser;
use Widop\HttpAdapter\HttpAdapterInterface;

/**
 * Abstract class for accesing google API.
 *
 * @author GeLo <geloen.eric@gmail.com>
 */
abstract class AbstractService
{
    /** @var \Widop\HttpAdapter\HttpAdapterInterface */
    protected $httpAdapter;

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

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

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

    /** @var \Ivory\GoogleMap\Services\Utils\XmlParser */
    protected $xmlParser;

    /** @var \Ivory\GoogleMap\Services\BusinessAccount */
    protected $businessAccount;

    /**
     * Creates a service.
     *
     * @param \Widop\HttpAdapter\HttpAdapterInterface   $httpAdapter     The http adapter.
     * @param string                                    $url             The service url.
     * @param boolean                                   $https           TRUE if the service uses HTTPS else FALSE.
     * @param string                                    $format          Format used by the service.
     * @param \Ivory\GoogleMap\Services\Utils\XmlParser $xmlParser       The xml parser.
     * @param \Ivory\GoogleMap\Services\BusinessAccount $businessAccount The business account.
     */
    public function __construct(
        HttpAdapterInterface $httpAdapter,
        $url,
        $https = false,
        $format = 'json',
        XmlParser $xmlParser = null,
        BusinessAccount $businessAccount = null
    ) {
        if ($xmlParser === null) {
            $xmlParser = new XmlParser();
        }

        $this->setHttpAdapter($httpAdapter);
        $this->setUrl($url);
        $this->setHttps($https);
        $this->setFormat($format);
        $this->setXmlParser($xmlParser);
        $this->setBusinessAccount($businessAccount);
    }

    /**
     * Gets the http adapter.
     *
     * @return \Widop\HttpAdapter\HttpAdapterInterface The http adapter.
     */
    public function getHttpAdapter()
    {
        return $this->httpAdapter;
    }

    /**
     * Sets the http adapter.
     *
     * @param \Widop\HttpAdapter\HttpAdapterInterface $httpAdapter The http adapter.
     */
    public function setHttpAdapter(HttpAdapterInterface $httpAdapter)
    {
        $this->httpAdapter = $httpAdapter;
    }

    /**
     * Gets the service API url according to the https flag.
     *
     * @return string The service API url.
     */
    public function getUrl()
    {
        if ($this->isHttps()) {
            return str_replace('http://', 'https://', $this->url);
        }

        return $this->url;
    }

    /**
     * Sets the service API url.
     *
     * @param string $url The service API url.
     *
     * @throws \Ivory\GoogleMap\Exception\ServiceException If the url is not valid.
     */
    public function setUrl($url)
    {
        if (!is_string($url)) {
            throw ServiceException::invalidServiceUrl();
        }

        $this->url = $url;
    }

    /**
     * Checks if the service uses HTTPS.
     *
     * @return boolean TRUE if the service uses HTTPS else FALSE.
     */
    public function isHttps()
    {
        return $this->https;
    }

    /**
     * Sets the service HTTPS flag.
     *
     * @param boolean $https TRUE if the service uses HTTPS else FALSE.
     *
     * @throws \Ivory\GoogleMap\Exception\ServiceException If the https flag is not valid.
     */
    public function setHttps($https)
    {
        if (!is_bool($https)) {
            throw ServiceException::invalidServiceHttps();
        }

        $this->https = $https;
    }

    /**
     * Gets the service format.
     *
     * @return string The service format.
     */
    public function getFormat()
    {
        return $this->format;
    }

    /**
     * Sets the service format
     *
     * @param string $format The service format.
     *
     * @throws \Ivory\GoogleMap\Exception\ServiceException If the format is not valid.
     */
    public function setFormat($format)
    {
        if (!in_array($format, array('json', 'xml'))) {
            throw ServiceException::invalidServiceFormat();
        }

        $this->format = $format;
    }

    /**
     * Gets the xml parser.
     *
     * @return \Ivory\GoogleMap\Services\Utils\XmlParser The xml parser.
     */
    public function getXmlParser()
    {
        return $this->xmlParser;
    }

    /**
     * Sets the xml parser.
     *
     * @param \Ivory\GoogleMap\Services\Geocoding\XmlParser $xmlParser The xml parser.
     */
    public function setXmlParser(XmlParser $xmlParser)
    {
        $this->xmlParser = $xmlParser;
    }

    /**
     * Checks if the service has a business account.
     *
     * @return boolean TRUE if the service has a business account else FALSE.
     */
    public function hasBusinessAccount()
    {
        return $this->businessAccount !== null;
    }

    /**
     * Gets the business account.
     *
     * @return \Ivory\GoogleMap\Services\BusinessAccount The business account.
     */
    public function getBusinessAccount()
    {
        return $this->businessAccount;
    }

    /**
     * Sets the business account.
     *
     * @param \Ivory\GoogleMap\Services\BusinessAccount $businessAccount The business account.
     */
    public function setBusinessAccount(BusinessAccount $businessAccount = null)
    {
        $this->businessAccount = $businessAccount;
    }

    /**
     * Sign an url for business account.
     *
     * @param string $url The url.
     *
     * @return string The signed url.
     */
    protected function signUrl($url)
    {
        if (!$this->hasBusinessAccount()) {
            return $url;
        }

        return $this->businessAccount->signUrl($url);
    }

    /**
     * Sends a service request.
     *
     * @param string $url The service url.
     *
     * @return \Widop\HttpAdapter\HttpResponse The response.
     *
     * @throws \Ivory\GoogleMap\Exception\ServiceException If the response is null.
     * @throws \Ivory\GoogleMap\Exception\ServiceException If the response has an error 4XX or 5XX.
     */
    protected function send($url)
    {
        $response = $this->httpAdapter->getContent($url);

        if ($response === null) {
            throw ServiceException::invalidServiceResult();
        }

        $statusCode = (string) $response->getStatusCode();

        if ($statusCode[0] === '4' || $statusCode[0] === '5') {
            throw ServiceException::invalidResponse($url, $statusCode);
        }

        return $response;
    }
}


<?php
namespace Perry\Representation;

use Perry\Response;
use Perry\Setup;

class Base
{
    protected $genericMembers = array();

    /**
     * @param null|array|object|string $inputData
     * @throws \Exception
     */
    public function __construct($inputData)
    {
        $inputData = $this->cleanInputData($inputData);

        foreach ($inputData as $key => $value) {
            $method = 'set'.ucfirst($key);
            // if there is a setter method for this call the setter
            if (method_exists($this, $method)) {
                $this->{$method}($value);
            } else {
                $this->genericMembers[$key] = $value;
            }
        }
    }

    /**
     * clean input data
     *
     * @param array|object|null|string $inputData
     * @throws \Exception
     * @returns array
     */
    private function cleanInputData($inputData)
    {

        switch (true) {
            case $inputData instanceof Response:
                $inputData = json_decode((string) $inputData);
                break;
            case is_null($inputData):
                throw new \Exception("got NULL in Base Construtor");
            case is_string($inputData):
                $inputData = json_decode($inputData);
                break;
            case is_object($inputData):
                $inputData = get_object_vars($inputData);
                break;
            default:
        }

        if (!is_array($inputData) && !is_object($inputData)) {
            throw new \Exception("inputData is not an array, and therefor can't be traversed");
        }

        return $inputData;
    }

    /**
     * @param string $key
     * @return \Perry\Representation\Base|string|integer|float|null
     */
    public function __get($key)
    {
        if (isset($this->genericMembers[$key])) {
            return $this->genericMembers[$key];
        }

        return null;
    }

    /**
     * @param string $method
     * @param array $args
     * @return \Perry\Representation\Base
     * @throws \Exception
     */
    public function __call($method, $args)
    {
        if (isset($this->{$method}) && $this->{$method} instanceof Interfaces\CanRefer) {
            /**
             * @var Interfaces\CanRefer $reference
             */
            $reference = $this->{$method};

            return $reference->call($args);
        } else {
            throw new \Exception("$method does not exist with this object");
        }
    }

    /**
     * Magic isset method, to ensure keys are found
     * @param string $key
     * @return bool
     */
    public function __isset($key)
    {
        return isset($this->genericMembers[$key]);
    }

    /**
     * @param string $url
     * @param string $representation
     * @throws \Exception
     * @return Response
     */
    protected static function doGetRequest($url, $representation)
    {
        // use configured fetcher
        return Setup::getInstance()->fetcher->doGetRequest($url, $representation);
    }
}


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

/**
 * Upload size.
 *
 * @since 150424 Initial release.
 */
class UploadSize extends Classes\Core\Base\Core
{
    /**
     * Max allowed file upload size.
     *
     * @since 150424 Initial release.
     * @since 170824.30708 Returns `int` instead of `float`.
     *
     * @return int Max allowed file upload size, in bytes.
     */
    public function limit(): int
    {
        $limits = [PHP_INT_MAX]; // Initialize.

        if (($max_upload_size = ini_get('upload_max_filesize'))) {
            $limits[] = $this->c::abbrToBytes($max_upload_size);
        }
        if (($post_max_size = ini_get('post_max_size'))) {
            $limits[] = $this->c::abbrToBytes($post_max_size);
        }
        if (($memory_limit = ini_get('memory_limit'))) {
            $limits[] = $this->c::abbrToBytes($memory_limit);
        }
        return $max = min($limits);
    }
}


<?php
//------------------------
//· 控制器类
//---------------------
namespace denha;

class Controller
{
    public static $assign = [];

    /**
     * 赋值
     * @date   2017-05-14T21:30:23+0800
     * @author ChenMingjiang
     * @param  [type]                   $field [变量名]
     * @param  [type]                   $value [变量值]
     * @return [type]                          [description]
     */
    protected function assign($name, $value = '')
    {
        if (is_array($name)) {
            self::$assign = array_merge(self::$assign, $name);
        } else {
            self::$assign[$name] = $value;
        }

    }

    /**
     * [show description]
     * @date   2018-06-25T20:37:55+0800
     * @author ChenMingjiang
     * @param  string                   $viewPath       [视图地址]
     * @param  array                    $viewParamData  [渲染变量值]
     * @param  array                    $options        [预定义参数]
     * @return [type]                                   [description]
     */
    protected function show($viewPath = '', $viewParamData = [], $options = [])
    {

        // true 自定义路径
        $peg = isset($options['peg']) ? $options['peg'] : false;
        // 单个视图关闭调试模式
        $trace = isset($options['trace']) ? $options['trace'] : true;

        if (get('all')) {
            extract(get('all'), EXTR_OVERWRITE);
        }

        if (self::$assign) {
            // 模板阵列变量分解成为独立变量
            extract(self::$assign, EXTR_OVERWRITE);
        }

        if ($viewParamData) {
            // 模板阵列变量分解成为独立变量
            extract($viewParamData, EXTR_OVERWRITE);
        }

        if (!$peg) {
            if (!$viewPath) {
                $path = VIEW_PATH . str_replace('.', DS, MODULE) . DS . parseName(CONTROLLER, false) . DS . ACTION . '.html';
            }
            //绝对路径
            elseif (stripos($viewPath, '/') === 0) {
                $path = VIEW_PATH . substr($viewPath, 1) . '.html';
            }
            //相对路径
            else {
                $path = VIEW_PATH . str_replace('.', DS, MODULE) . DS . $viewPath . '.html';
            }
        } else {
            $path = $viewPath;
        }

        if (!is_file($path)) {
            throw new Exception('视图地址:' . $path . '不存在 ');
        }

        $cachePath = DATA_TPL_PATH . md5($path) . '.php';

        ob_start();
        // 开启页面缓存
        if (is_file($cachePath) && filemtime($path) == filemtime($cachePath) && !config('debug')) {
            include $cachePath;
        } else {
            // 处理视图模板
            $template = new Template($path);
            $template->getContent();
            include $template->loadPath;
        }

        $content = ob_get_clean();

        // 标签翻译功能
        if (config('tag_trans')) {
            $content = $this->tagTrans($content);
        }

        echo $content;

        // 模块debug功能
        if (config('trace') && $trace) {
            Trace::run();
        }
    }

    /** 标签翻译功能 */
    protected function tagTrans($content)
    {

        $regular = '/{FY:(.*?):(.*?)}/is';
        preg_match_all('/{FY:(.*?):(.*?)}/is', $content, $matches);

        if (array_filter($matches)) {

            //组合翻译结构
            foreach ($matches[0] as $key => $value) {
                $transArray[$matches[2][$key]][] = $matches[1][$key];
            }

            //批量翻译
            if ($transArray) {
                foreach ($transArray as $key => $value) {
                    if ($key != 'zh') {
                        $transValue[$key] = dao('BaiduTrans')->baiduTrans($value, $key, 'zh');
                    } else {
                        foreach ($value as $key => $value) {
                            $transValue['zh'][$value] = $value;
                        }
                    }
                }

                foreach ($transValue as $key => $value) {
                    foreach ($value as $k => $v) {
                        $content = str_replace("{FY:$k:$key}", $v, $content);
                    }
                }
            }

        }

        return $content;
    }

    /**
     * ajax返回
     * @date   2019-01-17T21:33:34+0800
     * ajaxReturn(true,'xxx',['abc'=>111]) => ['status'=>true,'msg'=>'xxx',data=>['abc'=>111]]
     * ajaxReturn(false,'xxx') => ['status'=>false,'msg'=>'xxx']
     * ajaxReturn(['status'=>true,'msg'=>'xxx','data'=>['abc'=>111]]) => ['status'=>true,'msg'=>'xxx','data'=>['abc'=>111]]
     * ajaxReturn(false,['abc'=>111]) => ['status'=>false,'msg'=>'操作成功',data=>['abc'=>111]]
     * @author ChenMingjiang
     * @param  [type]                   $status [array则直接合并 bool则表示status]
     * @param  [type]                   $msg    [array则表示为data值 string则表示msg]
     * @param  [type]                   $data   [返回参数]
     * @param  string                   $lg     [语言类型]
     * @return [type]                           [description]
     */
    public function ajaxReturn($status, $msg = null, $data = null)
    {
        header("Content-Type:application/json; charset=utf-8");

        // 处理参数信息
        if (is_array($status)) {
            $value = $status;
        } else {
            $value['status'] = $status;
            if (is_array($msg)) {
                $value['data'] = $msg;
            } elseif ($msg !== null) {
                $value['msg'] = $msg;
            }

            if ($data !== null) {
                $value['data'] = $data;
            }
        }

        $array = [
            'status' => true,
            'data'   => [],
            'msg'    => '操作成功',
        ];

        // 控制开关
        if (Start::$config['app_debug']) {
            $debug = [
                'debug' => [
                    'param' => [
                        'post'  => (array) post('all'),
                        'get'   => (array) get('all'),
                        'files' => $_FILES,
                    ],
                    'ip'    => getIP(),
                    'sql'   => Trace::$sqlInfo,
                ],
            ];
            $array = array_merge($array, $debug);
        }

        $value = array_merge($array, $value);

        // jsonpReturn返回
        $callback = get('callbak', 'text', '');
        if ($callback && IS_GET) {
            exit($callback . '(' . json_encode($value) . ')');
        }

        // 正常ajax返回
        exit(json_encode($value));
    }

    /**
     * app返回参数
     * @date   2018-01-30T16:43:24+0800
     * @author ChenMingjiang
     * @param  [type]                   $value [description]
     * @param  [type]                   $lg    [国家]
     * @return [type]                          [description]
     */
    protected function appReturn($value, $lg = 'zh')
    {
        header("Content-Type:application/json; charset=utf-8");
        $array = array(
            'code'   => 200,
            'status' => true,
            'data'   => array(),
            'msg'    => '获取数据成功',
        );

        //控制开关
        if (Start::$config['app_debug']) {
            $debug = array(
                'debug' => [
                    'param' => [
                        'post'  => (array) post('all'),
                        'get'   => (array) get('all'),
                        'files' => $_FILES,
                    ],
                    'ip'    => getIP(),
                    'sql'   => Trace::$sqlInfo,
                ],
            );
            $array = array_merge($array, $debug);
        }

        $value = array_merge($array, $value);
        if ($lg != 'zh') {
            $value['msg'] = dao('BaiduTrans')->baiduTrans($value['msg'], $this->lg);
        }

        exit(json_encode($value));
    }

    /**
     * jsonpReturn返回
     * @date   2017-08-07T10:41:59+0800
     * @author ChenMingjiang
     * @param  array                    $value    [description]
     * @param  string                   $callback [description]
     * @return [type]                             [description]
     */
    protected function jsonpReturn(array $value, $callback = '')
    {
        if ($callback) {
            exit($callback . '(' . json_encode($value) . ')');
        } else {
            $this->ajaxReturn($value);
        }

    }
}


<?php
/**
 * AWS 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;
#
use Aws\Sdk;
use Aws\S3\S3Client;
use Aws\CloudFront\CloudFrontClient;

/**
 * AWS utilities.
 *
 * @since 160719 AWS utilities.
 */
class Aws extends Classes\Core\Base\Core
{
    /**
     * Get SDK instance.
     *
     * @since 170421.57490 SDK instance.
     *
     * @param array $args Instance args.
     *
     * @return Sdk SDK instance.
     */
    public function sdk(array $args = []): Sdk
    {
        if (($Sdk = &$this->cacheKey(__FUNCTION__, $args)) !== null) {
            return $Sdk; // Cached this already.
        }
        $default_args = [ // Global defaults.
            'version' => 'latest', // Dynamic.
            'region'  => $this->App->Config->©aws['©region'],

            'credentials' => [
                'key'    => $this->App->Config->©aws['©access_key'],
                'secret' => $this->App->Config->©aws['©secret_key'],
            ],
        ];
        $args       = array_replace_recursive($default_args, $args);
        return $Sdk = new Sdk($args); // Aws\Sdk instance.
    }

    /**
     * Get S3 client.
     *
     * @since 170421.57490 SDK instance.
     *
     * @param array $args Instance args.
     *
     * @return S3Client Client instance.
     */
    public function s3Client(array $args = []): S3Client
    {
        if (($S3Client = &$this->cacheKey(__FUNCTION__, $args)) !== null) {
            return $S3Client; // Cached this already.
        }
        $default_args = [ // Global config defaults.
            'version' => $this->App->Config->©aws['©s3_version'],
        ];
        $args            = array_replace_recursive($default_args, $args);
        return $S3Client = $this->sdk()->createS3($args);
    }

    /**
     * Get CloudFront client.
     *
     * @since 170421.57490 SDK instance.
     *
     * @param array $args Instance args.
     *
     * @return CloudFrontClient Client instance.
     */
    public function cloudFrontClient(array $args = []): CloudFrontClient
    {
        if (($CloudFrontClient = &$this->cacheKey(__FUNCTION__, $args)) !== null) {
            return $CloudFrontClient; // Cached this already.
        }
        $default_args = [ // Global config defaults.
            'version' => $this->App->Config->©aws['©cf_version'],
        ];
        $args                    = array_replace_recursive($default_args, $args);
        return $CloudFrontClient = $this->sdk()->createCloudFront($args);
    }

    /**
     * CloudFront URL signer.
     *
     * @since 170421.57490 Magic overload handler.
     *
     * @param string $url           URL to sign.
     * @param int    $expires_after Expiration in seconds.
     * @param array  $args          Args to URL signer.
     * @param array  $client_args   Client instance args.
     *
     * @return string Digitally signed URL.
     */
    public function cloudFrontSignUrl(string $url, int $expires_after = 86400, array $args = [], array $client_args = []): string
    {
        $default_args = [ // Global config defaults.
            'key_pair_id' => $this->App->Config->©aws['©cf_key_pair_id'],
            'private_key' => $this->App->Config->©aws['©cf_private_key_file'],
        ];
        $args            = array_replace_recursive($default_args, $args);
        $args['url']     = $url; // `url` and `expires` via params.
        $args['expires'] = time() + max(0, $expires_after);

        $CloudFrontClient  = $this->cloudFrontClient($client_args);
        return $signed_url = $CloudFrontClient->getSignedUrl($args);
    }
}


<?php
declare(strict_types=1);

namespace RabbitCMS\Modules\Support;

use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\View as ViewFacade;
use RabbitCMS\Modules\Support\Facade\Modules;

/**
 * Trait ModuleController
 *
 * @package RabbitCMS\Modules\Support
 */
trait ModuleConcerns
{
    use ModuleDetect;

    /**
     * @param string $path
     *
     * @return string
     */
    public function asset($path)
    {
        return Modules::asset($this->module()->getName(), $path);
    }

    /**
     * @param string $key
     * @param array  $parameters
     * @param null   $locale
     *
     * @return array|null|string
     */
    public function trans($key, array $parameters = [], $locale = null)
    {
        return trans($this->module()->getName() . '::' . $key, $parameters, $locale);
    }

    /**
     * @param string $view
     * @param array  $data
     *
     * @return View
     */
    protected function view($view, array $data = []):View
    {
        return ViewFacade::make($this->viewName($view), $data, []);
    }

    /**
     * Get module view name.
     *
     * @param string $view
     *
     * @return string
     */
    protected function viewName($view):string
    {
        return $this->module()->getName() . '::' . $view;
    }
}


<?php

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

namespace Ivory\GoogleMap\Helper\Overlays;

use Ivory\GoogleMap\Helper\AbstractHelper;
use Ivory\GoogleMap\Map;
use Ivory\GoogleMap\Overlays\Circle;

/**
 * Circle helper.
 *
 * @author GeLo <geloen.eric@gmail.com>
 */
class CircleHelper extends AbstractHelper
{
   /**
     * Renders a circle.
     *
     * @param \Ivory\GoogleMap\Overlays\Circle $circle The circle.
     * @param \Ivory\GoogleMap\Map             $map    The map.
     *
     * @return string The JS output.
     */
    public function render(Circle $circle, Map $map)
    {
        $this->jsonBuilder
            ->reset()
            ->setValue('[map]', $map->getJavascriptVariable(), false)
            ->setValue('[center]', $circle->getCenter()->getJavascriptVariable(), false)
            ->setValue('[radius]', $circle->getRadius())
            ->setValues($circle->getOptions());

        return sprintf(
            '%s = new google.maps.Circle(%s);'.PHP_EOL,
            $circle->getJavascriptVariable(),
            $this->jsonBuilder->build()
        );
    }
}


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

/**
 * Version utilities.
 *
 * @since 150424 Initial release.
 */
class Version extends Classes\Core\Base\Core implements Interfaces\VersionConstants
{
    /**
     * Is it a valid version?
     *
     * @since 150424 Initial release.
     *
     * @param string $version Input version.
     *
     * @return bool `TRUE` if valid.
     */
    public function isValid(string $version): bool
    {
        if (!$version) {
            return false; // Nope.
        }
        return (bool) preg_match($this::VERSION_REGEX_VALID, $version);
    }

    /**
     * Is it a valid dev version?
     *
     * @since 150424 Initial release.
     *
     * @param string $version Input version.
     *
     * @return bool `TRUE` if valid.
     */
    public function isValidDev(string $version): bool
    {
        if (!$version) {
            return false; // Nope.
        }
        return (bool) preg_match($this::VERSION_REGEX_VALID_DEV, $version);
    }

    /**
     * Is it a valid stable version?
     *
     * @since 150424 Initial release.
     *
     * @param string $version Input version.
     *
     * @return bool `TRUE` if valid.
     */
    public function isValidStable(string $version): bool
    {
        if (!$version) {
            return false; // Nope.
        }
        return (bool) preg_match($this::VERSION_REGEX_VALID_STABLE, $version);
    }
}


<?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 TournamentTeam extends Base
{
    public $banFrequencyAgainst = [];

    public $shipsKilled;

    public $pilots = [];

    public $matches = [];

    public $flagshipType;

    public $name;

    public $seed;

    public $banFrequency = [];

    public $members;

    public $captain;

    public $iskKilled;

    // by Warringer\Types\ArrayType
    public function setBanFrequencyAgainst($banFrequencyAgainst)
    {
        // by Warringer\Types\Dict
        $converters = [];
        $converters['numBans'] = function ($value) { return $value; };
        $converters['shipType'] = function ($value) { return new Reference($value); };

        $func = function ($value) use($converters) {
            $return = new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS);
            $return['numBans'] = isset($value->{'numBans'}) ? $converters['numBans']($value->{'numBans'}) : null;
            $return['shipType'] = isset($value->{'shipType'}) ? $converters['shipType']($value->{'shipType'}) : null;
            return $return;
        };

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

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

    // by Warringer\Types\ArrayType
    public function setPilots($pilots)
    {
        // by Warringer\Types\Reference
        $func = function ($value) { return new Reference($value); };

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

    // by Warringer\Types\ArrayType
    public function setMatches($matches)
    {
        // by Warringer\Types\Reference
        $func = function ($value) { return new Reference($value); };

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

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

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

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

    // by Warringer\Types\ArrayType
    public function setBanFrequency($banFrequency)
    {
        // by Warringer\Types\Dict
        $converters = [];
        $converters['numBans'] = function ($value) { return $value; };
        $converters['shipType'] = function ($value) { return new Reference($value); };

        $func = function ($value) use($converters) {
            $return = new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS);
            $return['numBans'] = isset($value->{'numBans'}) ? $converters['numBans']($value->{'numBans'}) : null;
            $return['shipType'] = isset($value->{'shipType'}) ? $converters['shipType']($value->{'shipType'}) : null;
            return $return;
        };

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

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

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

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

}


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

/**
 * Captcha utilities.
 *
 * @since 170309.60830 Captcha utilities.
 */
class Captcha extends Classes\Core\Base\Core
{
    /**
     * Verifies Google reCAPTCHA.
     *
     * @since 170309.60830 Captcha utilities.
     *
     * @param string      $token      reCAPTCHA token from user.
     * @param string|null $ip         The user's remote IP address.
     * @param string|null $secret_key reCAPTCHA secret key.
     *
     * @return bool True if reCAPTCHA is valid.
     */
    public function recaptchaVerify(string $token, string $ip = null, string $secret_key = null): bool
    {
        if (!isset($ip)) {
            if (!$this->c::isCli()) {
                $ip = $this->c::currentIp();
            } else {
                $ip = ''; // Not possible.
            }
        } // Defaults to current IP address.

        if (!isset($secret_key)) {
            $secret_key = $this->App->Config->©recaptcha['©secret_key'];
        } // Defaults to global config value.

        $remote_args = [
            'body' => [
                'secret'   => $secret_key,
                'response' => $token,
                'remoteip' => $ip,
            ],
        ];
        $endpoint = 'https://www.google.com/recaptcha/api/siteverify';
        $response = json_decode($this->c::remoteResponse($endpoint, $remote_args)->body);

        return $is_valid = !empty($response->success);
    }
}


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

/**
 * CSRF utils.
 *
 * @since 17xxxx CSRF utils.
 */
class Csrf extends Classes\Core\Base\Core
{
    /**
     * CSRF generator.
     *
     * @since 17xxxx CSRF utils.
     *
     * @param callable|null $callback Callback.
     *
     * @return string New, unique CSRF token.
     *                CaSe-insensitive, 32 bytes in length.
     *
     * @note Callback stores the token for future verification.
     * If no callback, a `$_SESSION` storage handler is used by default.
     */
    public function create(callable $callback = null): string
    {
        $_csrf = $this->c::uuidV4();

        if (isset($callback)) {
            $callback($_csrf);
            return $_csrf;
        }
        if ($this->c::isActiveSession()) {
            return $_SESSION['_csrf'] = $_csrf;
        }
        throw $this->c::issue('No storage handler.');
    }

    /**
     * CSRF input markup.
     *
     * @since 17xxxx CSRF utils.
     *
     * @param callable|null $callback Callback.
     *
     * @return string Hidden input field with `_csrf` token.
     *                See also: {@link create()}
     */
    public function input(callable $callback = null): string
    {
        $_csrf = $this->create($callback);
        return '<input type="hidden" name="_csrf" value="'.$this->c::escAttr($_csrf).'" />';
    }

    /**
     * CSRF verifier.
     *
     * @since 17xxxx CSRF utils.
     *
     * @param string|null   $_csrf    Token.
     * @param callable|null $callback Callback.
     *
     * @return bool True if token is verified.
     *
     * @note Callback verifies the CSRF token.
     * If no callback, `$_SESSION` is used by default.
     */
    public function verify(string $_csrf = null, callable $callback = null): bool
    {
        if (!isset($_csrf)) {
            $_csrf = (string) $_REQUEST['_csrf'];
        } // Defaults to current request.

        if ($this->c::isActiveSession()) {
            if (empty($_SESSION['_csrf'])) {
                return false; // No token.
            }
            return $this->c::hashEquals($_SESSION['_csrf'], $_csrf);
        }
        throw $this->c::issue('No verification handler.');
    }
}


<?php
/**
 * Request.
 *
 * @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;
#
use Slim\Http\Uri;
use Slim\Http\Headers;
use Slim\Http\Cookies;
use Slim\Http\UploadedFile;
use Slim\Http\HeadersInterface;
#
use Psr\Http\Message\UriInterface;
use Psr\Http\Message\StreamInterface;

/**
 * Request.
 *
 * @since 17xxxx
 */
class Request extends \Slim\Http\Request
{
    /**
     * App.
     *
     * @since 17xxxx
     *
     * @type Classes\App
     */
    protected $App;

    /**
     * Class constructor.
     *
     * @param Classes\App      $App            App.
     * @param string           $method         Method.
     * @param UriInterface     $Uri            URI object.
     * @param HeadersInterface $Headers        Headers instance.
     * @param array            $cookies        Array of cookies.
     * @param array            $server_params  Environment variables.
     * @param StreamInterface  $Body           A request body instance.
     * @param array            $uploaded_files Uploaded files collection.
     */
    public function __construct(
        Classes\App $App,
        string $method,
        UriInterface $Uri,
        HeadersInterface $Headers,
        array $cookies,
        array $server_params,
        StreamInterface $Body,
        array $uploaded_files = []
    ) {
        $this->App = $App;
        $method    = $method ?: null;
        parent::construct($method, $Uri, $Headers, $cookies, $server_params, $Body, $uploaded_files);
    }

    /**
     * Create from globals.
     *
     * @since 17xxxx Request object.
     *
     * @param array $globals Globals.
     *
     * @return Request Instance from globals.
     */
    public static function createFromGlobals(array $globals)
    {
        $g   = &$globals;
        $App = $g['App'] ?? null;

        if (!($App instanceof Classes\App)) {
            throw new Exception('Missing App.');
        }
        unset($g['App']); // Ditch this.

        foreach ($g as $_key => $_value) {
            if (mb_stripos((string) $_key, 'CFG_') === 0) {
                unset($g[$_key]);
            }
        } // unset($_key, $_value);

        $method         = $g['REQUEST_METHOD'] ?? '';
        $Uri            = Uri::createFromGlobals($g);
        $Headers        = Headers::createFromGlobals($g);
        $cookies        = Cookies::parseHeader($Headers->get('cookie', []));
        $server_params  = $g; // Simply a copy of globals.
        $Body           = $App->c::createRequestBody();
        $uploaded_files = UploadedFile::createFromGlobals($g);

        $Request = new static($App, $method, $Uri, $Headers, $cookies, $server_params, $Body, $uploaded_files);

        if ($method === 'POST' && in_array($Request->getMediaType(), ['application/x-www-form-urlencoded', 'multipart/form-data'])) {
            $Request = $Request->withParsedBody($_POST);
        }
        return $Request;
    }

    /**
     * Get all data.
     *
     * @since 17xxxx Request.
     *
     * @return array All data.
     */
    public function getData(): array
    {
        $form_data   = $this->getFormData();
        $query_args  = $this->getQueryArgs();
        return $data = $form_data + $query_args;
    }

    /**
     * Get query args.
     *
     * @since 17xxxx Request.
     *
     * @return array Query args.
     */
    public function getQueryArgs(): array
    {
        $args        = $this->getQueryParams();
        return $args = is_array($args) ? $args : [];
    }

    /**
     * Get form data.
     *
     * @since 17xxxx Request.
     *
     * @return array Form data.
     */
    public function getFormData(): array
    {
        if (!in_array($this->getMediaType(), ['application/x-www-form-urlencoded', 'multipart/form-data'], true)) {
            return $data = []; // Not possible.
        }
        $data        = $this->getParsedBody();
        return $data = is_array($data) ? $data : [];
    }

    /**
     * Get parsed JSON body.
     *
     * @since 17xxxx Request object.
     *
     * @param string $type Expected data return type.
     *                     One of: `array`, `object`, `any`.
     *
     * @return \StdClass|array Body as object|array.
     */
    public function getJson(string $type = 'object')
    {
        if ($this->getMediaType() !== 'application/json') {
            return $json = $type === 'array' ? [] : (object) [];
        }
        if ($type === 'array') {
            $json        = $this->getParsedBody();
            return $json = is_array($json) ? $json : [];
            //
        } elseif ($type === 'object') {
            $json        = $this->getParsedBody();
            return $json = $json instanceof \StdClass ? $json : (object) [];
            //
        } else { // Array or object — either.
            $json        = $this->getParsedBody();
            return $json = is_array($json) || is_object($json) ? $json : (object) [];
        }
    }
}


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

/**
 * MailChimp utilities.
 *
 * @since 160620 Adding MailChimp.
 */
class MailChimp extends Classes\Core\Base\Core
{
    /**
     * Class constructor.
     *
     * @since 160620 Adding MailChimp.
     *
     * @param Classes\App $App Instance of App.
     */
    public function __construct(Classes\App $App)
    {
        parent::__construct($App);
    }

    /**
     * Subscribe.
     *
     * @since 160620 Adding MailChimp.
     *
     * @param string $email Email address.
     * @param array  $args  API and request args.
     *
     * @return \StdClass Including a boolean `success` property.
     *
     * @link http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/
     */
    public function subscribe(string $email, array $args = []): \StdClass
    {
        # Parse incoming arguments.

        $default_args = [
            'list_id' => $this->App->Config->©mailchimp['©list_id'],
            'api_key' => $this->App->Config->©mailchimp['©api_key'],

            'email_address' => $email,
            'status'        => 'pending',
            /*
                'merge_fields'  => [
                    'FNAME' => '',
                    'LNAME' => '',
                ],
            */
            /*
                'ip_signup' => '', // Initial IP.
                'ip_opt'    => '', // IP on opt-in.
            */
        ];
        $args                  = array_merge($default_args, $args);
        $args['email_address'] = mb_strtolower($args['email_address']);

        # Prepare request args and endpoint URL.

        $request_data = $args; // Start w/ a copy of `$args`.
        unset($request_data['list_id'], $request_data['api_key']);

        $request_args = [
            'headers' => [
                'content-type: application/json',
                'authorization: Basic '.base64_encode('_:'.$args['api_key']),
            ],
            'body'          => json_encode($request_data),
            'max_con_secs'  => 5, 'max_stream_secs' => 5,
            'fail_on_error' => false,
        ];
        $dc       = preg_replace('/^.+?\-/u', '', $args['api_key']);
        $endpoint = 'https://'.$dc.'.api.mailchimp.com/3.0/lists/'.urlencode($args['list_id']).'/members/';

        # Query for remote response via API endpoint URL.

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

        # Return structured response data w/ `success` property.

        return (object) [
            'success'  => $response->code === 200,
            'data'     => json_decode($response->body),
            'response' => $response,
        ];
    }

    /**
     * Subscribe (or update).
     *
     * @since 160620 Adding MailChimp.
     *
     * @param string $email Email address.
     * @param array  $args  API and request args.
     *
     * @return \StdClass Including a boolean `success` property.
     *
     * @link http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/
     */
    public function subscribeOrUpdate(string $email, array $args = []): \StdClass
    {
        # Parse incoming arguments.

        $default_args = [
            'list_id' => $this->App->Config->©mailchimp['©list_id'],
            'api_key' => $this->App->Config->©mailchimp['©api_key'],

            'email_address' => $email,
            'status_if_new' => 'pending',
            /*
                'merge_fields'  => [
                    'FNAME' => '',
                    'LNAME' => '',
                ],
            */
            /*
                'ip_signup' => '', // Initial IP.
                'ip_opt'    => '', // IP on opt-in.
            */
        ];
        $args                  = array_merge($default_args, $args);
        $args['email_address'] = mb_strtolower($args['email_address']);

        # Prepare request args and endpoint URL.

        $request_data = $args; // Start w/ a copy of `$args`.
        unset($request_data['list_id'], $request_data['api_key']);

        $request_args = [
            'headers' => [
                'content-type: application/json',
                'authorization: Basic '.base64_encode('_:'.$args['api_key']),
            ],
            'body'          => json_encode($request_data),
            'max_con_secs'  => 5, 'max_stream_secs' => 5,
            'fail_on_error' => false,
        ];
        $dc       = preg_replace('/^.+?\-/u', '', $args['api_key']);
        $endpoint = 'https://'.$dc.'.api.mailchimp.com/3.0/lists/'.urlencode($args['list_id']).'/members/'.md5($args['email_address']);

        # Query for remote response via API endpoint URL.

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

        # Return structured response data w/ `success` property.

        return (object) [
            'success'  => $response->code === 200,
            'data'     => json_decode($response->body),
            'response' => $response,
        ];
    }

    /**
     * Update a subscriber.
     *
     * @since 160620 Adding MailChimp.
     *
     * @param string $email Email address.
     * @param array  $args  API and request args.
     *
     * @return \StdClass Including a boolean `success` property.
     *
     * @link http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/
     */
    public function updateSubscriber(string $email, array $args = []): \StdClass
    {
        # Parse incoming arguments.

        $default_args = [
            'list_id' => $this->App->Config->©mailchimp['©list_id'],
            'api_key' => $this->App->Config->©mailchimp['©api_key'],

            'email_address' => $email,
            /*
                'merge_fields'  => [
                    'FNAME' => '',
                    'LNAME' => '',
                ],
            */
        ];
        $args                  = array_merge($default_args, $args);
        $args['email_address'] = mb_strtolower($args['email_address']);

        # Prepare request args and endpoint URL.

        $request_data = $args; // Start w/ a copy of `$args`.
        unset($request_data['list_id'], $request_data['api_key']);
        unset($request_data['email_address']);

        $request_args = [
            'headers' => [
                'content-type: application/json',
                'authorization: Basic '.base64_encode('_:'.$args['api_key']),
            ],
            'body'          => json_encode($request_data),
            'max_con_secs'  => 5, 'max_stream_secs' => 5,
            'fail_on_error' => false,
        ];
        $dc       = preg_replace('/^.+?\-/u', '', $args['api_key']);
        $endpoint = 'https://'.$dc.'.api.mailchimp.com/3.0/lists/'.urlencode($args['list_id']).'/members/'.md5($args['email_address']);

        # Query for remote response via API endpoint URL.

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

        # Return structured response data w/ `success` property.

        return (object) [
            'success'  => $response->code === 200,
            'data'     => json_decode($response->body),
            'response' => $response,
        ];
    }

    /**
     * Get subscriber.
     *
     * @since 160620 Adding MailChimp.
     *
     * @param string $email Email address.
     * @param array  $args  API and request args.
     *
     * @return \StdClass Including a boolean `success` property.
     *
     * @link http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/
     */
    public function subscriber(string $email, array $args = []): \StdClass
    {
        # Parse incoming arguments.

        $default_args = [
            'list_id' => $this->App->Config->©mailchimp['©list_id'],
            'api_key' => $this->App->Config->©mailchimp['©api_key'],

            'email_address' => $email,
        ];
        $args                  = array_merge($default_args, $args);
        $args['email_address'] = mb_strtolower($args['email_address']);

        # Prepare request args and endpoint URL.

        $request_args = [
            'headers' => [
                'content-type: application/json',
                'authorization: Basic '.base64_encode('_:'.$args['api_key']),
            ],
            'max_con_secs'  => 5, 'max_stream_secs' => 5,
            'fail_on_error' => false,
        ];
        $dc       = preg_replace('/^.+?\-/u', '', $args['api_key']);
        $endpoint = 'https://'.$dc.'.api.mailchimp.com/3.0/lists/'.urlencode($args['list_id']).'/members/'.md5($args['email_address']);

        # Query for remote response via API endpoint URL.

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

        # Return structured response data w/ `success` property.

        return (object) [
            'success'  => $response->code === 200,
            'data'     => json_decode($response->body),
            'response' => $response,
        ];
    }
}


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

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

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

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

    /**
     * GET JSON response.
     *
     * @since 170124.74961 Initial release.
     *
     * @param string $url  API URL.
     * @param array  $args Additional args.
     *
     * @return \StdClass Including a boolean `success` property.
     */
    public function getJson(string $url, array $args = []): \StdClass
    {
        $default_args = [
            'cache'            => true,
            'cache_max_age'    => strtotime('-15 minutes'),
            'api_access_token' => $this->App->Config->©zenhub['©api_access_token'],
        ];
        $args += $default_args; // Merge defaults.

        $time = time(); // Current time.

        $args['cache']            = (bool) $args['cache'];
        $args['cache_max_age']    = max(0, min($time, (int) $args['cache_max_age']));
        $args['api_access_token'] = (string) $args['api_access_token'];

        $url_sha1              = sha1($url); // A SHA-1 hash of the URL.
        $cache_dir_permissions = $this->App->Config->©fs_permissions['©transient_dirs'];
        $cache_dir             = $this->cache_dir.'/'.$this->c::sha1ModShardId($url_sha1, true);
        $cache_file            = $cache_dir.'/'.$url_sha1; // Hash location.

        if ($args['cache'] && is_file($cache_file) && filemtime($cache_file) >= $args['cache_max_age']) {
            return $response_object = json_decode(file_get_contents($cache_file));
        }
        $response = $this->c::remoteRequest('GET::'.$url, [
            'headers' => [
                'accept: application/json',
                'x-authentication-token: '.$args['api_access_token'],
            ],
            'return_array' => true,
        ]); // Expecting the API to return JSON.

        $response_object = (object) [
            'success'  => $response['code'] && $response['code'] < 400,
            'data'     => json_decode($response['body']),
            'response' => $response,
        ];
        if ($args['cache']) {
            if (!is_dir($cache_dir)) {
                mkdir($cache_dir, $cache_dir_permissions, true);
            }
            file_put_contents($cache_file, json_encode($response_object));
        }
        return $response_object;
    }
}


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

/**
 * URL utilities.
 *
 * @since 151121 URL utilities.
 */
class Url extends Classes\Core\Base\Core implements Interfaces\UrlConstants, Interfaces\HtmlConstants
{
    /**
     * Build app URL.
     *
     * @since 151121 URL utilities.
     *
     * @param string $uri        URI to append.
     * @param string $scheme     Specific scheme?
     * @param bool   $cdn_filter CDN filter?
     *
     * @return string Output URL.
     */
    public function toApp(string $uri = '', string $scheme = '', bool $cdn_filter = true): string
    {
        if (!($host = $this->App->Config->©urls['©hosts']['©app'])) {
            throw $this->c::issue('App host is empty.');
        }
        $uri = $uri ? $this->c::mbLTrim($uri, '/') : '';
        $uri = $uri ? '/'.$uri : ''; // Force leading slash.

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

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

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

        if ($uri && $cdn_filter) {
            $url = $this->c::cdnFilter($url);
        }
        return $url;
    }

    /**
     * Build app parent URL.
     *
     * @since 160423 Parent utilities.
     *
     * @param string $uri        URI to append.
     * @param string $scheme     Specific scheme?
     * @param bool   $cdn_filter CDN filter?
     *
     * @return string Output URL.
     */
    public function toAppParent(string $uri = '', string $scheme = '', bool $cdn_filter = true): string
    {
        return $this->App->Parent ? $this->App->Parent->Utils->©Url->toApp($uri, $scheme, $cdn_filter) : $this->toApp($uri, $scheme, $cdn_filter);
    }

    /**
     * Build app core URL.
     *
     * @since 151121 URL utilities.
     *
     * @param string $uri        URI to append.
     * @param string $scheme     Specific scheme?
     * @param bool   $cdn_filter CDN filter?
     *
     * @return string Output URL.
     */
    public function toAppCore(string $uri = '', string $scheme = '', bool $cdn_filter = true): string
    {
        if ($this->App->Parent) { // Looking for the root core.
            return $this->App->Parent->Utils->©Url->toAppCore($uri, $scheme, $cdn_filter);
        }
        return $this->toApp($uri, $scheme, $cdn_filter);
    }

    /**
     * Build app WS core URL.
     *
     * @since 160925 URL utilities.
     *
     * @param string $uri        URI to append.
     * @param string $scheme     Specific scheme?
     * @param bool   $cdn_filter CDN filter?
     *
     * @return string Output URL.
     */
    public function toAppWsCore(string $uri = '', string $scheme = '', bool $cdn_filter = true): string
    {
        if ($this->App->Parent) { // Looking for the root core.
            return $this->App->Parent->Utils->©Url->toAppWsCore($uri, $scheme, $cdn_filter);
        }
        if (!$this->App->is_ws_core) {
            $uri = $uri ? $this->c::mbLTrim($uri, '/') : '';
            $uri = $uri ? '/'.$uri : ''; // Force leading slash.
            $uri = '/vendor/websharks/core/src'.$uri;
        }
        return $this->toApp($uri, $scheme, $cdn_filter);
    }

    /**
     * Build API URL.
     *
     * @since 17xxxx URL utilities.
     *
     * @param string $uri    URI to append.
     * @param string $scheme Specific scheme?
     *
     * @return string Output URL.
     */
    public function toApi(string $uri = '', string $scheme = ''): string
    {
        if (!($host = $this->App->Config->©urls['©hosts']['©api'])) {
            throw $this->c::issue('API host is empty.');
        }
        $uri = $uri ? $this->c::mbLTrim($uri, '/') : '';
        $uri = $uri ? '/'.$uri : ''; // Force leading slash.

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

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

        return $url;
    }

    /**
     * Build API parent URL.
     *
     * @since 17xxxx Parent utilities.
     *
     * @param string $uri    URI to append.
     * @param string $scheme Specific scheme?
     *
     * @return string Output URL.
     */
    public function toApiParent(string $uri = '', string $scheme = ''): string
    {
        return $this->App->Parent ? $this->App->Parent->Utils->©Url->toApi($uri, $scheme) : $this->toApi($uri, $scheme);
    }

    /**
     * Build API core URL.
     *
     * @since 17xxxx URL utilities.
     *
     * @param string $uri    URI to append.
     * @param string $scheme Specific scheme?
     *
     * @return string Output URL.
     */
    public function toApiCore(string $uri = '', string $scheme = ''): string
    {
        if ($this->App->Parent) { // Looking for the root core.
            return $this->App->Parent->Utils->©Url->toApiCore($uri, $scheme);
        }
        return $this->toApi($uri, $scheme);
    }

    /**
     * Build 17xxxx WS core URL.
     *
     * @since 17xxxx URL utilities.
     *
     * @param string $uri    URI to append.
     * @param string $scheme Specific scheme?
     *
     * @return string Output URL.
     */
    public function toApiWsCore(string $uri = '', string $scheme = ''): string
    {
        if ($this->App->Parent) { // Looking for the root core.
            return $this->App->Parent->Utils->©Url->toApiWsCore($uri, $scheme);
        }
        if (!$this->App->is_ws_core) {
            $uri = $uri ? $this->c::mbLTrim($uri, '/') : '';
            $uri = $uri ? '/'.$uri : ''; // Force leading slash.
            $uri = '/vendor/websharks/core/src'.$uri;
        }
        return $this->toApi($uri, $scheme);
    }

    /**
     * Build URL w/ current host.
     *
     * @since 151121 URL utilities.
     *
     * @param string $uri        URI to append.
     * @param string $scheme     Specific scheme?
     * @param bool   $cdn_filter CDN filter?
     *
     * @return string Output URL w/ current host.
     */
    public function toCurrent(string $uri = '', string $scheme = '', bool $cdn_filter = true): string
    {
        if (!($host = $this->c::currentHost())) {
            throw $this->c::issue('Current host is empty.');
        }
        $uri = $uri ? $this->c::mbLTrim($uri, '/') : '';
        $uri = $uri ? '/'.$uri : ''; // Force leading slash.

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

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

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

        if ($uri && $cdn_filter) {
            $url = $this->c::cdnFilter($url);
        }
        return $url;
    }

    /**
     * Build parent URL w/ current host.
     *
     * @since 160423 Parent utilities.
     *
     * @param string $uri        URI to append.
     * @param string $scheme     Specific scheme?
     * @param bool   $cdn_filter CDN filter?
     *
     * @return string Output URL w/ current host.
     */
    public function toCurrentParent(string $uri = '', string $scheme = '', bool $cdn_filter = true): string
    {
        return $this->App->Parent ? $this->App->Parent->Utils->©Url->toCurrent($uri, $scheme, $cdn_filter) : $this->toCurrent($uri, $scheme, $cdn_filter);
    }

    /**
     * Build current core URL.
     *
     * @since 151121 URL utilities.
     *
     * @param string $uri        URI to append.
     * @param string $scheme     Specific scheme?
     * @param bool   $cdn_filter CDN filter?
     *
     * @return string Output URL.
     */
    public function toCurrentCore(string $uri = '', string $scheme = '', bool $cdn_filter = true): string
    {
        if ($this->App->Parent) { // Looking for the root core.
            return $this->App->Parent->Utils->©Url->toCurrentCore($uri, $scheme, $cdn_filter);
        }
        return $this->toCurrent($uri, $scheme, $cdn_filter);
    }

    /**
     * Build current WS core URL.
     *
     * @since 160925 URL utilities.
     *
     * @param string $uri        URI to append.
     * @param string $scheme     Specific scheme?
     * @param bool   $cdn_filter CDN filter?
     *
     * @return string Output URL.
     */
    public function toCurrentWsCore(string $uri = '', string $scheme = '', bool $cdn_filter = true): string
    {
        if ($this->App->Parent) { // Looking for the root core.
            return $this->App->Parent->Utils->©Url->toCurrentWsCore($uri, $scheme, $cdn_filter);
        }
        if (!$this->App->is_ws_core) {
            $uri = $uri ? $this->c::mbLTrim($uri, '/') : '';
            $uri = $uri ? '/'.$uri : ''; // Force leading slash.
            $uri = '/vendor/websharks/core/src'.$uri;
        }
        return $this->toCurrent($uri, $scheme, $cdn_filter);
    }

    /**
     * Valid URL?
     *
     * @since 151121 URL utilities.
     *
     * @param string $url URL to check.
     *
     * @return bool True if URL is valid.
     */
    public function isValid(string $url): bool
    {
        return (bool) preg_match($this::URL_REGEX_VALID, $url);
    }

    /**
     * Normalize ampersands.
     *
     * @since 150424 Initial release.
     *
     * @param string $qs_url_uri A query string (w/ or w/o a leading `?`), a URL, or URI.
     *
     * @return string The `$qs_url_uri` w/ normalized ampersands.
     */
    public function normalizeAmps(string $qs_url_uri): string
    {
        if (!$qs_url_uri) {
            return $qs_url_uri; // Possible `0`.
        }
        if (($regex_amps = &$this->cacheKey(__FUNCTION__.'_regex_amps')) === null) {
            $regex_amps = implode('|', array_keys($this::HTML_AMPERSAND_ENTITIES));
        }
        return preg_replace('/(?:'.$regex_amps.')/uS', '&', $qs_url_uri);
    }
}


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

use Ivory\GoogleMap\Exception\HelperException;
use Ivory\GoogleMap\MapTypeId;

/**
 * Map type ID helper.
 *
 * @author GeLo <geloen.eric@gmail.com>
 */
class MapTypeIdHelper
{
    /**
     * Renders a map map type ID.
     *
     * @param string $mapTypeId The map type ID.
     *
     * @throws \Ivory\GoogleMap\Exception\HelperException If the map type ID is not valid.
     *
     * @return string The JS output.
     */
    public function render($mapTypeId)
    {
        switch ($mapTypeId) {
            case MapTypeId::HYBRID:
            case MapTypeId::ROADMAP:
            case MapTypeId::SATELLITE:
            case MapTypeId::TERRAIN:
                return sprintf('google.maps.MapTypeId.%s', strtoupper($mapTypeId));
            default:
                throw HelperException::invalidMapTypeId();
        }
    }
}


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

/**
 * Name utilities.
 *
 * @since 150424 Initial release.
 */
class Name extends Classes\Core\Base\Core
{
    /**
     * Strip prefixes/suffixes.
     *
     * @since 151121 Adding name utilities.
     *
     * @param string $name Full name.
     *
     * @return string Name w/o prefixes/suffixes.
     */
    protected function stripClean(string $name)
    {
        static $last_in, $last_out;

        if ($name === $last_in && isset($last_out)) {
            return $last_out;
        } else {
            $last_in = $name;
        }
        if (!isset($name[0])) {
            return $last_out = $name;
        }
        $name = str_replace('"', '', $name);
        $name = preg_replace('/^(?:Mr\.?|Mrs\.?|Ms\.?|Dr\.?)\s+/ui', '', $name);
        $name = preg_replace('/\s+(?:Sr\.?|Jr\.?|IV|I+)$/ui', '', $name);
        $name = preg_replace('/\s+/u', ' ', $name);
        $name = $this->c::mbTrim($name);

        return $last_out = $name;
    }

    /**
     * First from full name.
     *
     * @since 151121 Adding name utilities.
     *
     * @param string $name  Full name.
     * @param string $email Fallback on email?
     *
     * @return string First name.
     */
    public function firstIn(string $name, string $email = ''): string
    {
        $name = $this->stripClean($name);

        if ($name && mb_strpos($name, ' ', 1) !== false) {
            return explode(' ', $name, 2)[0];
        } elseif (!$name && $email && mb_strpos($email, '@', 1) !== false) {
            return $this->c::mbUcFirst(explode('@', $email, 2)[0]);
        } else {
            return $name;
        }
    }

    /**
     * Last from full name.
     *
     * @since 151121 Adding name utilities.
     *
     * @param string $name Full name.
     *
     * @return string Last name.
     */
    public function lastIn(string $name): string
    {
        $name = $this->stripClean($name);

        if ($name && mb_strpos($name, ' ', 1) !== false) {
            return explode(' ', $name, 2)[1];
        } else {
            return $name;
        }
    }

    /**
     * Convert name to acronym.
     *
     * @since 160220 Initial release.
     *
     * @param string $name   Full name.
     * @param bool   $strict Strict?
     *
     * @return string Acronym; based on name.
     */
    public function toAcronym(string $name, bool $strict = true): string
    {
        $acronym = ''; // Initialize.

        $name = $this->stripClean($name);
        $name = $this->c::forceAscii($name);

        $name = preg_replace('/([a-z])([A-Z0-9])/u', '${1} ${2}', $name);
        $name = preg_replace('/([a-z])([0-9])/ui', '${1} ${2}', $name);
        $name = preg_replace('/([0-9])([a-z])/ui', '${1} ${2}', $name);
        // `s2` = `s 2`, `S3` = `S 3`, `3s` = `3 s`, `xCache` = `x Cache`.

        foreach (preg_split('/\s+/u', $name, -1, PREG_SPLIT_NO_EMPTY) as $_word) {
            if (isset($_word[0]) && ctype_alnum($_word[0])) {
                $acronym .= $_word[0];
            }
        } // unset($_word);

        if ($strict && mb_strlen($acronym) < 2) {
            $acronym = $this->c::mbStrPad(mb_substr($acronym, 0, 2), 2, 'x');
        }
        return mb_strtoupper($acronym);
    }

    /**
     * Convert name to slug.
     *
     * @since 150424 Initial release.
     *
     * @param string $name   Full name.
     * @param bool   $strict Strict?
     *
     * @return string Slug; based on name.
     */
    public function toSlug(string $name, bool $strict = true): string
    {
        $name = $this->stripClean($name);

        $slug = $name; // Initialize.
        $slug = mb_strtolower($this->c::forceAscii($slug));
        $slug = preg_replace('/[^a-z0-9]+/u', '-', $slug);
        $slug = $this->c::mbTrim($slug, '', '-');

        if ($strict && $slug && !preg_match('/^[a-z]/u', $slug)) {
            $slug = 'x'.$slug; // Force `^[a-z]`.
        }
        return $slug;
    }

    /**
     * Convert name to var.
     *
     * @since 160220 Var utils.
     *
     * @param string $name   Full name.
     * @param bool   $strict Strict?
     *
     * @return string Var; based on name.
     */
    public function toVar(string $name, bool $strict = true): string
    {
        $name = $this->stripClean($name);

        $var = $name; // Initialize.
        $var = mb_strtolower($this->c::forceAscii($var));
        $var = preg_replace('/[^a-z0-9]+/u', '_', $var);
        $var = $this->c::mbTrim($var, '', '_');

        if ($strict && $var && !preg_match('/^[a-z]/u', $var)) {
            $var = 'x'.$var; // Force `^[a-z]`.
        }
        return $var;
    }
}