Initial xentral_oss_20.3.c9ffacf
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Components\Util;
|
||||
|
||||
use Xentral\Components\Util\Exception\InvalidArgumentException;
|
||||
|
||||
final class ColorUtil
|
||||
{
|
||||
/**
|
||||
* @example '#369' or '#336699' => array('r' => 51, 'g' => 102, 'b' => 153)
|
||||
*
|
||||
* @param string $hexColor
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return array ['r' => {$red}, 'g' => {$green}, 'b' => {$blue}]
|
||||
*/
|
||||
public static function convertHexToRgb($hexColor)
|
||||
{
|
||||
self::ensureHexColorFormat($hexColor);
|
||||
|
||||
$hexColor = self::normalizeHexColor($hexColor);
|
||||
$hexColor = str_replace('#', '', $hexColor);
|
||||
|
||||
$colorParts = str_split($hexColor, 2);
|
||||
$red = (int)hexdec($colorParts[0]);
|
||||
$green = (int)hexdec($colorParts[1]);
|
||||
$blue = (int)hexdec($colorParts[2]);
|
||||
|
||||
return ['r' => $red, 'g' => $green, 'b' => $blue];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $red [0-255]
|
||||
* @param int $green [0-255]
|
||||
* @param int $blue [0-255]
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function convertRgbToHex($red, $green, $blue)
|
||||
{
|
||||
if ($red < 0 || $red > 255) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Invalid value for color red "%s". Color values must be between 0 and 255.', $red
|
||||
));
|
||||
}
|
||||
if ($green < 0 || $green > 255) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Invalid value for color green "%s". Color values must be between 0 and 255.', $green
|
||||
));
|
||||
}
|
||||
if ($blue < 0 || $blue > 255) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Invalid value for color blue "%s". Color values must be between 0 and 255.', $blue
|
||||
));
|
||||
}
|
||||
|
||||
$rHex = StringUtil::padLeft(dechex($red), 2, '0');
|
||||
$gHex = StringUtil::padLeft(dechex($green), 2, '0');
|
||||
$bHex = StringUtil::padLeft(dechex($blue), 2, '0');
|
||||
$hexColor = '#' . strtoupper($rHex . $gHex . $bHex);
|
||||
self::ensureHexColorFormat($hexColor);
|
||||
|
||||
return $hexColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes hex colors
|
||||
*
|
||||
* - Transforms shorthand hexcolors (e.g. #369) into long hexcolor format (#336699)
|
||||
* - Long hexcolor format (#336699) will be unchanged
|
||||
*
|
||||
* @param string $hexColor
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function normalizeHexColor($hexColor)
|
||||
{
|
||||
self::ensureHexColorFormat($hexColor);
|
||||
|
||||
$hexColor = str_replace('#', '', $hexColor);
|
||||
|
||||
if (strlen($hexColor) === 3) {
|
||||
$red = !empty($hexColor[0]) ? $hexColor[0] : '0';
|
||||
$green = !empty($hexColor[1]) ? $hexColor[1] : '0';
|
||||
$blue = !empty($hexColor[2]) ? $hexColor[2] : '0';
|
||||
|
||||
$hexColor = $red . $red . $green . $green . $blue . $blue;
|
||||
}
|
||||
|
||||
return '#' . $hexColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the brightness level (YIQ formula); is the given color more darker or lighter?
|
||||
*
|
||||
* Result 'dark' means: The given color is more darker than light. When using the given color as
|
||||
* a background color, then use a light text color for better contrast (and vice versa).
|
||||
*
|
||||
* @param int $red [0-255]
|
||||
* @param int $green [0-255]
|
||||
* @param int $blue [0-255]
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return string [dark|light]
|
||||
*/
|
||||
public static function calculateBrightnessLevel($red, $green, $blue)
|
||||
{
|
||||
if ($red < 0 || $red > 255) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Invalid value for color red "%s". Color values must be between 0 and 255.', $red
|
||||
));
|
||||
}
|
||||
if ($green < 0 || $green > 255) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Invalid value for color green "%s". Color values must be between 0 and 255.', $green
|
||||
));
|
||||
}
|
||||
if ($blue < 0 || $blue > 255) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Invalid value for color blue "%s". Color values must be between 0 and 255.', $blue
|
||||
));
|
||||
}
|
||||
|
||||
/** @see http://dannyruchtie.com/color-contrast-calculator-with-yiq/ */
|
||||
$yiqValue = ($red * 299 + $green * 587 + $blue * 114) / 1000;
|
||||
|
||||
return $yiqValue >= 128 ? 'light' : 'dark';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $hexColor
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function ensureHexColorFormat($hexColor)
|
||||
{
|
||||
if (strpos($hexColor, '#') !== 0) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Invalid format for hex color "%s". Hex color must start with hash char (#).',
|
||||
$hexColor
|
||||
));
|
||||
}
|
||||
|
||||
$hexString = substr_replace($hexColor, '', 0, 1);
|
||||
$hexCleaned = preg_replace('/[^0-9A-Fa-f]/', '', $hexString);
|
||||
if ($hexString !== $hexCleaned) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Invalid format for hex color "%s". Hex color contains invalid chars. ' .
|
||||
'Valid chars are: A-F, a-f and 0-9.',
|
||||
$hexColor
|
||||
));
|
||||
}
|
||||
|
||||
$hexColorLength = strlen($hexColor);
|
||||
if ($hexColorLength !== 4 && $hexColorLength !== 7) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Invalid format for hex color "%s". Hex color must be four or seven chars long; ' .
|
||||
'with leading hash char (#).',
|
||||
$hexColor
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Components\Util\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class InsecureRandomStringException extends RuntimeException implements UtilExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Components\Util\Exception;
|
||||
|
||||
use InvalidArgumentException as SplInvalidArgumentException;
|
||||
|
||||
class InvalidArgumentException extends SplInvalidArgumentException implements UtilExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Components\Util\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class StringUtilException extends RuntimeException implements UtilExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Components\Util\Exception;
|
||||
|
||||
use Xentral\Core\Exception\ComponentExceptionInterface;
|
||||
|
||||
interface UtilExceptionInterface extends ComponentExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Components\Util;
|
||||
|
||||
use Exception;
|
||||
use Xentral\Components\Util\Exception\InsecureRandomStringException;
|
||||
use Xentral\Components\Util\Exception\InvalidArgumentException;
|
||||
use Xentral\Components\Util\Exception\StringUtilException;
|
||||
|
||||
final class StringUtil
|
||||
{
|
||||
/** @var array $translate */
|
||||
private static $transliteration = [
|
||||
'/m\x{00B2}/u' => 'qm',
|
||||
'/m\x{00B3}/u' => 'm3',
|
||||
'/ä/' => 'ae',
|
||||
'/ö/' => 'oe',
|
||||
'/ü/' => 'ue',
|
||||
'/Ä/' => 'Ae',
|
||||
'/Ö/' => 'Oe',
|
||||
'/Ü/' => 'Ue',
|
||||
'/\x{00DF}/u' => 'ss', //ß
|
||||
'/\x{20AC}/u' => 'EURO', //€
|
||||
'/\x{0026}/u' => 'und', //&
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns true if string starts with needle.
|
||||
*
|
||||
* @example startsWith('Apple', 'A') -> true
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function startsWith($haystack, $needle)
|
||||
{
|
||||
self::ensureString($haystack);
|
||||
self::ensureString($needle);
|
||||
|
||||
return $needle === '' || strpos($haystack, $needle) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if string ends with needle.
|
||||
*
|
||||
* @example endsWith('Apple', 'e') -> true
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function endsWith($haystack, $needle)
|
||||
{
|
||||
self::ensureString($haystack);
|
||||
self::ensureString($needle);
|
||||
|
||||
return $needle === substr($haystack, strlen($haystack) - strlen($needle), strlen($needle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if needle appears anywhere in string.
|
||||
*
|
||||
* @example contains('Apple', 'pp') -> true
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function contains($haystack, $needle)
|
||||
{
|
||||
self::ensureString($haystack);
|
||||
self::ensureString($needle);
|
||||
|
||||
return $needle === '' || strpos($haystack, $needle) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pads a string up to a specific length.
|
||||
*
|
||||
* @example padLeft('a', 4, '-') -> '---a'
|
||||
* @example padLeft('a', 4, '-+') -> '-+-a'
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $length
|
||||
* @param string $pad
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function padLeft($string, $length, $pad = ' ')
|
||||
{
|
||||
self::ensureString($string);
|
||||
self::ensureInteger($length);
|
||||
self::ensureString($pad);
|
||||
|
||||
return self::generatePadding($length - mb_strlen($string), $pad) . $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pads a string up to a specific length.
|
||||
*
|
||||
* @example padRight('a', 4, '-') -> 'a---'
|
||||
* @example padRight('a', 4, '-+') -> 'a-+-'
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $length
|
||||
* @param string $pad
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function padRight($string, $length, $pad = ' ')
|
||||
{
|
||||
self::ensureString($string);
|
||||
self::ensureInteger($length);
|
||||
self::ensureString($pad);
|
||||
|
||||
return $string . self::generatePadding($length - mb_strlen($string), $pad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a filesystem-friendly (win, mac, linux) file name from a given string
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string file name
|
||||
*/
|
||||
public static function toFilename($value)
|
||||
{
|
||||
self::ensureString($value);
|
||||
|
||||
$sanitize = [
|
||||
'/\s+/' => '_', //space
|
||||
'/:\\\/' => '-', //drive name
|
||||
'/\\\\\\\/' => '-', //unc path
|
||||
'/[\\/\\\<>:|]/' => '-', //seperators and reserved
|
||||
'/"/' => '\'',
|
||||
'/[\*\?&]+/' => '', //other special chars
|
||||
'/[\x{0000}-\x{001F}]/u' => '',
|
||||
'/[\x{0080}-\x{FFFF}]/u' => '', //only ascii
|
||||
];
|
||||
|
||||
$filename = preg_replace(array_keys(self::$transliteration), array_values(self::$transliteration), $value);
|
||||
$filename = preg_replace(array_keys($sanitize), array_values($sanitize), $filename);
|
||||
$filename = trim($filename, '.');
|
||||
|
||||
if ($filename === '') {
|
||||
throw new StringUtilException(
|
||||
sprintf('The specified string "%s" cannot be converted to a valid file name', $value)
|
||||
);
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/** Deletes all non-ASCII non-printable characters from a string
|
||||
*
|
||||
* Only characters between 32 to 127 (inclusive) remain.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string empty if whole string was non-ASCII
|
||||
*/
|
||||
public static function toAscii($value)
|
||||
{
|
||||
self::ensureString($value);
|
||||
|
||||
$value = preg_replace(['/[\x{0000}-\x{001F}]/u', '/[\x{0080}-\x{FFFF}]/u'], ['', ''], $value);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a String to Title case.
|
||||
*
|
||||
* Use delimiter='' to only capitalize the first character.
|
||||
*
|
||||
* @example "foo Bar BaZ" => "Foo Bar Baz"
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $delimiters all characters that seperate words
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function toTitleCase($value, $delimiters = '\s\v')
|
||||
{
|
||||
self::ensureString($value);
|
||||
self::ensureString($delimiters);
|
||||
|
||||
$inWords = [$value];
|
||||
$outWords = [];
|
||||
|
||||
if ($delimiters !== '') {
|
||||
$regex = sprintf('/[%s]/', $delimiters);
|
||||
$inWords = preg_split($regex, $value);
|
||||
}
|
||||
|
||||
foreach ($inWords as $word) {
|
||||
if ($word !== '') {
|
||||
$outWords[] = mb_strtoupper(mb_substr($word, 0, 1))
|
||||
. mb_strtolower(mb_substr($word, 1, mb_strlen($word)));
|
||||
}
|
||||
}
|
||||
|
||||
return implode(' ', $outWords);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a nubmer of Bytes to KB, MB etc.
|
||||
*
|
||||
* @example formatBytes(1024, ' ') -> '1,0 KB'
|
||||
*
|
||||
* @param integer $bytes
|
||||
* @param string $separator
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function formatBytes($bytes, $separator = '')
|
||||
{
|
||||
self::ensureInteger($bytes);
|
||||
$bytes = (int)$bytes;
|
||||
if ($bytes <= 0) {
|
||||
return sprintf('0%sBytes', $separator);
|
||||
}
|
||||
|
||||
$units = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
$exponent = (int)floor(log($bytes) / log(1024));
|
||||
if ($exponent > count($units) - 1) {
|
||||
$exponent = count($units) - 1;
|
||||
}
|
||||
$size = $bytes / pow(1024, $exponent);
|
||||
|
||||
return sprintf('%s%s%s',
|
||||
number_format($size, 1, ',', '.'),
|
||||
$separator,
|
||||
$units[$exponent]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse PHP byte size Values to number of bytes.
|
||||
*
|
||||
* Shorthand Format is used in php_ini (e.g. post_max_size).
|
||||
*
|
||||
* @example parsePhpShorthandByte('1M') -> 1048576
|
||||
*
|
||||
* @param string $phpSize php shorthand byte format
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function parsePhpByteSize($phpSize)
|
||||
{
|
||||
self::ensureString($phpSize);
|
||||
|
||||
preg_match('/^(\d+)([kKmMgG]?)$/', $phpSize, $matches);
|
||||
if (empty($matches)) {
|
||||
throw new InvalidArgumentException(sprintf('"%s" is not a PHP size format.', $phpSize));
|
||||
}
|
||||
|
||||
$sizes = ['' => 0, 'K' => 1, 'M' => 2, 'G' => 3];
|
||||
$number = (int)$matches[1];
|
||||
$unit = strtoupper($matches[2]);
|
||||
$exponent = $sizes[$unit];
|
||||
$bytes = $number * pow(1024, $exponent);
|
||||
|
||||
return (int)$bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random String with specified length
|
||||
*
|
||||
* @param int $length
|
||||
* @param bool $secure true=create cryptographically secure string
|
||||
*
|
||||
* @throws InsecureRandomStringException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function random($length, $secure = false)
|
||||
{
|
||||
$random = self::randomByOpenSsl($length, $secure);
|
||||
if ($random === false) {
|
||||
$random = self::randomByRandomBytes($length);
|
||||
}
|
||||
if ($random !== false) {
|
||||
return $random;
|
||||
}
|
||||
if ($random === false && $secure === true) {
|
||||
throw new InsecureRandomStringException('Could not generate cryptographically secure random string.');
|
||||
}
|
||||
|
||||
return self::randomByMd5($length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $length
|
||||
* @param bool $secure
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
private static function randomByOpenSsl($length, $secure = false)
|
||||
{
|
||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
||||
$bytes = openssl_random_pseudo_bytes(ceil($length / 2), $cryptoStrong);
|
||||
if ($bytes === false || ($secure === true && $cryptoStrong === false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return substr(bin2hex($bytes), 0, $length);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* random_bytes steht erst ab PHP7 zur Verfügung
|
||||
*
|
||||
* @param int $length
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
private static function randomByRandomBytes($length)
|
||||
{
|
||||
$random = false;
|
||||
if (function_exists('random_bytes')) {
|
||||
try {
|
||||
$bytes = random_bytes(ceil($length / 2));
|
||||
$random = substr(bin2hex($bytes), 0, $length);
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
return $random;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $length
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
private static function randomByMd5($length)
|
||||
{
|
||||
$random = '';
|
||||
for ($i = 0; $i * 32 <= $length; $i++) {
|
||||
$val = mt_rand();
|
||||
$random .= md5((string)$val);
|
||||
}
|
||||
|
||||
$random = substr($random, 0, $length);
|
||||
|
||||
return $random;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $lengthDelta
|
||||
* @param string $sequence
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function generatePadding($lengthDelta, $sequence)
|
||||
{
|
||||
if ($sequence === '' || $lengthDelta < 1) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$padding = '';
|
||||
for ($i = 0; $i < $lengthDelta; $i++) {
|
||||
$delta = $lengthDelta - mb_strlen($padding);
|
||||
$subSequence = mb_substr($sequence, 0, $delta);
|
||||
$padding .= $subSequence;
|
||||
}
|
||||
|
||||
return $padding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function ensureString($value)
|
||||
{
|
||||
$type = gettype($value);
|
||||
if ($type !== 'string') {
|
||||
throw new InvalidArgumentException(sprintf('Wrong type "%s". Only "string" is allowed.', $type));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function ensureInteger($value)
|
||||
{
|
||||
$type = gettype($value);
|
||||
if ($type !== 'integer') {
|
||||
throw new InvalidArgumentException(sprintf('Wrong type "%s". Only "integer" allowed.', $type));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user