【问题标题】: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
  • 嗯。这是一个非常有趣的。

标签: php algorithm


【解决方案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”验证吗?)所以这可能是自定义正则表达式允许整数或一位小数的情况。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多