【问题标题】:PHP - strlen($string) not co-operatingPHP - strlen($string) 不合作
【发布时间】:2012-02-03 20:25:41
【问题描述】:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" href="StylesTSL.css" type="text/css" media="screen">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Terraria Server List</title>
</head>
<body>
<div id="page">
<div id="logobase">
<div class="filler"></div>
<center><img src="logo.png" width="400" height="100"/></center>
</div>
<div class="filler"></div>
<div id="form">
<center>
<h1>Add a server!</h1>
<form action="" method="post">
Title: <input type="text" name="title" /><br />
IP & Port: <input type="text" name="ip" />(E.G: 127.0.0.1:7777)<br />
Description:<br />
<textarea name="desc"></textarea><br />
E-Mail: <input type="text" name="email" /><br />
Type:       <select name='type'><option value="Hamachi" selected>Hamachi</option><option value="Non-Hamachi">Non-Hamachi</option></select><br />
<input type="submit" name="submit" value="Submit server!" />
</form>
</center>
</div>
<div class="filler"></div>
<?php
//Our variables
$title = $_POST['title'];
$ip = $_POST['ip'];
$desc = $_POST['desc'];
$type = $_POST['type'];
$email = $_POST['email'];
$submit = $_POST['submit'];
//Connect to our DB
$con = mysql_connect("localhost", "x", "x");   
mysql_select_db("bobrocke_users", $con) or die("Could not select database");
if ($submit) {
    if (strlen($title) == 0) {
        die("Invalid title!");
    }
    else if (strlen($title) >= 51) {
        die("Invalid title!");
    }
    else if (strlen($ip) == 0) {
        die("Invalid IP!");
    }
    else if (strlen($ip) >= 51) {
        die("Invalid IP!");
    }
    else if (strlen($desc) == 0) {
        die("Invalid description!");
    }
    else if (strlen($email) == 0) {
        die("Invalid E-Mail!");
    }
    else if (strlen($email) >= 101) {
        die("Invalid E-Mail!");
    }
    else {
    mysql_query("INSERT INTO `Servers` (`ip`, `desc`, `type`, `title`, `email`) VALUES('".$ip."', '".$desc."', '".$type."', '".$title."', '".$email."')") or die(mysql_error()); 
    }
}
$get_all = mysql_query("SELECT * FROM Servers");
while ($row = mysql_fetch_assoc($get_all)) {
?>
<div class="servbox">
<center>
<h1><?php echo $row['title']?></h1><br /></center>
IP: <span class="ip"><?php echo $row['ip']?></span><br /><hr />
<p><?php echo $row['desc']?></p><br /><br />
<a href="http://bobcraft-games.com/TSL/page.php?id=<?php echo $row['id'] ?>">Server's Page</a><br />
Type: <?php echo $row['type']?><br /><br />
</div>
<div class="filler"></div>
<?php
}
?>
</div>
</body>
</html>

好吧,我想做的是限制用户发布无效的空白字段。

然而,无论出于何种原因,这些措施if (strlen($submittedValue) == 0) 或其他。

【问题讨论】:

  • 之前回显 $title 并在此处显示结果
  • 您是否将“空白”定义为包括空白字符?您可能想trim 字符串...
  • 在这行之前做一个var_dump($submittedValue);
  • 和 echo strlen($title) 也是如此,并在此处显示。 (或者更好的是,尝试一些常见的 PHP 测试,例如回显数据,然后找出您的实际问题并重新发布问题。)
  • 抱歉有误。我在我的数据库中收到很多带有空白字段的垃圾邮件,以为这是我的 strlen 部分。看来我有一些我似乎无法解决的漏洞。

标签: php mysql string strlen


【解决方案1】:

你最好在 PHP 中使用empty() 函数,它检查变量是否为空(如strlen(...) == 0

还有:不要忘记mysql_real_escape_string() 变量!

【讨论】:

  • 不完全。它做了他可能不想要的其他事情。
  • 这里你可以看到不同之处:stackoverflow.com/questions/7072948/… 但我认为在这种情况下没有必要
  • 不,没必要。这就是为什么它不是一个解决方案。
【解决方案2】:

使用 trim() 消除字符串开头和结尾的所有空格。使用 empty(trim($var)) 检查。

由于您在计算字符时使用的是 utf-8,因此请使用 mb_strlen() http://php.net/manual/en/function.mb-strlen.php

【讨论】:

【解决方案3】:

为什么不直接使用if (!$somestring) ...?空字符串将测试为falsenull 也将测试 - 只要$somestring 不能设置为应该可以正常工作的非字符串值(null 除外)。例如:

...
    if (!$title) {
        die("Invalid title!");
    }
    else if (strlen($title) >= 51) {
        die("Invalid title!");
    }
...

这也将捕获表单数据中未提交$title 的情况。

您还应该在查询中使用字符串时对其进行转义。所以:

mysql_query("INSERT INTO `Servers` (`ip`, `desc`, `type`, `title`, `email`) VALUES('".$ip."', '".$desc."', '".$type."', '".$title."', '".$email."')") or die(mysql_error()); 

..应该变成:

mysql_query("INSERT INTO `Servers` (`ip`, `desc`, `type`, `title`, `email`) VALUES('".
  mysql_real_escape_string($ip)."', '".
  mysql_real_escape_string($desc)."', '".
  mysql_real_escape_string($type)."', '".
  mysql_real_escape_string($title)."', '".
  mysql_real_escape_string($email)."')")
or die(mysql_error()); 

否则,人们可能会通过将 SQL 有效字符和代码放入表单数据中来弄乱您的查询。

【讨论】:

  • 好的,谢谢!如果我把它会影响 mysql_real_escape_string(ltrim($_POST['email']));例如作为 $email 变量? (另外,我添加了一些 strpos 来查看 IP 和电子邮件是否有一些特定字符,它位于 pastebin.com/gmMXHLWg
  • $email=mysql_real_escape_string(trim($_POST['email'])); 之类的东西会很好,但是如果检查电子邮件地址的格式,您可能需要在转义字符串之前执行此操作 - 只需确保在将其放入查询之前将其转义.此外,您不需要使用addslashes() mysql_real_escape_string() - 只需一个即可,否则您将转义转义序列(最好为您的数据库使用字符串转义函数类型,例如,mysql_real_escape_string() 用于 MySQL)。
【解决方案4】:

所以,strlen() 不合作。

如果某些代码不合作,我会做以下事情:

  • 隔离问题。通过删除所有不相关的内容。
  • 通过问自己“我的主张和期望是什么?”来尝试澄清应该做什么。
  • 编写一个特定的脚本来测试行为不端的代码
  • 修改行为不端的代码,直到其行为符合预期(如果需要,使其更可测试)。

隔离问题。通过删除所有不相关的内容。

首先,让我们带上您的original example code 并删除与手头特定问题无关的所有内容。留下validation code that uses strlen。所以,我们现在可以看得更清楚了。

然后,尝试通过询问“我的主张和期望是什么?”来阐明它应该做什么。

我从你的代码中猜想:

  • 给定
    • 信息的字段(标题、姓名、ip)
    • 及其最小长度
    • 及其最大长度
  • 用户提交的值小于最小长度或大于最大长度
    • 那么,该值应该被拒绝
  • 否则没关系

编写一个特定的脚本来测试行为不端的代码

我会创建一个 html/php 脚本,纯粹是为了测试导致悲伤的 php。例如,称之为FormFieldValidatorTest.php。测试脚本位于网站项目中,但仅供我运行。因此,我会将其放入受密码保护的目录或其他公众无法访问的地方。

我想要一个提交一些已知长度字符串的 UTF-8 html 页面。比如说,我们知道的字母“a”是一个字符长,而一个“空字段”我们知道是零个字符长。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>My Test</title>
</head>
<body>

<form action="" method="get">
<input name="singleCharacterString" type="text" value="a" size="1" maxlength="1">
<input name="emptyString" type="text" value="" size="1" >
<input type="submit" name="Submit" value="Run Tests">
</form>
...

然后,在FormFieldValidatorTest.php 的顶部,我会写一些 php 代码到:

  • 接收那些固定的、硬编码的表单值(夹具数据)。
  • 将夹具数据传递给一些测试,然后
  • 返回一组结果

为了保持简洁,我将测试代码的核心内容放入一个函数中,称之为runTestSuite(),下面将对其进行充实。

<?php 
//FormFieldValidatorTest.php

if ( $_REQUEST['Submit'] == 'Run Tests' ) {

    //perform the tests
    $testResultsArray = runTestSuite();

    /**
    * Lets loop through the results and count the failures
    */
    $failureCounter = 0;
    foreach ( $testResultsArray as $result ) {
        if ( $result['hasFailed'] ) {
            $failureCounter++;
        }   
    }
} else {
    //no tests were run set the results to null
    $testResultsArray = null;
}

?>
<html>
...

我会在FormFieldValidatorTest.php 的html 部分添加一些php。如果运行结果,这应该打印出结果(即当$testResultsArraynull 不同时):

<?php
//FormFieldValidatorTest.php
...
?>
<html>
...
<body>
<form action="" method="get">
...
</form>

<?php if ( $testResultsArray !== null ) : ?>
    <h1>Results</h1>
    <pre>
    <?php if ( $failureCounter > 0 ) : ?>
        Failed. <?php echo $failureCounter ?> tests failed.
        <?php print_r( $testResultsArray ); ?>
    <?php else : ?>
        Passed
    <?php endif; ?>
    </pre>
<?php endif; ?>
</body>
</html>

然后,我将填写我的 runTestSuite() 函数的内容。这基本上是对 assertTrue() 函数的两次调用,我稍后会详细说明。

  • 首先,它断言当给定singleCharacterString 时strlen 返回1 是真的。
  • 然后,当给定emptyString 时,它断言strlen 返回0 为真。

同时它传递一条描述每个测试的消息。这些存储在结果数组中,以在测试失败时帮助调试。


/**
* @desc A suite of tests to excercise that troublesome code
* @return array of results for each test done
*/
function runTestSuite() {

    /**
    * Lets Run some tests
    */

    /**
    * Create the results array
    */
    $testResultsArray = array();

    /**
    * Test some known data submitted via a form parameter 
    */
    assertTrue( 
        ( strlen( $_REQUEST['singleCharacterString'] ) == 1 ), 
        'Expect it to be TRUE that singleCharacterString http parameter
        has a stringlength of 1', 
        $testResultsArray );

    /**
    * @todo Add more tests here. 
    */
    assertTrue( 
        ( strlen( $_REQUEST['emptyString'] ) == 0 ), 
        'Expect it to be TRUE that emptyString http parameter
        has a stringlength of 0', 
        $testResultsArray );

    return $testResultsArray;
}

最后,我充实了assertTrue() 函数。这基本上测试第一个参数是否失败断言测试。然后,它将该结果和message 作为记录附加到$testResultsArray 中。

/**
* @desc A TRUE assertion
* @param mixed - a value that we expect to be true
* @param string - a message to help understand what we are testing
* @param array - a collection of results so far passed by reference
* @return void
*/
function assertTrue( $value, $message, &$testResultsArray ) {   

    if ( $value ) {
        //value is as asserted
        $currentResult = array(         
            'message' => $message,
            'hasFailed' => FALSE
            );
    } else {
        //value is not as asserted
        $currentResult = array(
            'message' => $message,
            'hasFailed' => TRUE
            );
    }   

    $testResultsArray[] = $currentResult;   

}

现在测试脚本已经完成。 See it here.

如果我运行它并看到它通过,我可以确定 strlen 正在合作。如果失败,我可以继续:

修改行为不端的代码,直到其行为符合预期。

为了做到这一点,您可能需要能够将行为不端的位拆分为位于其自己文件中的单独函数或类。这使得它可重复使用,因此可以从测试代码和实时生产代码中调用它。

我们真正应该测试的是长度规则,它规定只允许特定长度的字符串。

所以,让我们隔离它,以便我们可以测试它。可能是 rules 库中名为 isCorrectLength() 的函数位于名为 ServerValidationRules.php

的文件中
<?php 
//ServerValidationRules.php

/**
* @desc boolean function to test string length 
* @param string to test
* @param integer defining minimum length required
* @param integer defining maximum length required
* @return TRUE if its the correct length, FALSE otherwise.
*/
function isCorrectLength( $string, $minLength, $maxLength ) {

    if ( strlen( $string ) < $minLength ) {
        //its too short
        return FALSE;
    }

    if ( strlen( $string ) > $maxLength ) {
        //its too long
        return FALSE;
    }
    return TRUE;
}

然后我们可以替换测试中的代码以使用它来代替原生的strlen() 函数。

<?php
//FormFieldValidatorTest.php

require_once('ServerValidationRules.php');

...

/**
* @desc A suite of tests to excercise that troublesome code
* @return array of results for each test done
*/
function runTestSuite() {


    $testResultsArray = array();

    /**
    * Test some known data submitted via a form parameter 
    */
    assertTrue( 
        //( strlen( $_REQUEST['singleCharacterString'] ) == 1 ), 
        isCorrectLength( $_REQUEST['singleCharacterString'], 0, 1 ),
        'Expect it to be TRUE that singleCharacterString http parameter
        has a stringlength of 1', 
        $testResultsArray );

    /**
    * @todo Add more tests here. 
    */
    assertTrue( 
        //( strlen( $_REQUEST['emptyString'] ) == 0 ), 
        isCorrectLength( $_REQUEST['emptyString'], 0, 0 ),
        'Expect it to be TRUE that emptyString http parameter
        has a stringlength of 0', 
        $testResultsArray );

    return $testResultsArray;
}
...

所以,我们现在可以将生产脚本中的代码替换为:

<title>Terraria Server List</title>
...
<?php
...
if ( $submit ) {    
    if (!( isCorrectLength( $title, 0, 50 ) )) {
        die("Invalid title!");
    }    
    elseif (!( isCorrectLength($ip, 0, 50) )) {
        die("Invalid IP!");
    }
    elseif (!( isCorrectLength( $desc, 0, 10000 ) )) {
        die("Invalid description!");
    }    
    elseif (!( isCorrectLength( $email, 0, 100 ) )) {
        die("Invalid E-Mail!");
    }
    else {
        //do the insert
    }
}
...

或者,更好的是,将 length 规则移动到单个函数中,以便其他代码(即测试套件中的某些代码)可以重复使用该逻辑。

我们可以将所有这些包装到一个名为 isServerFieldsValid 的函数中,并将其放入我们的 ServerValidationRules.php 文件中。

<?php
//ServerValidationRules.php
...
/**
* @desc tests user-submitted fields appending feedback to an array of messages upon failure.
* @param associative array of Posted values keyed by field name
* @param array of messages passed by reference
* @return boolean True if all fields are valid. False otherwise.
*/
function isServerFieldsValid( $values, &$messages ) {   

    $hasFailed = FALSE;

    if (!( isCorrectLength( $values['title'], 1, 50 ) )) {
        $hasFailed = TRUE;
        $messages[] = "Invalid title!";
    }    

    if (!( isCorrectLength($values['ip'], 1, 50) )) {
        $hasFailed = TRUE;
        $messages[] = "Invalid IP!";    
    }

    if (!( isCorrectLength( $values['desc'], 1, 1000 ) )) {
        $hasFailed = TRUE;
        $messages[] = "Invalid description!";
    }    

    if (!( isCorrectLength( $values['email'], 1, 100 ) )) {
        $hasFailed = TRUE;
        $messages[] = "Invalid E-Mail!";
    }

    if ( $hasFailed ) {
        return FALSE;
    } 
    //else
    return TRUE;
}

然后在我们的测试脚本中为测试套件函数添加更多断言。

...

/**
* @desc A suite of tests to excercise that troublesome code
* @return array of results for each test done
*/
function runTestSuite() {

    /**
    * Initialize the results array
    */
    $testResultsArray = array();

    ...

    /**
    * Test some variants of possible user submitted data
    * 
    * @todo We ought to invoke an assertFalse() function.
    *  In meantime, hack it by passing a negated value to assertTrue().
    */

    /**
    * When given values that are too long, 
    * expect a validation failure.
    */
    $validationMessages = array();
    $result = isServerFieldsValid(
            array(
                'title' => str_repeat( 'a' , 51 ),
                'ip' => str_repeat( 'a' , 51 ),
                'desc' => str_repeat( 'a' , 1001 ),
                //?'type' => str_repeat( 'a' , 1001 ),
                'email' => str_repeat( 'a' , 101 ),
                ),
            $validationMessages
            );

    assertTrue(                 
        (!( $result )), 
        'Expect it to be TRUE that result is False when given long values',
        $testResultsArray );

    assertTrue(                 
        in_array( 'Invalid title!', $validationMessages ), 
        'Expect messages to say "Invalid title!"', 
        $testResultsArray );

    assertTrue(                 
        in_array( 'Invalid IP!', $validationMessages ), 
        'Expect messages to say "Invalid IP!"', 
        $testResultsArray );

    assertTrue(                 
        in_array( 'Invalid description!', $validationMessages ), 
        'Expect messages to say "Invalid description!"', 
        $testResultsArray );

    assertTrue(                 
        in_array( 'Invalid E-Mail!', $validationMessages ), 
        'Expect messages to say "Invalid E-Mail!"', 
        $testResultsArray );

    /**
    * When given values that are too short,
    *  expect a validation failure.
    */
    $validationMessages = array();
    $result = isServerFieldsValid(
            array(
                'title' => null,
                'ip' => null,
                'desc' => null,             
                'email' => null,
                ),
            $validationMessages
            );

    assertTrue(                 
        (!( $result )), 
        'Expect it to be TRUE that result is False when given short values',
        $testResultsArray );

    assertTrue(                 
        in_array( 'Invalid title!', $validationMessages ), 
        'Expect messages to say "Invalid title!"', 
        $testResultsArray );

    assertTrue(                 
        in_array( 'Invalid IP!', $validationMessages ), 
        'Expect messages to say "Invalid IP!"', 
        $testResultsArray );

    assertTrue(                 
        in_array( 'Invalid description!', $validationMessages ), 
        'Expect messages to say "Invalid description!"', 
        $testResultsArray );

    assertTrue(                 
        in_array( 'Invalid E-Mail!', $validationMessages ), 
        'Expect messages to say "Invalid E-Mail!"', 
        $testResultsArray );

    /**
    * When given values that are the correct length,
    *  expect a validation success.
    */
    $validationMessages = array();
    $result = isServerFieldsValid(
            array(
                'title' => 'a title',
                'ip' => 'an ip',
                'desc' => 'a desc',             
                'email' => 'an email',
                ),
            $validationMessages
            );

    assertTrue(                 
        ( $result ), 
        'Expect it to be TRUE that result is True when given correct values',
        $testResultsArray );

    assertTrue(                 
        (!( in_array( 'Invalid title!', $validationMessages ) )), 
        'Expect messages NOT to say "Invalid title!"', 
        $testResultsArray );

    assertTrue(                 
        (!( in_array( 'Invalid IP!', $validationMessages ) )), 
        'Expect messages NOT to say "Invalid IP!"', 
        $testResultsArray );

    assertTrue(                 
        (!( in_array( 'Invalid description!', $validationMessages ) )), 
        'Expect messages NOT to say "Invalid description!"', 
        $testResultsArray );

    assertTrue(                 
        (!( in_array( 'Invalid E-Mail!', $validationMessages ) )), 
        'Expect messages NOT to say "Invalid E-Mail!"', 
        $testResultsArray );


    return $testResultsArray;
}

...

那么,完整的测试脚本now looks like this

因此,如果通过了,我们可以通过调用新的、经过良好测试的 isServerFieldsValid() 函数替换所有这些 if (strlen ) { die } 语句来修改生产代码:

<title>Terraria Server List</title>

...

if ( $submit ) {
    $messages = array();
    if (!( isServerFieldsValid( $_POST, $messages ) )) {
        echo 'Invalid data was submitted:' . PHP_EOL;

        foreach( $messages as $message ) {
            echo $message . PHP_EOL;
        }

        exit;
    } else {
        //do the insert
} 
...

好吧,这就是我处理无论如何不合作的代码的方式。

代码:

注意:

我花了几个小时来写这个答案,但写实际测试的时间却很少。一旦你养成了习惯,通常只需要几分钟就可以草草写出一个测试并找出为什么某些代码不合作。

提示:您不需要编写自己的断言函数。 见:http://www.simpletest.org/en/start-testing.html

【讨论】:

    猜你喜欢
    • 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
    相关资源
    最近更新 更多