Enh: Code de-duplication by introducing PermissionManager::handlePermissionStateChange() (#6750)

* Code deduplication by introducing `PermissionManager::handlePermissionStateChange()`

* Throw error if record isn't saved

* Fix: clear local cache also if permission is reset to default

* Move `checkClassType` and `classUsesTraits` from `Helpers` to `DataTypeHelper`

* Requested Changes Pt. I

* Requested Changes Pt. 2

* Requested Changes Pt. 3

* Requested Changes Pt. 4

* Update documentation

* Remove `filterClassType()`

* Fix `filterBool()`

* Change `ensure*` methods' return type to `void`
This commit is contained in:
Martin Rüegg 2024-01-19 12:32:01 +01:00 committed by GitHub
parent 344ebc0a6f
commit fab82dbd76
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 1614 additions and 621 deletions

View File

@ -3,6 +3,7 @@ HumHub Changelog
1.16.0 (Unreleased)
-------------------
- Enh #6750: Code de-duplication by introducing `PermissionManager::handlePermissionStateChange()`
- Fix #6772: Polymorphic relation lookup (Regression #6587)
- Enh #6745: Harmonise term `enabled/disabled` vs `active/inactive` for modules
- Fix #6754: Regression due to return type (#6550)

View File

@ -10,7 +10,7 @@ namespace humhub\components\behaviors;
use Exception;
use humhub\components\ActiveRecord;
use humhub\libs\Helpers;
use humhub\helpers\DataTypeHelper;
use humhub\modules\content\components\ContentActiveRecord;
use humhub\modules\content\components\ContentAddonActiveRecord;
use ReflectionClass;
@ -156,7 +156,7 @@ class PolymorphicRelation extends Behavior
return true;
}
if (Helpers::checkClassType($object, $this->mustBeInstanceOf, false)) { //|| $object->asa($instance) !== null
if (DataTypeHelper::matchClassType($object, $this->mustBeInstanceOf)) { //|| $object->asa($instance) !== null
return true;
}

View File

@ -23,9 +23,9 @@ trait InvalidArgumentExceptionTrait
protected bool $isInstantiating = true;
/**
* @param string $parameterOrMessage Name of parameter in question, or alternatively the full message string containing at
* least one space character (ASCII 32). In this case, `$valid` and `$given` are considered to be
* `$code` and `$previous` respectively
* @param string $parameterOrMessage Name of parameter in question, or alternatively the full message string
* containing at least one space character (ASCII 32). In this case, `$valid` and `$given` are considered to be
* `$code` and `$previous` respectively
* @param string|string[] $valid (List of) valid parameter(s)
* @param mixed $given Parameter received
* @param int $code Optional exception code
@ -41,25 +41,46 @@ trait InvalidArgumentExceptionTrait
try {
if (!is_string($parameterOrMessage)) {
throw new InvalidArgumentTypeException('$parameterOrMessage', ['string'], $parameterOrMessage, 0, $this);
throw new InvalidArgumentTypeException(
'$parameterOrMessage',
['string'],
$parameterOrMessage,
0,
$this
);
}
if (empty($parameterOrMessage = trim($parameterOrMessage))) {
throw new InvalidArgumentValueException('$parameterOrMessage', 'non-empty string', $parameterOrMessage, 0, $this);
throw new InvalidArgumentValueException(
'$parameterOrMessage',
'non-empty string',
$parameterOrMessage,
0,
$this
);
}
// check if $parameter is actually the $message
if (strpos($parameterOrMessage, ' ') !== false) {
$message = $parameterOrMessage;
$code = $code ?? $valid ?? 0;
$previous = $previous ?? $given;
$code ??= is_int($valid) ? $valid : 0;
if ($given instanceof Throwable) {
$previous ??= $given;
}
} else {
$trace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$trace = end($trace);
$this->methodName = ltrim(($trace['class'] ?? '') . '::' . ($trace['function'] ?? 'unknown method'), ':');
$this->parameter = $parameterOrMessage;
if (false !== $pos = strrpos($parameterOrMessage, '::')) {
$this->methodName = trim(substr($parameterOrMessage, 0, $pos), ':');
$this->parameter = trim(substr($parameterOrMessage, $pos), ':');
} else {
$trace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$trace = end($trace);
$this->methodName = ltrim(
($trace['class'] ?? '') . '::' . ($trace['function'] ?? 'unknown method'),
':'
);
$this->parameter = $parameterOrMessage;
}
try {
$this->setValid($valid);
} catch (InvalidArgumentTypeException $t) {
@ -84,7 +105,7 @@ trait InvalidArgumentExceptionTrait
}
/**
* @see static::__construct()
* @see static::__construct()
* @noinspection PhpUnhandledExceptionInspection
* @noinspection PhpDocMissingThrowsInspection
*/

View File

@ -0,0 +1,711 @@
<?php
/*
* @link https://www.humhub.org/
* @copyright Copyright (c) 2023 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\helpers;
use humhub\exceptions\InvalidArgumentClassException;
use humhub\exceptions\InvalidArgumentTypeException;
use humhub\exceptions\InvalidArgumentValueException;
use Stringable;
use function gettype;
/**
* @since 1.16
*/
class DataTypeHelper
{
public const TYPE_CHECK_VALUE_IS_NULL = 128;
public const TYPE_CHECK_TYPE_NOT_IN_LIST = 32;
public const TYPE_CHECK_INVALID_TYPE_PARAMETER = 2;
public const TYPE_CHECK_VALUE_IS_EMPTY = 4;
public const TYPE_CHECK_INVALID_VALUE_PARAMETER = 1;
public const TYPE_CHECK_INVALID_TYPE = 8;
public const TYPE_CHECK_NON_EXISTING_CLASS = 16;
public const TYPE_CHECK_VALUE_IS_INSTANCE = 64;
public const BOOLEAN = 'boolean';
public const INTEGER = 'integer';
public const STRING = 'string';
public const ARRAY = 'array';
public const OBJECT = 'object';
public const RESOURCE = 'resource';
public const RESOURCE_CLOSED = 'resource (closed)';
public const UNKNOWN_TYPE = 'unknown type';
public const DOUBLE = 'double';
public const FLOAT = 'float';
public const NULL = 'NULL';
/**
* @param mixed $value Variable to be checked.
* @param string|string[]|object[] $allowedTypes Allowed types.
*
* @since 1.16
* @see self::matchClassType()
*/
public static function isClassType($value, $allowedTypes): bool
{
return self::matchClassType($value, $allowedTypes) !== null;
}
/**
* @param mixed $value Variable to be checked.
* @param string|string[]|object[] $allowedTypes Allowed types.
*
* @since 1.16
* @see self::matchType()
*/
public static function isType($value, $allowedTypes): bool
{
return self::matchType($value, $allowedTypes) !== null;
}
/**
* @param mixed $value Variable to be checked.
* @param string|string[]|object[] $allowedTypes Allowed types. Valid input are
* ``
* - simple type names as returned by gettype()
* - `null` value or `'NULL'` string
* - class, interface, or trait names
* - class instances whose class type will be checked
* - `callable`, e.g. `is_scalar`
* ``
* rather that it's name.
*
* @return string|null Returns the first match of `$value` against the `$allowedTypes`.
* ``
* - If the matched type is a `NULL` value, the string "NULL" is returned.
* - If the matched type is an object instance, its class name is returned.
* - If the matched type is a `callable`, the callable´s string representation (name) is returned.
* - If no match is found, a `NULL` value is returned.
*``
* @throws InvalidArgumentTypeException|InvalidArgumentValueException
* @since 1.16
* @see gettype()
*/
public static function matchType($value, $allowedTypes, bool $throwException = false): ?string
{
$validTypes = self::parseTypes($allowedTypes, $allowNull, $checkTraits);
if ($value === null) {
if ($allowNull) {
return self::NULL;
}
if (!$throwException) {
return null;
}
throw new InvalidArgumentTypeException(
'$value',
$validTypes,
$value,
self::TYPE_CHECK_INVALID_VALUE_PARAMETER + self::TYPE_CHECK_VALUE_IS_EMPTY + self::TYPE_CHECK_INVALID_TYPE + self::TYPE_CHECK_VALUE_IS_NULL
);
}
$inputType = gettype($value);
$inputTraits = $checkTraits ? self::classUsesTraits($value, false) : null;
foreach ($allowedTypes as $i => $typeToCheck) {
if (static::matchTypeHelper($typeToCheck, $value, $inputType, $inputTraits) !== null) {
return $validTypes[$i];
}
}
if (!$throwException) {
return null;
}
$code = self::TYPE_CHECK_INVALID_VALUE_PARAMETER + self::TYPE_CHECK_TYPE_NOT_IN_LIST;
if (is_object($value)) {
$code |= self::TYPE_CHECK_VALUE_IS_INSTANCE;
}
throw new InvalidArgumentClassException(
'$value',
$allowedTypes,
$value,
$code
);
}
protected static function matchTypeHelper($typeToCheck, &$input, string $inputType, ?array &$inputTraits = null): ?string
{
if (is_string($typeToCheck) || (is_object($typeToCheck) && $typeToCheck = get_class($typeToCheck))) {
switch ($typeToCheck) {
case self::STRING:
case self::ARRAY:
case self::OBJECT:
case self::RESOURCE:
case self::RESOURCE_CLOSED: // as of PHP 7.2.0
case self::NULL:
case self::UNKNOWN_TYPE:
return $inputType === $typeToCheck
? $typeToCheck
: null;
case self::BOOLEAN: // the result of gettype()
case 'bool': // the name as it is defined in code
return $inputType === self::BOOLEAN
// return it the way it was tested
? $typeToCheck
: null;
case self::INTEGER: // the result of gettype()
case 'int': // the name as it is defined in code
return $inputType === self::INTEGER
// return it the way it was tested
? $typeToCheck
: null;
case self::DOUBLE:
case self::FLOAT:
return $inputType === self::DOUBLE
// return it the way it was tested
? $typeToCheck
: null;
case Stringable::class:
return $inputType === self::OBJECT
&& ($input instanceof Stringable || is_callable([$input, '__toString']))
? $typeToCheck
: null;
default:
/**
* Autoload is not used in any of the following functions, since the $typeToCheck has already been loaded in `self::parseTypes()`
*
* @see self::parseTypes()
* @noinspection NotOptimalIfConditionsInspection
*/
if (
(class_exists($typeToCheck, false) || interface_exists($typeToCheck, false))
&& $input instanceof $typeToCheck
) {
return $typeToCheck;
}
if (
$inputTraits !== null
&& trait_exists($typeToCheck, false)
&& in_array($typeToCheck, $inputTraits, true)
) {
return $typeToCheck;
}
}
}
if (
is_callable($typeToCheck, false, $name)
&& $typeToCheck($input)
) {
return $name;
}
return null;
}
/**
* Ensures that the provided `$value` is of or implements any class, interface or trait as provided by the
* `$allowedTypes` parameter. The function throws an Exception if none of the provided types is matched. Please see
* self::matchClassType() for more information on the parameters.
*
* @see self::matchClassType()
* @throws InvalidArgumentTypeException|InvalidArgumentClassException|InvalidArgumentValueException
* @since 1.16
*/
public static function ensureClassType($value, $allowedTypes): void
{
self::matchClassType($value, $allowedTypes, true);
}
/**
* Ensures that the provided `$value` is of or implements any type as provided by the `$allowedTypes` parameter.
* The function throws an Exception if none of the provided types is matched.
* Please see self::matchType() for more information on the parameters.
*
* @since 1.16
* @see self::matchType()
* @see InvalidArgumentTypeException
* @throws InvalidArgumentTypeException|InvalidArgumentValueException
*/
public static function ensureType($value, $allowedTypes): void
{
self::matchType($value, $allowedTypes, true);
}
/**
* Helper variable that returns the input `$value` if it is matched against the `$allowedTypes`
*
* @return mixed|null The `$value` if it matches any type given in $allowedTypes, or NULL otherwise
*
* @since 1.16
* @see self::matchType()
* @see InvalidArgumentTypeException
*/
private static function filterType($value, $allowedTypes, bool $throwException = false)
{
return self::matchType($value, $allowedTypes, $throwException) === null
? null
: $value;
}
/**
* Returns the boolean value of `$value`, or NULL if it's not a boolean and cannot be converted.
* See the parameter description for more information.
*
* @param mixed $value value to be tested or converted
* @param bool $strict indicates if strict comparison should be performed:
* ``
* - if TRUE, `$value` must already be of type `boolean`. In that case, its value is returned, NULL otherwise.
* - if FALSE, a conversion to `boolean` is attempted using `(bool)`. No exception is thrown, ever.
* Note: "false", "off", "no" yield TRUE in this case.
* - if NULL, a conversion to `boolean` is attempted, where
* "1", "true", "on", and "yes" yield TRUE,
* "0", "false", "off", "no", and "" yield FALSE.
* ``
* @param bool $throwException throws an exception instead of returning `null`
*
* @return bool|null see `$strict` parameter for details
*
* @see filter_var()
* @since 1.16
*/
public static function filterBool($value, ?bool $strict = false, bool $throwException = false): ?bool
{
if ($strict) {
return self::filterType($value, [self::BOOLEAN], $throwException);
}
if ($strict === false) {
try {
return (bool)$value;
} catch (\Throwable $e) {
}
return false;
}
$input = is_array($value) ? !empty($value) : filter_var(
$value,
FILTER_VALIDATE_BOOLEAN,
FILTER_NULL_ON_FAILURE
);
return self::filterType($input, [self::BOOLEAN, null], $throwException);
}
/**
* Checks if the class or object has one of the given classes, interfaces or traits as one of its parents or
* implements it.
*
* @param string|object|null|mixed $value Object or classname to be checked. Null may be valid if included in
* $type. Everything else is invalid and either throws an error (default) or returns NULL, if $throw is false.
* @param string|string[]|object[] $allowedTypes (List of) allowed class, interface or trait names, or object
* instances. Object instances may only be passed as part of an array. In such a case, the object's type/class
* is used for comparison. If a string is provided, it will be split by `|`. If NULL value or the "NULL"
* string
* is included, NULL values are also allowed. *
* @param bool $throwException throws an exception instead of returning `null`.
* Code of the thrown Exception is a bit-mask consisting of the following bits:
* ``
* - self::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER: Invalid $className parameter
* - self::CLASS_CHECK_INVALID_TYPE_PARAMETER: Invalid $type parameter
* - self::CLASS_CHECK_VALUE_IS_EMPTY: Empty parameter
* - self::CLASS_CHECK_INVALID_TYPE: Invalid type
* - self::CLASS_CHECK_NON_EXISTING_CLASS: Non-existing class
* - self::CLASS_CHECK_TYPE_NOT_IN_LIST: Class that is not in $type parameter
* - self::CLASS_CHECK_VALUE_IS_INSTANCE: $className is an object instance
* - self::CLASS_CHECK_VALUE_IS_NULL: NULL value
* ``
*
* @return string|null
* @throws InvalidArgumentTypeException|InvalidArgumentClassException|InvalidArgumentValueException
* @noinspection PhpDocMissingThrowsInspection
* @noinspection PhpUnhandledExceptionInspection
* @since 1.16
*/
public static function matchClassType($value, $allowedTypes, bool $throwException = false): ?string
{
$allowedTypes = static::parseTypes($allowedTypes, $allowNull, $checkTraits, false, false);
// check for null input
if ($value === null) {
// check if null is allowed
if ($allowNull) {
return null;
}
if (!$throwException) {
return null;
}
throw new InvalidArgumentTypeException(
'$value',
$allowedTypes,
$value,
self::TYPE_CHECK_INVALID_VALUE_PARAMETER + self::TYPE_CHECK_VALUE_IS_EMPTY + self::TYPE_CHECK_INVALID_TYPE + self::TYPE_CHECK_VALUE_IS_NULL
);
}
// check for other empty input values
if (empty($value)) {
if (!$throwException) {
return null;
}
/** @noinspection PhpUnhandledExceptionInspection */
throw is_string($value)
? new InvalidArgumentClassException(
'$value',
$allowedTypes,
$value,
self::TYPE_CHECK_INVALID_VALUE_PARAMETER + self::TYPE_CHECK_VALUE_IS_EMPTY + self::TYPE_CHECK_INVALID_TYPE + self::TYPE_CHECK_TYPE_NOT_IN_LIST
)
: new InvalidArgumentTypeException(
'$value',
$allowedTypes,
$value,
self::TYPE_CHECK_INVALID_VALUE_PARAMETER + self::TYPE_CHECK_VALUE_IS_EMPTY + self::TYPE_CHECK_INVALID_TYPE
);
}
if ($checkTraits) {
$checkTraits = self::classUsesTraits($value, false);
}
if ($isObject = is_object($value)) {
$type = get_class($value);
} elseif (is_string($value)) {
if (!class_exists($value)) {
if (!$throwException) {
return null;
}
throw new InvalidArgumentValueException(
'$value',
'a valid class name or an object instance',
$value,
self::TYPE_CHECK_INVALID_VALUE_PARAMETER + self::TYPE_CHECK_NON_EXISTING_CLASS
);
}
$type = $value;
} else {
if (!$throwException) {
return null;
}
throw new InvalidArgumentTypeException(
'$value',
$allowedTypes,
$value,
self::TYPE_CHECK_INVALID_VALUE_PARAMETER + self::TYPE_CHECK_INVALID_TYPE
);
}
foreach ($allowedTypes as $matchingClass) {
if ($checkTraits !== null && in_array($matchingClass, $checkTraits, true)) {
return $type;
}
if ($isObject ? $value instanceof $matchingClass : is_a($value, $matchingClass, true)) {
return $type;
}
}
if (!$throwException) {
return null;
}
$code = self::TYPE_CHECK_INVALID_VALUE_PARAMETER + self::TYPE_CHECK_TYPE_NOT_IN_LIST;
if ($isObject) {
$code |= self::TYPE_CHECK_VALUE_IS_INSTANCE;
}
throw new InvalidArgumentClassException(
'$value',
$allowedTypes,
$value,
$code
);
}
/**
* Returns the boolean value of `$value`, or NULL if it's not a float and cannot be converted.
* See the parameter description for more information.
*
* @param mixed $value value to be tested or converted
* @param bool $strict indicates if strict comparison should be performed:
* ``
* - if TRUE, `$value` must already be of type `float`.
* - if FALSE, a conversion to `float` is attempted.
* ``
* @param bool $throwException throws an exception instead of returning `null`
*
* @since 1.16
*/
public static function filterFloat($value, bool $strict = false, bool $throwException = false): ?float
{
if ($strict) {
return self::filterType($value, [self::FLOAT], $throwException);
}
$input = filter_var($value, FILTER_VALIDATE_FLOAT, FILTER_NULL_ON_FAILURE);
return self::filterType($input, [self::FLOAT, null], $throwException);
}
/**
* Returns the boolean value of `$value`, or NULL if it's not an integer and cannot be converted.
* See the parameter description for more information.
*
* @param mixed $value value to be tested or converted
* @param bool $strict indicates if strict comparison should be performed:
* ``
* - if TRUE, `$value` must already be of type `int`.
* - if FALSE, a conversion to `int` is attempted.
* ``
* @param bool $throwException throws an exception instead of returning `null`
*
* @return int|null
* @since 1.16
*/
public static function filterInt($value, bool $strict = false, bool $throwException = false): ?int
{
if ($strict) {
return self::filterType($value, [self::INTEGER], $throwException);
}
$input = filter_var($value, FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE);
return self::filterType($input, [self::INTEGER, null], $throwException);
}
/**
* Returns the boolean value of `$value`, or NULL if it's not a scalar.
* See the parameter description for more information.
*
* @param mixed $value value to be tested or converted
* @param bool $strict indicates if strict comparison should be performed:
* ``
* - if TRUE, `$value` must already be a scalar value.
* - if FALSE, `NULL` is also allowed (not throwing an exception, if `$throw` is TRUE).
* ``
* @param bool $throwException throws an exception instead of returning `null`
*
* @return bool|int|float|string|null
* @since 1.16
*/
public static function filterScalar($value, bool $strict = false, bool $throwException = false)
{
if ($strict) {
return self::filterType($value, ['is_scalar'], $throwException);
}
return self::filterType($value, [null, 'is_scalar'], $throwException);
}
/**
* Returns the boolean value of `$value`, or NULL if it's not a string and cannot be converted.
* See the parameter description for more information.
*
* @param mixed $value value to be tested or converted
* @param bool $strict indicates if strict comparison should be performed:
* ``
* - if TRUE, `$value` must already be of type `string`.
* - if FALSE, a conversion to `string` is attempted.
* ``
* @param bool $throwException throws an exception instead of returning `null`
*
* @since 1.16
*/
public static function filterString($value, bool $strict = false, bool $throwException = false): ?string
{
$allowedTypes = $strict ? [self::STRING] : [self::STRING, null, Stringable::class, 'is_scalar'];
switch (self::matchType($value, $allowedTypes, $throwException)) {
case self::STRING:
return $value;
case self::NULL:
return null;
case Stringable::class:
return $value->__toString();
case 'is_scalar':
return (string)$value;
}
return null;
}
/**
* Method evaluates all the traits used in an object/class, including the ones inherited from parent classes
*
* @param string|object $class Class name or instance to be checked
* @param bool $autoload Indicates whether autoload should be performed for classes that are not yet loaded.
*
* @return array an array of trait names used by the given class
* @since 1.16
* @see https://www.php.net/manual/en/function.class-uses.php#122427
*/
public static function classUsesTraits($class, bool $autoload = true): array
{
$traits = [];
// Get all the traits of $class and its parent classes
do {
$class_name = is_object($class) ? get_class($class) : $class;
if (class_exists($class_name, $autoload)) {
$traits = array_merge(class_uses($class, $autoload), $traits);
}
} while ($class = get_parent_class($class));
// Get traits of all parent traits
$traits_to_search = $traits;
while (!empty($traits_to_search)) {
$new_traits = class_uses(array_pop($traits_to_search), $autoload);
$traits = array_merge($new_traits, $traits);
$traits_to_search = array_merge($new_traits, $traits_to_search);
};
return array_unique($traits);
}
/**
* @return string[]
* @throws InvalidArgumentTypeException|InvalidArgumentValueException
* @since 1.16
*/
protected static function parseTypes(&$allowedTypes, ?bool &$allowNull = false, ?array &$checkTraits = null, bool $allowCallables = true, bool $allowGetTypes = true): array
{
$allowNull = false;
$checkTraits = null;
if ($allowedTypes === null) {
$allowNull = true;
$allowedTypes = [null];
return [];
}
if (is_string($allowedTypes)) {
if ($allowedTypes === '') {
throw new InvalidArgumentValueException(
'$allowedTypes',
[self::STRING, 'string[]', 'object[]'],
$allowedTypes,
self::TYPE_CHECK_INVALID_TYPE_PARAMETER + self::TYPE_CHECK_VALUE_IS_EMPTY
);
}
$allowedTypes = explode('|', $allowedTypes);
}
if (!is_array($allowedTypes)) {
throw new InvalidArgumentTypeException(
'$allowedTypes',
[self::STRING, 'string[]', 'object[]', null],
$allowedTypes,
self::TYPE_CHECK_INVALID_TYPE_PARAMETER + self::TYPE_CHECK_INVALID_TYPE
);
}
if (count($allowedTypes) === 0) {
throw new InvalidArgumentValueException(
'$allowedTypes',
[self::STRING, 'string[]', 'object[]'],
$allowedTypes,
self::TYPE_CHECK_INVALID_TYPE_PARAMETER + self::TYPE_CHECK_VALUE_IS_EMPTY
);
}
$valid = [];
// validate the type array
foreach ($allowedTypes as $index => &$item) {
if ($item === null || $item === self::NULL) {
$allowNull = true;
$item = null;
continue;
}
if (is_object($item)) {
$item = $valid[$index] = get_class($item);
continue;
}
if ($allowCallables && is_callable($item, false, $name)) {
$valid[$index] = $name;
continue;
}
if (!is_string($item)) {
throw new InvalidArgumentValueException(
sprintf('$allowedTypes[%s]', $index),
['class', self::OBJECT],
$item,
self::TYPE_CHECK_INVALID_TYPE_PARAMETER + self::TYPE_CHECK_INVALID_TYPE
);
}
if ($allowGetTypes) {
switch ($item) {
case self::BOOLEAN: // the result of gettype()
case 'bool': // the name as it is defined in code
case self::INTEGER: // the result of gettype()
case 'int': // the name as it is defined in code
case self::STRING:
case self::ARRAY:
case self::OBJECT:
case self::RESOURCE:
case self::RESOURCE_CLOSED: // as of PHP 7.2.0
case self::UNKNOWN_TYPE:
case self::DOUBLE:
case self::FLOAT:
$valid[$index] = $item;
continue 2;
}
}
// Here autoload is active, so the class is loaded even if it is an interface or a trait
if (class_exists($item)) {
$valid[$index] = $item;
continue;
}
// Here autoload is no longer required. See above.
if (interface_exists($item, false)) {
$valid[$index] = $item;
continue;
}
// Here autoload is no longer required. See above.
if (trait_exists($item, false)) {
$valid[$index] = $item;
$checkTraits[] = $item;
continue;
}
throw new InvalidArgumentValueException(
sprintf('$allowedTypes[%s]', $index),
'a valid class/interface/trait name or an object instance',
$item,
self::TYPE_CHECK_INVALID_TYPE_PARAMETER + self::TYPE_CHECK_NON_EXISTING_CLASS
);
}
return $valid;
}
}

View File

@ -11,8 +11,8 @@ namespace humhub\libs;
use humhub\exceptions\InvalidArgumentClassException;
use humhub\exceptions\InvalidArgumentTypeException;
use humhub\exceptions\InvalidArgumentValueException;
use humhub\helpers\DataTypeHelper;
use Yii;
use yii\base\InvalidArgumentException;
/**
* This class contains a lot of html helpers for the views
@ -21,20 +21,68 @@ use yii\base\InvalidArgumentException;
*/
class Helpers
{
public const CLASS_CHECK_INVALID_CLASSNAME_PARAMETER = 1;
public const CLASS_CHECK_INVALID_TYPE_PARAMETER = 2;
public const CLASS_CHECK_VALUE_IS_EMPTY = 4;
public const CLASS_CHECK_INVALID_TYPE = 8;
public const CLASS_CHECK_NON_EXISTING_CLASS = 16;
public const CLASS_CHECK_TYPE_NOT_IN_LIST = 32;
public const CLASS_CHECK_VALUE_IS_INSTANCE = 64;
public const CLASS_CHECK_VALUE_IS_NULL = 128;
/**
* @var int
* @deprecated since 1.16; Use constant in DataTypeHelper class instead
* @see DataTypeHelper
* */
public const CLASS_CHECK_INVALID_CLASSNAME_PARAMETER = DataTypeHelper::TYPE_CHECK_INVALID_VALUE_PARAMETER;
/**
* @var int
* @deprecated since 1.16; Use constant in DataTypeHelper class instead
* @see DataTypeHelper
* */
public const CLASS_CHECK_INVALID_TYPE_PARAMETER = DataTypeHelper::TYPE_CHECK_INVALID_TYPE_PARAMETER;
/**
* @var int
* @deprecated since 1.16; Use constant in DataTypeHelper class instead
* @see DataTypeHelper
* */
public const CLASS_CHECK_VALUE_IS_EMPTY = DataTypeHelper::TYPE_CHECK_VALUE_IS_EMPTY;
/**
* @var int
* @deprecated since 1.16; Use constant in DataTypeHelper class instead
* @see DataTypeHelper
* */
public const CLASS_CHECK_INVALID_TYPE = DataTypeHelper::TYPE_CHECK_INVALID_TYPE;
/**
* @var int
* @deprecated since 1.16; Use constant in DataTypeHelper class instead
* @see DataTypeHelper
* */
public const CLASS_CHECK_NON_EXISTING_CLASS = DataTypeHelper::TYPE_CHECK_NON_EXISTING_CLASS;
/**
* @var int
* @deprecated since 1.16; Use constant in DataTypeHelper class instead
* @see DataTypeHelper
* */
public const CLASS_CHECK_TYPE_NOT_IN_LIST = DataTypeHelper::TYPE_CHECK_TYPE_NOT_IN_LIST;
/**
* @var int
* @deprecated since 1.16; Use constant in DataTypeHelper class instead
* @see DataTypeHelper
* */
public const CLASS_CHECK_VALUE_IS_INSTANCE = DataTypeHelper::TYPE_CHECK_VALUE_IS_INSTANCE;
/**
* @var int
* @deprecated since 1.16; Use constant in DataTypeHelper class instead
* @see DataTypeHelper
* */
public const CLASS_CHECK_VALUE_IS_NULL = DataTypeHelper::TYPE_CHECK_VALUE_IS_NULL;
/**
* Shorten a text string
*
* @param string $text - Text string you will shorten
* @param integer $length - Count of characters to show
*
* @return string
*/
public static function truncateText($text, $length): string
@ -56,6 +104,7 @@ class Helpers
/**
* Compare two arrays values
*
* @param array $a - First array to compare against..
* @param array $b - Second array
*
@ -76,20 +125,27 @@ class Helpers
/**
* Temp Function to use UTF8 SubStr
*
* @deprecated since 1.11 Use mb_substr() instead.
*
* @param string $str
* @param integer $from
* @param integer $len
*
* @return string
*/
public static function substru($str, $from, $len): string
{
return preg_replace('#^(?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,' . $from . '}' . '((?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,' . $len . '}).*#s', '$1', $str);
return preg_replace(
'#^(?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,' . $from . '}' . '((?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,' . $len . '}).*#s',
'$1',
$str
);
}
/**
* Get a readable time format from seconds
*
* @param string $sekunden - Seconds you will formatting
* */
public static function getFormattedTime($sekunden)
@ -121,6 +177,7 @@ class Helpers
* Source: http://php.net/manual/en/function.ini-get.php
*
* @param String $val
*
* @return int bytes
* @deprecated bug on PHP7 "A non well formed numeric value encountered"
* @see \humhub\libs\Helpers::getBytesOfIniValue instead
@ -188,240 +245,25 @@ class Helpers
}
/**
* Checks if the class has this class as one of its parents
*
* Code of the thrown Exception is a bit-mask consisting of the following bits
* - self::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER: Invalid $className parameter
* - self::CLASS_CHECK_INVALID_TYPE_PARAMETER: Invalid $type parameter
* - self::CLASS_CHECK_VALUE_IS_EMPTY: Empty parameter
* - self::CLASS_CHECK_INVALID_TYPE: Invalid type
* - self::CLASS_CHECK_NON_EXISTING_CLASS: Non-existing class
* - self::CLASS_CHECK_TYPE_NOT_IN_LIST: Class that is not in $type parameter
* - self::CLASS_CHECK_VALUE_IS_INSTANCE: $className is an object instance
* - self::CLASS_CHECK_VALUE_IS_NULL: NULL value
*
* @param string|object|null|mixed $className Object or classname to be checked. Null may be valid if included in $type.
* Everything else is invalid and either throws an error (default) or returns NULL, if $throw is false.
* @param string|string[] $types (List of) class, interface or trait names that are allowed.
* If NULL is included, NULL values are also allowed.
* @param bool $throw Determines if an Exception should be thrown if $className doesn't match $type, or simply return NULL.
* Invalid $types always throw an error!
* @param bool $strict If set to true, no invalid characters are removed from a $className string.
* If set to false, please make sure you use the function's return value, rather than $className, as they might diverge
*
* @return string|null
* @throws InvalidArgumentTypeException|InvalidArgumentClassException|InvalidArgumentValueException
* @noinspection PhpDocMissingThrowsInspection
* @noinspection PhpUnhandledExceptionInspection
* @deprecated since 1.16; use DataTypeHelper::checkClassType()
* @see DataTypeHelper::matchClassType
*/
public static function checkClassType($className, $types, bool $throw = true, ?bool $strict = true): ?string
public static function checkClassType($className, $type = '')
{
if (empty($types)) {
throw new InvalidArgumentValueException('$type', ['string', 'string[]'], $types, self::CLASS_CHECK_INVALID_TYPE_PARAMETER + self::CLASS_CHECK_VALUE_IS_EMPTY);
if (is_string($className)) {
$className = preg_replace('/[^a-z0-9_\-\\\]/i', '', $className);
}
$types = (array)$types;
$valid = [];
$allowNull = false;
// validate the type array
foreach ($types as $index => &$item) {
if ($item === null) {
$allowNull = true;
continue;
}
if (is_object($item)) {
$valid[get_class($item)] = false;
continue;
}
if (!is_string($item)) {
throw new InvalidArgumentValueException(sprintf('$type[%s]', $index), ['class', 'object'], $item, self::CLASS_CHECK_INVALID_TYPE_PARAMETER + self::CLASS_CHECK_INVALID_TYPE);
}
$isTrait = false;
if (!class_exists($item) && !interface_exists($item, false) && !($isTrait = trait_exists($item, false))) {
throw new InvalidArgumentValueException(sprintf('$type[%s]', $index), 'a valid class/interface/trait name or an object instance', $item, self::CLASS_CHECK_INVALID_TYPE_PARAMETER + self::CLASS_CHECK_NON_EXISTING_CLASS);
}
$valid[$item] = $isTrait;
}
// make sure the reference is not going to be overwritten
unset($item);
// save the types for throwing exceptions
$types = array_keys($valid);
if ($allowNull) {
$types[] = null;
}
// check for null input
if ($className === null) {
// check if null is allowed
if ($allowNull) {
return null;
}
if (!$throw) {
return null;
}
throw new InvalidArgumentTypeException(
'$className',
$types,
$className,
self::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER + self::CLASS_CHECK_VALUE_IS_EMPTY + self::CLASS_CHECK_INVALID_TYPE + self::CLASS_CHECK_VALUE_IS_NULL
);
}
// check for other empty input
if (empty($className)) {
if ((!$strict && $allowNull) || !$throw) {
return null;
}
throw is_string($className)
? new InvalidArgumentClassException(
'$className',
$types,
$className,
self::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER + self::CLASS_CHECK_VALUE_IS_EMPTY + self::CLASS_CHECK_INVALID_TYPE + self::CLASS_CHECK_TYPE_NOT_IN_LIST
)
: new InvalidArgumentTypeException(
'$className',
$types,
$className,
self::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER + self::CLASS_CHECK_VALUE_IS_EMPTY + self::CLASS_CHECK_INVALID_TYPE
)
;
}
// Validation for object instances
if (is_object($className)) {
foreach ($valid as $matchingClass => $isTrait) {
if ($isTrait) {
if (in_array($matchingClass, static::classUsesTraits($className, false), true)) {
return get_class($className);
}
} elseif ($className instanceof $matchingClass) {
return get_class($className);
}
}
if (!$throw) {
return null;
}
throw new InvalidArgumentClassException(
'$className',
$types,
$className,
self::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER + self::CLASS_CHECK_TYPE_NOT_IN_LIST + self::CLASS_CHECK_VALUE_IS_INSTANCE
);
}
if (!is_string($className)) {
if (!$throw) {
return null;
}
throw new InvalidArgumentTypeException(
'$className',
$types,
$className,
self::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER + self::CLASS_CHECK_INVALID_TYPE
);
}
$cleaned = preg_replace('/[^a-z0-9_\-\\\]/i', '', $className);
if ($strict && $cleaned !== $className) {
if (!$throw) {
return null;
}
throw new InvalidArgumentClassException(
'$className',
'a valid class name or an object instance',
$className,
self::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER
);
}
$className = $cleaned;
if (!class_exists($className)) {
if (!$throw) {
return null;
}
throw new InvalidArgumentValueException(
'$className',
'a valid class name or an object instance',
$className,
self::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER + self::CLASS_CHECK_NON_EXISTING_CLASS
);
}
foreach ($valid as $matchingClass => $isTrait) {
if ($isTrait) {
if (in_array($matchingClass, static::classUsesTraits($className, false), true)) {
return $className;
}
} elseif (is_a($className, $matchingClass, true)) {
return $className;
}
}
if (!$throw) {
return null;
}
throw new InvalidArgumentClassException(
'$className',
$types,
$className,
self::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER + self::CLASS_CHECK_TYPE_NOT_IN_LIST
);
return DataTypeHelper::matchClassType($className, $type, false, true);
}
/**
* @param string|object $class
* @param bool $autoload
*
* @return array|null
* @see https://www.php.net/manual/en/function.class-uses.php#122427
* @deprecated since 1.16; use DataTypeHelper::classUsesTraits()
* @see DataTypeHelper::classUsesTraits
*/
public static function &classUsesTraits($class, bool $autoload = true): ?array
{
$traits = [];
// Get all the traits of $class and its parent classes
do {
$class_name = is_object($class) ? get_class($class) : $class;
if (class_exists($class_name, $autoload)) {
$traits = array_merge(class_uses($class, $autoload), $traits);
}
} while ($class = get_parent_class($class));
// Get traits of all parent traits
$traits_to_search = $traits;
while (!empty($traits_to_search)) {
$new_traits = class_uses(array_pop($traits_to_search), $autoload);
$traits = array_merge($new_traits, $traits);
$traits_to_search = array_merge($new_traits, $traits_to_search);
};
if (count($traits) === 0) {
$traits = null;
} else {
$traits = array_unique($traits);
}
return $traits;
return DataTypeHelper::classUsesTraits($class, $autoload);
}
/**
@ -442,6 +284,7 @@ class Helpers
*
* @param string $a First subject string to compare.
* @param string $b Second subject string to compare.
*
* @return bool true if the strings are the same, false if they are different or if
* either is not a string.
*/
@ -472,6 +315,7 @@ class Helpers
* This is mainly required for grouped notifications.
*
* @param $event
*
* @since 1.2.1
*/
public static function SqlMode($event)

View File

@ -31,7 +31,6 @@ use yii\web\HttpException;
*/
class GroupController extends Controller
{
/**
* @inheritdoc
*/
@ -118,18 +117,12 @@ class GroupController extends Controller
$this->checkGroupAccess($group);
// Save changed permission states
if (!$group->isNewRecord && Yii::$app->request->post('dropDownColumnSubmit')) {
$permission = Yii::$app->user->permissionManager->getById(Yii::$app->request->post('permissionId'), Yii::$app->request->post('moduleId'));
if ($permission === null) {
throw new HttpException(500, 'Could not find permission!');
}
Yii::$app->user->permissionManager->setGroupState($group->id, $permission, Yii::$app->request->post('state'));
return $this->asJson([]);
if (!$group->isNewRecord) {
// Save changed permission state
$return = Yii::$app->user->permissionManager->handlePermissionStateChange($group->id);
}
return $this->render('permissions', ['group' => $group]);
return $return ?? $this->render('permissions', ['group' => $group]);
}
public function actionManageGroupUsers()

View File

@ -9,6 +9,8 @@
namespace humhub\modules\admin\controllers;
use humhub\modules\admin\models\SpaceSearch;
use humhub\modules\admin\permissions\ManageSettings;
use humhub\modules\admin\permissions\ManageSpaces;
use humhub\modules\content\components\ContentContainerDefaultPermissionManager;
use humhub\modules\content\models\Content;
use humhub\modules\space\models\Space;
@ -16,8 +18,6 @@ use humhub\modules\space\Module;
use humhub\modules\user\helpers\AuthHelper;
use Yii;
use humhub\modules\admin\components\Controller;
use humhub\modules\admin\permissions\ManageSpaces;
use humhub\modules\admin\permissions\ManageSettings;
use yii\web\HttpException;
/**
@ -27,7 +27,6 @@ use yii\web\HttpException;
*/
class SpaceController extends Controller
{
/**
* @inheritdoc
*/
@ -164,21 +163,12 @@ class SpaceController extends Controller
}
// Handle permission state change
if (Yii::$app->request->post('dropDownColumnSubmit')) {
Yii::$app->response->format = 'json';
$permission = $defaultPermissionManager->getById(Yii::$app->request->post('permissionId'), Yii::$app->request->post('moduleId'));
if ($permission === null) {
throw new HttpException(500, 'Could not find permission!');
}
$defaultPermissionManager->setGroupState($groupId, $permission, Yii::$app->request->post('state'));
return [];
}
$return = $defaultPermissionManager->handlePermissionStateChange($groupId);
return $this->render('permissions', [
return $return ?? $this->render('permissions', [
'defaultPermissionManager' => $defaultPermissionManager,
'groups' => $groups,
'groupId' => $groupId,
]);
}
}

View File

@ -69,17 +69,9 @@ class UserPermissionsController extends Controller
}
// Handle permission state change
if (Yii::$app->request->post('dropDownColumnSubmit')) {
Yii::$app->response->format = 'json';
$permission = $defaultPermissionManager->getById(Yii::$app->request->post('permissionId'), Yii::$app->request->post('moduleId'));
if ($permission === null) {
throw new HttpException(500, 'Could not find permission!');
}
$defaultPermissionManager->setGroupState($groupId, $permission, Yii::$app->request->post('state'));
return [];
}
$return = $defaultPermissionManager->handlePermissionStateChange($groupId);
return $this->render('default', [
return $return ?? $this->render('default', [
'defaultPermissionManager' => $defaultPermissionManager,
'groups' => $groups,
'groupId' => $groupId,

View File

@ -10,7 +10,7 @@ namespace humhub\modules\comment\controllers;
use humhub\components\access\ControllerAccess;
use humhub\components\Controller;
use humhub\libs\Helpers;
use humhub\helpers\DataTypeHelper;
use humhub\modules\comment\models\Comment;
use humhub\modules\comment\models\forms\AdminDeleteCommentForm;
use humhub\modules\comment\models\forms\CommentForm;
@ -65,7 +65,7 @@ class CommentController extends Controller
$modelPk = (int)Yii::$app->request->get('objectId', Yii::$app->request->post('objectId'));
/** @var Comment|ContentActiveRecord $modelClass */
$modelClass = Helpers::checkClassType($modelClass, [Comment::class, ContentActiveRecord::class]);
$modelClass = DataTypeHelper::matchClassType($modelClass, [Comment::class, ContentActiveRecord::class], true);
$this->target = $modelClass::findOne(['id' => $modelPk]);
if (!$this->target) {

View File

@ -10,12 +10,11 @@ namespace humhub\modules\content\components;
use humhub\components\behaviors\PolymorphicRelation;
use humhub\components\Controller;
use humhub\libs\Helpers;
use humhub\helpers\DataTypeHelper;
use Yii;
use yii\base\Exception;
use yii\web\HttpException;
/**
* ContentAddonController is a base controller for ContentAddons.
*
@ -29,7 +28,6 @@ use yii\web\HttpException;
*/
class ContentAddonController extends Controller
{
/**
* Content this addon belongs to
*
@ -88,7 +86,11 @@ class ContentAddonController extends Controller
}
/** @var ContentAddonActiveRecord|ContentActiveRecord $modelClass */
$modelClass = Helpers::checkClassType($modelClass, [ContentAddonActiveRecord::class, ContentActiveRecord::class]);
$modelClass = DataTypeHelper::matchClassType(
$modelClass,
[ContentAddonActiveRecord::class, ContentActiveRecord::class],
true
);
$target = $modelClass::findOne(['id' => $pk]);
if ($target === null) {
@ -122,7 +124,7 @@ class ContentAddonController extends Controller
public function loadContentAddon($className, $pk)
{
/** @var ContentAddonActiveRecord|null $className */
$className = Helpers::checkClassType($className, ContentAddonActiveRecord::class);
$className = DataTypeHelper::matchClassType($className, ContentAddonActiveRecord::class);
if ($className === null) {
throw new Exception("Given className is not a content addon model!");
}
@ -139,5 +141,4 @@ class ContentAddonController extends Controller
$this->contentAddon = $target;
}
}

View File

@ -8,18 +8,18 @@
namespace humhub\modules\file\actions;
use humhub\helpers\DataTypeHelper;
use humhub\libs\Html;
use humhub\modules\content\components\ContentActiveRecord;
use humhub\modules\content\components\ContentAddonActiveRecord;
use humhub\modules\file\libs\FileHelper;
use humhub\modules\file\libs\ImageHelper;
use humhub\modules\file\models\File;
use humhub\modules\file\models\FileUpload;
use Yii;
use yii\base\Action;
use yii\db\ActiveRecord;
use yii\web\UploadedFile;
use humhub\libs\Helpers;
use humhub\modules\file\models\FileUpload;
use humhub\modules\file\libs\FileHelper;
use humhub\modules\file\models\File;
use humhub\modules\content\components\ContentActiveRecord;
use humhub\modules\content\components\ContentAddonActiveRecord;
/**
* UploadAction provides an Ajax/JSON way to upload new files
@ -138,7 +138,7 @@ class UploadAction extends Action
/** @var ActiveRecord|string $model */
if ($model != '' && $pk != '' && $model = Helpers::checkClassType($model, ActiveRecord::class)) {
if ($model != '' && $pk != '' && $model = DataTypeHelper::matchClassType($model, ActiveRecord::class, true)) {
$record = $model::findOne(['id' => $pk]);
if ($record !== null && ($record instanceof ContentActiveRecord || $record instanceof ContentAddonActiveRecord)) {
if ($record->content->canEdit()) {

View File

@ -68,30 +68,22 @@ class SecurityController extends Controller
}
/**
* Shows space permessions
* Shows space permissions
*/
public function actionPermissions()
{
$space = $this->getSpace();
$groups = $space->getUserGroups();
$groups = $space::getUserGroups();
$groupId = Yii::$app->request->get('groupId', Space::USERGROUP_MEMBER);
if (!array_key_exists($groupId, $groups)) {
throw new HttpException(500, 'Invalid group id given!');
}
// Handle permission state change
if (Yii::$app->request->post('dropDownColumnSubmit')) {
Yii::$app->response->format = 'json';
$permission = $space->permissionManager->getById(Yii::$app->request->post('permissionId'), Yii::$app->request->post('moduleId'));
if ($permission === null) {
throw new HttpException(500, 'Could not find permission!');
}
$space->permissionManager->setGroupState($groupId, $permission, Yii::$app->request->post('state'));
return [];
}
$return = $space->permissionManager->handlePermissionStateChange($groupId);
return $this->render('permissions', [
return $return ?? $this->render('permissions', [
'space' => $space,
'groups' => $groups,
'groupId' => $groupId

View File

@ -9,14 +9,22 @@
namespace humhub\modules\user\components;
use humhub\components\Module;
use humhub\helpers\DataTypeHelper;
use humhub\libs\BasePermission;
use humhub\modules\user\models\Group;
use humhub\modules\user\models\GroupPermission;
use humhub\modules\user\models\User as UserModel;
use RuntimeException;
use Throwable;
use Yii;
use yii\base\Component;
use yii\base\Exception;
use yii\base\InvalidConfigException;
use yii\base\Module as BaseModule;
use yii\db\ActiveQuery;
use yii\db\ActiveRecord;
use yii\db\StaleObjectException;
use yii\web\HttpException;
/**
* Description of PermissionManager
@ -27,7 +35,8 @@ class PermissionManager extends Component
{
/**
* User identity.
* @var \humhub\modules\user\models\User
*
* @var UserModel
*/
public $subject;
@ -59,8 +68,9 @@ class PermissionManager extends Component
* @param string|array|BasePermission $permission
* @param array $params
* @param boolean $allowCaching
*
* @return boolean
* @throws \yii\base\InvalidConfigException
* @throws InvalidConfigException
*/
public function can($permission, $params = [], $allowCaching = true)
{
@ -94,6 +104,7 @@ class PermissionManager extends Component
* Return boolean for verifyAll
*
* @param array $params
*
* @return bool
*/
private function isVerifyAll($params = [])
@ -114,6 +125,7 @@ class PermissionManager extends Component
* Verifies a single permission for a given permission subject.
*
* @param BasePermission $permission
*
* @return boolean
*/
protected function verify(BasePermission $permission)
@ -131,7 +143,7 @@ class PermissionManager extends Component
* If the permission objects $subject property is not set this method returns the currently
* logged in user identity.
*
* @return \humhub\modules\user\models\User
* @return UserModel
*/
protected function getSubject()
{
@ -155,9 +167,9 @@ class PermissionManager extends Component
* @param string $groupId
* @param string|BasePermission $permission either permission class or instance
* @param string $state
* @throws \Exception
* @throws \yii\base\InvalidConfigException
* @throws \yii\db\StaleObjectException
*
* @throws InvalidConfigException
* @throws Throwable
*/
public function setGroupState($groupId, $permission, $state)
{
@ -168,6 +180,7 @@ class PermissionManager extends Component
if ($state === '' || $state === null) {
if ($record !== null) {
$record->delete();
$this->clear();
}
return;
}
@ -179,11 +192,14 @@ class PermissionManager extends Component
$record->permission_id = $permission->getId();
$record->module_id = $permission->getModuleId();
$record->class = get_class($permission);
$record->group_id = $groupId;
$record->group_id = (string)$groupId; // content container permissions require a text value here
$record->state = $state;
if ($record->save()) {
$this->clear();
if ($record->save() === false) {
throw new RuntimeException("Saving permission failed: " . implode('; ', $record->getErrorSummary(true)));
}
$this->clear();
}
/**
@ -335,7 +351,7 @@ class PermissionManager extends Component
* @param string $permissionId
* @param string $moduleId
* @return BasePermission|null
* @throws \yii\base\InvalidConfigException
* @throws InvalidConfigException
*/
public function getById($permissionId, $moduleId)
{
@ -355,7 +371,8 @@ class PermissionManager extends Component
* Not used anymore, permissions are now prefetched into $_groupPermissions array
* @param $groupId
* @param BasePermission $permission
* @return array|null|\yii\db\ActiveRecord
*
* @return array|null|ActiveRecord
* @deprecated since 1.10
*
*/
@ -372,7 +389,7 @@ class PermissionManager extends Component
* Returns a list of all Permission objects
*
* @return array of BasePermissions
* @throws \yii\base\InvalidConfigException
* @throws InvalidConfigException
*/
public function getPermissions()
{
@ -398,7 +415,7 @@ class PermissionManager extends Component
*
* @param BaseModule $module
* @return array of BasePermissions
* @throws \yii\base\InvalidConfigException
* @throws InvalidConfigException
*/
protected function getModulePermissions(BaseModule $module)
{
@ -418,7 +435,7 @@ class PermissionManager extends Component
/**
* Creates a Permission Database record
*
* @return \yii\db\ActiveRecord
* @return ActiveRecord
*/
protected function createPermissionRecord()
{
@ -428,7 +445,7 @@ class PermissionManager extends Component
/**
* Creates a Permission Database Query
*
* @return \yii\db\ActiveQuery
* @return ActiveQuery
*/
protected function getQuery()
{
@ -441,8 +458,8 @@ class PermissionManager extends Component
* @param int $groupId id of the group
* @param bool $returnOnlyChangeable
* @return array the permission array
* @throws \yii\base\Exception
* @throws \yii\base\InvalidConfigException
* @throws Exception
* @throws InvalidConfigException
*/
public function createPermissionArray($groupId, $returnOnlyChangeable = false)
{
@ -476,6 +493,38 @@ class PermissionManager extends Component
return $permissions;
}
/**
* @param int|string $groupId
*
* @return array|null
* @throws InvalidConfigException
* @throws HttpException
* @throws StaleObjectException
* @since 1.16
*/
public function handlePermissionStateChange($groupId): ?array
{
if (Yii::$app->request->post('dropDownColumnSubmit')) {
Yii::$app->response->format = 'json';
$permission = $this->getById(
Yii::$app->request->post('permissionId'),
Yii::$app->request->post('moduleId')
);
if ($permission === null) {
throw new HttpException(500, 'Could not find permission!');
}
$groupId = DataTypeHelper::filterInt($groupId) ?? DataTypeHelper::filterString($groupId);
$state = DataTypeHelper::filterInt(Yii::$app->request->post('state'));
$this->setGroupState($groupId, $permission, $state);
return [];
}
return null;
}
/**
* Returns a query for users which are granted given permission

View File

@ -8,6 +8,8 @@
namespace humhub\modules\user\controllers;
use Collator;
use Exception;
use humhub\compat\HForm;
use humhub\modules\content\widgets\ContainerTagPicker;
use humhub\modules\space\helpers\MembershipHelper;
@ -18,10 +20,14 @@ use humhub\modules\user\helpers\AuthHelper;
use humhub\modules\user\models\forms\AccountChangeEmail;
use humhub\modules\user\models\forms\AccountChangeUsername;
use humhub\modules\user\models\forms\AccountDelete;
use humhub\modules\user\models\forms\AccountSettings;
use humhub\modules\user\models\Password;
use humhub\modules\user\models\User;
use humhub\modules\user\Module;
use Throwable;
use Yii;
use yii\web\HttpException;
use yii\web\Response;
/**
* AccountController provides all standard actions for the current logged in
@ -59,7 +65,7 @@ class AccountController extends BaseAccountController
/**
* Redirect to current users profile
* @throws \Throwable
* @throws Throwable
*/
public function actionIndex()
{
@ -72,7 +78,7 @@ class AccountController extends BaseAccountController
/**
* Edit Users Profile
* @throws \Throwable
* @throws Throwable
*/
public function actionEdit()
{
@ -113,7 +119,7 @@ class AccountController extends BaseAccountController
/** @var User $user */
$user = Yii::$app->user->getIdentity();
$model = new \humhub\modules\user\models\forms\AccountSettings();
$model = new AccountSettings();
$model->language = Yii::$app->i18n->getAllowedLanguage($user->language);
$model->timeZone = $user->time_zone;
if (empty($model->timeZone)) {
@ -148,7 +154,7 @@ class AccountController extends BaseAccountController
// Sort countries list based on user language
$languages = Yii::$app->i18n->getAllowedLanguages();
$col = new \Collator(Yii::$app->language);
$col = new Collator(Yii::$app->language);
$col->asort($languages);
/* @var $module Module */
@ -175,7 +181,7 @@ class AccountController extends BaseAccountController
/**
* Change Account
* @throws \Exception
* @throws Exception
* @todo Add Group
*/
public function actionPermissions()
@ -204,17 +210,9 @@ class AccountController extends BaseAccountController
}
// Handle permission state change
if (Yii::$app->request->post('dropDownColumnSubmit')) {
Yii::$app->response->format = 'json';
$permission = $this->getUser()->permissionManager->getById(Yii::$app->request->post('permissionId'), Yii::$app->request->post('moduleId'));
if ($permission === null) {
throw new HttpException(500, 'Could not find permission!');
}
$this->getUser()->permissionManager->setGroupState($currentGroup, $permission, Yii::$app->request->post('state'));
return [];
}
$return = $this->getUser()->permissionManager->handlePermissionStateChange($currentGroup);
return $this->render('permissions', ['user' => $this->getUser(), 'groups' => $groups, 'group' => $currentGroup, 'multipleGroups' => (count($groups) > 1)]);
return $return ?? $this->render('permissions', ['user' => $this->getUser(), 'groups' => $groups, 'group' => $currentGroup, 'multipleGroups' => (count($groups) > 1)]);
}
public function actionConnectedAccounts()
@ -268,9 +266,9 @@ class AccountController extends BaseAccountController
}
/**
* @return array|AccountController|\yii\console\Response|\yii\web\Response
* @return array|AccountController|\yii\console\Response|Response
* @throws HttpException
* @throws \Throwable
* @throws Throwable
*/
public function actionEnableModule()
{
@ -290,9 +288,9 @@ class AccountController extends BaseAccountController
}
/**
* @return array|AccountController|\yii\console\Response|\yii\web\Response
* @return array|AccountController|\yii\console\Response|Response
* @throws HttpException
* @throws \Throwable
* @throws Throwable
*/
public function actionDisableModule()
{
@ -394,7 +392,7 @@ class AccountController extends BaseAccountController
}
// Check if E-Mail is in use, e.g. by other user
$emailAvailablyCheck = \humhub\modules\user\models\User::findOne(['email' => $email]);
$emailAvailablyCheck = User::findOne(['email' => $email]);
if ($emailAvailablyCheck != null) {
throw new HttpException(404, Yii::t('UserModule.account', 'The entered e-mail address is already in use by another user.'));
}
@ -414,7 +412,7 @@ class AccountController extends BaseAccountController
throw new HttpException(500, 'Password change is not allowed');
}
$userPassword = new \humhub\modules\user\models\Password();
$userPassword = new Password();
$userPassword->scenario = 'changePassword';
if ($userPassword->load(Yii::$app->request->post()) && $userPassword->validate()) {
@ -491,7 +489,7 @@ class AccountController extends BaseAccountController
*
* @return User the user
* @throws HttpException
* @throws \Throwable
* @throws Throwable
*/
public function getUser()
{

View File

@ -9,11 +9,10 @@
namespace humhub\modules\user\models;
use humhub\components\ActiveRecord;
use humhub\libs\Helpers;
use humhub\helpers\DataTypeHelper;
use humhub\modules\user\models\fieldtype\BaseType;
use Yii;
use yii\db\ActiveQuery;
use yii\helpers\Html;
/**
* This is the model class for table "profile_field".
@ -45,7 +44,6 @@ use yii\helpers\Html;
*/
class ProfileField extends ActiveRecord
{
/**
* Field Type Instance
*
@ -151,10 +149,14 @@ class ProfileField extends ActiveRecord
*/
public function getFieldType(): ?BaseType
{
if ($this->_fieldType != null)
if ($this->_fieldType != null) {
return $this->_fieldType;
}
if ($this->field_type_class != '' && $type = Helpers::checkClassType($this->field_type_class, fieldtype\BaseType::class)) {
if (
$this->field_type_class != ''
&& $type = DataTypeHelper::matchClassType($this->field_type_class, fieldtype\BaseType::class, true)
) {
$this->_fieldType = new $type();
$this->_fieldType->setProfileField($this);
return $this->_fieldType;
@ -287,7 +289,6 @@ class ProfileField extends ActiveRecord
{
if (!$this->isNewRecord) {
// Dont allow changes of internal_name - Maybe not the best way to check it.
$currentProfileField = ProfileField::findOne(['id' => $this->id]);
if ($this->field_type_class != $currentProfileField->field_type_class) {
@ -328,5 +329,4 @@ class ProfileField extends ActiveRecord
return "UserModule.profile";
}
}

View File

@ -7,7 +7,7 @@
namespace humhub\modules\user\models\fieldtype;
use humhub\libs\Helpers;
use humhub\helpers\DataTypeHelper;
use humhub\modules\user\models\Profile;
use humhub\modules\user\models\ProfileField;
use humhub\modules\user\models\User;
@ -172,7 +172,7 @@ class BaseType extends Model
{
$types = [];
foreach ($this->getFieldTypes() as $className => $title) {
$className = Helpers::checkClassType($className, static::class);
$className = DataTypeHelper::matchClassType($className, static::class, true);
/** @var BaseType $instance */
$instance = new $className();
if ($profileField !== null) {

View File

@ -13,8 +13,8 @@ use humhub\components\Application;
use humhub\components\Event;
use humhub\components\Module;
use humhub\events\MigrationEvent;
use humhub\helpers\DataTypeHelper;
use humhub\interfaces\ApplicationInterface;
use humhub\libs\Helpers;
use Throwable;
use Yii;
use yii\base\ActionEvent;
@ -44,7 +44,7 @@ class MigrationService extends Component
*/
public function __construct(?BaseModule $module = null)
{
Helpers::checkClassType($module, [ApplicationInterface::class, Module::class, null]);
DataTypeHelper::ensureClassType($module, [ApplicationInterface::class, Module::class, null]);
$this->module = $module ?? Yii::$app;

View File

@ -0,0 +1,31 @@
<?php
/*
* @link https://www.humhub.org/
* @copyright Copyright (c) 2018-2023 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
/**
* @noinspection PhpIllegalPsrClassPathInspection
*/
namespace humhub\tests\codeception\unit\helpers;
use humhub\helpers\DataTypeHelper;
/**
* Class DataTypeHelperTest
*/
class DataTypeHelperMock extends DataTypeHelper
{
public static function matchTypeHelper($typeToCheck, &$input, string $inputType, ?array &$inputTraits = null): ?string
{
return parent::matchTypeHelper($typeToCheck, $input, $inputType, $inputTraits);
}
public static function parseTypes(&$allowedTypes, ?bool &$allowNull = false, ?array &$checkTraits = null, bool $allowCallables = true, bool $allowGetTypes = true): array
{
return parent::parseTypes($allowedTypes, $allowNull, $checkTraits, $allowCallables, $allowGetTypes);
}
}

View File

@ -0,0 +1,630 @@
<?php
/*
* @link https://www.humhub.org/
* @copyright Copyright (c) 2018-2023 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
/**
* @noinspection PhpIllegalPsrClassPathInspection
*/
namespace humhub\tests\codeception\unit\helpers;
use Codeception\Test\Feature\Stub;
use Codeception\Test\Unit;
use Codeception\TestInterface;
use humhub\exceptions\InvalidArgumentClassException;
use humhub\exceptions\InvalidArgumentTypeException;
use humhub\exceptions\InvalidArgumentValueException;
use humhub\helpers\DataTypeHelper;
use PHPUnit\Framework\Exception;
use stdClass;
use Stringable;
use yii\base\ArrayableTrait;
use yii\base\BaseObject;
use yii\base\Configurable;
use yii\base\Model;
/**
* Class DataTypeHelperTest
*/
class DataTypeHelperTest extends Unit
{
public function testClassTypeHelperCase1()
{
static::assertNull(DataTypeHelperMock::matchTypeHelper(null, $value, ''));
static::assertNull(DataTypeHelperMock::matchTypeHelper(1, $value, ''));
static::assertNull(DataTypeHelperMock::matchTypeHelper(1.2, $value, ''));
static::assertNull(DataTypeHelperMock::matchTypeHelper(true, $value, ''));
}
public function testClassTypeHelperCase2()
{
$handle = fopen('php://memory', 'ab');
fclose($handle);
$tests = [
'boolean' => [
true,
'bool'
],
'integer' => [
1,
'int'
],
'string' => [
'',
],
'array' => [
[],
],
'object' => [
new stdClass(),
],
'resource' => [
fopen('php://memory', 'ab'),
],
'resource (closed)' => [
$handle,
],
'NULL' => [
null,
],
'float' => [
1.2,
'double'
],
];
$values = array_combine(array_keys($tests), array_column($tests, 0));
foreach ($tests as $key => $test) {
codecept_debug("- Testing $key");
$current = gettype($test[0]);
static::assertEquals($key, DataTypeHelperMock::matchTypeHelper($key, $value, $current));
if (array_key_exists(1, $test)) {
static::assertEquals($test[1], DataTypeHelperMock::matchTypeHelper($test[1], $value, $current));
}
foreach ($values as $i => $type) {
if ($i === $key) {
continue;
}
$current = gettype($type);
static::assertNull(DataTypeHelperMock::matchTypeHelper($key, $value, $current));
}
}
}
public function testClassTypeHelperCase3()
{
$value = new class () implements Stringable {
public function __toString(): string
{
return '';
}
};
static::assertEquals('object', DataTypeHelperMock::matchTypeHelper('object', $value, 'object'));
static::assertEquals(
Stringable::class,
DataTypeHelperMock::matchTypeHelper(Stringable::class, $value, 'object')
);
$value = new class () {
public function __toString(): string
{
return '';
}
};
static::assertEquals(
'object',
DataTypeHelperMock::matchTypeHelper('object', $value, gettype($value))
);
static::assertEquals(
Stringable::class,
DataTypeHelperMock::matchTypeHelper(Stringable::class, $value, 'object')
);
$value = new static();
static::assertEquals(
'object',
DataTypeHelperMock::matchTypeHelper('object', $value, gettype($value))
);
// test class
static::assertEquals(
static::class,
DataTypeHelperMock::matchTypeHelper(static::class, $value, gettype($value))
);
// test interface
static::assertEquals(
TestInterface::class,
DataTypeHelperMock::matchTypeHelper(TestInterface::class, $value, gettype($value))
);
// test trait
$traits = DataTypeHelper::classUsesTraits($value);
static::assertEquals(
Stub::class,
DataTypeHelperMock::matchTypeHelper(Stub::class, $value, gettype($value), $traits)
);
}
public function testParseTypeCase1()
{
$types = null;
static::assertEquals([], DataTypeHelperMock::parseTypes($types));
static::assertEquals([null], $types);
$types = ['string'];
static::assertEquals(['string'], DataTypeHelperMock::parseTypes($types));
static::assertEquals(['string'], $types);
$types = 'int';
static::assertEquals(['int'], DataTypeHelperMock::parseTypes($types));
static::assertEquals(['int'], $types);
$types = 'string|int';
static::assertEquals(['string', 'int'], DataTypeHelperMock::parseTypes($types));
static::assertEquals(['string', 'int'], $types);
}
public function testParseTypeCase2()
{
$message = 'Argument $allowedTypes passed to humhub\helpers\DataTypeHelper::parseTypes must be one of string, string[], object[] - empty string given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$types = '';
DataTypeHelperMock::parseTypes($types);
}
public function testParseTypeCase3()
{
$message = 'Argument $allowedTypes passed to humhub\helpers\DataTypeHelper::parseTypes must be one of string, string[], object[] - [] given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$types = [];
DataTypeHelperMock::parseTypes($types);
}
public function testParseTypeCase4()
{
$message = 'Argument $allowedTypes[0] passed to humhub\helpers\DataTypeHelper::parseTypes must be a valid class/interface/trait name or an object instance - test given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$types = ['test'];
DataTypeHelperMock::parseTypes($types);
}
/**
* @depends testClassTypeHelperCase1
* @depends testClassTypeHelperCase2
* @depends testClassTypeHelperCase3
* @depends testParseTypeCase1
* @depends testParseTypeCase2
* @depends testParseTypeCase3
*/
public function testCheckTypeCase()
{
$tests = [
'boolean' => true,
'bool' => true,
'integer' => 1,
'int' => 1,
'string' => '',
'array' => [],
'object' => new stdClass(),
'resource' => fopen('php://memory', 'ab'),
'NULL' => null,
'float' => 1.2,
'double' => 1.2,
];
foreach ($tests as $key => $value) {
codecept_debug("- Testing $key");
static::assertEquals($key, DataTypeHelper::matchType($value, [$key]));
}
static::assertEquals('string', DataTypeHelper::matchType('', [null, 'string']));
static::assertEquals('string', DataTypeHelper::matchType('', ['string', null]));
static::assertEquals('string', DataTypeHelper::matchType('', ['string', 'NULL']));
$values = [
new class () implements Stringable {
public function __toString(): string
{
return '';
}
},
new class () {
public function __toString(): string
{
return '';
}
}
];
foreach ($values as $value) {
static::assertEquals('object', DataTypeHelper::matchType($value, ['object']));
static::assertEquals(
Stringable::class,
DataTypeHelper::matchType($value, [Stringable::class])
);
static::assertEquals('is_object', DataTypeHelper::matchType($value, ['is_object']));
// type order is of significance, if multiple types match
static::assertEquals(
'object',
DataTypeHelper::matchType($value, ['object', Stringable::class])
);
static::assertEquals(
Stringable::class,
DataTypeHelper::matchType($value, [Stringable::class, 'object'])
);
}
}
/**
* @depends testCheckTypeCase
*/
public function testFilterBoolStrict()
{
static::assertTrue(DataTypeHelper::filterBool(true, true));
static::assertNull(DataTypeHelper::filterBool('true', true));
static::assertNull(DataTypeHelper::filterBool(1, true));
static::assertFalse(DataTypeHelper::filterBool(false, true));
static::assertNull(DataTypeHelper::filterBool('false', true));
static::assertNull(DataTypeHelper::filterBool(0, true));
}
/**
* @depends testCheckTypeCase
*/
public function testFilterBoolConversion()
{
static::assertTrue(DataTypeHelper::filterBool(true, null));
static::assertTrue(DataTypeHelper::filterBool('true', null));
static::assertTrue(DataTypeHelper::filterBool(1, null));
static::assertTrue(DataTypeHelper::filterBool('1', null));
static::assertTrue(DataTypeHelper::filterBool([''], null));
static::assertTrue(DataTypeHelper::filterBool([0], null));
static::assertTrue(DataTypeHelper::filterBool([1], null));
static::assertFalse(DataTypeHelper::filterBool('false', null));
static::assertFalse(DataTypeHelper::filterBool('0', null));
static::assertFalse(DataTypeHelper::filterBool('', null));
static::assertFalse(DataTypeHelper::filterBool(0, null));
static::assertFalse(DataTypeHelper::filterBool([], null));
static::assertFalse(DataTypeHelper::filterBool(null, null));
static::assertNull(DataTypeHelper::filterBool(new static(), null));
}
/**
* @depends testCheckTypeCase
*/
public function testFilterBoolDefault()
{
static::assertTrue(DataTypeHelper::filterBool(true, false));
static::assertTrue(DataTypeHelper::filterBool(1, false));
static::assertTrue(DataTypeHelper::filterBool('1', false));
static::assertTrue(DataTypeHelper::filterBool('foo', false));
static::assertTrue(DataTypeHelper::filterBool(['1'], false));
static::assertTrue(DataTypeHelper::filterBool('false', false));
static::assertFalse(DataTypeHelper::filterBool(false, false));
static::assertFalse(DataTypeHelper::filterBool('0', false));
static::assertFalse(DataTypeHelper::filterBool('', false));
static::assertFalse(DataTypeHelper::filterBool(0, false));
static::assertFalse(DataTypeHelper::filterBool([], false));
static::assertFalse(DataTypeHelper::filterBool(null, false));
}
/**
* @depends testCheckTypeCase
*/
public function testFilterFloat()
{
static::assertEquals(1.1, DataTypeHelper::filterFloat(1.1, true));
static::assertNull(DataTypeHelper::filterFloat('1.1', true));
static::assertEquals(1.0, DataTypeHelper::filterFloat(1));
static::assertEquals(1.0, DataTypeHelper::filterFloat('1'));
static::assertEquals(1.1, DataTypeHelper::filterFloat(1.1));
static::assertEquals(1.1, DataTypeHelper::filterFloat('1.1'));
static::assertNull(DataTypeHelper::filterFloat('1.1.3'));
}
/**
* @depends testCheckTypeCase
*/
public function testFilterInt()
{
static::assertEquals(1, DataTypeHelper::filterInt(1, true));
static::assertNull(DataTypeHelper::filterInt('1', true));
static::assertEquals(1, DataTypeHelper::filterInt(1));
static::assertEquals(1, DataTypeHelper::filterInt('1'));
static::assertNull(DataTypeHelper::filterInt('1.1'));
}
/**
* @depends testCheckTypeCase
*/
public function testFilterScalar()
{
static::assertEquals('', DataTypeHelper::filterScalar('', true));
static::assertEquals(1, DataTypeHelper::filterScalar(1, true));
static::assertNull(DataTypeHelper::filterScalar(null, true));
static::assertNull(DataTypeHelper::filterScalar(fopen('php://memory', 'ab'), true));
static::assertEquals('', DataTypeHelper::filterScalar(''));
static::assertEquals(1, DataTypeHelper::filterScalar(1));
static::assertNull(DataTypeHelper::filterScalar(null));
static::assertNull(DataTypeHelper::filterScalar(fopen('php://memory', 'ab')));
}
/**
* @depends testCheckTypeCase
*/
public function testFilterString()
{
static::assertEquals('', DataTypeHelper::filterString('', true));
static::assertEquals('1', DataTypeHelper::filterString('1', true));
static::assertNull(DataTypeHelper::filterString(1, true));
static::assertEquals('', DataTypeHelper::filterString(''));
static::assertEquals('1', DataTypeHelper::filterString('1'));
static::assertEquals('1', DataTypeHelper::filterString(1));
static::assertEquals('1', DataTypeHelper::filterString('1'));
static::assertEquals('1.1', DataTypeHelper::filterString('1.1'));
static::assertEquals('1.1', DataTypeHelper::filterString(1.1));
$value = new class () implements Stringable {
public function __toString(): string
{
return 'foo';
}
};
static::assertEquals('foo', DataTypeHelper::filterString($value));
$value = new class () {
public function __toString(): string
{
return 'bar';
}
};
static::assertEquals('bar', DataTypeHelper::filterString($value));
static::assertNull(DataTypeHelper::filterString([]));
static::assertNull(DataTypeHelper::filterString((object)[]));
}
public function testClassTypeCheckCase2()
{
$message = 'Argument $allowedTypes passed to humhub\helpers\DataTypeHelper::parseTypes must be one of string, string[], object[] - empty string given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(DataTypeHelper::TYPE_CHECK_INVALID_TYPE_PARAMETER + DataTypeHelper::TYPE_CHECK_VALUE_IS_EMPTY);
DataTypeHelper::matchClassType(null, '');
}
public function testClassTypeCheckCase3()
{
$message = 'Argument $allowedTypes passed to humhub\helpers\DataTypeHelper::parseTypes must be one of string, string[], object[] - [] given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(DataTypeHelper::TYPE_CHECK_INVALID_TYPE_PARAMETER + DataTypeHelper::TYPE_CHECK_VALUE_IS_EMPTY);
DataTypeHelper::matchClassType(null, []);
}
public function testClassTypeCheckCase4()
{
$message = 'Argument $allowedTypes passed to humhub\helpers\DataTypeHelper::parseTypes must be one of the following types: string, string[], object[], NULL - int given.';
$this->expectException(InvalidArgumentTypeException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(DataTypeHelper::TYPE_CHECK_INVALID_TYPE_PARAMETER + DataTypeHelper::TYPE_CHECK_INVALID_TYPE);
DataTypeHelper::matchClassType(null, 0);
}
public function testClassTypeCheckCase5()
{
$message = 'Argument $allowedTypes[0] passed to humhub\helpers\DataTypeHelper::parseTypes must be a valid class/interface/trait name or an object instance - \'0\' given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(DataTypeHelper::TYPE_CHECK_INVALID_TYPE_PARAMETER + DataTypeHelper::TYPE_CHECK_NON_EXISTING_CLASS);
DataTypeHelper::matchClassType(null, '0');
}
public function testClassTypeCheckCase6()
{
$message = 'Argument $allowedTypes[0] passed to humhub\helpers\DataTypeHelper::parseTypes must be a valid class/interface/trait name or an object instance - humhub\tests\codeception\unit\helpers\NonExistingClassName given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(DataTypeHelper::TYPE_CHECK_INVALID_TYPE_PARAMETER + DataTypeHelper::TYPE_CHECK_NON_EXISTING_CLASS);
/** @noinspection PhpUndefinedClassInspection */
DataTypeHelper::matchClassType(null, NonExistingClassName::class);
}
public function testClassTypeCheckCase7()
{
$message = 'Argument $allowedTypes[1] passed to humhub\helpers\DataTypeHelper::parseTypes must be a valid class/interface/trait name or an object instance - humhub\tests\codeception\unit\helpers\NonExistingClassName given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(DataTypeHelper::TYPE_CHECK_INVALID_TYPE_PARAMETER + DataTypeHelper::TYPE_CHECK_NON_EXISTING_CLASS);
/** @noinspection PhpUndefinedClassInspection */
DataTypeHelper::matchClassType(null, [BaseObject::class, NonExistingClassName::class]);
}
public function testClassTypeCheckCaseNull()
{
static::assertNull(
DataTypeHelper::matchClassType(null, BaseObject::class)
);
static::assertNull(
DataTypeHelper::matchClassType(null, [BaseObject::class, null])
);
$message = 'Argument $value passed to humhub\helpers\DataTypeHelper::matchClassType must be of type yii\base\BaseObject - NULL given.';
$this->expectException(InvalidArgumentTypeException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(DataTypeHelper::TYPE_CHECK_INVALID_VALUE_PARAMETER + DataTypeHelper::TYPE_CHECK_VALUE_IS_EMPTY + DataTypeHelper::TYPE_CHECK_INVALID_TYPE + DataTypeHelper::TYPE_CHECK_VALUE_IS_NULL);
DataTypeHelper::ensureClassType(null, BaseObject::class);
}
public function testClassTypeCheckCaseEmptyString()
{
static::assertNull(
DataTypeHelper::matchClassType('', BaseObject::class)
);
$message = 'Argument $value passed to humhub\helpers\DataTypeHelper::matchClassType must be of type yii\base\BaseObject - empty string given.';
$this->expectException(InvalidArgumentClassException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(DataTypeHelper::TYPE_CHECK_INVALID_VALUE_PARAMETER + DataTypeHelper::TYPE_CHECK_VALUE_IS_EMPTY + DataTypeHelper::TYPE_CHECK_INVALID_TYPE + DataTypeHelper::TYPE_CHECK_TYPE_NOT_IN_LIST);
DataTypeHelper::ensureClassType('', BaseObject::class);
}
public function testClassTypeCheckCaseString()
{
/** @noinspection PhpUndefinedClassInspection */
static::assertNull(
DataTypeHelper::matchClassType(NonExistingClassName::class, BaseObject::class, false)
);
$message = 'Argument $value passed to humhub\helpers\DataTypeHelper::matchClassType must be a valid class name or an object instance - humhub\tests\codeception\unit\helpers\NonExistingClassName given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(DataTypeHelper::TYPE_CHECK_INVALID_VALUE_PARAMETER + DataTypeHelper::TYPE_CHECK_NON_EXISTING_CLASS);
/** @noinspection PhpUndefinedClassInspection */
DataTypeHelper::ensureClassType(NonExistingClassName::class, BaseObject::class);
}
public function testClassTypeCheckCaseWrongClass()
{
static::assertNull(
DataTypeHelper::matchClassType(Exception::class, BaseObject::class)
);
$message = 'Argument $value passed to humhub\helpers\DataTypeHelper::matchClassType must be of type yii\base\BaseObject - PHPUnit\Framework\Exception given.';
$this->expectException(InvalidArgumentClassException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(DataTypeHelper::TYPE_CHECK_INVALID_VALUE_PARAMETER + DataTypeHelper::TYPE_CHECK_TYPE_NOT_IN_LIST);
DataTypeHelper::ensureClassType(Exception::class, BaseObject::class);
}
public function testClassTypeCheckCaseWrongInstance()
{
static::assertNull(
DataTypeHelper::matchClassType(new Exception('hello'), BaseObject::class)
);
$message = 'Argument $value passed to humhub\helpers\DataTypeHelper::matchClassType must be of type yii\base\BaseObject - PHPUnit\Framework\Exception given.';
$this->expectException(InvalidArgumentClassException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(DataTypeHelper::TYPE_CHECK_INVALID_VALUE_PARAMETER + DataTypeHelper::TYPE_CHECK_TYPE_NOT_IN_LIST + DataTypeHelper::TYPE_CHECK_VALUE_IS_INSTANCE);
DataTypeHelper::ensureClassType(new Exception('hello'), BaseObject::class);
}
public function testClassTypeCheckCaseCorrectClass()
{
static::assertEquals(
Exception::class,
DataTypeHelper::matchClassType(Exception::class, Exception::class)
);
static::assertEquals(
Exception::class,
DataTypeHelper::matchClassType(Exception::class, [new \Exception()])
);
static::assertEquals(
BaseObject::class,
DataTypeHelper::matchClassType(BaseObject::class, Configurable::class)
);
static::assertEquals(
Model::class,
DataTypeHelper::matchClassType(Model::class, ArrayableTrait::class)
);
static::assertNull(
DataTypeHelper::matchClassType('#%' . Exception::class, Exception::class)
);
$message = 'Argument $value passed to humhub\helpers\DataTypeHelper::matchClassType must be a valid class name or an object instance - #%PHPUnit\Framework\Exception given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(DataTypeHelper::TYPE_CHECK_INVALID_VALUE_PARAMETER + DataTypeHelper::TYPE_CHECK_NON_EXISTING_CLASS);
DataTypeHelper::ensureClassType('#%' . Exception::class, Exception::class);
}
public function testClassTypeCheckCaseCorrectInstance()
{
static::assertEquals(
Exception::class,
DataTypeHelper::matchClassType(new Exception('hello'), Exception::class)
);
static::assertEquals(
Exception::class,
DataTypeHelper::matchClassType(new Exception('hello'), [new \Exception()])
);
static::assertEquals(
BaseObject::class,
DataTypeHelper::matchClassType(new BaseObject(), Configurable::class)
);
static::assertEquals(
Model::class,
DataTypeHelper::matchClassType(new Model(), ArrayableTrait::class)
);
}
}

View File

@ -1,260 +0,0 @@
<?php
/*
* @link https://www.humhub.org/
* @copyright Copyright (c) 2018 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
/**
* @noinspection PhpIllegalPsrClassPathInspection
*/
namespace humhub\tests\codeception\unit\libs;
use Codeception\Test\Unit;
use humhub\exceptions\InvalidArgumentClassException;
use humhub\exceptions\InvalidArgumentTypeException;
use humhub\exceptions\InvalidArgumentValueException;
use humhub\libs\Helpers;
use PHPUnit\Framework\Exception;
use yii\base\ArrayableTrait;
use yii\base\BaseObject;
use yii\base\Configurable;
use yii\base\Model;
/**
* Class MimeHelperTest
*
* @noinspection IdentifierGrammar
*/
class HelpersTest extends Unit
{
public function testClassTypeCheckCase1()
{
$message = 'Argument $type passed to humhub\libs\Helpers::checkClassType must be one of string, string[] - NULL given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(Helpers::CLASS_CHECK_INVALID_TYPE_PARAMETER + Helpers::CLASS_CHECK_VALUE_IS_EMPTY);
Helpers::checkClassType(null, null);
}
public function testClassTypeCheckCase2()
{
$message = 'Argument $type passed to humhub\libs\Helpers::checkClassType must be one of string, string[] - empty string given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(Helpers::CLASS_CHECK_INVALID_TYPE_PARAMETER + Helpers::CLASS_CHECK_VALUE_IS_EMPTY);
Helpers::checkClassType(null, '');
}
public function testClassTypeCheckCase3()
{
$message = 'Argument $type passed to humhub\libs\Helpers::checkClassType must be one of string, string[] - [] given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(Helpers::CLASS_CHECK_INVALID_TYPE_PARAMETER + Helpers::CLASS_CHECK_VALUE_IS_EMPTY);
Helpers::checkClassType(null, []);
}
public function testClassTypeCheckCase4()
{
$message = 'Argument $type passed to humhub\libs\Helpers::checkClassType must be one of string, string[] - 0 given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(Helpers::CLASS_CHECK_INVALID_TYPE_PARAMETER + Helpers::CLASS_CHECK_VALUE_IS_EMPTY);
Helpers::checkClassType(null, 0);
}
public function testClassTypeCheckCase5()
{
$message = 'Argument $type passed to humhub\libs\Helpers::checkClassType must be one of string, string[] - \'0\' given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(Helpers::CLASS_CHECK_INVALID_TYPE_PARAMETER + Helpers::CLASS_CHECK_VALUE_IS_EMPTY);
Helpers::checkClassType(null, '0');
}
public function testClassTypeCheckCase6()
{
$message = 'Argument $type[0] passed to humhub\libs\Helpers::checkClassType must be a valid class/interface/trait name or an object instance - humhub\tests\codeception\unit\libs\NonExistingClassName given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(Helpers::CLASS_CHECK_INVALID_TYPE_PARAMETER + Helpers::CLASS_CHECK_NON_EXISTING_CLASS);
/** @noinspection PhpUndefinedClassInspection */
Helpers::checkClassType(null, NonExistingClassName::class);
}
public function testClassTypeCheckCase7()
{
$message = 'Argument $type[1] passed to humhub\libs\Helpers::checkClassType must be a valid class/interface/trait name or an object instance - humhub\tests\codeception\unit\libs\NonExistingClassName given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(Helpers::CLASS_CHECK_INVALID_TYPE_PARAMETER + Helpers::CLASS_CHECK_NON_EXISTING_CLASS);
/** @noinspection PhpUndefinedClassInspection */
Helpers::checkClassType(null, [BaseObject::class, NonExistingClassName::class]);
}
public function testClassTypeCheckCaseNull()
{
static::assertNull(
Helpers::checkClassType(null, BaseObject::class, false)
);
static::assertNull(
Helpers::checkClassType(null, [BaseObject::class, null])
);
$message = 'Argument $className passed to humhub\libs\Helpers::checkClassType must be of type yii\base\BaseObject - NULL given.';
$this->expectException(InvalidArgumentTypeException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(Helpers::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER + Helpers::CLASS_CHECK_VALUE_IS_EMPTY + Helpers::CLASS_CHECK_INVALID_TYPE + Helpers::CLASS_CHECK_VALUE_IS_NULL);
Helpers::checkClassType(null, BaseObject::class);
}
public function testClassTypeCheckCaseEmptyString()
{
static::assertNull(
Helpers::checkClassType('', BaseObject::class, false)
);
static::assertNull(
Helpers::checkClassType('', [BaseObject::class, null], true, false)
);
$message = 'Argument $className passed to humhub\libs\Helpers::checkClassType must be of type yii\base\BaseObject - empty string given.';
$this->expectException(InvalidArgumentClassException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(Helpers::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER + Helpers::CLASS_CHECK_VALUE_IS_EMPTY + Helpers::CLASS_CHECK_INVALID_TYPE + Helpers::CLASS_CHECK_TYPE_NOT_IN_LIST);
Helpers::checkClassType('', BaseObject::class);
}
public function testClassTypeCheckCaseString()
{
/** @noinspection PhpUndefinedClassInspection */
static::assertNull(
Helpers::checkClassType(NonExistingClassName::class, BaseObject::class, false)
);
$message = 'Argument $className passed to humhub\libs\Helpers::checkClassType must be a valid class name or an object instance - humhub\tests\codeception\unit\libs\NonExistingClassName given.';
$this->expectException(InvalidArgumentValueException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(Helpers::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER + Helpers::CLASS_CHECK_NON_EXISTING_CLASS);
/** @noinspection PhpUndefinedClassInspection */
Helpers::checkClassType(NonExistingClassName::class, BaseObject::class);
}
public function testClassTypeCheckCaseWrongClass()
{
static::assertNull(
Helpers::checkClassType(Exception::class, BaseObject::class, false)
);
$message = 'Argument $className passed to humhub\libs\Helpers::checkClassType must be of type yii\base\BaseObject - PHPUnit\Framework\Exception given.';
$this->expectException(InvalidArgumentClassException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(Helpers::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER + Helpers::CLASS_CHECK_TYPE_NOT_IN_LIST);
Helpers::checkClassType(Exception::class, BaseObject::class);
}
public function testClassTypeCheckCaseWrongInstance()
{
static::assertNull(
Helpers::checkClassType(new Exception('hello'), BaseObject::class, false)
);
$message = 'Argument $className passed to humhub\libs\Helpers::checkClassType must be of type yii\base\BaseObject - PHPUnit\Framework\Exception given.';
$this->expectException(InvalidArgumentClassException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(Helpers::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER + Helpers::CLASS_CHECK_TYPE_NOT_IN_LIST + Helpers::CLASS_CHECK_VALUE_IS_INSTANCE);
Helpers::checkClassType(new Exception('hello'), BaseObject::class);
}
public function testClassTypeCheckCaseCorrectClass()
{
static::assertEquals(
Exception::class,
Helpers::checkClassType(Exception::class, Exception::class)
);
static::assertEquals(
Exception::class,
Helpers::checkClassType(Exception::class, [new \Exception()])
);
static::assertEquals(
BaseObject::class,
Helpers::checkClassType(BaseObject::class, Configurable::class)
);
static::assertEquals(
Model::class,
Helpers::checkClassType(Model::class, ArrayableTrait::class)
);
static::assertEquals(
Exception::class,
Helpers::checkClassType('#%' . Exception::class, Exception::class, false, false)
);
static::assertNull(
Helpers::checkClassType('#%' . Exception::class, Exception::class, false, true)
);
$message = 'Argument $className passed to humhub\libs\Helpers::checkClassType must be a valid class name or an object instance - #%PHPUnit\Framework\Exception given.';
$this->expectException(InvalidArgumentClassException::class);
$this->expectExceptionMessage($message);
$this->expectExceptionCode(Helpers::CLASS_CHECK_INVALID_CLASSNAME_PARAMETER);
Helpers::checkClassType('#%' . Exception::class, Exception::class);
}
public function testClassTypeCheckCaseCorrectInstance()
{
static::assertEquals(
Exception::class,
Helpers::checkClassType(new Exception('hello'), Exception::class)
);
static::assertEquals(
Exception::class,
Helpers::checkClassType(new Exception('hello'), [new \Exception()])
);
static::assertEquals(
BaseObject::class,
Helpers::checkClassType(new BaseObject(), Configurable::class)
);
static::assertEquals(
Model::class,
Helpers::checkClassType(new Model(), ArrayableTrait::class)
);
}
}