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

Posted

技术标签:

【中文标题】将用户提交的值转换为本地值【英文标题】:Converting user submitted value into local value 【发布时间】:2015-11-16 11:44:43 【问题描述】:

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

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

例如,源帖子可以在100 中获得50 的评分。

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

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

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

【问题讨论】:

如果符合条件,我会奖励这个问题 100 分。 插入数字分数尺度很容易,但是其他的呢? A 评级的范围是多少? A+ 有什么不同?您能否详细说明no score - 这相当于0 的评级吗?如果是这样,在这种情况下,我希望不会接受用户输入的评分? 为什么不将所有内容都转换为“满分100”,然后将其划分为满分5?只需为 A、B、C 分配一个数值,您就可以对它们执行相同的操作。 我会写一些东西,但我不会保证这是最有效的方法,但它会起作用:p 嗯。这是一个非常有趣的。 【参考方案1】:

我没有得到它,还是我的无数学方法不是一个简单的解决方案?只需将以下内容复制并粘贴到 PHP 文档中就可以了?还是容易?可能因为我只是合并 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
    
?>

【讨论】:

【参考方案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 或定义每个允许的可能性,如字母。

解释

如果帖子是 F,它会在 $rating 中保存为 f。现在如果我调用 $$rating,它会调用 $f。

在数字帖子上也是如此:如果帖子是 10,我最终会调用 10 美元。 $10 没有定义,所以 php 抛出一个警告并说它假设 $10 = 10。

其他的版本也不错,这只是你们这些懒人的另一个方面。

【讨论】:

【参考方案3】:

这是一个有趣的问题。这是我对一个非常机械的解决方案的天真尝试,但尝试相当统一。我确信它可以得到显着改进/优化。

编辑

我对之前的回答不满意(我匆忙回答,所以出现了一些错误 - 感谢您指出这些错误!)。我决定删除它并尝试一种不同的方法,这对于正则表达式和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)$/'

这个正则表达式定义了两个命名的子模式。一,名为&lt;char&gt;,捕获AA+;另一个名为&lt;digit&gt; 捕获100106 等。

preg_match() 如果没有匹配则返回0(或false 出错)所以我们可以在这种情况下返回false,因为这意味着用户输入(或比例)POSTed 是无效。

否则,$match 数组将包含任何捕获的值,char 和(可选)digit 作为键。如果存在digit 键,我们知道匹配是一个数字,我们可以将其转换为float 并返回它。否则,我们必须匹配到 char,所以我们可以 strtoupper() 并返回该值:

return isset($matches['digit']) 
       ? (float) $matches['digit']    
       : strtoupper($matches['char']);

两个回调是相同的(除了正则表达式本身),因此您可以在那里创建一个可调用对象并可能节省一些重复。

我希望在这个阶段不要开始感到有点复杂!希望这会有所帮助:)

【讨论】:

首先,这是一段可爱的代码,非常感谢。在我将它投入测试之前,您能否稍微调整一下代码,使其也支持小写字符,例如: b+ -- 也许与 strtoupper() 一起使用是否合适?不确定。您还可以更改 round() 以添加 2 位小数,因此星星可能是:2.45 另外,带有小数的数字似乎不适用于 $rating。例如,如果用户添加 2.65 进行评分,它将在“评分 2.65 对...无效”处退出。 回复:我将A 刻度读作直接的[A,B,C,D,E,F] 刻度,不允许+/-。我刚刚重新阅读了问题 cmets,我发现您的意思不同。同样,它应该很容易调整。 在那里发布了一个更新——它再次是概念验证,显然只是经过完整性检查(所以它没有经过全面测试),但它应该符合你的规格,而且它可能也更整洁. 啊……我的第二个答案完全仓促了。我添加了第三次迭代......实际上它不是一个迭代,因为我改变了很多。但是,我认为这应该会更好,而且(我认为)它更短。【参考方案4】:

@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

【讨论】:

你永远不会有太多的验证;) 是的,@Darragh 兄弟,你是对的,只需通过简单的验证来测试它,呵呵 :)【参考方案5】:

这就是数值的工作方式。

如何进行字母评分:只需创建一个 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 评分系统,也找不到任何转换。也许有这方面知识的人可以完成op的答案。

【讨论】:

is_int 不会对用户输入做任何有用的事情 - 所有 提供的值都是变量类型的字符串;你想要ctype_digit,它检查字符串中的所有字符是否都是数字。 @IMSoP ctype_digit(2.5) 将返回 false(注意点)。因此,我建议改用 is_numeric() ... @HenrikPatterson 我正在纠正 i​​s_int 的使用,但你说得对,在这种情况下我们可能需要分数。不幸的是,is_numeric 相当灵活(你想要“0.549E-5”验证吗?)所以这可能是自定义正则表达式允许整数或一位小数的情况。

以上是关于将用户提交的值转换为本地值的主要内容,如果未能解决你的问题,请参考以下文章

将 postgresql 中的 UTC 时区转换为 EST(本地时间)

无法将类型“()”的值转换为预期的参数类型“字符串”

无法将类型“x”的值转换为预期的参数类型“[String:Any]”

无法将“UIView”类型的值转换为“SKView”

如何在角度 2 中将输入值转换为大写(传递给 ngControl 的值)

无法将“查询”类型的值转换为预期的条件类型“布尔”