将用户提交的值转换为本地值

Hen*_*son 5 php algorithm

我正在创建一个metacritic类型的网站,用户链接评论帖子(来自外部来源).

在表单中,我允许用户选择源站点使用的评级类型:

评分

例如,源后可以评价50出的100.

要在我的网站上显示评级,我想将源评级转换为简单的5星评级.因此,上述例子的评级变为2.5

我的问题是,在考虑性能和效率时,PHP在制作这些类型的计算时最好的方法是什么?

我很难找到一个好的手段去,尤其是A+等等......

Dar*_*ght 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>
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>,捕获AA+; 另外,命名<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)

两个回调都是相同的(除了正则表达式本身),所以你可以在那里创建一个可调用的,也许可以节省一些重复.

我希望现阶段没有开始感到有点复杂!希望这可以帮助 :)