将用户提交的值转换为本地值
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)$/'
这个正则表达式定义了两个命名的子模式。一,名为<char>
,捕获A
和A+
;另一个名为<digit>
捕获100
、10
、6
等。
preg_match()
如果没有匹配则返回0
(或false
出错)所以我们可以在这种情况下返回false
,因为这意味着用户输入(或比例)POST
ed 是无效。
否则,$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 我正在纠正 is_int 的使用,但你说得对,在这种情况下我们可能需要分数。不幸的是,is_numeric 相当太灵活(你想要“0.549E-5”验证吗?)所以这可能是自定义正则表达式允许整数或一位小数的情况。以上是关于将用户提交的值转换为本地值的主要内容,如果未能解决你的问题,请参考以下文章
将 postgresql 中的 UTC 时区转换为 EST(本地时间)
无法将类型“x”的值转换为预期的参数类型“[String:Any]”