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

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 image.

@since 17xxxx Imagick utils.

@param array $args Named arguments.

@return bool True on success.


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

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

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


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');
    }

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'
)
;
}

Decode image data URL.

@since 161011 Initial release.

@param string $url Data URL.

@return array `[raw_image_data, extension]`.


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

One pixel image.

@since 17xxxx Imagick utils.

@param string $format Format.

@return string One pixel image.


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

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

Extension to format.

@since 17xxxx Imagick utils.

@param string $ext_file Extension (or file).

@return string Extension format.


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

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

Format to extension.

@since 17xxxx Imagick utils.

@param string $format Image format.

@return string Format extension.


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

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

Extension to MIME type.

@since 17xxxx Imagick utils.

@param string $ext_file Extension (or file).

@return string Extension MIME 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;
    }

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 type.

@since 17xxxx Imagick utils.

@param string $format Image format.

@return int Format compression type.


protected function formatToCompressionQuality(string $format): int
    {
        switch (($ext = $this->formatToExt($format))) {
            case 'jpg':
                $quality = 85;
                break;

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

protected function formatToCompressionQuality ( string $ format ) : int { switch ( ( $ ext = $ this -> formatToExt ( $ format ) ) ) { case 'jpg' : $ quality = 85 ; break ; default : $ quality = 92 ; } return $ quality ; }

Format to compression quality.

@since 17xxxx Imagick utils.

@param string $format Image format.

@return int Format compression quality.


protected function setFormatExt(string $file, string $format): string
    {
        return $this->c::setFileExt($file, $this->formatToExt($format));
    }

protected function setFormatExt ( string $ file , string $ format ) : string { return $ this -> c :: setFileExt ( $ file , $ this -> formatToExt ( $ format ) ) ; }

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 changeFormatExt(string $file, string $format): string
    {
        return $this->c::changeFileExt($file, $this->formatToExt($format));
    }

protected function changeFormatExt ( string $ file , string $ format ) : string { return $ this -> c :: changeFileExt ( $ 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 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;
    }

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

Parse format args.

@since 17xxxx Imagick utils.

@param array $args Named arguments.

@return array Parsed arguments.


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

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

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.


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

clean input data

@param array|object|null|string $inputData
@throws \Exception
@returns array


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);
    }

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
)
;
}

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 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()
        );
    }

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
(
)
)
;
}

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 isValid(string $version): bool
    {
        if (!$version) {
            return false; // Nope.
        }
        return (bool) preg_match($this::VERSION_REGEX_VALID, $version);
    }

public
function
isValid
(
string
$
version
)
:
bool
{
if
(
!
$
version
)
{
return
false
;
// Nope.
}
return
(
bool
)
preg_match
(
$
this
::
VERSION_REGEX_VALID
,
$
version
)
;
}

Is it a valid 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);
    }

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 dev 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);
    }

public
function
isValidStable
(
string
$
version
)
:
bool
{
if
(
!
$
version
)
{
return
false
;
// Nope.
}
return
(
bool
)
preg_match
(
$
this
::
VERSION_REGEX_VALID_STABLE
,
$
version
)
;
}

Is it a valid stable version?

@since 150424 Initial release.

@param string $version Input version.

@return bool `TRUE` if valid.


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.');
    }

public
function
create
(
callable
$
callback
=
)
:
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 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 input(callable $callback = null): string
    {
        $_csrf = $this->create($callback);
        return '<input type="hidden" name="_csrf" value="'.$this->c::escAttr($_csrf).'" />';
    }

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 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.');
    }

public
function
verify
(
string
$
_csrf
=
,
callable
$
callback
=
)
:
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.'
)
;
}

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

public
static
function
createFromGlobals
(
array
$
globals
)
{
$
g
=
&
$
globals
;
$
App
=
$
g
[
'App'
]
??
;
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
;
}

Create from globals.

@since 17xxxx Request object.

@param array $globals Globals.

@return Request Instance from globals.


public function getData(): array
    {
        $form_data   = $this->getFormData();
        $query_args  = $this->getQueryArgs();
        return $data = $form_data + $query_args;
    }

public function getData ( ) : array { $ form_data = $ this -> getFormData ( ) ; $ query_args = $ this -> getQueryArgs ( ) ; return $ data = $ form_data + $ query_args ; }

Get all data.

@since 17xxxx Request.

@return array All 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 : [];
    }

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 form data.

@since 17xxxx Request.

@return array Form data.


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) [];
        }
    }

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
)
[
]
;
}
}

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 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);
    }

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'
)
)
===
)
{
$
regex_amps
=
implode
(
'|'
,
array_keys
(
$
this
::
HTML_AMPERSAND_ENTITIES
)
)
;
}
return
preg_replace
(
'/(?:'
.
$
regex_amps
.
')/uS'
,
'&'
,
$
qs_url_uri
)
;
}

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 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();
        }
    }

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 ( ) ; } }

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

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

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 lastIn(string $name): string
    {
        $name = $this->stripClean($name);

        if ($name && mb_strpos($name, ' ', 1) !== false) {
            return explode(' ', $name, 2)[1];
        } else {
            return $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
;
}
}

Last from full name.

@since 151121 Adding name utilities.

@param string $name Full name.

@return string Last 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 acronym.

@since 160220 Initial release.

@param string $name   Full name.
@param bool   $strict Strict?

@return string Acronym; 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;
    }

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

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 is(string $string, bool $strict = false): bool
    {
        if ($strict) {
            return mb_strpos($string, '</html>') !== false;
        }
        return mb_strpos($string, '<') !== false && preg_match('/\<[^<>]+\>/u', $string);
    }

public function is ( string $ string , bool $ strict = false ) : bool { if ( $ strict ) { return mb_strpos ( $ string , '</html>' ) !== false ; } return mb_strpos ( $ string , '<' ) !== false && preg_match ( '/\<[^<>]+\>/u' , $ string ) ; }

Is a string in HTML format?

@since 150424 Initial release.

@param string $string Any input string to test here.
@param bool   $strict Use strict (full DOM) check?

@return bool `TRUE` if string is HTML.


public function validate(int $uuid, int $expecting_type_id = null): int
    {
        if ($uuid < 1 || $uuid > 9223372036854775807) {
            throw $this->c::issue('UUID64 out of range.');
        }
        if (isset($expecting_type_id)) {
            $this->typeIdIn($uuid, $expecting_type_id);
        }
        return $uuid;
    }

public
function
validate
(
int
$
uuid
,
int
$
expecting_type_id
=
)
:
int
{
if
(
$
uuid
<
1
||
$
uuid
>
9223372036854775807
)
{
throw
$
this
->
c
::
issue
(
'UUID64 out of range.'
)
;
}
if
(
isset
(
$
expecting_type_id
)
)
{
$
this
->
typeIdIn
(
$
uuid
,
$
expecting_type_id
)
;
}
return
$
uuid
;
}

UUID64 validator.

@since 150424 Initial release.

@param int      $uuid              Input UUID64 (64 bits signed max `9223372036854775807`).
@param int|null $expecting_type_id Expected type ID.

@return int Validated UUID64 (64-bits signed max `9223372036854775807`).


public function shardIdIn(int $uuid, bool $validate = true): int
    {
        $shard_id = ($uuid >> (64 - 2 - 16)) & 65535;

        if ($validate) {
            $this->validateShardId($shard_id);
        }
        return $shard_id;
    }

public function shardIdIn ( int $ uuid , bool $ validate = true ) : int { $ shard_id = ( $ uuid >> ( 64 - 2 - 16 ) ) & 65535 ; if ( $ validate ) { $ this -> validateShardId ( $ shard_id ) ; } return $ shard_id ; }

Shard ID from UUID64.

@since 150424 Initial release.

@param int  $uuid     Input UUID64 (64 bits signed max `9223372036854775807`).
@param bool $validate Validate the shard ID after extraction? Defaults to `true`.

@return int Shard ID from UUID64 (16 bits unsigned max `65535`).


public function validateShardId(int $shard_id): int
    {
        if ($shard_id < 0 || $shard_id > 65535) {
            throw $this->c::issue('Shard ID out of range.');
        }
        return $shard_id;
    }

public
function
validateShardId
(
int
$
shard_id
)
:
int
{
if
(
$
shard_id
<
0
||
$
shard_id
>
65535
)
{
throw
$
this
->
c
::
issue
(
'Shard ID out of range.'
)
;
}
return
$
shard_id
;
}

Validate shard ID.

@since 150424 Initial release.

@param int $shard_id Input shard ID (16 bits unsigned max `65535`).

@return int Validated shard ID (16 bits unsigned max `65535`).


public function typeIdIn(int $uuid, int $expecting_type_id = null, bool $validate = true): int
    {
        $type_id = ($uuid >> (64 - 2 - 16 - 8)) & 255;

        if (isset($expecting_type_id)) {
            $this->validateTypeId($type_id, $expecting_type_id);
        } elseif ($validate) {
            $this->validateTypeId($type_id);
        }
        return $type_id;
    }

public function typeIdIn ( int $ uuid , int $ expecting_type_id = null , bool $ validate = true ) : int { $ type_id = ( $ uuid >> ( 64 - 2 - 16 - 8 ) ) & 255 ; if ( isset ( $ expecting_type_id ) ) { $ this -> validateTypeId ( $ type_id , $ expecting_type_id ) ; } elseif ( $ validate ) { $ this -> validateTypeId ( $ type_id ) ; } return $ type_id ; }

Type ID from UUID64.

@since 150424 Initial release.

@param int      $uuid              Input UUID64 (64 bits signed max `9223372036854775807`).
@param int|null $expecting_type_id Expected type ID; i.e., validate for this type ID?
@param bool     $validate          Validate the type ID after extraction? Defaults to `true`.

@return int Type ID from UUID64 (8 bits unsigned max `255`).


public function validateTypeId(int $type_id, int $expecting_type_id = null): int
    {
        if ($type_id < 0 || $type_id > 255) {
            throw $this->c::issue('Type ID out of range.');
        } elseif (isset($expecting_type_id) && $type_id !== $expecting_type_id) {
            throw $this->c::issue(sprintf('Type ID mismatch: `%1$s`/`%2$s`.', $type_id, $expecting_type_id));
        }
        return $type_id;
    }

public
function
validateTypeId
(
int
$
type_id
,
int
$
expecting_type_id
=
)
:
int
{
if
(
$
type_id
<
0
||
$
type_id
>
255
)
{
throw
$
this
->
c
::
issue
(
'Type ID out of range.'
)
;
}
elseif
(
isset
(
$
expecting_type_id
)
&&
$
type_id
!==
$
expecting_type_id
)
{
throw
$
this
->
c
::
issue
(
sprintf
(
'Type ID mismatch: `%1$s`/`%2$s`.'
,
$
type_id
,
$
expecting_type_id
)
)
;
}
return
$
type_id
;
}

Validate type ID.

@since 150424 Initial release.

@param int      $type_id           Input type ID (8 bits unsigned max `255`).
@param int|null $expecting_type_id Expected type ID.

@return int Validated type ID (8 bits unsigned max `255`).


public function localIdIn(int $uuid, bool $validate = true): int
    {
        $local_id = ($uuid >> (64 - 2 - 16 - 8 - 38)) & 274877906943;

        if ($validate) {
            $this->validateLocalId($local_id);
        }
        return $local_id;
    }

public function localIdIn ( int $ uuid , bool $ validate = true ) : int { $ local_id = ( $ uuid >> ( 64 - 2 - 16 - 8 - 38 ) ) & 274877906943 ; if ( $ validate ) { $ this -> validateLocalId ( $ local_id ) ; } return $ local_id ; }

Local ID from UUID64.

@since 150424 Initial release.

@param int  $uuid     Input UUID64 (64 bits signed max `9223372036854775807`).
@param bool $validate Validate the local ID after extraction? Defaults to `true`.

@return int Local ID from UUID64 (38 bits unsigned max `274877906943`).


public function validateLocalId(int $local_id): int
    {
        if ($local_id < 1 || $local_id > 274877906943) {
            throw $this->c::issue('Local ID out of range.');
        }
        return $local_id;
    }

public
function
validateLocalId
(
int
$
local_id
)
:
int
{
if
(
$
local_id
<
1
||
$
local_id
>
274877906943
)
{
throw
$
this
->
c
::
issue
(
'Local ID out of range.'
)
;
}
return
$
local_id
;
}

Validate local ID.

@since 150424 Initial release.

@param int $local_id Input local ID (38 bits unsigned max `274877906943`).

@return int Validated local ID (38 bits unsigned max `274877906943`).


public function parse(int $uuid, int $expecting_type_id = null, bool $validate = true): array
    {
        if ($validate) {
            $this->validate($uuid);
        }
        $shard_id = $this->shardIdIn($uuid, $validate);
        $type_id  = $this->typeIdIn($uuid, $expecting_type_id, $validate);
        $local_id = $this->localIdIn($uuid, $validate);

        return compact('shard_id', 'type_id', 'local_id');
    }

public function parse ( int $ uuid , int $ expecting_type_id = null , bool $ validate = true ) : array { if ( $ validate ) { $ this -> validate ( $ uuid ) ; } $ shard_id = $ this -> shardIdIn ( $ uuid , $ validate ) ; $ type_id = $ this -> typeIdIn ( $ uuid , $ expecting_type_id , $ validate ) ; $ local_id = $ this -> localIdIn ( $ uuid , $ validate ) ; return compact ( 'shard_id' , 'type_id' , 'local_id' ) ; }

UUID64 parser.

@since 150424 Initial release.

@param int      $uuid              Input UUID64 (64 bits signed max `9223372036854775807`).
@param int|null $expecting_type_id Expected type ID; i.e., validate this type ID?
@param bool     $validate          Validate the UUID and each part?

@return array IDs: `shard_id`, `type_id`, `local_id` from UUID64.

- `shard_id` (16 bits unsigned max `65535`).
- `type_id` (8 bits unsigned max `255`).
- `local_id` (38 bits unsigned max `274877906943`).


public function build(int $shard_id, int $type_id, int $local_id, bool $validate = true): int
    {
        if ($validate) {
            $this->validateShardId($shard_id);
            $this->validateTypeId($type_id);
            $this->validateLocalId($local_id);
        }
        return (0 << (64 - 2))
            | ($shard_id << (64 - 2 - 16))
            | ($type_id << (64 - 2 - 16 - 8))
            | ($local_id << (64 - 2 - 16 - 8 - 38));
    }

public function build ( int $ shard_id , int $ type_id , int $ local_id , bool $ validate = true ) : int { if ( $ validate ) { $ this -> validateShardId ( $ shard_id ) ; $ this -> validateTypeId ( $ type_id ) ; $ this -> validateLocalId ( $ local_id ) ; } return ( 0 << ( 64 - 2 ) ) | ( $ shard_id << ( 64 - 2 - 16 ) ) | ( $ type_id << ( 64 - 2 - 16 - 8 ) ) | ( $ local_id << ( 64 - 2 - 16 - 8 - 38 ) ) ; }

UUID64 builder.

@since 150424 Initial release.

@param int  $shard_id Shard ID (16 bits unsigned max `65535`).
@param int  $type_id  Type ID (8 bits unsigned max `255`).
@param int  $local_id Local ID (38 bits unsigned max `274877906943`).
@param bool $validate Validate each of the IDs first? Defaults to `true`.

@return int UUID64 (64 bits signed max `9223372036854775807`).

- (2 reserve bits; set to a value of 0).
- `shard_id` (16 bits unsigned max `65535`).
- `type_id` (8 bits unsigned max `255`).
- `local_id` (38 bits unsigned max `274877906943`).

= 64 bits signed max: `9223372036854775807` = `PHP_INT_MAX`.


public function render(Polyline $polyline, Map $map)
    {
        $this->jsonBuilder
            ->reset()
            ->setValue('[map]', $map->getJavascriptVariable(), false)
            ->setValue('[path]', array());

        foreach ($polyline->getCoordinates() as $index => $coordinate) {
            $this->jsonBuilder->setValue(sprintf('[path][%d]', $index), $coordinate->getJavascriptVariable(), false);
        }

        $this->jsonBuilder->setValues($polyline->getOptions());

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

public
function
render
(
Polyline
$
polyline
,
Map
$
map
)
{
$
this
->
jsonBuilder
->
reset
(
)
->
setValue
(
'[map]'
,
$
map
->
getJavascriptVariable
(
)
,
false
)
->
setValue
(
'[path]'
,
array
(
)
)
;
foreach
(
$
polyline
->
getCoordinates
(
)
as
$
index
=>
$
coordinate
)
{
$
this
->
jsonBuilder
->
setValue
(
sprintf
(
'[path][%d]'
,
$
index
)
,
$
coordinate
->
getJavascriptVariable
(
)
,
false
)
;
}
$
this
->
jsonBuilder
->
setValues
(
$
polyline
->
getOptions
(
)
)
;
return
sprintf
(
'%s = new google.maps.Polyline(%s);'
.
PHP_EOL
,
$
polyline
->
getJavascriptVariable
(
)
,
$
this
->
jsonBuilder
->
build
(
)
)
;
}

Renders a polyline.

@param \Ivory\GoogleMap\Overlays\Polyline $polyline The polyline.
@param \Ivory\GoogleMap\Map               $map      The map.

@return string The JS output.


public function setCenter()
    {
        $args = func_get_args();

        if (isset($args[0]) && ($args[0] instanceof Coordinate)) {
            $this->center = $args[0];
        } elseif ((isset($args[0]) && is_numeric($args[0])) && (isset($args[1]) && is_numeric($args[1]))) {
            $this->center->setLatitude($args[0]);
            $this->center->setLongitude($args[1]);

            if (isset($args[2]) && is_bool($args[2])) {
                $this->center->setNoWrap($args[2]);
            }
        } else {
            throw OverlayException::invalidCircleCenter();
        }
    }

public function setCenter ( ) { $ args = func_get_args ( ) ; if ( isset ( $ args [ 0 ] ) && ( $ args [ 0 ] instanceof Coordinate ) ) { $ this -> center = $ args [ 0 ] ; } elseif ( ( isset ( $ args [ 0 ] ) && is_numeric ( $ args [ 0 ] ) ) && ( isset ( $ args [ 1 ] ) && is_numeric ( $ args [ 1 ] ) ) ) { $ this -> center -> setLatitude ( $ args [ 0 ] ) ; $ this -> center -> setLongitude ( $ args [ 1 ] ) ; if ( isset ( $ args [ 2 ] ) && is_bool ( $ args [ 2 ] ) ) { $ this -> center -> setNoWrap ( $ args [ 2 ] ) ; } } else { throw OverlayException :: invalidCircleCenter ( ) ; } }

Sets the circle center.

Available prototypes:
- function setCenter(Ivory\GoogleMap\Base\Coordinate $center)
- function setCenter(double $latitude, double $longitude, boolean $noWrap = true)

@throws \Ivory\GoogleMap\Exception\OverlayException If the center is not valid (prototypes).


public function setLatitude($latitude)
    {
        if (!is_numeric($latitude) && ($latitude !== null)) {
            throw BaseException::invalidCoordinateLatitude();
        }

        $this->latitude = $latitude;
    }

public function setLatitude ( $ latitude ) { if ( ! is_numeric ( $ latitude ) && ( $ latitude !== null ) ) { throw BaseException :: invalidCoordinateLatitude ( ) ; } $ this -> latitude = $ latitude ; }

Sets the latitude

@param double $latitude The latitude.

@throws \Ivory\GoogleMap\Exception\BaseException If the latitude is not valid.


public function setLongitude($longitude)
    {
        if (!is_numeric($longitude) && ($longitude !== null)) {
            throw BaseException::invalidCoordinateLongitude();
        }

        $this->longitude = $longitude;
    }

public function setLongitude ( $ longitude ) { if ( ! is_numeric ( $ longitude ) && ( $ longitude !== null ) ) { throw BaseException :: invalidCoordinateLongitude ( ) ; } $ this -> longitude = $ longitude ; }

Sets the longitude.

@param double $longitude The longitude.

@throws \Ivory\GoogleMap\Exception\BaseException If the longitude is not valid.


public function setNoWrap($noWrap)
    {
        if (!is_bool($noWrap) && ($noWrap !== null)) {
            throw BaseException::invalidCoordinateNoWrap();
        }

        $this->noWrap = $noWrap;
    }

public function setNoWrap ( $ noWrap ) { if ( ! is_bool ( $ noWrap ) && ( $ noWrap !== null ) ) { throw BaseException :: invalidCoordinateNoWrap ( ) ; } $ this -> noWrap = $ noWrap ; }

Sets if the coordinate is wrap.

@param boolean $noWrap TRUE if the coordinate is not wrap else FALSE.

@throws \Ivory\GoogleMap\Exception\BaseException If the no wrap flag is not valid.


public function setControlPosition($controlPosition)
    {
        if (!in_array($controlPosition, ControlPosition::getControlPositions())) {
            throw ControlException::invalidControlPosition();
        }

        $this->controlPosition = $controlPosition;
    }

public function setControlPosition ( $ controlPosition ) { if ( ! in_array ( $ controlPosition , ControlPosition :: getControlPositions ( ) ) ) { throw ControlException :: invalidControlPosition ( ) ; } $ this -> controlPosition = $ controlPosition ; }

Sets the control position.

@param string $controlPosition The control position.

@throws \Ivory\GoogleMap\Exception\ControlException If the control position is not valid.


public function setScaleControlStyle($scaleControlStyle)
    {
        if (!in_array($scaleControlStyle, ScaleControlStyle::getScaleControlStyles())) {
            throw ControlException::invalidScaleControlStyle();
        }

        $this->scaleControlStyle = $scaleControlStyle;
    }

public function setScaleControlStyle ( $ scaleControlStyle ) { if ( ! in_array ( $ scaleControlStyle , ScaleControlStyle :: getScaleControlStyles ( ) ) ) { throw ControlException :: invalidScaleControlStyle ( ) ; } $ this -> scaleControlStyle = $ scaleControlStyle ; }

Sets the scale control style.

@param type $scaleControlStyle The scale control style.

@throws \Ivory\GoogleMap\Exception\ControlException If the scale control style is not valid.


public static function getGeocoderStatus()
    {
        return array(
            self::ERROR,
            self::INVALID_REQUEST,
            self::OK,
            self::OVER_QUERY_LIMIT,
            self::REQUEST_DENIED,
            self::UNKNOWN_ERROR,
            self::ZERO_RESULTS,
        );
    }

public static function getGeocoderStatus ( ) { return array ( self :: ERROR , self :: INVALID_REQUEST , self :: OK , self :: OVER_QUERY_LIMIT , self :: REQUEST_DENIED , self :: UNKNOWN_ERROR , self :: ZERO_RESULTS , ) ; }

Gets the available geocoder status.

@return array The available geocoder status.


public function get(string $primary_key, $sub_key, int $max_age = 0, array $args = [])
    {
        if (!isset($primary_key, $sub_key)) {
            return null; // Not possible.
        }
        $file = $this->dir; // App-specific.
        $file .= '/'.$this->subPath($primary_key, $sub_key, $args);

        if (!is_file($file)) {
            return null; // Cache miss.
        } elseif ($max_age && time() - filemtime($file) > $max_age) {
            return null; // Cache miss, too old.
        } elseif (($cache = file_get_contents($file)) === false) {
            return null; // Cache miss, read failure.
        } elseif (($cache = unserialize($cache)) === false) {
            return null; // Cache miss, unserialize failure.
        }
        if (isset($cache['x___value'], $cache['x___expires'])) {
            if ($cache['x___expires'] && $cache['x___expires'] <= time()) {
                return null; // Cache miss, value has expired.
            } else {
                return $cache['x___value']; // Cache hit!
            }
        } else { // e.g., Perhaps stored by an older version.
            return null; // Cache miss, unexpected data.
        }
    }

Get a cached value.

@since 170824.30708 Dir cache utils.

@param string     $primary_key Primary key.
@param string|int $sub_key     Sub-key to get.
@param int        $max_age     Max age (in seconds).
@param array      $args        Any behavioral args.

@return mixed|null Null if missing or expired.


protected function subPath(string $primary_key, $sub_key = null, array $args = []): string
    {
        if (!isset($sub_key)) {
            if (empty($args['preserve_keys'])) {
                extract($this->sha1Keys($primary_key, null, $args));
            }
            return $sub_path = $primary_key;
        } else {
            if (empty($args['preserve_keys'])) {
                extract($this->sha1Keys($primary_key, $sub_key, $args));
            }
            $shard_id        = $this->c::sha1ModShardId($primary_key.$sub_key);
            return $sub_path = $primary_key.'/'.$shard_id.'/'.$sub_key;
        }
    }

protected function subPath ( string $ primary_key , $ sub_key = null , array $ args = [ ] ) : string { if ( ! isset ( $ sub_key ) ) { if ( empty ( $ args [ 'preserve_keys' ] ) ) { extract ( $ this -> sha1Keys ( $ primary_key , null , $ args ) ) ; } return $ sub_path = $ primary_key ; } else { if ( empty ( $ args [ 'preserve_keys' ] ) ) { extract ( $ this -> sha1Keys ( $ primary_key , $ sub_key , $ args ) ) ; } $ shard_id = $ this -> c :: sha1ModShardId ( $ primary_key . $ sub_key ) ; return $ sub_path = $ primary_key . '/' . $ shard_id . '/' . $ sub_key ; } }

Key sub-path.

@since 170824.30708 Dir cache utils.

@param string          $primary_key Primary key.
@param string|int|null $sub_key     Sub-key to get.
@param array           $args        Any behavioral args.

@return string Key sub-path.


protected function sha1Keys(string $primary_key, $sub_key = null, array $args = []): array
    {
        if (!$primary_key || !$this->c::isSha1($primary_key)) {
            $primary_key = sha1($primary_key);
        }
        if (!isset($sub_key)) {
            return compact('primary_key');
        } else {
            if (!$sub_key || !$this->c::isSha1($sub_key)) {
                $sub_key = sha1((string) $sub_key);
            }
            return compact('primary_key', 'sub_key');
        }
    }

protected function sha1Keys ( string $ primary_key , $ sub_key = null , array $ args = [ ] ) : array { if ( ! $ primary_key || ! $ this -> c :: isSha1 ( $ primary_key ) ) { $ primary_key = sha1 ( $ primary_key ) ; } if ( ! isset ( $ sub_key ) ) { return compact ( 'primary_key' ) ; } else { if ( ! $ sub_key || ! $ this -> c :: isSha1 ( $ sub_key ) ) { $ sub_key = sha1 ( ( string ) $ sub_key ) ; } return compact ( 'primary_key' , 'sub_key' ) ; } }

SHA-1 cache keys.

@since 170824.30708 Dir cache utils.

@param string          $primary_key Primary key.
@param string|int|null $sub_key     Sub-key to get.
@param array           $args        Any behavioral args.

@return array `[primary_key]` or `[primary_key, sub_key]`.


public function get(string $class_name, array $args = [], bool $as_is = false)
    {
        return parent::get($as_is ? $class_name : $this->App->getClass($class_name), $args);
    }

public function get ( string $ class_name , array $ args = [ ] , bool $ as_is = false ) { return parent :: get ( $ as_is ? $ class_name : $ this -> App -> getClass ( $ class_name ) , $ args ) ; }

Get a class instance.

@since 160224 Initial release.

@param string $class_name Class path.
@param array  $args       Constructor args.
@param bool   $as_is      Class name as-is?

@return object An object class instance.


public function match(string $string, $value, bool $collect_key_props = false)
    {
        if (!isset($string[0])) { // Empty?
            return $collect_key_props ? [] : false;
        }
        $matching_key_props = []; // Initialize.

        if (is_array($value) || is_object($value)) {
            foreach ($value as $_key_prop => $_value) {
                if (is_array($_value) || is_object($_value)) {
                    if (($_matching_key_props = $this->match($string, $_value, $collect_key_props))) {
                        if ($collect_key_props) {
                            $matching_key_props[] = [$_key_prop => $_matching_key_props];
                        } else {
                            return true;
                        }
                    }
                } elseif (is_string($_value) && isset($_value[0])) {
                    if (@preg_match($_value, $string)) {
                        if ($collect_key_props) {
                            $matching_key_props[] = $_key_prop;
                        } else {
                            return true;
                        }
                    }
                }
            } // unset($_key_prop, $_value, $_matching_key_props);
        } elseif (!$collect_key_props && is_string($value) && isset($value[0])) {
            if (@preg_match($value, $string)) {
                return true;
            }
        }
        return $collect_key_props ? $matching_key_props : false;
    }

Search values containing regex patterns against a string.

@since 150424 Initial release.

@param string $string            String to search within.
@param mixed  $value             Any value can be converted into a regex pattern.
Actually, objects can't, but this recurses into objects.
@param bool   $collect_key_props Collect array keys and/or object properties?

@return bool|array Will return `TRUE` if any regex pattern finds a match.
If `$collect_key_props` is `TRUE`, this will return an array.

Search values containing regex patterns against a string .


public function get(string $file, string $dir = '', array $args = []): Classes\Core\Template
    {
        return $this->App->Di->get(Classes\Core\Template::class, compact('dir', 'file', 'args'));
    }

public function get ( string $ file , string $ dir = '' , array $ args = [ ] ) : Classes \ Core \ Template { return $ this -> App -> Di -> get ( Classes \ Core \ Template :: class , compact ( 'dir' , 'file' , 'args' ) ) ; }

Gets template.

@since 151121 Template utilities.

@param string $file Relative to templates dir.
@param string $dir  From a specific directory?
@param array  $args Additional template args.

@return Classes\Core\Template Template instance.


public function strength(string $password): int
    {
        $score = 0; // Initialize.

        if (!isset($password[0])) {
            return $score;
        }
        if (preg_match('/\p{N}/u', $password)) {
            $score += 25; // Number.
        }
        if (preg_match('/\p{Ll}/u', $password)) {
            $score += 25; // Lowercase.
        }
        if (preg_match('/\p{Lu}/u', $password)) {
            $score += 25; // Uppercase.
        }
        if (preg_match('/[^\p{N}\p{Ll}\p{Lu}]/u', $password)) {
            $score += 25; // Special symbol.
        }
        return $score;
    }

public
function
strength
(
string
$
password
)
:
int
{
$
score
=
0
;
// Initialize.
if
(
!
isset
(
$
password
[
0
]
)
)
{
return
$
score
;
}
if
(
preg_match
(
'/\p{N}/u'
,
$
password
)
)
{
$
score
+=
25
;
// Number.
}
if
(
preg_match
(
'/\p{Ll}/u'
,
$
password
)
)
{
$
score
+=
25
;
// Lowercase.
}
if
(
preg_match
(
'/\p{Lu}/u'
,
$
password
)
)
{
$
score
+=
25
;
// Uppercase.
}
if
(
preg_match
(
'/[^\p{N}\p{Ll}\p{Lu}]/u'
,
$
password
)
)
{
$
score
+=
25
;
// Special symbol.
}
return
$
score
;
}

Password strength.

@since 17xxxx Password strength.

@param string $password Password.

@return int Strength (0 - 100).


public function &checkProcess(string $url, string $sha1)
    {
        if (is_string($sri = &$this->cacheGet('sris', $sha1))) {
            return $sri; // Cached this already.
        } else { // Set in-process cache to `null`.
            $sri = null; // Not in the cache.
            return $sri; // By reference.
        }
    }

public
function
&
checkProcess
(
string
$
url
,
string
$
sha1
)
{
if
(
is_string
(
$
sri
=
&
$
this
->
cacheGet
(
'sris'
,
$
sha1
)
)
)
{
return
$
sri
;
// Cached this already.
}
else
{
// Set in-process cache to `null`.
$
sri
=
;
// Not in the cache.
return
$
sri
;
// By reference.
}
}

Checks for SRI in process.

@since 170211.63148 SRI utils.

@param string $url  URL to get SRI for.
@param string $sha1 SHA-1 hash of URL.

@return string|null SRI hash, else `null`.


public function checkMemcache(string $url, string $sha1)
    {
        if (!$this->memcache_enabled) {
            return $sri = null; // Not possible.
        } elseif (is_string($sri = $this->c::memcacheGet('sris', $sha1))) {
            return $sri; // Ideal; this avoids disk IO.
        }
        return $sri = null; // Not in the cache.
    }

public
function
checkMemcache
(
string
$
url
,
string
$
sha1
)
{
if
(
!
$
this
->
memcache_enabled
)
{
return
$
sri
=
;
// Not possible.
}
elseif
(
is_string
(
$
sri
=
$
this
->
c
::
memcacheGet
(
'sris'
,
$
sha1
)
)
)
{
return
$
sri
;
// Ideal; this avoids disk IO.
}
return
$
sri
=
;
// Not in the cache.
}

Checks for SRI in memcache.

@since 170211.63148 SRI utils.

@param string $url  URL to get SRI for.
@param string $sha1 SHA-1 hash of URL.

@return string|null SRI hash, else `null`.


public function checkMap(string $url, string $sha1)
    {
        $this->map = $this->getMap();

        if (isset($this->map[$url]['sri'])) {
            return $sri = (string) $this->map[$url]['sri'];
        }
        if (mb_strpos($url, '//') !== 0) { // Should check `//`?
            ${'//'} = preg_replace('/^https?\:/ui', '', $url);

            if (isset($this->map[${'//'}]['sri'])) {
                return $sri = (string) $this->map[${'//'}]['sri'];
            } // i.e., If a map entry covers all schemes.
        }
        return $sri = null; // Not in the map.
    }

Checks for SRI in map.

@since 170211.63148 SRI utils.

@param string $url  URL to get SRI for.
@param string $sha1 SHA-1 hash of URL.

@return string|null SRI hash, else `null`.


public function checkMapCache(string $url, string $sha1)
    {
        $this->map_cache = $this->getMapCache();

        if (isset($this->map_cache[$url]['sri']) && $this->map_cache[$url]['time'] >= $this->cache_max_age) {
            return $sri = (string) $this->map_cache[$url]['sri'];
        }
        if (mb_strpos($url, '//') !== 0) { // Should check `//`?
            ${'//'} = preg_replace('/^https?\:/ui', '', $url);

            if (isset($this->map_cache[${'//'}]['sri']) && $this->map_cache[${'//'}]['time'] >= $this->cache_max_age) {
                return $sri = (string) $this->map_cache[${'//'}]['sri'];
            } // i.e., If a map entry covers all schemes.
        }
        return $sri = null; // Not in the map.
    }

Checks for SRI in map cache.

@since 170211.63148 SRI utils.

@param string $url  URL to get SRI for.
@param string $sha1 SHA-1 hash of URL.

@return string|null SRI hash, else `null`.


public function checkContents(string $url, string $sha1)
    {
        if (static::$content_checks >= $this->max_content_checks) {
            return $sri = null; // Not possible at this time.
        } // There is a limit on the number of checks per process.

        if (mb_stripos($url, '//') === 0) {
            $url = ($this->current_scheme ?: 'http').':'.$url;
        } // Because we must have a scheme to do a lookup.

        $args = [ // HTTP args.
            'max_con_secs'    => 2,
            'max_stream_secs' => 6,
            'max_stream_size' => $this::BYTES_IN_MB * 2,
        ]; // Most servers can download 1MB+ in this time.

        ++static::$content_checks; // Increment counter.
        $response = $this->c::remoteResponse('GET::'.$url, $args);

        if ($response->code !== 200) {
            return $sri = ''; // Unable to determine.
            // However, we do return an empty SRI in this case anyway.

            // NOTE: This may (at times) return an empty SRI whenever
            // there is a temporary connectivity issue that is resolved later.

            // Rationale: If we chose not to cache failed responses, that could result in HTTP
            // requests occurring over and over again, which is want we need to avoid at all costs.
            // Therefore, if it fails, game is over until clearing the cache or adding a new map entry.
        }
        return $sri = 'sha384-'.base64_encode(hash('sha384', $response->body, true));
    }

Check contents.

@since 170211.63148 SRI utils.

@param string $url  URL to get SRI for.
@param string $sha1 SHA-1 hash of URL.

@return string|null SRI hash, else `null`.


public function cacheIt(string $url, string $sha1, string $sri)
    {
        $this->memcacheIt($url, $sha1, $sri);
        $this->mapCacheIt($url, $sha1, $sri);
    }

public function cacheIt ( string $ url , string $ sha1 , string $ sri ) { $ this -> memcacheIt ( $ url , $ sha1 , $ sri ) ; $ this -> mapCacheIt ( $ url , $ sha1 , $ sri ) ; }

Cache the SRI hash.

@since 170211.63148 SRI utils.

@param string $url  URL to get SRI for.
@param string $sha1 SHA-1 hash of URL.
@param string $sri  The SRI hash.

@note The `$sri` is allowed to be empty.


public function memcacheIt(string $url, string $sha1, string $sri)
    {
        if ($this->memcache_enabled) {
            $this->c::memcacheSet('sris', $sha1, $sri, $this->cache_expires_in);
        } // This is, by far, the fastest way.
    }

public
function
memcacheIt
(
string
$
url
,
string
$
sha1
,
string
$
sri
)
{
if
(
$
this
->
memcache_enabled
)
{
$
this
->
c
::
memcacheSet
(
'sris'
,
$
sha1
,
$
sri
,
$
this
->
cache_expires_in
)
;
}
// This is, by far, the fastest way.
}

Memcache the SRI hash.

@since 170211.63148 SRI utils.

@param string $url  URL to get SRI for.
@param string $sha1 SHA-1 hash of URL.
@param string $sri  The SRI hash.

@note The `$sri` is allowed to be empty.


protected function &getMap(): array
    {
        if (!isset($this->map)) {
            $this->map = []; // Define.

            if (is_file($this->map_file)) {
                $this->map = file_get_contents($this->map_file);
                $this->map = json_decode($this->map, true);
                $this->map = is_array($this->map) ? $this->map : [];
            } // Only if file exists, otherwise empty array.
        }
        return $this->map;
    }

Get the SRIs map.

@since 170215.53419 SRI utils.

@return array Map of SRIs.


protected function &getMapCache(): array
    {
        if (!isset($this->map_cache)) {
            $this->map_cache = []; // Define.

            if (is_file($this->map_cache_file)) {
                $this->map_cache = file_get_contents($this->map_cache_file);
                $this->map_cache = json_decode($this->map_cache, true);
                $this->map_cache = is_array($this->map_cache) ? $this->map_cache : [];
            } // Only if file exists, otherwise empty array.
        }
        return $this->map_cache;
    }

Get SRIs map cache.

@since 170215.53419 SRI utils.

@return array SRIs map cache.


protected function isLocal(string $url)
    {
        if (mb_stripos($url, '//') === false) {
            return true; // Relative.
        } elseif ($this->current_host) {
            return mb_stripos($url, '//'.$this->current_host.'/') !== false;
        }
        return null; // Unable to determine.
    }

protected
function
isLocal
(
string
$
url
)
{
if
(
mb_stripos
(
$
url
,
'//'
)
===
false
)
{
return
true
;
// Relative.
}
elseif
(
$
this
->
current_host
)
{
return
mb_stripos
(
$
url
,
'//'
.
$
this
->
current_host
.
'/'
)
!==
false
;
}
return
;
// Unable to determine.
}

URL is local?

@since 170215.53419 SRI utils.

@param string $url URL to get SRI for.

@return bool|null True if URL is local.


public function isValid(string $var, bool $strict = true): bool
    {
        return (bool) preg_match($strict ? $this::VAR_STRICT_REGEX_VALID : $this::VAR_REGEX_VALID, $var);
    }

public function isValid ( string $ var , bool $ strict = true ) : bool { return ( bool ) preg_match ( $ strict ? $ this :: VAR_STRICT_REGEX_VALID : $ this :: VAR_REGEX_VALID , $ var ) ; }

Validate var.

@since 160220 Initial release.

@param string $var    Var.
@param bool   $strict Strict?

@return bool True if var is valid.


public function toAcronym(string $var, bool $strict = true): string
    {
        return $this->c::nameToAcronym($this->toName($var), $strict);
    }

public function toAcronym ( string $ var , bool $ strict = true ) : string { return $ this -> c :: nameToAcronym ( $ this -> toName ( $ var ) , $ strict ) ; }

Convert var to acronym.

@since 160220 Initial release.

@param string $var    Var.
@param bool   $strict Strict?

@return string Acronym; based on var.


public function toSlug(string $var, bool $strict = true): string
    {
        $slug = $var; // 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;
    }

public
function
toSlug
(
string
$
var
,
bool
$
strict
=
true
)
:
string
{
$
slug
=
$
var
;
// 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 var to slug.

@since 160220 Initial release.

@param string $var    Var.
@param bool   $strict Strict?

@return string Slug; based on var.


public function toCamelCase(string $var, bool $strict = true): string
    {
        $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 $camelCase = $var ? preg_replace_callback('/_(.)/u', function (array $m): string {
            return mb_strtoupper($m[1]); // e.g., `_x` becomes `X` for camelCase.
        }, $var) : $var; // Converts variable into camelCase variable.
    }

Convert var to camelCase.

@since 170413.34876 Initial release.

@param string $var    Var.
@param bool   $strict Strict?

@return string camelCase; based on var.


public function render(Size $size)
    {
        if ($size->hasUnits()) {
            return sprintf(
                '%s = new google.maps.Size(%s, %s, "%s", "%s");'.PHP_EOL,
                $size->getJavascriptVariable(),
                $size->getWidth(),
                $size->getHeight(),
                $size->getWidthUnit(),
                $size->getHeightUnit()
            );
        }

        return sprintf(
            '%s = new google.maps.Size(%s, %s);'.PHP_EOL,
            $size->getJavascriptVariable(),
            $size->getWidth(), $size->getHeight()
        );
    }

Renders a size.

@param \Ivory\GoogleMap\Base\Size $size The size.

@return string The JS output.


public function isRoleBased(string $email): bool
    {
        $user = mb_strtolower(mb_strstr($email, '@', true));
        $user = str_replace(['-', '_', '.'], '', $user);

        if (!is_null($is = &$this->cacheKey(__FUNCTION__, $user))) {
            return $is; // Cached this already.
        }
        if (in_array($user, $this::EMAIL_ROLE_BASED_STRINGS, true)) {
            return $is = true;
        }
        foreach ($this::EMAIL_ROLE_BASED_REGEX_FRAGS as $_regex_frag) {
            if (preg_match('/^'.$_regex_frag.'$/ui', $user)) {
                return $is = true;
            }
        } // unset($_regex_frag); // Housekeeping.

        return $is = false;
    }

Is role-based address?

@since 151121 Email utilities.

@param string $email Email address to check.

@return bool True if email is role-based.


public function parseAddresses($value, bool $strict = false): array
    {
        $addresses = []; // Initialize.

        if (is_array($value) || is_object($value)) {
            foreach ($value as $_key => $_value) {
                $addresses = array_merge($addresses, $this->parseAddresses($_value, $strict));
            } // unset($_key, $_value); // Housekeeping.
            return $addresses;
        }
        $string                      = (string) $value;
        $delimiter                   = mb_strpos($string, ';') !== false ? ';' : ',';
        $regex_delimitation_splitter = '/'.$delimiter.'+/u'; // `$this->c::escRegex()` unnecessary.

        $possible_addresses = preg_split($regex_delimitation_splitter, $string, -1, PREG_SPLIT_NO_EMPTY);
        $possible_addresses = $this->c::mbTrim($possible_addresses);

        foreach ($possible_addresses as $_address) {
            if (!$_address || mb_strpos($_address, '@', 1) === false) {
                continue; // NOT an email address.
            }
            if (mb_strpos($_address, '<') !== false && preg_match('/(?:"(?<name>[^"]+?)"\s*)?\<(?<email>.+?)\>/u', $_address, $_m)) {
                if ($_m['email'] && mb_strpos($_m['email'], '@', 1) !== false && (!$strict || $this->isValid($_m['email']))) {
                    $_email             = mb_strtolower($_m['email']);
                    $_name              = !empty($_m['name']) ? $_m['name'] : '';
                    $_fname             = $_name ? $this->c::fnameIn($_name) : '';
                    $_lname             = $_name ? $this->c::lnameIn($_name) : '';
                    $addresses[$_email] = (object) ['name' => $_name, 'fname' => $_fname, 'lname' => $_lname, 'email' => $_email];
                }
            } elseif (!$strict || $this->isValid($_address)) {
                $_email             = mb_strtolower($_address);
                $addresses[$_email] = (object) ['name' => '', 'fname' => '', 'lname' => '', 'email' => $_email];
            }
        } // unset($_address, $_m, $_email, $_name, $_fname, $_lname); // Housekeeping.

        return $addresses;
    }

Parse addresses.

@since 151121 Email utilities.

@param mixed $value  Any input value w/ recipients.
@param bool  $strict Optional. Defaults to `false` (faster). Parses all strings w/ `@` signs.
If `true`, validate each address; and only return 100% valid email addresses.

@return \StdClass[] Each object in the array contains 3 properties: `name`, `fname`, `lname`, `email`.


protected function parseHeaders($value, array $refs = [], bool $strict = false): array
    {
        $headers = []; // Initialize parsed headers.

        foreach ($this->c::parseHeaders($value) as $_header => $_value) {
            switch ($_header) { // Maybe populate refs.

                case 'from':
                    if (array_key_exists('from_name', $refs) && array_key_exists('from_email', $refs)) {
                        if (($_from_addresses = $this->parseAddresses($_value, $strict))) {
                            $_from              = array_shift($_from_addresses);
                            $refs['from_name']  = $this->c::mbTrim($_from->fname.' '.$_from->lname);
                            $refs['from_email'] = $_from->email; // By reference.
                        } // unset($_from_addresses, $_from);
                    } else {
                        $headers[$_header] = $_value;
                    }
                    break; // Break switch.

                case 'reply-to':
                    if (array_key_exists('reply_to_name', $refs) && array_key_exists('reply_to_email', $refs)) {
                        if (($_reply_to_addresses = $this->parseAddresses($_value, $strict))) {
                            $_reply_to              = array_shift($_reply_to_addresses);
                            $refs['reply_to_name']  = $this->c::mbTrim($_reply_to->fname.' '.$_reply_to->lname);
                            $refs['reply_to_email'] = $_reply_to->email; // By reference.
                        } // unset($_reply_to_addresses, $_reply_to);
                    } else {
                        $headers[$_header] = $_value;
                    }
                    break; // Break switch.

                case 'cc':
                case 'bcc':
                    if (array_key_exists('recipients', $refs)) {
                        if (($_recipient_addresses = $this->parseAddresses($_value, $strict))) {
                            $refs['recipients'] = array_merge($refs['recipients'], $_recipient_addresses);
                            $refs['recipients'] = array_unique($refs['recipients']);
                        } // unset($_recipient_addresses);
                    } else {
                        $headers[$_header] = $_value;
                    }
                    break; // Break switch.

                default: // Everything else (default).
                    $headers[$_header] = $_value;
                    break; // Break switch.
            }
        }
        return $headers;
    }

Parse headers.

@since 151121 Email utilities.

@param mixed $value  Any input value w/ headers.
@param array $refs   An input array of references to fill; for special headers.
@param bool  $strict Optional. Defaults to `false` (faster). Parses all strings w/ `@` signs.
If `true`, validate each address, and only return 100% valid email addresses.

@return array Unique/associative array of all parsed headers.


protected function parseAttachments($value): array
    {
        $attachments = []; // Initialize.

        if (is_array($value) || is_object($value)) {
            foreach ($value as $_key => $_value) {
                $attachments = array_merge($attachments, $this->parseAttachments($_value));
            } // unset($_key, $_value); // Housekeeping.
            return $attachments;
        }
        $string = (string) $value;

        if (is_file($string)) {
            $attachments[$string] = $string;
        }
        return $attachments;
    }

Parse attachments.

@since 151121 Email utilities.

@param mixed $value Any input value w/ attachments.

@return array Unique/associative array of all attachments.


public static function parseContentTypeToRepresentation($contentType)
    {
        $matches = array();

        preg_match('/^application\/(.*)\+json; charset=utf-8$/im', $contentType, $matches);

        if (count($matches) == 2) {
            return $matches[1];
        }

        return false;
    }

Parse a content-type header to a representation name

@param string $contentType
@return bool

Parse a content - type header to a representation name


public static function parseRepresentationToClass($inputRepresentation)
    {
        $version = substr($inputRepresentation, -2);
        $representation = substr($inputRepresentation, 0, -3);

        switch (substr($representation, 0, 7)) {
            case "vnd.ccp": // EVE
                $data = explode(".", $representation);
                array_shift($data);
                array_shift($data);
                array_shift($data);
                $classname = '\Perry\Representation\Eve\\'.$version.'\\'.$data[0];
                break;
            case "net.3rd": // OldApi
                $data = explode(".", $representation);
                array_shift($data);
                array_shift($data);
                array_shift($data);

                $classname = '\Perry\Representation\OldApi\\'.$version.'\\'.$data[0];
                if (count($data) > 1) {
                    $classname .= '\\'.$data[1];
                }
                break;
            default:
                throw new \Exception("wtf, what representation is this? ".$inputRepresentation);
        }

        return $classname;
    }

public
static
function
parseRepresentationToClass
(
$
inputRepresentation
)
{
$
version
=
substr
(
$
inputRepresentation
,
2
)
;
$
representation
=
substr
(
$
inputRepresentation
,
0
,
3
)
;
switch
(
substr
(
$
representation
,
0
,
7
)
)
{
case
"vnd.ccp"
:
// EVE
$
data
=
explode
(
"."
,
$
representation
)
;
array_shift
(
$
data
)
;
array_shift
(
$
data
)
;
array_shift
(
$
data
)
;
$
classname
=
'\Perry\Representation\Eve\\'
.
$
version
.
'\\'
.
$
data
[
0
]
;
break
;
case
"net.3rd"
:
// OldApi
$
data
=
explode
(
"."
,
$
representation
)
;
array_shift
(
$
data
)
;
array_shift
(
$
data
)
;
array_shift
(
$
data
)
;
$
classname
=
'\Perry\Representation\OldApi\\'
.
$
version
.
'\\'
.
$
data
[
0
]
;
if
(
count
(
$
data
)
>
1
)
{
$
classname
.=
'\\'
.
$
data
[
1
]
;
}
break
;
default
:
throw
new
\
Exception
(
"wtf, what representation is this? "
.
$
inputRepresentation
)
;
}
return
$
classname
;
}

convert a representation name including version to the
corresponding class

@param  string $inputRepresentation
@return string
@throws \Exception


public function diff(float $from, float $to, int $precision = 0, bool $format_string = false)
    {
        if (!$from) {
            ++$from;
            ++$to;
        } // Stop division by `0`.

        $precision = max(0, $precision);
        $percent   = ($to - $from) / $from * 100;

        if ($precision) {
            $percent = (float) number_format($percent, $precision, '.', '');
        } else {
            $percent = (int) $percent;
        }
        if ($format_string) { // e.g., add a prefix/suffix?
            $percent = $percent > 0 ? '+'.$percent.'%' : $percent.'%';
        }
        return $percent;
    }

public
function
diff
(
float
$
from
,
float
$
to
,
int
$
precision
=
0
,
bool
$
format_string
=
false
)
{
if
(
!
$
from
)
{
++
$
from
;
++
$
to
;
}
// Stop division by `0`.
$
precision
=
max
(
0
,
$
precision
)
;
$
percent
=
(
$
to
$
from
)
/
$
from
*
100
;
if
(
$
precision
)
{
$
percent
=
(
float
)
number_format
(
$
percent
,
$
precision
,
'.'
,
''
)
;
}
else
{
$
percent
=
(
int
)
$
percent
;
}
if
(
$
format_string
)
{
// e.g., add a prefix/suffix?
$
percent
=
$
percent
>
0
'+'
.
$
percent
.
'%'
:
$
percent
.
'%'
;
}
return
$
percent
;
}

Calculates percentage difference.

@param int|float $from          Calculate from (i.e., value then).
@param int|float $to            Calculate to (i.e., the value now).
@param int       $precision     Defaults to `0`; i.e., no decimal place.
@param bool      $format_string If true, formatted `+|-[percent]%`.

@return int|float|string A float if `$precision` is passed, else an integer (default behavior).
If `$format_string`, converted to string format: `+|-[percent]%`.


public function urlSafeEncode(string $string, array $url_unsafe_chars = ['+', '/'], array $url_safe_chars = ['-', '_'], string $trim_padding_chars = '='): string
    {
        if (!is_string($base64_url_safe = base64_encode($string))) {
            throw $this->c::issue('Base64 encoding failure.');
        }
        $base64_url_safe = str_replace($url_unsafe_chars, $url_safe_chars, $base64_url_safe);
        $base64_url_safe = isset($trim_padding_chars[0]) ? rtrim($base64_url_safe, $trim_padding_chars) : $base64_url_safe;

        return $base64_url_safe;
    }

public
function
urlSafeEncode
(
string
$
string
,
array
$
url_unsafe_chars
=
[
'+'
,
'/'
]
,
array
$
url_safe_chars
=
[
'-'
,
'_'
]
,
string
$
trim_padding_chars
=
'='
)
:
string
{
if
(
!
is_string
(
$
base64_url_safe
=
base64_encode
(
$
string
)
)
)
{
throw
$
this
->
c
::
issue
(
'Base64 encoding failure.'
)
;
}
$
base64_url_safe
=
str_replace
(
$
url_unsafe_chars
,
$
url_safe_chars
,
$
base64_url_safe
)
;
$
base64_url_safe
=
isset
(
$
trim_padding_chars
[
0
]
)
rtrim
(
$
base64_url_safe
,
$
trim_padding_chars
)
:
$
base64_url_safe
;
return
$
base64_url_safe
;
}

Base64 URL-safe encoding.

@since 150424 Initial release.

@param string $string             Input string to be base64 encoded.
@param array  $url_unsafe_chars   Optional array of un-safe characters. Defaults to: `['+', '/']`.
@param array  $url_safe_chars     Optional array of safe character replacements. Defaults to: `['-', '_']`.
@param string $trim_padding_chars Optional string of padding chars to right-trim. Defaults to: `=`.

@throws Exception If the call to `base64_encode()` fails.

@return string The base64 URL-safe encoded string.


public function urlSafeDecode(string $base64_url_safe, array $url_unsafe_chars = ['+', '/'], array $url_safe_chars = ['-', '_'], string $trim_padding_chars = '='): string
    {
        $string = isset($trim_padding_chars[0]) ? rtrim($base64_url_safe, $trim_padding_chars) : $base64_url_safe;
        $string = isset($trim_padding_chars[0]) ? str_pad($string, strlen($string) % 4, '=', STR_PAD_RIGHT) : $string;
        $string = str_replace($url_safe_chars, $url_unsafe_chars, $string);

        if (!is_string($string = base64_decode($string, true))) {
            throw $this->c::issue('Base64 decoding failure.');
        }
        return $string;
    }

public
function
urlSafeDecode
(
string
$
base64_url_safe
,
array
$
url_unsafe_chars
=
[
'+'
,
'/'
]
,
array
$
url_safe_chars
=
[
'-'
,
'_'
]
,
string
$
trim_padding_chars
=
'='
)
:
string
{
$
string
=
isset
(
$
trim_padding_chars
[
0
]
)
rtrim
(
$
base64_url_safe
,
$
trim_padding_chars
)
:
$
base64_url_safe
;
$
string
=
isset
(
$
trim_padding_chars
[
0
]
)
str_pad
(
$
string
,
strlen
(
$
string
)
%
4
,
'='
,
STR_PAD_RIGHT
)
:
$
string
;
$
string
=
str_replace
(
$
url_safe_chars
,
$
url_unsafe_chars
,
$
string
)
;
if
(
!
is_string
(
$
string
=
base64_decode
(
$
string
,
true
)
)
)
{
throw
$
this
->
c
::
issue
(
'Base64 decoding failure.'
)
;
}
return
$
string
;
}

Base64 URL-safe decoding.

@since 150424 Initial release.

@param string $base64_url_safe    Input string to be base64 decoded.
@param array  $url_unsafe_chars   Optional array of un-safe characters. Defaults to: `['+', '/']`.
@param array  $url_safe_chars     Optional array of safe character replacements. Defaults to: `['-', '_']`.
@param string $trim_padding_chars Optional string of padding chars to right-trim. Defaults to: `=`.

@throws Exception If the call to `base64_decode()` fails.

@return string The decoded string value.


public function create(string $action = null, callable $callback = null): string
    {
        $Nonce = (object) [
            'token'       => $this->c::uuidV4(),
            'action'      => mb_strtolower($action ?? ''),
            'expire_time' => time() + $this::SECONDS_IN_DAY,
            'used'        => false, // Unused on creation.
        ];
        if (isset($callback)) {
            $callback($Nonce);
            return $Nonce->token;
        }
        if ($this->c::isActiveSession()) {
            $_SESSION['_nonce'] = $_SESSION['_nonce'] ?? [];
            $this->c::arrayUnshiftAssoc($_SESSION['_nonce'], $Nonce->token, $Nonce);
            $_SESSION['_nonce'] = array_slice($_SESSION['_nonce'], 0, 64, true);
            return $Nonce->token;
        }
        throw $this->c::issue('No storage handler.');
    }

public
function
create
(
string
$
action
=
,
callable
$
callback
=
)
:
string
{
$
Nonce
=
(
object
)
[
'token'
=>
$
this
->
c
::
uuidV4
(
)
,
'action'
=>
mb_strtolower
(
$
action
??
''
)
,
'expire_time'
=>
time
(
)
+
$
this
::
SECONDS_IN_DAY
,
'used'
=>
false
,
// Unused on creation.
]
;
if
(
isset
(
$
callback
)
)
{
$
callback
(
$
Nonce
)
;
return
$
Nonce
->
token
;
}
if
(
$
this
->
c
::
isActiveSession
(
)
)
{
$
_SESSION
[
'_nonce'
]
=
$
_SESSION
[
'_nonce'
]
??
[
]
;
$
this
->
c
::
arrayUnshiftAssoc
(
$
_SESSION
[
'_nonce'
]
,
$
Nonce
->
token
,
$
Nonce
)
;
$
_SESSION
[
'_nonce'
]
=
array_slice
(
$
_SESSION
[
'_nonce'
]
,
0
,
64
,
true
)
;
return
$
Nonce
->
token
;
}
throw
$
this
->
c
::
issue
(
'No storage handler.'
)
;
}

Nonce generator.

@since 17xxxx Nonce utils.

@param string|null   $action   Action.
@param callable|null $callback Callback.

@return string New, unique nonce 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 input(string $action = null, callable $callback = null): string
    {
        $token = $this->create($action, $callback);
        return '<input type="hidden" name="_nonce" value="'.$this->c::escAttr($token).'" />';
    }

Nonce input markup.

@since 17xxxx Nonce utils.

@param string|null   $action   Action.
@param callable|null $callback Callback.

@return string Hidden input field with `_nonce` token.
See also: {@link create()}


public function verify(string $action = null, string $token = null, callable $callback = null): bool
    {
        if (!isset($token)) {
            $token = (string) $_REQUEST['_nonce'];
        } // Defaults to current request.

        $action = mb_strtolower($action ?? '');
        $token  = mb_strtolower($token);

        if (isset($callback)) {
            return $callback($action, $token);
        }
        if ($this->c::isActiveSession()) {
            if (!isset($_SESSION['_nonce'][$token])) {
                return false; // Bad token.
            } // Reference Nonce in session.
            $Nonce = &$_SESSION['_nonce'][$token];

            if ($action !== $Nonce->action) {
                return false;
            } elseif (time() >= $Nonce->expire_time) {
                return false;
            } elseif ($Nonce->used) {
                return false;
            }
            return $Nonce->used = true;
        }
        throw $this->c::issue('No verification handler.');
    }

public
function
verify
(
string
$
action
=
,
string
$
token
=
,
callable
$
callback
=
)
:
bool
{
if
(
!
isset
(
$
token
)
)
{
$
token
=
(
string
)
$
_REQUEST
[
'_nonce'
]
;
}
// Defaults to current request.
$
action
=
mb_strtolower
(
$
action
??
''
)
;
$
token
=
mb_strtolower
(
$
token
)
;
if
(
isset
(
$
callback
)
)
{
return
$
callback
(
$
action
,
$
token
)
;
}
if
(
$
this
->
c
::
isActiveSession
(
)
)
{
if
(
!
isset
(
$
_SESSION
[
'_nonce'
]
[
$
token
]
)
)
{
return
false
;
// Bad token.
}
// Reference Nonce in session.
$
Nonce
=
&
$
_SESSION
[
'_nonce'
]
[
$
token
]
;
if
(
$
action
!==
$
Nonce
->
action
)
{
return
false
;
}
elseif
(
time
(
)
>=
$
Nonce
->
expire_time
)
{
return
false
;
}
elseif
(
$
Nonce
->
used
)
{
return
false
;
}
return
$
Nonce
->
used
=
true
;
}
throw
$
this
->
c
::
issue
(
'No verification handler.'
)
;
}

Nonce verifier.

@since 17xxxx Nonce utils.

@param string|null   $action   Action.
@param string|null   $token    Token.
@param callable|null $callback Callback.

@return bool True if token is verified.

@note Callback verifies token and flags token as having been used once before.
Or, if there is no callback, a built-in `$_SESSION` storage handler is used by default.


public function setOrigins(array $origins)
    {
        $this->origins = array();

        foreach ($origins as $origin) {
            $this->addOrigin($origin);
        }
    }

public function setOrigins ( array $ origins ) { $ this -> origins = array ( ) ; foreach ( $ origins as $ origin ) { $ this -> addOrigin ( $ origin ) ; } }

Sets the distance matrix routes.

@param array $origins The distance matrix origins.