我正在创建一个metacritic类型的网站,用户链接评论帖子(来自外部来源)。
在表单中,我允许用户选择源站点使用的评级类型:
例如,源帖子的评分50
可以是100
。
要在我的网站上显示评分,我想将源评分转换为简单的 5星评级。因此,上述示例的评分变为2.5
我的问题是,在考虑性能和效率时,PHP在进行这些类型的计算时最好的方法是什么?
我正在努力寻找一个好的手段,尤其是A+
等......
答案 0 :(得分:6)
这是一个有趣的问题。这是我对一个非常机械的解决方案的天真尝试,但尝试相当统一。我确信它可以得到显着改善/优化。
修改强>
我对之前的回答并不满意(我赶紧去,所以有一些错误 - 感谢你指出那些!)。我决定删除它并尝试一种不同的方法,这对于正则表达式和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>
的{{1}},100
,10
等。
6
出错), preg_match()
会返回0
,因此我们可以在这种情况下返回false
,因为这意味着用户输入(或比例)false
ed无效。
否则,POST
数组将包含任何捕获的值,$match
和(可选)char
作为键。如果digit
密钥存在,我们知道匹配是一个数字,我们可以将其转换为digit
并返回它。否则,我们必须匹配float
,因此我们可以char
该值并将其返回:
strtoupper()
两个回调都是相同的(除了正则表达式本身),所以你可以在那里创建一个可调用的,也许可以节省一些重复。
我希望在这个阶段没有开始感到有点复杂!希望这会有所帮助:)
答案 1 :(得分:2)
我得到了一个完全不同的观点。在PHP中,您可以dynamicly addres variables。
我的PHP方面(我非常懒惰):
<?php
const REGEX_INPUT = '/^(?<char>[a-fA-F]{1}[-+]?)$|^(?<digit>[1-9]?[0-9](\.\d+)?|100)$/';
if (preg_match (REGEX_INPUT , $_POST['rating'] )
&& preg_match (REGEX_INPUT , $_POST['scale'] )){//filter that input through given regex
$f- = 0;
$f = 1;
//add all of the non numeric possibilities you allow (lowercase)
$a = 16;
$a+ = 17;
$rating = strtolower($_POST['rating']);
$scale = strtolower($_POST['scale']) ;
try{
$yourstars = 5.0 * $$rating/$$scale; //here is the magic
} catch (Exception $e) {
}else{
//not allowed inputs
}
您会收到有关数字的警告,supress them或定义与字母一样的每个允许的可能性。
<强> Explenation 强>:
如果帖子是F,它会在$ rating中保存为f。现在,如果我拨打$ {$ rating},则会调用$ {f}。
在数字帖子上相同:如果帖子是10,我最后拨打10美元。 $ 10未定义,因此php会发出警告,并表示它假设10美元= 10。
其他版本的版本也很好,这只是懒惰人的另一个方面。
答案 2 :(得分:0)
这就是它对数值的作用。
如何进行字母分数:只需array
按住A-F
并将其分配给numeric value's
。然后将新variable
分配给最大数值(如果A +为100,则最大值为100),再次进行相同的数学计算,得出5分。
<?php
// Assign your $_POST / $_GET data to these variables
$maximum = 100;
$rating = 50;
// Checks wether the entries are a number, if true then automatically calculates score out of 5
if (ctype_digit($maximum) && ctype_digit($rating)) {
$score = ($rating/$maximum)*5;
}
echo $score;
?>
我不熟悉A-F
评分系统,但我无法找到任何转化。也许有这方面知识的人可以完成操作的答案。
答案 3 :(得分:0)
@Darrgh答案几乎解决了你的问题,但它的验证太多了。所以我让它变得非常简单,仍然可以满足你的需求。
这是我的代码:
const MAX_STARS = 5;
$letters = [
'F-', 'F', 'F+',
'G-', 'G', 'G+',
'D-', 'D', 'D+',
'C-', 'C', 'C+',
'B-', 'B', 'B+',
'A-', 'A', 'A+',
];
$scales = [
4 => 4,
5 => 5,
6 => 6,
10 => 10,
100 => 100,
'A' => 16,
'A+' => 17,
];
$rating = is_numeric($_POST['rating']) ? (float) $_POST['rating'] : strtoupper($_POST['rating']);
$scale = is_numeric($_POST['scale']) ? (float) $_POST['scale'] : $_POST['scale'];
if (false === $rating) {
echo 'please supply a valid rating';
} elseif (!(gettype($rating) === gettype($scale))) {
echo 'rating does not match scale';
} else {
$scale = $scales[$scale];
if (!is_float($rating)) {
$haystack = 'A+' === $scale ? $letters : array_slice($letters, 0, -1);
$rating = array_search($rating, $haystack, true);
}
$stars = round(($rating / $scale) * MAX_STARS, 2);
printf('rating: %s. scale: %s. stars: %s', $_POST['rating'], $_POST['scale'], $stars);
}
可能这会对你有所帮助。 :d
答案 4 :(得分:0)
我没有得到它,或者我的无数方法不是一个简单的解决方案?只需将下面的内容复制并粘贴到PHP文档中即可。还是容易? Probaly,因为我刚刚合并了alle post并将vars作为字符串获得1个数组然后我只是替换字母或其相应的数值。然后我再次将所有值视为数字,以达到相同的100点量表。我有一个字符串,其中包含100%的百分比,其中100是最高的。那么一颗星很容易就像每1%的额外费率奖励0.05颗星(100 * 0,05颗星5次开始)。
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<label>rating</label>
<input name="rating" id="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
if(!empty($_SERVER['REQUEST_METHOD']))
{
// Patterns
$patterns = array();
$patterns[0] = '/A$/';
$patterns[1] = '/A\+/';
// Replace with
$replacements = array();
$replacements[0] = '80';
$replacements[1] = '100';
// Merge $_GET & $_POST
foreach (array_merge($_GET, $_POST) AS $name => $value) {
// If string matches pattern then replace A or A+ with value
if (is_string($value) && !empty($value) && !is_numeric($value)) {
$value = preg_replace($patterns, $replacements, $value);
}
// All others are either valid or should be multiplied by 10
$value = ($value < 10 ? ($value*10) : $value);
}?>
<!-- write the value of $_POST['rate'] to text input if post is set -->
<script>
document.getElementById("rating").value = "<?php echo $value; ?>";
</script>
<?php
}
?>