diff --git a/src/Application/UI/Component.php b/src/Application/UI/Component.php index c170c00d2..f9b7f5eb2 100644 --- a/src/Application/UI/Component.php +++ b/src/Application/UI/Component.php @@ -135,13 +135,12 @@ public function loadState(array $params): void $reflection = $this->getReflection(); foreach ($reflection->getPersistentParams() as $name => $meta) { if (isset($params[$name])) { // nulls are ignored - $type = gettype($meta['def']); - if (!$reflection->convertType($params[$name], $type)) { + if (!$reflection->convertType($params[$name], $meta['type'])) { throw new Nette\Application\BadRequestException(sprintf( "Value passed to persistent parameter '%s' in %s must be %s, %s given.", $name, $this instanceof Presenter ? 'presenter ' . $this->getName() : "component '{$this->getUniqueId()}'", - $type === 'NULL' ? 'scalar' : $type, + $meta['type'] === 'NULL' ? 'scalar' : $meta['type'], is_object($params[$name]) ? get_class($params[$name]) : gettype($params[$name]) )); } diff --git a/src/Application/UI/ComponentReflection.php b/src/Application/UI/ComponentReflection.php index 32d122515..55fe9b612 100644 --- a/src/Application/UI/ComponentReflection.php +++ b/src/Application/UI/ComponentReflection.php @@ -52,6 +52,7 @@ public function getPersistentParams(string $class = null): array if (!$rp->isStatic() && self::parseAnnotation($rp, 'persistent')) { $params[$name] = [ 'def' => $default, + 'type' => Nette\Utils\Reflection::getPropertyType($rp) ?: gettype($default), 'since' => $isPresenter ? Nette\Utils\Reflection::getPropertyDeclaringClass($rp)->getName() : null, ]; } @@ -111,13 +112,12 @@ public function saveState(Component $component, array &$params): void $params[$name] = $component->$name; // object property value } - $type = gettype($meta['def']); - if (!self::convertType($params[$name], $type)) { + 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()}'", - $type === 'NULL' ? 'scalar' : $type, + $meta['type'] === 'NULL' ? 'scalar' : $meta['type'], is_object($params[$name]) ? get_class($params[$name]) : gettype($params[$name]) )); } diff --git a/tests/UI/Presenter.link().74.phpt b/tests/UI/Presenter.link().74.phpt new file mode 100644 index 000000000..237651e32 --- /dev/null +++ b/tests/UI/Presenter.link().74.phpt @@ -0,0 +1,319 @@ +invalidLinkMode = self::INVALID_LINK_TEXTUAL; + + // standard + Assert::same("#error: Invalid destination ''.", $this->link('')); + Assert::same('/index.php?action=params&presenter=Test', $this->link('params')); + Assert::same(['pint' => null, 'parr' => null, 'pbool' => null, 'action' => 'params'], $this->getLastCreatedRequest()->getParameters()); + Assert::same('http://localhost/index.php?action=params&presenter=Test', $this->link('//params')); + Assert::same('#error: Argument $arr passed to TestPresenter::actionParams() must be scalar, array given.', $this->link('params', [1, 2, 3, []])); + Assert::same('/index.php?xx=1&action=params&presenter=Test', $this->link('params', ['xx' => 1, 'yy' => []])); + Assert::same(['xx' => 1, 'yy' => [], 'pint' => null, 'parr' => null, 'pbool' => null, 'action' => 'params'], $this->getLastCreatedRequest()->getParameters()); + Assert::same('/index.php?sort%5By%5D%5Basc%5D=1&action=default&presenter=Test', $this->link('this', ['sort' => ['y' => ['asc' => true]]])); + Assert::same(['sort' => ['y' => ['asc' => true]], 'pint' => null, 'parr' => null, 'pbool' => null, 'mycontrol-order' => null, 'mycontrol-round' => null, 'action' => 'default'], $this->getLastCreatedRequest()->getParameters()); + + Assert::same("#error: Unable to pass parameters to action 'Test:product', missing corresponding method.", $this->link('product', 1)); + Assert::same('/index.php?a=1&action=product&presenter=Test', $this->link('product', ['a' => 1])); + Assert::same('#error: Passed more parameters than method TestPresenter::actionParams() expects.', $this->link('params', 1, 2, 3, 4, 5)); + + // special url + Assert::same('/index.php?x=1&y=2&action=product&presenter=Test', $this->link('product?x=1&y=2')); + Assert::same('/index.php?x=1&y=2&action=product&presenter=Test#fragment', $this->link('product?x=1&y=2#fragment')); + + // absolute + Assert::same('http://localhost/index.php?x=1&y=2&action=product&presenter=Test#fragment', $this->link('//product?x=1&y=2#fragment')); + $this->absoluteUrls = true; + Assert::same('http://localhost/index.php?x=1&y=2&action=product&presenter=Test#fragment', $this->link('product?x=1&y=2#fragment')); + Assert::same('http://localhost/index.php?x=1&y=2&action=product&presenter=Test#fragment', $this->link('//product?x=1&y=2#fragment')); + $this->absoluteUrls = false; + + // persistent params + Assert::same('/index.php?action=params&presenter=Test', $this->link('params', ['pint' => $this->pint, 'p' => ''])); + Assert::same('/index.php?pint=20&parr%5B0%5D=1&action=params&presenter=Test', $this->link('params', ['pint' => $this->pint * 2, 'pbool' => true, 'parr' => [1]])); + Assert::same(['pint' => 20, 'pbool' => null, 'parr' => [1], 'action' => 'params'], $this->getLastCreatedRequest()->getParameters()); + Assert::same('/index.php?pint=1&pbool=0&action=params&presenter=Test', $this->link('params', ['pint' => true, 'pbool' => '0', 'parr' => []])); + Assert::same('/index.php?pint=0&pbool=0&p=0&action=params&presenter=Test', $this->link('params', ['pint' => false, 'pbool' => false, 'p' => false, 'parr' => null])); + Assert::same("#error: Value passed to persistent parameter 'pbool' in presenter Test must be boolean, string given.", $this->link('this', ['p' => null, 'pbool' => 'a'])); + Assert::same("#error: Value passed to persistent parameter 'p' in presenter Test must be scalar, array given.", $this->link('this', ['p' => [1], 'pbool' => false])); + Assert::same('/index.php?action=persistent&presenter=Test', $this->link('persistent')); + + Assert::same('/index.php?pbooln=1&parrn%5B0%5D=1&action=params&presenter=Test', $this->link('params', ['pbooln' => true, 'parrn' => [1]])); + Assert::same(['pbooln' => true, 'parrn' => [1], 'pint' => null, 'parr' => null, 'pbool' => null, 'action' => 'params'], $this->getLastCreatedRequest()->getParameters()); + Assert::same('/index.php?pbooln=0&action=params&presenter=Test', $this->link('params', ['pbooln' => '0', 'parrn' => []])); + Assert::same(['pbooln' => false, 'parrn' => [], 'pint' => null, 'parr' => null, 'pbool' => null, 'action' => 'params'], $this->getLastCreatedRequest()->getParameters()); + Assert::same("#error: Value passed to persistent parameter 'pbooln' in presenter Test must be bool, string given.", $this->link('params', ['pbooln' => 'a'])); + Assert::same("#error: Value passed to persistent parameter 'parrn' in presenter Test must be array, string given.", $this->link('params', ['parrn' => 'a'])); + + // Other presenter & action link + Assert::same('/index.php?action=product&presenter=Other', $this->link('Other:product', ['p' => $this->p])); + Assert::same('/index.php?p=0&action=product&presenter=Other', $this->link('Other:product', ['p' => $this->p * 2])); + Assert::same('/index.php?p=123&presenter=Nette%3AMicro', $this->link('Nette:Micro:', ['p' => 123])); + Assert::same(['p' => 123], $this->getLastCreatedRequest()->getParameters()); + + // signal link + Assert::same('#error: Signal must be non-empty string.', $this->link('mycontrol:!')); + Assert::same('/index.php?action=default&presenter=Test', $this->link('this', ['p' => $this->p])); + Assert::same('/index.php?action=default&presenter=Test', $this->link('this!', ['p' => $this->p])); + Assert::same('/index.php?action=default&do=signal&presenter=Test', $this->link('signal!', ['p' => $this->p])); + Assert::same('/index.php?p=0&action=default&do=signal&presenter=Test', $this->link('signal!', [1, 'p' => $this->p * 2])); + Assert::same('/index.php?y=2&action=default&do=signal&presenter=Test', $this->link('signal!', 1, 2)); + Assert::same(['x' => null, 'y' => 2, 'pint' => null, 'parr' => null, 'pbool' => null, 'mycontrol-order' => null, 'mycontrol-round' => null, 'action' => 'default', 'do' => 'signal'], $this->getLastCreatedRequest()->getParameters()); + + // Component link + Assert::same("#error: Invalid destination ''.", $this['mycontrol']->link('', 0, 1)); + Assert::same('/index.php?mycontrol-x=0&mycontrol-y=1&action=default&do=mycontrol-click&presenter=Test', $this['mycontrol']->link('click', 0, 1)); + Assert::same('/index.php?mycontrol-x=0a&mycontrol-y=1a&action=default&do=mycontrol-click&presenter=Test', $this['mycontrol']->link('click', '0a', '1a')); + Assert::same('/index.php?mycontrol-x=1&action=default&do=mycontrol-click&presenter=Test', $this['mycontrol']->link('click', [1])); + Assert::same('#error: Argument $x passed to TestControl::handleClick() must be scalar, array given.', $this['mycontrol']->link('click', [1], (object) [1])); + Assert::same('/index.php?mycontrol-x=1&mycontrol-y=0&action=default&do=mycontrol-click&presenter=Test', $this['mycontrol']->link('click', true, false)); + Assert::same('/index.php?action=default&do=mycontrol-click&presenter=Test', $this['mycontrol']->link('click', null, '')); + Assert::same('#error: Passed more parameters than method TestControl::handleClick() expects.', $this['mycontrol']->link('click', 1, 2, 3)); + Assert::same('http://localhost/index.php?mycontrol-x=1&mycontrol-round=1&action=default&presenter=Test#frag', $this['mycontrol']->link('//this?x=1&round=1#frag')); + Assert::same('/index.php?mycontrol-x=1&mycontrol-y=2&action=default&do=mycontrol-click&presenter=Test', $this->link('mycontrol:click!', ['x' => 1, 'y' => 2, 'round' => 0])); + Assert::same(['mycontrol-x' => 1, 'mycontrol-y' => 2, 'mycontrol-round' => null, 'mycontrol-order' => null, 'pint' => null, 'parr' => null, 'pbool' => null, 'action' => 'default', 'do' => 'mycontrol-click'], $this->getLastCreatedRequest()->getParameters()); + + // Component link type checking + Assert::same("#error: Value passed to persistent parameter 'order' in component 'mycontrol' must be array, integer given.", $this['mycontrol']->link('click', ['order' => 1])); + Assert::same("#error: Value passed to persistent parameter 'round' in component 'mycontrol' must be integer, array given.", $this['mycontrol']->link('click', ['round' => []])); + $this['mycontrol']->order = 1; + Assert::same("#error: Value passed to persistent parameter 'order' in component 'mycontrol' must be array, integer given.", $this['mycontrol']->link('click')); + $this['mycontrol']->order = null; + + // type checking + Assert::same('/index.php?action=params&presenter=Test', $this->link('params', [])); + Assert::same(['pint' => null, 'parr' => null, 'pbool' => null, 'action' => 'params'], $this->getLastCreatedRequest()->getParameters()); + Assert::same('/index.php?action=params&presenter=Test', $this->link('params', ['int' => null, 'bool' => null, 'str' => null, 'arr' => null])); + Assert::same(['int' => null, 'bool' => null, 'str' => null, 'arr' => null, 'pint' => null, 'parr' => null, 'pbool' => null, 'action' => 'params'], $this->getLastCreatedRequest()->getParameters()); + Assert::same('/index.php?int=1&bool=1&str=abc&arr=1&action=params&presenter=Test', $this->link('params', ['int' => 1, 'bool' => true, 'str' => 'abc', 'arr' => '1'])); + Assert::same('/index.php?int=0&bool=0&action=params&presenter=Test', $this->link('params', ['int' => 0, 'bool' => false, 'str' => '', 'arr' => ''])); + Assert::same('/index.php?action=params&presenter=Test', $this->link('params', ['int' => new stdClass])); + + Assert::same('#error: Missing parameter $int required by TestPresenter::actionHints()', $this->link('hints', [])); + Assert::same('#error: Missing parameter $int required by TestPresenter::actionHints()', $this->link('hints', ['int' => null, 'bool' => null, 'str' => null, 'arr' => null])); + Assert::same('/index.php?int=1&bool=1&str=abc&arr%5B0%5D=1&action=hints&presenter=Test', $this->link('hints', ['int' => '1', 'bool' => '1', 'str' => 'abc', 'arr' => [1]])); + Assert::same('/index.php?int=0&bool=0&action=hints&presenter=Test', $this->link('hints', ['int' => 0, 'bool' => false, 'str' => '', 'arr' => []])); + Assert::same('#error: Argument $int passed to TestPresenter::actionHints() must be int, string given.', $this->link('hints', ['int' => ''])); + Assert::same('#error: Argument $int passed to TestPresenter::actionHints() must be int, stdClass given.', $this->link('hints', ['int' => new stdClass])); + Assert::same('#error: Argument $int passed to TestPresenter::actionHints() must be int, array given.', $this->link('hints', ['int' => []])); + Assert::same('#error: Argument $bool passed to TestPresenter::actionHints() must be bool, string given.', $this->link('hints', ['int' => '1', 'bool' => ''])); + Assert::same('#error: Argument $arr passed to TestPresenter::actionHints() must be array, string given.', $this->link('hints', ['int' => '1', 'bool' => '1', 'str' => '', 'arr' => ''])); + + Assert::same('/index.php?action=hintsNulls&presenter=Test', $this->link('hintsNulls', [])); + Assert::same('/index.php?action=hintsNulls&presenter=Test', $this->link('hintsNulls', ['int' => null, 'bool' => null, 'str' => null, 'arr' => null])); + Assert::same('/index.php?int=1&bool=1&str=abc&arr%5B0%5D=1&action=hintsNulls&presenter=Test', $this->link('hintsNulls', ['int' => '1', 'bool' => '1', 'str' => 'abc', 'arr' => [1]])); + Assert::same('/index.php?int=0&bool=0&action=hintsNulls&presenter=Test', $this->link('hintsNulls', ['int' => 0, 'bool' => false, 'str' => '', 'arr' => []])); + Assert::same('#error: Argument $int passed to TestPresenter::actionHintsNulls() must be int, string given.', $this->link('hintsNulls', ['int' => ''])); + Assert::same('#error: Argument $int passed to TestPresenter::actionHintsNulls() must be int, stdClass given.', $this->link('hintsNulls', ['int' => new stdClass])); + Assert::same('#error: Argument $int passed to TestPresenter::actionHintsNulls() must be int, array given.', $this->link('hintsNulls', ['int' => []])); + Assert::same('#error: Argument $bool passed to TestPresenter::actionHintsNulls() must be bool, string given.', $this->link('hintsNulls', ['int' => '1', 'bool' => ''])); + Assert::same('#error: Argument $arr passed to TestPresenter::actionHintsNulls() must be array, string given.', $this->link('hintsNulls', ['int' => '1', 'bool' => '1', 'str' => '', 'arr' => ''])); + + Assert::same('/index.php?action=hintsNullable&presenter=Test', $this->link('hintsNullable', [])); + Assert::same('/index.php?action=hintsNullable&presenter=Test', $this->link('hintsNullable', ['int' => null, 'bool' => null, 'str' => null, 'arr' => null])); + Assert::same('/index.php?int=1&bool=1&str=abc&arr%5B0%5D=1&action=hintsNullable&presenter=Test', $this->link('hintsNullable', ['int' => '1', 'bool' => '1', 'str' => 'abc', 'arr' => [1]])); + Assert::same('/index.php?int=0&bool=0&action=hintsNullable&presenter=Test', $this->link('hintsNullable', ['int' => 0, 'bool' => false, 'str' => '', 'arr' => []])); + Assert::same('#error: Argument $int passed to TestPresenter::actionHintsNullable() must be int, string given.', $this->link('hintsNullable', ['int' => ''])); + Assert::same('#error: Argument $int passed to TestPresenter::actionHintsNullable() must be int, stdClass given.', $this->link('hintsNullable', ['int' => new stdClass])); + Assert::same('#error: Argument $int passed to TestPresenter::actionHintsNullable() must be int, array given.', $this->link('hintsNullable', ['int' => []])); + Assert::same('#error: Argument $bool passed to TestPresenter::actionHintsNullable() must be bool, string given.', $this->link('hintsNullable', ['int' => '1', 'bool' => ''])); + Assert::same('#error: Argument $arr passed to TestPresenter::actionHintsNullable() must be array, string given.', $this->link('hintsNullable', ['int' => '1', 'bool' => '1', 'str' => '', 'arr' => ''])); + + Assert::same('/index.php?action=hintsDefaults&presenter=Test', $this->link('hintsDefaults', [])); + Assert::same('/index.php?action=hintsDefaults&presenter=Test', $this->link('hintsDefaults', ['int' => null, 'bool' => null, 'str' => null, 'arr' => null])); + Assert::same('/index.php?int=1&bool=1&str=abc&arr%5B0%5D=1&action=hintsDefaults&presenter=Test', $this->link('hintsDefaults', ['int' => '1', 'bool' => '1', 'str' => 'abc', 'arr' => [1]])); + Assert::same(['int' => 1, 'bool' => true, 'str' => 'abc', 'arr' => [1], 'pint' => null, 'parr' => null, 'pbool' => null, 'action' => 'hintsDefaults'], $this->getLastCreatedRequest()->getParameters()); + Assert::same('/index.php?action=hintsDefaults&presenter=Test', $this->link('hintsDefaults', ['int' => 0, 'bool' => false, 'str' => '', 'arr' => []])); + Assert::same('#error: Argument $int passed to TestPresenter::actionHintsDefaults() must be int, string given.', $this->link('hintsDefaults', ['int' => ''])); + Assert::same('#error: Argument $int passed to TestPresenter::actionHintsDefaults() must be int, stdClass given.', $this->link('hintsDefaults', ['int' => new stdClass])); + Assert::same('#error: Argument $int passed to TestPresenter::actionHintsDefaults() must be int, array given.', $this->link('hintsDefaults', ['int' => []])); + Assert::same('#error: Argument $bool passed to TestPresenter::actionHintsDefaults() must be bool, string given.', $this->link('hintsDefaults', ['int' => '1', 'bool' => ''])); + Assert::same('#error: Argument $arr passed to TestPresenter::actionHintsDefaults() must be array, string given.', $this->link('hintsDefaults', ['int' => '1', 'bool' => '1', 'str' => '', 'arr' => ''])); + + Assert::same('/index.php?action=defaults&presenter=Test', $this->link('defaults', [])); + Assert::same('/index.php?action=defaults&presenter=Test', $this->link('defaults', ['int' => null, 'bool' => null, 'str' => null, 'arr' => null])); + Assert::same('/index.php?action=defaults&presenter=Test', $this->link('defaults', ['int' => '1', 'bool' => '1', 'str' => 'a', 'arr' => [1]])); + Assert::same(['int' => null, 'bool' => null, 'str' => null, 'arr' => null, 'pint' => null, 'parr' => null, 'pbool' => null, 'action' => 'defaults'], $this->getLastCreatedRequest()->getParameters()); + Assert::same('/index.php?int=0&bool=0&str=&action=defaults&presenter=Test', $this->link('defaults', ['int' => 0, 'bool' => false, 'str' => '', 'arr' => []])); + Assert::same('#error: Argument $int passed to TestPresenter::actionDefaults() must be integer, string given.', $this->link('defaults', ['int' => ''])); + Assert::same('#error: Argument $int passed to TestPresenter::actionDefaults() must be integer, array given.', $this->link('defaults', ['int' => []])); + Assert::same('#error: Argument $int passed to TestPresenter::actionDefaults() must be integer, stdClass given.', $this->link('defaults', ['int' => new stdClass])); + Assert::same('#error: Argument $bool passed to TestPresenter::actionDefaults() must be boolean, string given.', $this->link('defaults', ['int' => '1', 'bool' => ''])); + Assert::same('#error: Argument $arr passed to TestPresenter::actionDefaults() must be array, string given.', $this->link('defaults', ['int' => '1', 'bool' => '1', 'str' => '', 'arr' => ''])); + + Assert::same('/index.php?action=objects&presenter=Test', $this->link('objects', ['req' => new stdClass, 'nullable' => new stdClass, 'opt' => new stdClass])); + Assert::same('#error: Missing parameter $req required by TestPresenter::actionObjects()', $this->link('objects', [])); + Assert::same('#error: Missing parameter $req required by TestPresenter::actionObjects()', $this->link('objects', ['req' => null, 'nullable' => null, 'opt' => null])); + Assert::same('#error: Argument $req passed to TestPresenter::actionObjects() must be stdClass, Exception given.', $this->link('objects', ['req' => new Exception, 'opt' => null])); + Assert::same('#error: Argument $req passed to TestPresenter::actionObjects() must be stdClass, array given.', $this->link('objects', ['req' => []])); + + // silent invalid link mode + $this->invalidLinkMode = self::INVALID_LINK_SILENT; + Assert::same('#', $this->link('params', ['p' => null, 'pbool' => 'a'])); + + // warning invalid link mode + $this->invalidLinkMode = self::INVALID_LINK_WARNING; + Assert::error(function () { + $this->link('params', ['p' => null, 'pbool' => 'a']); + }, E_USER_WARNING, "Invalid link: Value passed to persistent parameter 'pbool' in presenter Test must be boolean, string given."); + + // exception invalid link mode + $this->invalidLinkMode = self::INVALID_LINK_EXCEPTION; + Assert::exception(function () { + $this->link('params', ['p' => null, 'pbool' => 'a']); + }, Nette\Application\UI\InvalidLinkException::class, "Value passed to persistent parameter 'pbool' in presenter Test must be boolean, string given."); + + $this->p = null; // null in persistent parameter means default + Assert::same('/index.php?action=params&presenter=Test', $this->link('params')); + $this->terminate(); + } + + + public function actionParams($int, $bool, $str, $arr) + { + } + + + public function actionHints(int $int, bool $bool, string $str, array $arr) + { + } + + + public function actionHintsNulls(int $int = null, bool $bool = null, string $str = null, array $arr = null) + { + } + + + public function actionHintsNullable(?int $int, ?bool $bool, ?string $str, ?array $arr) + { + } + + + public function actionHintsDefaults(int $int = 0, bool $bool = false, string $str = '', array $arr = []) + { + } + + + public function actionDefaults($int = 1, $bool = true, $str = 'a', $arr = [1]) + { + } + + + public function actionObjects(stdClass $req, ?stdClass $nullable, stdClass $opt = null) + { + } + + + public function actionPersistent(int $pint) + { + } + + + public function handleSignal($x = 1, $y = 1) + { + } +} + + +class OtherPresenter extends TestPresenter +{ + /** @persistent */ + public $p = 20; +} + + +$url = new Http\UrlScript('http://localhost/index.php', '/index.php'); + +$presenterFactory = Mockery::mock(Nette\Application\IPresenterFactory::class); +$presenterFactory->shouldReceive('getPresenterClass') + ->andReturnUsing(function ($presenter) { + return $presenter . 'Presenter'; + }); + +$presenter = new TestPresenter; +$presenter->injectPrimary( + null, + $presenterFactory, + new Application\Routers\SimpleRouter, + new Http\Request($url), + new Http\Response +); + +$presenter->invalidLinkMode = TestPresenter::INVALID_LINK_WARNING; +$presenter->autoCanonicalize = false; + +$request = new Application\Request('Test', Http\Request::GET, []); +$presenter->run($request); diff --git a/tests/UI/Presenter.link().persistent.phpt b/tests/UI/Presenter.link().persistent.phpt index 04ef25169..7e25ebb12 100644 --- a/tests/UI/Presenter.link().persistent.phpt +++ b/tests/UI/Presenter.link().persistent.phpt @@ -96,28 +96,28 @@ class ThirdPresenter extends BasePresenter Assert::same([ - 'p1' => ['def' => null, 'since' => 'BasePresenter'], - 't1' => ['def' => null, 'since' => 'PersistentParam1'], + 'p1' => ['def' => null, 'type' => 'NULL', 'since' => 'BasePresenter'], + 't1' => ['def' => null, 'type' => 'NULL', 'since' => 'PersistentParam1'], ], BasePresenter::getReflection()->getPersistentParams()); Assert::same([ - 'p2' => ['def' => null, 'since' => 'TestPresenter'], - 'p1' => ['def' => null, 'since' => 'BasePresenter'], - 't1' => ['def' => null, 'since' => 'PersistentParam1'], - 't2' => ['def' => null, 'since' => 'PersistentParam2A'], + 'p2' => ['def' => null, 'type' => 'NULL', 'since' => 'TestPresenter'], + 'p1' => ['def' => null, 'type' => 'NULL', 'since' => 'BasePresenter'], + 't1' => ['def' => null, 'type' => 'NULL', 'since' => 'PersistentParam1'], + 't2' => ['def' => null, 'type' => 'NULL', 'since' => 'PersistentParam2A'], ], TestPresenter::getReflection()->getPersistentParams()); Assert::same([ - 'p1' => ['def' => 20, 'since' => 'BasePresenter'], - 'p3' => ['def' => null, 'since' => 'SecondPresenter'], - 't1' => ['def' => null, 'since' => 'PersistentParam1'], - 't3' => ['def' => null, 'since' => 'PersistentParam3'], + 'p1' => ['def' => 20, 'type' => 'integer', 'since' => 'BasePresenter'], + 'p3' => ['def' => null, 'type' => 'NULL', 'since' => 'SecondPresenter'], + 't1' => ['def' => null, 'type' => 'NULL', 'since' => 'PersistentParam1'], + 't3' => ['def' => null, 'type' => 'NULL', 'since' => 'PersistentParam3'], ], SecondPresenter::getReflection()->getPersistentParams()); Assert::same([ - 'p1' => ['def' => null, 'since' => 'BasePresenter'], - 't1' => ['def' => null, 'since' => 'PersistentParam1'], - 't2' => ['def' => null, 'since' => 'PersistentParam2A'], + 'p1' => ['def' => null, 'type' => 'NULL', 'since' => 'BasePresenter'], + 't1' => ['def' => null, 'type' => 'NULL', 'since' => 'PersistentParam1'], + 't2' => ['def' => null, 'type' => 'NULL', 'since' => 'PersistentParam2A'], ], ThirdPresenter::getReflection()->getPersistentParams());