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

时间:2015-08-22 17:17:30

标签: php algorithm

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

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

rating

例如,源帖子的评分50可以是100

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

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

我正在努力寻找一个好的手段,尤其是A+等......

5 个答案:

答案 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>,捕获AA+;另一个名为<digit>的{​​{1}},10010等。

如果没有匹配(或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
    }
?>