这是一个有趣的问题。这是我对一个非常机械的解决方案的天真尝试,但尝试相当统一。我确信它可以得到显着改进/优化。
编辑
我对之前的回答不满意(我匆忙回答,所以出现了一些错误 - 感谢您指出这些错误!)。我决定删除它并尝试一种不同的方法,这对于正则表达式和filter_input 来确定用户输入是否有效 - 如果有效 - 将其转换为适当的类型有点疯狂。
这使得根据所选量表验证评级(通过类型和值比较)更加统一。我对此进行了理智的检查,我认为我对这种方法更满意;)
再一次...假设 HTML 表单如下:
<form method="post">
<label>rating</label>
<input name="rating" type="text" autofocus>
<label>out of</label>
<select name="scale">
<option value="100">100</option>
<option value="10">10</option>
<option value="6">6</option>
<option value="5">5</option>
<option value="4">4</option>
<option value="A">A</option>
<option value="A+">A+</option>
</select>
<input type="submit">
</form>
以及以下 PHP 向用户提交的输入:
<?php
const MAX_STARS = 5;
const REGEX_RATING = '/^(?<char>[a-fA-F]{1}[-+]?)$|^(?<digit>[1-9]?[0-9](\.\d+)?|100)$/';
const REGEX_SCALE = '/^(?<char>A\+?)$|^(?<digit>100|10|6|5|4)$/';
$letters = [
'F-', 'F', 'F+',
'G-', 'G', 'G+',
'D-', 'D', 'D+',
'C-', 'C', 'C+',
'B-', 'B', 'B+',
'A-', 'A', 'A+',
];
if ('POST' === $_SERVER['REQUEST_METHOD']) {
// validate user-submitted `rating`
$rating = filter_input(INPUT_POST, 'rating', FILTER_CALLBACK, [
'options' => function($input) {
if (preg_match(REGEX_RATING, $input, $matches)) {
return isset($matches['digit'])
? (float) $matches['digit']
: strtoupper($matches['char']);
}
return false; // no match on regex
},
]);
// validate user-submitted `scale`
$scale = filter_input(INPUT_POST, 'scale', FILTER_CALLBACK, [
'options' => function($input) {
if (preg_match(REGEX_SCALE, $input, $matches)) {
return isset($matches['digit'])
? (float) $matches['digit']
: strtoupper($matches['char']);
}
return false; // no match on regex
}
]);
// if a valid letter rating, convert to calculable values
if (in_array($scale, ['A+', 'A']) && in_array($rating, $letters)) {
$scale = array_search($scale, $letters);
$rating = array_search($rating, $letters);
}
// error! types don't match
if (gettype($rating) !== gettype($scale)) {
$error = 'rating %s and scale %s do not match';
exit(sprintf($error, $_POST['rating'], $_POST['scale']));
}
// error! rating is higher than scale
if ($rating > $scale) {
$error = 'rating %s is greater than scale %s';
exit(sprintf($error, $_POST['rating'], $_POST['scale']));
}
// done! print our rating...
$stars = round(($rating / $scale) * MAX_STARS, 2);
printf('%s stars out of %s (rating: %s scale: %s)', $stars, MAX_STARS, $_POST['rating'], $_POST['scale']);
}
?>
可能值得解释一下正则表达式和回调到底发生了什么;)
例如,采用以下正则表达式:
/^(?<char>A\+?)$|^(?<digit>100|10|6|5|4)$/'
这个正则表达式定义了两个命名的子模式。一,名为<char>,捕获A和A+;另一个名为<digit> 捕获100、10、6 等。
preg_match() 如果没有匹配则返回0(或false 出错)所以我们可以在这种情况下返回false,因为这意味着用户输入(或比例)POSTed 是无效。
否则,$match 数组将包含任何捕获的值,char 和(可选)digit 作为键。如果存在digit 键,我们知道匹配是一个数字,我们可以将其转换为float 并返回它。否则,我们必须匹配到 char,所以我们可以 strtoupper() 并返回该值:
return isset($matches['digit'])
? (float) $matches['digit']
: strtoupper($matches['char']);
两个回调是相同的(除了正则表达式本身),因此您可以在那里创建一个可调用对象并可能节省一些重复。
我希望在这个阶段不要开始感到有点复杂!希望这会有所帮助:)