我正在创建一个metacritic类型的网站,用户链接评论帖子(来自外部来源).
在表单中,我允许用户选择源站点使用的评级类型:
例如,源后可以评价50出的100.
要在我的网站上显示评级,我想将源评级转换为简单的5星评级.因此,上述例子的评级变为2.5
我的问题是,在考虑性能和效率时,PHP在制作这些类型的计算时最好的方法是什么?
我很难找到一个好的手段去,尤其是A+等等......
这是个有趣的问题.这是我对一个非常机械的解决方案的天真尝试,但尝试相当统一.我相信它可以得到显着改善/优化.
编辑
我对我以前的回答不满意(我赶紧过去,所以有一些错误 - 感谢你指出那些!).我决定删除它并尝试一种不同的方法,这对于正则表达式有点疯狂,并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>
Run Code Online (Sandbox Code Playgroud)
以下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']);
}
?>
Run Code Online (Sandbox Code Playgroud)
可能值得解释一下正则表达式和回调是怎么回事;)
例如,采用以下正则表达式:
/^(?<char>A\+?)$|^(?<digit>100|10|6|5|4)$/'
Run Code Online (Sandbox Code Playgroud)
此正则表达式定义了两个命名的子模式.一个,命名<char>,捕获A和A+; 另外,命名<digit>捕获100,10,6等.
preg_match()返回0如果没有匹配(或false在错误),所以我们可以返回false在这种情况下,因为这意味着用户输入(或比例)POSTED是无效的.
否则,$match数组将包含任何捕获的值,使用char和(可选)digit作为键.如果digit密钥存在,我们知道匹配是一个数字,我们可以将它转换为a float并返回它.否则,我们必须匹配a char,所以我们可以strtoupper()将该值并返回:
return isset($matches['digit'])
? (float) $matches['digit']
: strtoupper($matches['char']);
Run Code Online (Sandbox Code Playgroud)
两个回调都是相同的(除了正则表达式本身),所以你可以在那里创建一个可调用的,也许可以节省一些重复.
我希望现阶段没有开始感到有点复杂!希望这可以帮助 :)