Skip to content

Commit 8e097dc

Browse files
committed
Presenter: better exception messages
1 parent 3108ebe commit 8e097dc

File tree

6 files changed

+67
-39
lines changed

6 files changed

+67
-39
lines changed

src/Application/UI/Presenter.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,13 @@ public static function argsToParams($class, $method, & $args, $supplemental = []
10381038
$def = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : NULL;
10391039
list($type, $isClass) = PresenterComponentReflection::getParameterType($param);
10401040
if (!PresenterComponentReflection::convertType($args[$name], $type, $isClass)) {
1041-
throw new InvalidLinkException("Invalid value for parameter '$name' in method $class::$method(), expected " . ($type === 'NULL' ? 'scalar' : $type) . ".");
1041+
throw new InvalidLinkException(sprintf(
1042+
'Argument $%s passed to %s() must be %s, %s given.',
1043+
$name,
1044+
$rm->getDeclaringClass()->getName() . '::' . $rm->getName(),
1045+
$type === 'NULL' ? 'scalar' : $type,
1046+
is_object($args[$name]) ? get_class($args[$name]) : gettype($args[$name])
1047+
));
10421048
}
10431049

10441050
if ($args[$name] === $def || ($def === NULL && is_scalar($args[$name]) && (string) $args[$name] === '')) {

src/Application/UI/PresenterComponent.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,13 @@ public function loadState(array $params)
126126
if (isset($params[$name])) { // NULLs are ignored
127127
$type = gettype($meta['def']);
128128
if (!$reflection->convertType($params[$name], $type)) {
129-
throw new Nette\Application\BadRequestException("Invalid value for persistent parameter '$name' in '{$this->getName()}', expected " . ($type === 'NULL' ? 'scalar' : $type) . ".");
129+
throw new Nette\Application\BadRequestException(sprintf(
130+
"Value passed to persistent parameter '%s' in %s must be %s, %s given.",
131+
$name,
132+
$this instanceof Presenter ? 'presenter ' . $this->getName() : "component '{$this->getUniqueId()}'",
133+
$type === 'NULL' ? 'scalar' : $type,
134+
is_object($params[$name]) ? get_class($params[$name]) : gettype($params[$name])
135+
));
130136
}
131137
$this->$name = $params[$name];
132138
} else {
@@ -163,7 +169,13 @@ public function saveState(array & $params, $reflection = NULL)
163169

164170
$type = gettype($meta['def']);
165171
if (!PresenterComponentReflection::convertType($params[$name], $type)) {
166-
throw new InvalidLinkException(sprintf("Invalid value for persistent parameter '%s' in '%s', expected %s.", $name, $this->getName(), $type === 'NULL' ? 'scalar' : $type));
172+
throw new InvalidLinkException(sprintf(
173+
"Value passed to persistent parameter '%s' in %s must be %s, %s given.",
174+
$name,
175+
$this instanceof Presenter ? 'presenter ' . $this->getName() : "component '{$this->getUniqueId()}'",
176+
$type === 'NULL' ? 'scalar' : $type,
177+
is_object($params[$name]) ? get_class($params[$name]) : gettype($params[$name])
178+
));
167179
}
168180

169181
if ($params[$name] === $meta['def'] || ($meta['def'] === NULL && is_scalar($params[$name]) && (string) $params[$name] === '')) {

src/Application/UI/PresenterComponentReflection.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,16 @@ public static function combineArgs(\ReflectionFunctionAbstract $method, $args)
123123
if (!isset($args[$name]) && $param->isDefaultValueAvailable()) {
124124
$res[$i++] = $param->getDefaultValue();
125125
} else {
126-
$res[$i++] = isset($args[$name]) ? $args[$name] : NULL;
126+
$res[$i++] = $arg = isset($args[$name]) ? $args[$name] : NULL;
127127
list($type, $isClass) = self::getParameterType($param);
128-
if (!self::convertType($res[$i - 1], $type, $isClass)) {
129-
$mName = $method instanceof \ReflectionMethod ? $method->getDeclaringClass()->getName() . '::' . $method->getName() : $method->getName();
130-
throw new BadRequestException("Invalid value for parameter '$name' in method $mName(), expected " . ($type === 'NULL' ? 'scalar' : $type) . ".");
128+
if (!self::convertType($arg, $type, $isClass)) {
129+
throw new BadRequestException(sprintf(
130+
'Argument $%s passed to %s() must be %s, %s given.',
131+
$name,
132+
($method instanceof \ReflectionMethod ? $method->getDeclaringClass()->getName() . '::' : '') . $method->getName(),
133+
$type === 'NULL' ? 'scalar' : $type,
134+
is_object($arg) ? get_class($arg) : gettype($arg)
135+
));
131136
}
132137
}
133138
}
@@ -206,7 +211,12 @@ public static function getParameterType(\ReflectionParameter $param)
206211
return ($ref = $param->getClass()) ? [$ref->getName(), TRUE] : [$def, FALSE];
207212
} catch (\ReflectionException $e) {
208213
if (preg_match('#Class (.+) does not exist#', $e->getMessage(), $m)) {
209-
return [$m[1], TRUE];
214+
throw new \LogicException(sprintf(
215+
"Class %s not found. Check type hint of parameter $%s in %s() or 'use' statements.",
216+
$m[1],
217+
$param->getName(),
218+
$param->getDeclaringFunction()->getDeclaringClass()->getName() . '::' . $param->getDeclaringFunction()->getName()
219+
));
210220
}
211221
throw $e;
212222
}

tests/Application/Presenter.link().php7.phpt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class TestPresenter extends Application\UI\Presenter
3535
Assert::same('/index.php?y=2&bool=1&str=1&action=default&do=hint&presenter=Test', $this->link('hint!', '1', '2', TRUE, TRUE));
3636
Assert::same('/index.php?y=2&str=0&action=default&do=hint&presenter=Test', $this->link('hint!', '1', '2', FALSE, FALSE));
3737
Assert::same('/index.php?action=default&do=hint&presenter=Test', $this->link('hint!', [1]));
38-
Assert::same("#error: Invalid value for parameter 'x' in method TestPresenter::handlehint(), expected int.", $this->link('hint!', [1], (object) [1]));
38+
Assert::same('#error: Argument $x passed to TestPresenter::handleHint() must be int, array given.', $this->link('hint!', [1], (object) [1]));
3939
Assert::same('/index.php?y=2&action=default&do=hint&presenter=Test', $this->link('hint!', [1, 'y' => 2]));
4040
Assert::same('/index.php?y=2&action=default&do=hint&presenter=Test', $this->link('hint!', ['x' => 1, 'y' => 2, 'var1' => $this->var1]));
4141
Assert::same('#error: Signal must be non-empty string.', $this->link('!'));
@@ -44,11 +44,11 @@ class TestPresenter extends Application\UI\Presenter
4444
Assert::same('/index.php?sort%5By%5D%5Basc%5D=1&action=default&presenter=Test', $this->link('this', ['sort' => ['y' => ['asc' => TRUE]]]));
4545

4646
// Presenter & signal link type checking
47-
Assert::same("#error: Invalid value for parameter 'x' in method TestPresenter::handlehint(), expected int.", $this->link('hint!', 'x'));
48-
Assert::same("#error: Invalid value for parameter 'bool' in method TestPresenter::handlehint(), expected bool.", $this->link('hint!', 1, 2, 3));
49-
Assert::same("#error: Invalid value for parameter 'x' in method TestPresenter::handlehint(), expected int.", $this->link('hint!', [[]]));
47+
Assert::same('#error: Argument $x passed to TestPresenter::handleHint() must be int, string given.', $this->link('hint!', 'x'));
48+
Assert::same('#error: Argument $bool passed to TestPresenter::handleHint() must be bool, integer given.', $this->link('hint!', 1, 2, 3));
49+
Assert::same('#error: Argument $x passed to TestPresenter::handleHint() must be int, array given.', $this->link('hint!', [[]]));
5050
Assert::same('/index.php?action=default&do=hint&presenter=Test', $this->link('hint!'));
51-
Assert::same("#error: Invalid value for parameter 'x' in method TestPresenter::handlehint(), expected int.", $this->link('hint!', [new stdClass]));
51+
Assert::same('#error: Argument $x passed to TestPresenter::handleHint() must be int, stdClass given.', $this->link('hint!', [new stdClass]));
5252
}
5353

5454

tests/Application/Presenter.link().phpt

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ class TestPresenter extends Application\UI\Presenter
8383
Assert::same('/index.php?action=product&presenter=Test', $this->link('product', ['var1' => $this->var1]));
8484
Assert::same('/index.php?var1=20&action=product&presenter=Test', $this->link('product', ['var1' => $this->var1 * 2, 'ok' => TRUE]));
8585
Assert::same('/index.php?var1=1&ok=0&action=product&presenter=Test', $this->link('product', ['var1' => TRUE, 'ok' => '0']));
86-
Assert::same("#error: Invalid value for persistent parameter 'ok' in 'Test', expected boolean.", $this->link('product', ['var1' => NULL, 'ok' => 'a']));
87-
Assert::same("#error: Invalid value for persistent parameter 'var1' in 'Test', expected integer.", $this->link('product', ['var1' => [1], 'ok' => FALSE]));
86+
Assert::same("#error: Value passed to persistent parameter 'ok' in presenter Test must be boolean, string given.", $this->link('product', ['var1' => NULL, 'ok' => 'a']));
87+
Assert::same("#error: Value passed to persistent parameter 'var1' in presenter Test must be integer, array given.", $this->link('product', ['var1' => [1], 'ok' => FALSE]));
8888
Assert::same("#error: Unable to pass parameters to action 'Test:product', missing corresponding method.", $this->link('product', 1, 2));
8989
Assert::same('/index.php?x=1&y=2&action=product&presenter=Test', $this->link('product', ['x' => 1, 'y' => 2]));
9090
Assert::same('/index.php?action=product&presenter=Test', $this->link('product'));
@@ -105,7 +105,7 @@ class TestPresenter extends Application\UI\Presenter
105105
Assert::same('/index.php?y=2&bool=1&str=1&action=default&do=buy&presenter=Test', $this->link('buy!', '1', '2', TRUE, TRUE));
106106
Assert::same('/index.php?y=2&str=0&action=default&do=buy&presenter=Test', $this->link('buy!', '1', '2', FALSE, FALSE));
107107
Assert::same('/index.php?action=default&do=buy&presenter=Test', $this->link('buy!', [1]));
108-
Assert::same("#error: Invalid value for parameter 'x' in method TestPresenter::handlebuy(), expected integer.", $this->link('buy!', [1], (object) [1]));
108+
Assert::same('#error: Argument $x passed to TestPresenter::handleBuy() must be integer, array given.', $this->link('buy!', [1], (object) [1]));
109109
Assert::same('/index.php?y=2&action=default&do=buy&presenter=Test', $this->link('buy!', [1, 'y' => 2]));
110110
Assert::same('/index.php?y=2&action=default&do=buy&presenter=Test', $this->link('buy!', ['x' => 1, 'y' => 2, 'var1' => $this->var1]));
111111
Assert::same('#error: Signal must be non-empty string.', $this->link('!'));
@@ -114,27 +114,27 @@ class TestPresenter extends Application\UI\Presenter
114114
Assert::same('/index.php?sort%5By%5D%5Basc%5D=1&action=default&presenter=Test', $this->link('this', ['sort' => ['y' => ['asc' => TRUE]]]));
115115

116116
// Presenter & signal link type checking
117-
Assert::same("#error: Invalid value for parameter 'x' in method TestPresenter::handlebuy(), expected integer.", $this->link('buy!', 'x'));
118-
Assert::same("#error: Invalid value for parameter 'bool' in method TestPresenter::handlebuy(), expected boolean.", $this->link('buy!', 1, 2, 3));
119-
Assert::same("#error: Invalid value for parameter 'x' in method TestPresenter::handlebuy(), expected integer.", $this->link('buy!', [[]]));
117+
Assert::same('#error: Argument $x passed to TestPresenter::handleBuy() must be integer, string given.', $this->link('buy!', 'x'));
118+
Assert::same('#error: Argument $bool passed to TestPresenter::handleBuy() must be boolean, integer given.', $this->link('buy!', 1, 2, 3));
119+
Assert::same('#error: Argument $x passed to TestPresenter::handleBuy() must be integer, array given.', $this->link('buy!', [[]]));
120120
Assert::same('/index.php?action=default&do=buy&presenter=Test', $this->link('buy!'));
121-
Assert::same("#error: Invalid value for parameter 'x' in method TestPresenter::handlebuy(), expected integer.", $this->link('buy!', [new stdClass]));
121+
Assert::same('#error: Argument $x passed to TestPresenter::handleBuy() must be integer, stdClass given.', $this->link('buy!', [new stdClass]));
122122

123-
Assert::same("#error: Invalid value for parameter 'a' in method TestPresenter::handleobj(), expected stdClass.", $this->link('obj!', ['x']));
123+
Assert::same('#error: Argument $a passed to TestPresenter::handleObj() must be stdClass, string given.', $this->link('obj!', ['x']));
124124
Assert::same('/index.php?action=default&do=obj&presenter=Test', $this->link('obj!', [new stdClass]));
125-
Assert::same("#error: Invalid value for parameter 'a' in method TestPresenter::handleobj(), expected stdClass.", $this->link('obj!', [new Exception]));
125+
Assert::same('#error: Argument $a passed to TestPresenter::handleObj() must be stdClass, Exception given.', $this->link('obj!', [new Exception]));
126126
Assert::same('/index.php?action=default&do=obj&presenter=Test', $this->link('obj!', [NULL]));
127-
Assert::same("#error: Invalid value for parameter 'b' in method TestPresenter::handleobj(), expected stdClass.", $this->link('obj!', ['b' => 'x']));
127+
Assert::same('#error: Argument $b passed to TestPresenter::handleObj() must be stdClass, string given.', $this->link('obj!', ['b' => 'x']));
128128
Assert::same('/index.php?action=default&do=obj&presenter=Test', $this->link('obj!', ['b' => new stdClass]));
129-
Assert::same("#error: Invalid value for parameter 'b' in method TestPresenter::handleobj(), expected stdClass.", $this->link('obj!', ['b' => new Exception]));
129+
Assert::same('#error: Argument $b passed to TestPresenter::handleObj() must be stdClass, Exception given.', $this->link('obj!', ['b' => new Exception]));
130130
Assert::same('/index.php?action=default&do=obj&presenter=Test', $this->link('obj!', ['b' => NULL]));
131131

132132
// Component link
133133
Assert::same('#error: Signal must be non-empty string.', $this['mycontrol']->link('', 0, 1));
134134
Assert::same('/index.php?mycontrol-x=0&mycontrol-y=1&action=default&do=mycontrol-click&presenter=Test', $this['mycontrol']->link('click', 0, 1));
135135
Assert::same('/index.php?mycontrol-x=0a&mycontrol-y=1a&action=default&do=mycontrol-click&presenter=Test', $this['mycontrol']->link('click', '0a', '1a'));
136136
Assert::same('/index.php?mycontrol-x=1&action=default&do=mycontrol-click&presenter=Test', $this['mycontrol']->link('click', [1]));
137-
Assert::same("#error: Invalid value for parameter 'x' in method TestControl::handleclick(), expected scalar.", $this['mycontrol']->link('click', [1], (object) [1]));
137+
Assert::same('#error: Argument $x passed to TestControl::handleClick() must be scalar, array given.', $this['mycontrol']->link('click', [1], (object) [1]));
138138
Assert::same('/index.php?mycontrol-x=1&action=default&do=mycontrol-click&presenter=Test', $this['mycontrol']->link('click', TRUE, FALSE));
139139
Assert::same('/index.php?action=default&do=mycontrol-click&presenter=Test', $this['mycontrol']->link('click', NULL, ''));
140140
Assert::same('#error: Passed more parameters than method TestControl::handleClick() expects.', $this['mycontrol']->link('click', 1, 2, 3));
@@ -146,10 +146,10 @@ class TestPresenter extends Application\UI\Presenter
146146
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'));
147147

148148
// Component link type checking
149-
Assert::same("#error: Invalid value for persistent parameter 'order' in 'mycontrol', expected array.", $this['mycontrol']->link('click', ['order' => 1]));
150-
Assert::same("#error: Invalid value for persistent parameter 'round' in 'mycontrol', expected integer.", $this['mycontrol']->link('click', ['round' => []]));
149+
Assert::same("#error: Value passed to persistent parameter 'order' in component 'mycontrol' must be array, integer given.", $this['mycontrol']->link('click', ['order' => 1]));
150+
Assert::same("#error: Value passed to persistent parameter 'round' in component 'mycontrol' must be integer, array given.", $this['mycontrol']->link('click', ['round' => []]));
151151
$this['mycontrol']->order = 1;
152-
Assert::same("#error: Invalid value for persistent parameter 'order' in 'mycontrol', expected array.", $this['mycontrol']->link('click'));
152+
Assert::same("#error: Value passed to persistent parameter 'order' in component 'mycontrol' must be array, integer given.", $this['mycontrol']->link('click'));
153153
$this['mycontrol']->order = NULL;
154154

155155
// silent invalid link mode
@@ -160,13 +160,13 @@ class TestPresenter extends Application\UI\Presenter
160160
$this->invalidLinkMode = self::INVALID_LINK_WARNING;
161161
Assert::error(function () {
162162
Assert::same('#', $this->link('product', ['var1' => NULL, 'ok' => 'a']));
163-
}, E_USER_WARNING, "Invalid link: Invalid value for persistent parameter 'ok' in 'Test', expected boolean.");
163+
}, E_USER_WARNING, "Invalid link: Value passed to persistent parameter 'ok' in presenter Test must be boolean, string given.");
164164

165165
// exception invalid link mode
166166
$this->invalidLinkMode = self::INVALID_LINK_EXCEPTION;
167167
Assert::exception(function () {
168168
$this->link('product', ['var1' => NULL, 'ok' => 'a']);
169-
}, Nette\Application\UI\InvalidLinkException::class, "Invalid value for persistent parameter 'ok' in 'Test', expected boolean.");
169+
}, Nette\Application\UI\InvalidLinkException::class, "Value passed to persistent parameter 'ok' in presenter Test must be boolean, string given.");
170170

171171
$this->var1 = NULL; // NULL in persistent parameter means default
172172
Assert::same('/index.php?action=product&presenter=Test', $this->link('product'));

0 commit comments

Comments
 (0)