%PDF- %PDF-
| Direktori : /www/varak.net/losik.varak.net/vendor/nette/application/src/Application/UI/ |
| Current File : /www/varak.net/losik.varak.net/vendor/nette/application/src/Application/UI/ComponentReflection.php |
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Application\UI;
use Nette;
/**
* Helpers for Presenter & Component.
* @property-read string $name
* @property-read string $fileName
* @internal
*/
final class ComponentReflection extends \ReflectionClass
{
use Nette\SmartObject;
/** @var array getPersistentParams cache */
private static $ppCache = [];
/** @var array getPersistentComponents cache */
private static $pcCache = [];
/** @var array isMethodCallable cache */
private static $mcCache = [];
/**
* Returns array of classes persistent parameters. They have public visibility,
* are non-static and have annotation @persistent.
*/
public function getPersistentParams(?string $class = null): array
{
$class = $class ?? $this->getName();
$params = &self::$ppCache[$class];
if ($params !== null) {
return $params;
}
$params = [];
if (is_subclass_of($class, Component::class)) {
$isPresenter = is_subclass_of($class, Presenter::class);
$defaults = get_class_vars($class);
foreach ($defaults as $name => $default) {
$rp = new \ReflectionProperty($class, $name);
if (!$rp->isStatic()
&& ((PHP_VERSION_ID >= 80000 && $rp->getAttributes(Nette\Application\Attributes\Persistent::class))
|| self::parseAnnotation($rp, 'persistent'))
) {
$params[$name] = [
'def' => $default,
'type' => self::getPropertyType($rp, $default),
'since' => $isPresenter ? Nette\Utils\Reflection::getPropertyDeclaringClass($rp)->getName() : null,
];
}
}
foreach ($this->getPersistentParams(get_parent_class($class)) as $name => $param) {
if (isset($params[$name])) {
$params[$name]['since'] = $param['since'];
} else {
$params[$name] = $param;
}
}
}
return $params;
}
public function getPersistentComponents(?string $class = null): array
{
$class = $class ?? $this->getName();
$components = &self::$pcCache[$class];
if ($components !== null) {
return $components;
}
$components = [];
if (is_subclass_of($class, Presenter::class)) {
foreach ($class::getPersistentComponents() as $name => $meta) {
if (is_string($meta)) {
$name = $meta;
}
$components[$name] = ['since' => $class];
}
$components = $this->getPersistentComponents(get_parent_class($class)) + $components;
}
return $components;
}
/**
* Saves state informations for next request.
*/
public function saveState(Component $component, array &$params): void
{
$tree = self::getClassesAndTraits(get_class($component));
foreach ($this->getPersistentParams() as $name => $meta) {
if (isset($params[$name])) {
// injected value
} elseif (
array_key_exists($name, $params) // nulls are skipped
|| (isset($meta['since']) && !isset($tree[$meta['since']])) // not related
|| !isset($component->$name)
) {
continue;
} else {
$params[$name] = $component->$name; // object property value
}
if (!self::convertType($params[$name], $meta['type'])) {
throw new InvalidLinkException(sprintf(
"Value passed to persistent parameter '%s' in %s must be %s, %s given.",
$name,
$component instanceof Presenter ? 'presenter ' . $component->getName() : "component '{$component->getUniqueId()}'",
$meta['type'],
is_object($params[$name]) ? get_class($params[$name]) : gettype($params[$name])
));
}
if ($params[$name] === $meta['def'] || ($meta['def'] === null && $params[$name] === '')) {
$params[$name] = null; // value transmit is unnecessary
}
}
}
/**
* Is a method callable? It means class is instantiable and method has
* public visibility, is non-static and non-abstract.
*/
public function hasCallableMethod(string $method): bool
{
$class = $this->getName();
$cache = &self::$mcCache[strtolower($class . ':' . $method)];
if ($cache === null) {
try {
$cache = false;
$rm = new \ReflectionMethod($class, $method);
$cache = $this->isInstantiable() && $rm->isPublic() && !$rm->isAbstract() && !$rm->isStatic();
} catch (\ReflectionException $e) {
}
}
return $cache;
}
public static function combineArgs(\ReflectionFunctionAbstract $method, array $args): array
{
$res = [];
foreach ($method->getParameters() as $i => $param) {
$name = $param->getName();
$type = self::getParameterType($param);
if (isset($args[$name])) {
$res[$i] = $args[$name];
if (!self::convertType($res[$i], $type)) {
throw new Nette\InvalidArgumentException(sprintf(
'Argument $%s passed to %s() must be %s, %s given.',
$name,
($method instanceof \ReflectionMethod ? $method->getDeclaringClass()->getName() . '::' : '') . $method->getName(),
$type,
is_object($args[$name]) ? get_class($args[$name]) : gettype($args[$name])
));
}
} elseif ($param->isDefaultValueAvailable()) {
$res[$i] = $param->getDefaultValue();
} elseif ($type === 'scalar' || $param->allowsNull()) {
$res[$i] = null;
} elseif ($type === 'array' || $type === 'iterable') {
$res[$i] = [];
} else {
throw new Nette\InvalidArgumentException(sprintf(
'Missing parameter $%s required by %s()',
$name,
($method instanceof \ReflectionMethod ? $method->getDeclaringClass()->getName() . '::' : '') . $method->getName()
));
}
}
return $res;
}
/**
* Non data-loss type conversion.
*/
public static function convertType(&$val, string $types): bool
{
foreach (explode('|', $types) as $type) {
if (self::convertSingleType($val, $type)) {
return true;
}
}
return false;
}
/**
* Non data-loss type conversion.
*/
private static function convertSingleType(&$val, string $type): bool
{
$builtin = [
'string' => 1, 'int' => 1, 'float' => 1, 'bool' => 1, 'array' => 1, 'object' => 1,
'callable' => 1, 'iterable' => 1, 'void' => 1, 'null' => 1, 'mixed' => 1,
'boolean' => 1, 'integer' => 1, 'double' => 1, 'scalar' => 1,
];
if (empty($builtin[$type])) {
return $val instanceof $type;
} elseif ($type === 'object') {
return is_object($val);
} elseif ($type === 'callable') {
return false;
} elseif ($type === 'scalar') {
return !is_array($val);
} elseif ($type === 'array' || $type === 'iterable') {
return is_array($val);
} elseif ($type === 'mixed') {
return true;
} elseif (!is_scalar($val)) { // array, resource, null, etc.
return false;
} else {
$tmp = ($val === false ? '0' : (string) $val);
if ($type === 'double' || $type === 'float') {
$tmp = preg_replace('#\.0*$#D', '', $tmp);
}
$orig = $tmp;
settype($tmp, $type);
if ($orig !== ($tmp === false ? '0' : (string) $tmp)) {
return false; // data-loss occurs
}
$val = $tmp;
}
return true;
}
/**
* Returns an annotation value.
* @param \ReflectionClass|\ReflectionMethod $ref
*/
public static function parseAnnotation(\Reflector $ref, string $name): ?array
{
if (!preg_match_all('#[\s*]@' . preg_quote($name, '#') . '(?:\(\s*([^)]*)\s*\)|\s|$)#', (string) $ref->getDocComment(), $m)) {
return null;
}
$tokens = ['true' => true, 'false' => false, 'null' => null];
$res = [];
foreach ($m[1] as $s) {
foreach (preg_split('#\s*,\s*#', $s, -1, PREG_SPLIT_NO_EMPTY) ?: ['true'] as $item) {
$res[] = array_key_exists($tmp = strtolower($item), $tokens)
? $tokens[$tmp]
: $item;
}
}
return $res;
}
public static function getParameterType(\ReflectionParameter $param): string
{
$default = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null;
$type = $param->getType();
return $type
? ($type instanceof \ReflectionNamedType ? $type->getName() : (string) $type)
: ($default === null ? 'scalar' : gettype($default));
}
public static function getPropertyType(\ReflectionProperty $prop, $default): string
{
$type = PHP_VERSION_ID < 70400 ? null : $prop->getType();
return $type
? ($type instanceof \ReflectionNamedType ? $type->getName() : (string) $type)
: ($default === null ? 'scalar' : gettype($default));
}
/**
* Has class specified annotation?
*/
public function hasAnnotation(string $name): bool
{
return (bool) self::parseAnnotation($this, $name);
}
/**
* Returns an annotation value.
* @return mixed
*/
public function getAnnotation(string $name)
{
$res = self::parseAnnotation($this, $name);
return $res ? end($res) : null;
}
public function getMethod($name): MethodReflection
{
return new MethodReflection($this->getName(), $name);
}
/**
* @return MethodReflection[]
*/
public function getMethods($filter = -1): array
{
foreach ($res = parent::getMethods($filter) as $key => $val) {
$res[$key] = new MethodReflection($this->getName(), $val->getName());
}
return $res;
}
/**
* return string[]
*/
public static function getClassesAndTraits(string $class): array
{
$res = [$class => $class] + class_parents($class);
$addTraits = function (string $type) use (&$res, &$addTraits): void {
$res += class_uses($type);
foreach (class_uses($type) as $trait) {
$addTraits($trait);
}
};
foreach ($res as $type) {
$addTraits($type);
}
return $res;
}
}
class_exists(PresenterComponentReflection::class);