Updated UTF8 & ReflectionTypeHint classes (#318)

* Updated UTF8 class

* Updated

* Updated

* Update CHANGELOG.md
This commit is contained in:
Roman Kelesidis 2023-10-10 17:50:24 +07:00 committed by GitHub
parent 36d00c40ff
commit d89e7d3efa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 1161 additions and 1030 deletions

View File

@ -8,6 +8,7 @@
**Merged pull requests:**
- Release v2.1.5-2023.10 🎉
- Updated UTF8 & ReflectionTypeHint classes [\#318](https://github.com/torrentpier/torrentpier-lts/pull/318) ([belomaxorka](https://github.com/belomaxorka))
- Updated Text_LangCorrect class [\#309](https://github.com/torrentpier/torrentpier-lts/pull/309) ([belomaxorka](https://github.com/belomaxorka))
- Minor improvements [\#297](https://github.com/torrentpier/torrentpier-lts/pull/297), [\#298](https://github.com/torrentpier/torrentpier-lts/pull/298), [\#300](https://github.com/torrentpier/torrentpier-lts/pull/300), [\#301](https://github.com/torrentpier/torrentpier-lts/pull/301), [\#302](https://github.com/torrentpier/torrentpier-lts/pull/302), [\#303](https://github.com/torrentpier/torrentpier-lts/pull/303), [\#305](https://github.com/torrentpier/torrentpier-lts/pull/305), [\#306](https://github.com/torrentpier/torrentpier-lts/pull/306), [\#307](https://github.com/torrentpier/torrentpier-lts/pull/307), [\#310](https://github.com/torrentpier/torrentpier-lts/pull/310), [\#312](https://github.com/torrentpier/torrentpier-lts/pull/312), [\#313](https://github.com/torrentpier/torrentpier-lts/pull/313), [\#315](https://github.com/torrentpier/torrentpier-lts/pull/315), [\#316](https://github.com/torrentpier/torrentpier-lts/pull/316), [\#317](https://github.com/torrentpier/torrentpier-lts/pull/317) ([belomaxorka](https://github.com/belomaxorka))

View File

@ -1,21 +1,36 @@
<?php
if (!defined('BB_ROOT')) die(basename(__FILE__));
<?php if (!defined('BB_ROOT')) die(basename(__FILE__));
/**
* A class for validating method parameters to allowed types via reflection.
*
* Purpose
* Used as a more convenient multiple assert(), standing after the declaration of the methods.
* * Used as a more convenient mechanism than a big code for checking types,
* standing after the declaration of the methods.
* * Requires write correct phpDoc
*
* Features and advantage
* Features
* * Very easy to use
* * Ability to turn off on the production server
*
* Understanding
* All built-in PHP functions check the type of input variables and the "swearing", if not given.
* ReflectionTypeHint does too.
* Previously, I wrote this (the correct way, but a lot of code):
* if (! is_bool($b)) {
* trigger_error('A bool type expected in 1-st parameter, ' . gettype($b) . ' type given!', E_USER_WARNING);
* return false;
* }
* if (! is_string($s)) {
* trigger_error('A string type expected in 2-nd parameter, ' . gettype($s) . ' type given!', E_USER_WARNING);
* return false;
* }
* Now I'm doing this one line of code:
* if (! ReflectionTypeHint::isValid()) return false;
*
* WARNING
* On a production server, it is important to disable assert, that would save server resources.
* For this, use the assert_options(ASSERT_ACTIVE, false) or INI setting "assert.active 0".
* In this case ReflectionTypeHint::isValid() always returns TRUE!
* In this case ReflectionTypeHint::isValid() always immediately returns TRUE!
*
* Useful links
* http://www.ilia.ws/archives/205-Type-hinting-for-PHP-5.3.html
@ -27,46 +42,47 @@ if (!defined('BB_ROOT')) die(basename(__FILE__));
* @author Nasibullin Rinat
* @version 1.1.0
*/
class ReflectionTypeHint
{
protected static $hints = array(
'int' => 'is_int',
'integer' => 'is_int',
'digit' => 'ctype_digit',
'number' => 'ctype_digit',
'float' => 'is_float',
'double' => 'is_float',
'real' => 'is_float',
'numeric' => 'is_numeric',
'str' => 'is_string',
'string' => 'is_string',
'char' => 'is_string',
'bool' => 'is_bool',
'boolean' => 'is_bool',
'null' => 'is_null',
'array' => 'is_array',
'obj' => 'is_object',
'object' => 'is_object',
'res' => 'is_resource',
'int' => 'is_int',
'integer' => 'is_int',
'digit' => 'ctype_digit',
'number' => 'ctype_digit',
'float' => 'is_float',
'double' => 'is_float',
'real' => 'is_float',
'numeric' => 'is_numeric',
'str' => 'is_string',
'string' => 'is_string',
'char' => 'is_string',
'bool' => 'is_bool',
'boolean' => 'is_bool',
'null' => 'is_null',
'array' => 'is_array',
'obj' => 'is_object',
'object' => 'is_object',
'res' => 'is_resource',
'resource' => 'is_resource',
'scalar' => 'is_scalar', #integer, float, string or boolean
'cb' => 'is_callable',
'scalar' => 'is_scalar', #integer, float, string or boolean
'cb' => 'is_callable',
'callback' => 'is_callable',
);
#calling the methods of this class only statically!
private function __construct() {}
private function __construct()
{
}
public static function isValid()
{
if (! assert_options(ASSERT_ACTIVE)) return true;
if (!assert_options(ASSERT_ACTIVE)) return true;
$bt = self::debugBacktrace(null, 1);
extract($bt); //to $file, $line, $function, $class, $object, $type, $args
if (! $args) return true; #speed improve
if (!$args) return true; #speed improve
$r = new ReflectionMethod($class, $function);
$doc = $r->getDocComment();
$cache_id = $class. $type. $function;
$cache_id = $class . $type . $function;
preg_match_all('~ [\r\n]++ [\x20\t]++ \* [\x20\t]++
@param
[\x20\t]++
@ -79,34 +95,30 @@ class ReflectionTypeHint
~sixSX', $doc, $params, PREG_SET_ORDER);
$parameters = $r->getParameters();
//d($args, $params, $parameters);
if (count($parameters) > count($params))
{
if (count($parameters) > count($params)) {
$message = 'phpDoc %d piece(s) @param description expected in %s%s%s(), %s given, ' . PHP_EOL
. 'called in %s on line %d ' . PHP_EOL
. 'and defined in %s on line %d';
. 'called in %s on line %d ' . PHP_EOL
. 'and defined in %s on line %d';
$message = sprintf($message, count($parameters), $class, $type, $function, count($params), $file, $line, $r->getFileName(), $r->getStartLine());
trigger_error($message, E_USER_NOTICE);
}
foreach ($args as $i => $value)
{
if (! isset($params[$i])) return true;
if ($parameters[$i]->name !== $params[$i][2])
{
foreach ($args as $i => $value) {
if (!isset($params[$i])) return true;
if ($parameters[$i]->name !== $params[$i][2]) {
$param_num = $i + 1;
$message = 'phpDoc @param %d in %s%s%s() must be named as $%s, $%s given, ' . PHP_EOL
. 'called in %s on line %d ' . PHP_EOL
. 'and defined in %s on line %d';
. 'called in %s on line %d ' . PHP_EOL
. 'and defined in %s on line %d';
$message = sprintf($message, $param_num, $class, $type, $function, $parameters[$i]->name, $params[$i][2], $file, $line, $r->getFileName(), $r->getStartLine());
trigger_error($message, E_USER_NOTICE);
}
$hints = preg_split('~[|/,]~sSX', $params[$i][1]);
if (! self::checkValueTypes($hints, $value))
{
if (!self::checkValueTypes($hints, $value)) {
$param_num = $i + 1;
$message = 'Argument %d passed to %s%s%s() must be an %s, %s given, ' . PHP_EOL
. 'called in %s on line %d ' . PHP_EOL
. 'and defined in %s on line %d';
. 'called in %s on line %d ' . PHP_EOL
. 'and defined in %s on line %d';
$message = sprintf($message, $param_num, $class, $type, $function, implode('|', $hints), (is_object($value) ? get_class($value) . ' ' : '') . gettype($value), $file, $line, $r->getFileName(), $r->getStartLine());
trigger_error($message, E_USER_WARNING);
return false;
@ -120,8 +132,8 @@ class ReflectionTypeHint
* (totally skip them correcting caller references).
* If $return_frame is present, return only $return_frame matched caller, not all stacktrace.
*
* @param string|null $re_ignore example: '~^' . preg_quote(__CLASS__, '~') . '(?![a-zA-Z\d])~sSX'
* @param int|null $return_frame
* @param string|null $re_ignore example: '~^' . preg_quote(__CLASS__, '~') . '(?![a-zA-Z\d])~sSX'
* @param int|null $return_frame
* @return array
*/
public static function debugBacktrace($re_ignore = null, $return_frame = null)
@ -130,20 +142,18 @@ class ReflectionTypeHint
$a = array();
$frames = 0;
for ($i = 0, $n = count($trace); $i < $n; $i++)
{
for ($i = 0, $n = count($trace); $i < $n; $i++) {
$t = $trace[$i];
if (! $t) continue;
if (!$t) continue;
// Next frame.
$next = isset($trace[$i+1])? $trace[$i+1] : null;
$next = isset($trace[$i + 1]) ? $trace[$i + 1] : null;
// Dummy frame before call_user_func*() frames.
if (! isset($t['file']) && $next)
{
$t['over_function'] = $trace[$i+1]['function'];
$t = $t + $trace[$i+1];
$trace[$i+1] = null; // skip call_user_func on next iteration
if (!isset($t['file']) && $next) {
$t['over_function'] = $trace[$i + 1]['function'];
$t = $t + $trace[$i + 1];
$trace[$i + 1] = null; // skip call_user_func on next iteration
}
// Skip myself frame.
@ -151,11 +161,10 @@ class ReflectionTypeHint
// 'class' and 'function' field of next frame define where this frame function situated.
// Skip frames for functions situated in ignored places.
if ($re_ignore && $next)
{
if ($re_ignore && $next) {
// Name of function "inside which" frame was generated.
$frame_caller = (isset($next['class']) ? $next['class'] . $next['type'] : '')
. (isset($next['function']) ? $next['function'] : '');
. (isset($next['function']) ? $next['function'] : '');
if (preg_match($re_ignore, $frame_caller)) continue;
}
@ -169,14 +178,13 @@ class ReflectionTypeHint
/**
* Checks a value to the allowed types
*
* @param array $types
* @param mixed $value
* @param array $types
* @param mixed $value
* @return bool
*/
public static function checkValueTypes(array $types, $value)
{
foreach ($types as $type)
{
foreach ($types as $type) {
$type = strtolower($type);
if (array_key_exists($type, self::$hints) && call_user_func(self::$hints[$type], $value)) return true;
if (is_object($value) && @is_a($value, $type)) return true;
@ -184,4 +192,4 @@ class ReflectionTypeHint
}
return false;
}
}
}

File diff suppressed because it is too large Load Diff