v9.35.0: Logging datetime to iso date format with time zone, unit checks fixes

This commit is contained in:
Clemens Schwaighofer
2025-10-09 12:01:57 +09:00
parent 1b46b378a3
commit fb9e04fe55
11 changed files with 144 additions and 94 deletions

View File

@@ -10,12 +10,16 @@ class Email
/** @var array<int,string> */
private static array $email_regex_check = [
0 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$", // MASTER
// . "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$", // MASTER
// fixed pattern matching for domain
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$", // MASTER
1 => "@(.*)@(.*)", // double @
2 => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@", // wrong part before @
3 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$", // wrong part after @
4 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.", // wrong domain name part
5 => "\.([a-zA-Z]{2,6}){1}$", // wrong top level part
// 3 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$", // wrong part after @
3 => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$", // wrong part after @
// 4 => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.", // wrong domain name part
4 => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.", // wrong domain name part
5 => "\.[a-zA-Z]{2,6}$", // wrong top level part
6 => "@(.*)\.{2,}", // double .. in domain name part
7 => "@.*\.$" // ends with a dot, top level, domain missing
];

View File

@@ -150,14 +150,14 @@ class ArrayHandler
* array search simple. looks for key, value combination, if found, returns true
* on default does not strict check, so string '4' will match int 4 and vica versa
*
* @param array<mixed> $array search in as array
* @param array<mixed> $in_array search in as array
* @param string|int $key key (key to search in)
* @param string|int|bool|array<string|int|bool> $value values list (what to find)
* @param bool $strict [false], if set to true, will strict check key/value
* @return bool true on found, false on not found
*/
public static function arraySearchSimple(
array $array,
array $in_array,
string|int $key,
string|int|bool|array $value,
bool $strict = false
@@ -166,7 +166,7 @@ class ArrayHandler
if (!is_array($value)) {
$value = [$value];
}
foreach ($array as $_key => $_value) {
foreach ($in_array as $_key => $_value) {
// if value is an array, we search
if (is_array($_value)) {
// call recursive, and return result if it is true, else continue
@@ -189,19 +189,19 @@ class ArrayHandler
* If prefix is turned on each found group will be prefixed with the
* search key
*
* @param array<mixed> $array array to search in
* @param array<mixed> $in_array array to search in
* @param array<mixed> $needles keys to find in array
* @param bool $flat [false] Turn on flat output
* @param bool $prefix [false] Prefix found with needle key
* @return array<mixed> Found values
*/
public static function arraySearchKey(
array $array,
array $in_array,
array $needles,
bool $flat = false,
bool $prefix = false
): array {
$iterator = new \RecursiveArrayIterator($array);
$iterator = new \RecursiveArrayIterator($in_array);
$recursive = new \RecursiveIteratorIterator(
$iterator,
\RecursiveIteratorIterator::SELF_FIRST
@@ -248,7 +248,7 @@ class ArrayHandler
* If not found return an array with the array block there the required key is missing,
* the path as string with seperator block set and the missing key entry
*
* @param array<mixed> $array
* @param array<mixed> $in_array
* @param string|int|float|bool $search_value
* @param string|array<string> $required_key
* @param ?string $search_key [null]
@@ -257,7 +257,7 @@ class ArrayHandler
* @return array<array{content?:array<mixed>,path?:string,missing_key?:array<string>}>
*/
public static function findArraysMissingKey(
array $array,
array $in_array,
string|int|float|bool $search_value,
string|array $required_key,
?string $search_key = null,
@@ -265,7 +265,7 @@ class ArrayHandler
string $current_path = ''
): array {
$results = [];
foreach ($array as $key => $value) {
foreach ($in_array as $key => $value) {
$path = $current_path ? $current_path . $path_separator . $key : $key;
if (is_array($value)) {
@@ -321,7 +321,7 @@ class ArrayHandler
* Find key => value entry and return set with key for all matching
* Can search recursively through nested arrays if recursive flag is set
*
* @param array<mixed> $array
* @param array<mixed> $in_array
* @param string $lookup
* @param int|string|float|bool $search
* @param bool $strict [false]
@@ -332,7 +332,7 @@ class ArrayHandler
* @return array<mixed>
*/
public static function selectArrayFromOption(
array $array,
array $in_array,
string $lookup,
int|string|float|bool $search,
bool $strict = false,
@@ -342,7 +342,7 @@ class ArrayHandler
string $flat_separator = self::DATA_SEPARATOR
): array {
// skip on empty
if ($array == []) {
if ($in_array == []) {
return [];
}
// init return result
@@ -352,7 +352,7 @@ class ArrayHandler
$search = strtolower($search);
}
foreach ($array as $key => $value) {
foreach ($in_array as $key => $value) {
// Handle current level search
if (isset($value[$lookup])) {
$compareValue = $value[$lookup];
@@ -399,14 +399,14 @@ class ArrayHandler
/**
* main wrapper function for next/prev key
*
* @param array<mixed> $array array to search in
* @param array<mixed> $in_array array to search in
* @param int|string $key key for next/prev
* @param bool $next [=true] if to search next or prev
* @return int|string|null Next/prev key or null for end/first
*/
private static function arrayGetKey(array $array, int|string $key, bool $next = true): int|string|null
private static function arrayGetKey(array $in_array, int|string $key, bool $next = true): int|string|null
{
$keys = array_keys($array);
$keys = array_keys($in_array);
if (($position = array_search($key, $keys, true)) === false) {
return null;
}
@@ -422,26 +422,26 @@ class ArrayHandler
* Get previous array key from an array
* null on not found
*
* @param array<mixed> $array
* @param array<mixed> $in_array
* @param int|string $key
* @return int|string|null Next key, or null for not found
*/
public static function arrayGetPrevKey(array $array, int|string $key): int|string|null
public static function arrayGetPrevKey(array $in_array, int|string $key): int|string|null
{
return self::arrayGetKey($array, $key, false);
return self::arrayGetKey($in_array, $key, false);
}
/**
* Get next array key from an array
* null on not found
*
* @param array<mixed> $array
* @param array<mixed> $in_array
* @param int|string $key
* @return int|string|null Next key, or null for not found
*/
public static function arrayGetNextKey(array $array, int|string $key): int|string|null
public static function arrayGetNextKey(array $in_array, int|string $key): int|string|null
{
return self::arrayGetKey($array, $key, true);
return self::arrayGetKey($in_array, $key, true);
}
/**
@@ -463,27 +463,27 @@ class ArrayHandler
}
// default key is not string
$key_is_string = false;
$arrays = func_get_args();
$in_arrays = func_get_args();
// if last is not array, then assume it is trigger for key is always string
if (!is_array(end($arrays))) {
if (array_pop($arrays)) {
if (!is_array(end($in_arrays))) {
if (array_pop($in_arrays)) {
$key_is_string = true;
}
}
// check that arrays count is at least two, else we don't have enough to do anything
if (count($arrays) < 2) {
if (count($in_arrays) < 2) {
throw new \ArgumentCountError(__FUNCTION__ . ' needs two or more array arguments');
}
$merged = [];
while ($arrays) {
$array = array_shift($arrays);
if (!is_array($array)) {
while ($in_arrays) {
$in_array = array_shift($in_arrays);
if (!is_array($in_array)) {
throw new \TypeError(__FUNCTION__ . ' encountered a non array argument');
}
if (!$array) {
if (!$in_array) {
continue;
}
foreach ($array as $key => $value) {
foreach ($in_array as $key => $value) {
// if string or if key is assumed to be string do key match
// else add new entry
if (is_string($key) || $key_is_string === false) {
@@ -589,14 +589,14 @@ class ArrayHandler
* converts multi dimensional array to a flat array
* does NOT preserve keys
*
* @param array<mixed> $array multi dimensionial array
* @param array<mixed> $in_array multi dimensionial array
* @return array<mixed> flattened array
*/
public static function flattenArray(array $array): array
public static function flattenArray(array $in_array): array
{
$return = [];
array_walk_recursive(
$array,
$in_array,
function ($value) use (&$return) {
$return[] = $value;
}
@@ -607,13 +607,13 @@ class ArrayHandler
/**
* will loop through an array recursivly and write the array keys back
*
* @param array<mixed> $array multidemnsional array to flatten
* @param array<mixed> $in_array multidemnsional array to flatten
* @param array<mixed> $return recoursive pass on array of keys
* @return array<mixed> flattened keys array
*/
public static function flattenArrayKey(array $array, array $return = []): array
public static function flattenArrayKey(array $in_array, array $return = []): array
{
foreach ($array as $key => $sub) {
foreach ($in_array as $key => $sub) {
$return[] = $key;
if (is_array($sub) && count($sub) > 0) {
$return = self::flattenArrayKey($sub, $return);
@@ -626,14 +626,14 @@ class ArrayHandler
* as above will flatten an array, but in this case only the outmost
* leave nodes, all other keyswill be skipped
*
* @param array<mixed> $array multidemnsional array to flatten
* @param array<mixed> $in_array multidemnsional array to flatten
* @return array<mixed> flattened keys array
*/
public static function flattenArrayKeyLeavesOnly(array $array): array
public static function flattenArrayKeyLeavesOnly(array $in_array): array
{
$return = [];
array_walk_recursive(
$array,
$in_array,
function ($value, $key) use (&$return) {
$return[] = $key;
}
@@ -645,14 +645,14 @@ class ArrayHandler
* searches for key -> value in an array tree and writes the value one level up
* this will remove this leaf will all other values
*
* @param array<mixed> $array nested array
* @param array<mixed> $in_array nested array
* @param string|int $search key to find that has no sub leaf
* and will be pushed up
* @return array<mixed> modified, flattened array
*/
public static function arrayFlatForKey(array $array, string|int $search): array
public static function arrayFlatForKey(array $in_array, string|int $search): array
{
foreach ($array as $key => $value) {
foreach ($in_array as $key => $value) {
// if it is not an array do just nothing
if (!is_array($value)) {
continue;
@@ -660,14 +660,14 @@ class ArrayHandler
// probe it has search key
if (isset($value[$search])) {
// set as current
$array[$key] = $value[$search];
$in_array[$key] = $value[$search];
} else {
// call up next node down
// $array[$key] = call_user_func(__METHOD__, $value, $search);
$array[$key] = self::arrayFlatForKey($value, $search);
// $in_array[$key] = call_user_func(__METHOD__, $value, $search);
$in_array[$key] = self::arrayFlatForKey($value, $search);
}
}
return $array;
return $in_array;
}
/**
@@ -677,13 +677,13 @@ class ArrayHandler
*
* https://stackoverflow.com/a/369608
*
* @param array<mixed> $array Array where elements are located
* @param array<mixed> $in_array Array where elements are located
* @param array<mixed> $remove Elements to remove
* @return array<mixed> Array with $remove elements removed
*/
public static function arrayRemoveEntry(array $array, array $remove): array
public static function arrayRemoveEntry(array $in_array, array $remove): array
{
return array_diff($array, $remove);
return array_diff($in_array, $remove);
}
/**
@@ -693,20 +693,20 @@ class ArrayHandler
* key list is a list[string]
* if key list is empty, return array as is
*
* @param array<string,mixed> $array
* @param array<string,mixed> $in_array
* @param array<string> $key_list
* @return array<string,mixed>
*/
public static function arrayReturnMatchingKeyOnly(
array $array,
array $in_array,
array $key_list
): array {
// on empty return as is
if (empty($key_list)) {
return $array;
return $in_array;
}
return array_filter(
$array,
$in_array,
fn($key) => in_array($key, $key_list),
ARRAY_FILTER_USE_KEY
);
@@ -748,16 +748,19 @@ class ArrayHandler
* sort ascending or descending with or without lower case convert
* value only, will loose key connections unless preserve_keys is set to true
*
* @param array<mixed> $array array to sort by values
* @param int $params sort flags
* @param array<mixed> $in_array Array to sort by values
* @param bool $case_insensitive [false] Sort case insensitive
* @param bool $reverse [false] Reverse sort
* @param bool $maintain_keys [false] Maintain keys
* @param int $flag [SORT_REGULAR] Sort flags
* @return array<mixed>
*/
public static function sortArray(
array $array,
array $in_array,
bool $case_insensitive = false,
bool $reverse = false,
bool $maintain_keys = false,
int $params = SORT_REGULAR
int $flag = SORT_REGULAR
): array {
$fk_sort_lower_case = function (string $a, string $b): int {
return strtolower($a) <=> strtolower($b);
@@ -767,26 +770,26 @@ class ArrayHandler
};
$case_insensitive ? (
$maintain_keys ?
(uasort($array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case)) :
(usort($array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case))
(uasort($in_array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case)) :
(usort($in_array, $reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case))
) :
(
$maintain_keys ?
($reverse ? arsort($array, $params) : asort($array, $params)) :
($reverse ? rsort($array, $params) : sort($array, $params))
($reverse ? arsort($in_array, $flag) : asort($in_array, $flag)) :
($reverse ? rsort($in_array, $flag) : sort($in_array, $flag))
);
return $array;
return $in_array;
}
/**
* sort by key ascending or descending and return
*
* @param array<mixed> $array
* @param bool $case_insensitive [false]
* @param bool $reverse [false]
* @param array<mixed> $in_array Array to srt
* @param bool $case_insensitive [false] Sort keys case insenstive
* @param bool $reverse [false] Reverse key sort
* @return array<mixed>
*/
public static function ksortArray(array $array, bool $case_insensitive = false, bool $reverse = false): array
public static function ksortArray(array $in_array, bool $case_insensitive = false, bool $reverse = false): array
{
$fk_sort_lower_case = function (string $a, string $b): int {
return strtolower($a) <=> strtolower($b);
@@ -801,12 +804,12 @@ class ArrayHandler
return $b <=> $a;
};
uksort(
$array,
$in_array,
$case_insensitive ?
($reverse ? $fk_sort_lower_case_reverse : $fk_sort_lower_case) :
($reverse ? $fk_sort_reverse : $fk_sort)
);
return $array;
return $in_array;
}
}

View File

@@ -55,10 +55,10 @@ class Json
* Deos not throw errors
*
* @param array<mixed> $data
* @param int $flags json_encode flags as is
* @param int $flags [JSON_UNESCAPED_UNICODE] json_encode flags as is
* @return string JSON string or '{}' if false
*/
public static function jsonConvertArrayTo(array $data, int $flags = 0): string
public static function jsonConvertArrayTo(array $data, int $flags = JSON_UNESCAPED_UNICODE): string
{
$json_string = json_encode($data, $flags) ?: '{}';
self::$json_last_error = json_last_error();

View File

@@ -199,15 +199,17 @@ class Math
callback: fn ($col) => is_array($row) ?
array_reduce(
array: $row,
callback: fn ($a, $v, $i = null) => $a + $v * (
// TODO check that v is not an array
callback: fn ($a, $v, $i = null) => $a + $v * ( /** @phpstan-ignore-line Possible array + int */
// if last entry missing for full copy add a 0 to it
$col[$i ?? array_search($v, $row, true)] ?? 0 /** @phpstan-ignore-line */
$col[$i ?? array_search($v, $row, true)] ?? 0
),
initial: 0,
) :
array_reduce(
array: $col,
callback: fn ($a, $v) => $a + $v * $row,
// TODO check that v is not an array
callback: fn ($a, $v) => $a + $v * $row, /** @phpstan-ignore-line Possible array + int */
initial: 0,
),
array: $bCols,

View File

@@ -263,11 +263,11 @@ class ConvertPlaceholder
}
}
// add the connectors back (1), and the data sets only if no replacement will be done
return $params_lookup[$match] ??
return $params_lookup[$match]/* ??
throw new \RuntimeException(
'Cannot lookup ' . $match . ' in params lookup list',
211
);
)*/;
},
$converted_placeholders['original']['query']
);
@@ -327,11 +327,11 @@ class ConvertPlaceholder
}
}
// add the connectors back (1), and the data sets only if no replacement will be done
return $params_lookup[$match] ??
return $params_lookup[$match]/* ??
throw new \RuntimeException(
'Cannot lookup ' . $match . ' in params lookup list',
231
);
)*/;
},
$converted_placeholders['original']['query']
);

View File

@@ -33,6 +33,36 @@ class Support
return $string;
}
/**
* print ISO type datetime with microseconds and timezone
* Y-m-dTH:i:s.uP
* if no micro time the ".u" part is omitted
*
* @param bool $set_micro_time
* @return string
*/
public static function printIsoTime(bool $set_micro_time = true): string
{
$datetime = new \DateTime();
// Format the DateTime object to ISO 8601 with microseconds
// 'Y-m-d\TH:i:s.uP' is the format string:
// Y: Full year (e.g., 2025)
// m: Month (01-12)
// d: Day of the month (01-31)
// T: Literal 'T' to separate date and time (escaped with a backslash)
// H: Hour (00-23)
// i: Minute (00-59)
// s: Second (00-59)
// u: Microseconds (e.g., 654321)
// P: Difference to Greenwich time (GMT) with colon (e.g., +09:00)
if ($set_micro_time) {
return $datetime->format('Y-m-d\TH:i:s.uP');
} else {
return $datetime->format('Y-m-d\TH:i:sP');
}
}
/**
* prints a html formatted (pre) data
*

View File

@@ -112,7 +112,7 @@ enum Level: int
}
/**
* Returns true if the passed $level is higher or equal to $this
* Returns true if the passed $level is included in set level
*
* @param Level $level
* @return bool

View File

@@ -13,6 +13,7 @@ namespace CoreLibs\Logging\Logger;
enum MessageLevel: int
{
case noset = 0;
case ok = 100;
case success = 150; // special for file uploads
case info = 200;

View File

@@ -96,6 +96,11 @@ class Logging
'type' => 'bool', 'mandatory' => false,
'default' => false, 'deprecated' => true, 'use' => 'log_per_date'
],
// if turned off uses old time format without time zone
'log_time_format_iso' => [
'type' => 'bool', 'mandatory' => false,
'default' => true, 'deprecated' => false
]
];
// options
@@ -646,7 +651,11 @@ class Logging
}
// print "CLASS: " . $class . "<br>";
// get timestamp
if (!empty($this->options['log_time_format_iso'])) {
$timestamp = Support::printIsoTime();
} else {
$timestamp = Support::printTime();
}
// if group id is empty replace it with current level
$group_str = $level->getName();
@@ -1475,14 +1484,15 @@ class Logging
Level::Error, Level::Critical, Level::Alert, Level::Emergency
] as $l
) {
print "Check: " . $this->log_level->getName() . " | " . $l->getName() . "<br>";
if ($this->log_level->isHigherThan($l)) {
print "L: " . $this->log_level->getName() . " > " . $l->getName() . "<br>";
print "L(gt): " . $this->log_level->getName() . " > " . $l->getName() . "<br>";
}
if ($this->log_level->includes($l)) {
print "L: " . $this->log_level->getName() . " <= " . $l->getName() . "<br>";
print "L(le): " . $this->log_level->getName() . " <= " . $l->getName() . "<br>";
}
if ($this->log_level->isLowerThan($l)) {
print "L: " . $this->log_level->getName() . " < " . $l->getName() . "<br>";
print "L(lt): " . $this->log_level->getName() . " < " . $l->getName() . "<br>";
}
echo "<br>";
}

View File

@@ -24,12 +24,12 @@ final class CoreLibsCheckEmailTest extends TestCase
'get email regex invalid -1, will be 0' => [
-1,
"^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
],
'get email regex invalid 10, will be 0' => [
10,
"^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
],
'get email regex valid 1, will be 1' => [
1,
@@ -157,7 +157,7 @@ final class CoreLibsCheckEmailTest extends TestCase
'error' => 0,
'message' => 'Invalid email address',
'regex' => "^[A-Za-z0-9!#$%&'*+\-\/=?^_`{|}~][A-Za-z0-9!#$%:\(\)&'*+\-\/=?^_`{|}~\.]{0,63}@"
. "[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]{1,})*\.([a-zA-Z]{2,}){1}$"
. "(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
]
],
'error 1 will return double @ error' => [
@@ -181,7 +181,7 @@ final class CoreLibsCheckEmailTest extends TestCase
[
'error' => 3,
'message' => 'Invalid domain part after @ sign',
'regex' => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$"
'regex' => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\.[a-zA-Z]{2,6}$"
]
],
'error 4 will be invalid domain' => [
@@ -189,7 +189,7 @@ final class CoreLibsCheckEmailTest extends TestCase
[
'error' => 4,
'message' => 'Invalid domain name part',
'regex' => "@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\."
'regex' => "@(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\.[A-Za-z0-9-]{1,63}(?<!-))*\."
]
],
'error 5 will be invalid domain top level only' => [
@@ -197,7 +197,7 @@ final class CoreLibsCheckEmailTest extends TestCase
[
'error' => 5,
'message' => 'Wrong domain top level part',
'regex' => "\.([a-zA-Z]{2,6}){1}$"
'regex' => "\.[a-zA-Z]{2,6}$"
]
],
'error 6 will be domain double dot' => [

View File

@@ -18,7 +18,7 @@ use CoreLibs\Logging\Logger\Flag;
final class CoreLibsLoggingLoggingTest extends TestCase
{
private const LOG_FOLDER = __DIR__ . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR;
private const REGEX_BASE = "\[[\d\-\s\.:]+\]\s{1}" // date
private const REGEX_BASE = "\[[\d\-\s\.:+T]+\]\s{1}" // date, just basic checks
. "\[[\w\.]+(:\d+)?\]\s{1}" // host:port
. "\[(phar:\/\/)?[\w\-\.\/]+:\d+\]\s{1}" // folder/file [note phar:// is for phpunit]
. "\[\w+\]\s{1}" // run id