mb_strlen()和strlen()在从Ajax调用PHP时无法返回正确的值

4
如何在PHP中添加检查传递的 $username 的长度?该网站是UTF-8编码,但我认为Javascript使用了不同的编码。您可以看到,在PHP中尝试了不同的方法但它们都无法正常工作。
尝试过的方法并没有起作用: 更改Ajax(javascript)以通过UTF-8而不是javascript编码传递变量 在PHP中尝试strlen、mb_strlen - 两者都返回错误值
更多信息: 我的Ajax将用户名发送给PHP,PHP会检查SQL数据库并返回可用或不可用。我决定在检查数据库之前对PHP进行一些额外的检查,如mb_strlen($username)。同时设置mb_internal_encoding("UTF-8")。
我打算尝试以UTF-8格式发送Ajax请求,但没找到合适的方法。
MySQL是否正确使用了UPPER?针对UTF-8相关内容?
以下是PHP代码:
// Only checks for the username being valid or not and returns 'taken' or 'available'
require_once('../defines/mainDefines.php'); // Connection variables
require_once('commonMethods.php');
require_once('sessionInit.php');    // start session, check for HTTP redid to HHHTPs

sleep(2);   // Looks cool watching the spinner

$username = $_POST['username'];

//if (mb_strlen($username) < MIN_USERNAME_SIZE) echo 'invalid_too_short';

//if (mb_strlen($username, 'UTF-8') < 10) { echo ('invalid_too_short'); exit; }
//die ('!1!' .  $username . '!2!' . mb_strlen($username) . '!3!' . strlen($username) . '!4!');

$dbc = mysqli_connect(DB_HOST, DB_READER, DB_READER_PASSWORD, DB_NAME) or     die(DB_CONNECT_ERROR . DB_HOST .  '--QueryDB--checkName.php');
$stmt = mysqli_stmt_init($dbc);

$query = "SELECT username FROM pcsuser WHERE UPPER(username) = UPPER(?)";
if (!mysqli_stmt_prepare($stmt, $query)) {
    die('SEL:mysqli_prepare failed somehow:' . $query . '--QueryDB--checkName.php');
}

if (!mysqli_stmt_bind_param($stmt, 's', $username)) {
    die('mysqli_stmt_bind_param failed somehow --checkName.php');
}

if (!mysqli_stmt_execute($stmt)) {
    die('mysqli_stmt_execute failed somehow' . '--checkName.php');
}

mysqli_stmt_store_result($stmt);
$num_rows = mysqli_stmt_num_rows($stmt);
mysqli_stmt_bind_result($stmt, $row);           
echo ($num_rows >= 1) ? 'taken' : 'available';

mysqli_stmt_close($stmt);
mysqli_close($dbc);

以下是 AJAX 代码:
function CheckUsername(sNameToCheck) {
document.getElementById("field_username").className = "validated";
registerRequest = CreateRequest();
if (registerRequest === null)
    alert("Unable to create AJAX request");
else {
  var url= "https://www.perrycs.com/php/checkName.php";
  var requestData = "username=" + escape(sNameToCheck); // data to send
  registerRequest.onreadystatechange = ShowUsernameStatus;
  registerRequest.open("POST", url, true);
  registerRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
  registerRequest.send(requestData);
}
}


function ShowUsernameStatus() {
var img_sad = "graphics/signup/smiley-sad006.gif";
var img_smile = "graphics/signup/smiley-happy088.gif";
var img_checking = "graphics/signup/bluespinner.gif";

if (request.readyState === 4) {
    if (request.status === 200) {
        var txtUsername = document.getElementById('txt_username');
        var fieldUsername = document.getElementById('field_username');
        var imgUsername = document.getElementById('img_username');
        var error = true;
        var response = request.responseText;

        switch (response) {
            case "available":
                txtUsername.innerHTML = "NAME AVAILABLE!";
                error = false;                  
                break;
            case "taken":
                txtUsername.innerHTML = "NAME TAKEN!";
                break;
            case "invalid_too_short": 
                txtUsername.innerHTML = "TOO SHORT!";
                break;
            default:
                txtUsername.innerHTML = "AJAX ERROR!";
                break;
        } // matches switch

        if (error) {
            imgUsername.src = img_sad;
            fieldUsername.className = 'error';
        } else {
            imgUsername.src = img_smile;
            fieldUsername.className = 'validated';
        }
    } // matches ===200
} // matches ===4
}

测试结果

当我在PHP中死亡并像下面这样回显时,以下是我得到的内容(在进行Ajax更改之前 [添加UTF-8到请求]...

PHP代码片段

die ('!1!' .  $username . '!2!' . mb_strlen($username) . '!3!' . strlen($username) . '!4!');

测试数据

用户名:David Perry

!1!David Perry!2!11!3!11!4!

用户名:ܦ"~÷Û♦

!1!ܦ\"~��%u2666!2!9!3!13!4!

第一个有效。第二个应该有效,但看起来编码有问题(可以理解)。

第二个有7个可见字符。mb_strlen显示为9,strlen显示为13。

在阅读了Joeri Sebrechts的解决方案和他们给我的链接之后,我查找了Ajax请求参数,有人提供了以下内容...

AJAX片段(已更改原始代码)

registerRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");

我已将charset=UTF-8添加进来,这是我在一篇文章中看到的例子。
更新:11月27日,东部标准时间晚上9:11
好的,经过大量阅读,我相信我编码JS的方式是错误的。我以前一直在使用escape函数...如下所示...
var requestData = "username=" + escape(sNameToCheck);

浏览了这个网站后...

http://www.the-art-of-web.com/javascript/escape/

我更加理解其每个函数的作用及其编码和解码方式。我应该能够完成这个任务。

var requestData = "username=" + encodeURIComponent(sNameToCheck);

在 JavaScript 和 PHP 中,我应该能够做到这一点...
$username = rawurldecode($_POST['username']);

我尝试这样做,但是仍然得到了8个字符而不是7个,这是为什么呢?如果我在屏幕上浏览文本,它只有7个字符。你有什么想法可以帮助我更好地理解这个问题吗?

问题已解决!

好的,感谢您的提示,让我找到了解决方案。我的更改如下。

在AJAX中——我以前使用的是escape(sNameToCheck);

var requestData = "username=" + encodeURIComponent(sNameToCheck);

在PHP中,我曾经使用$ username = $ _POST ['username'];
$username = rawurldecode($_POST['username']);
if (get_magic_quotes_gpc()) $username = stripslashes($username);

我真的很讨厌魔术引号(magic_quotes)……在处理表单数据时,它已经给我带来了50多个小时的烦恼,因为我忘记了它的存在。只要它工作正常,我就很开心!

现在,mb_strlen函数可以正常使用了,我可以轻松地把它加回去……

if (mb_strlen($username) < MIN_USERNAME_SIZE) { echo 'invalid_too_short'; exit; }

非常好用!


1
那段话...让人感到痛苦。单片式的、迂腐的、冗长的。请编辑一下(至少添加一些段落分隔符)。 - Jared Farrish
好的,你把 mb_strlen() 这一部分注释掉了,不管出于什么原因。具体是哪里出了问题? - Sean Walsh
你好。mb_strlen和strlen都给出了错误的值,因为我认为在Ajax调用中它被编码不同了。 - PerryCS
2个回答

6

PHP是一个字节处理器,它不支持字符集。这会带来很多棘手的后果。

strlen() 返回的是字符串长度的字节数,而不是字符数。这是因为 PHP 的 "string" 类型实际上是一个字节数组。UTF-8 对于“特殊字符”使用超过一个字节。因此 strlen() 只能正确地给出一小部分文本(= 英语文本)的长度。

Mb_strlen() 将字符串看作实际的字符,但假定它是通过 mbstring.internal_encoding 指定的编码。这是因为字符串本身只是一个字节数组,并没有元数据指定其字符集。如果您使用的是 UTF-8 数据并将 internal_encoding 设置为 utf8,则会得到正确的答案。如果您的数据不是 UTF-8,则会得到错误的答案。

MySQL 将从 PHP 接收到一个字节流,并根据数据库会话的字符集进行解析,这是通过 SET NAMES 指令进行设置的。每次连接到数据库时,您必须告诉它您的 PHP 字符串所使用的编码。

浏览器将从 PHP 接收到一个字节流,并根据内容类型字符集 http 头进行解析,这是通过 php.ini default_charset 进行控制的。Ajax 调用将以与其运行的页面相同的编码方式提交。

总之,您可以在以下页面上找到确保所有数据都被视为 UTF-8 的建议。遵循它,您的问题应该就可以解决了。 http://malevolent.com/weblog/archive/2007/03/12/unicode-utf8-php-mysql/


我忘记了一些代码行可能是问题所在...在初始的Ajax调用中registerRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded");当我打印PHP看到的内容时,它是一堆奇怪的/u2323东西。因此,我认为这是Ajax编码。有没有办法将我的Ajax传递为UTF-8? - PerryCS
好的,在阅读了几个小时后,我相信你让我走上了正确的道路来解决这个问题。我会在我的原始问题中添加一些更多的信息来展示给你...它仍然不能百分之百地工作,但也许你可以告诉我为什么...(拜托了,谢谢你)。 - PerryCS
已修复!我知道为什么它显示8个字符而不是7个了……这是之前发生的一个“错误”。我讨厌魔术引号!我将编辑我的问题并展示修复过程……感谢大家的帮助! - PerryCS
谢谢您解释了每个步骤是如何将数据传输到其他设备的过程,这是我需要的信息。我习惯于像《Mapping the C64》(1984年的汇编语言书籍)这样的书籍,它可以解释所有内容。调用此函数会执行此操作,并将CPU保留在此状态下。我讨厌不知道今天的软件中的所有功能。我曾经自己编写所有内容。使用他人的函数由于缺乏信息而让我感到疯狂。感谢您为我填补了一些空白!真正理解发生的事情对我非常有帮助! - PerryCS
很高兴你在我睡觉的时候找到了答案。 ;) 我忘记了魔术引号,因为在过去的五年中,我一直包含一个检查 php.ini 的正确设置的健全性检查文件,而那是其中之一的检查项。顺便说一下,要阅读有关 mbstring 的函数重载的内容,它有时也会导致奇怪的行为。 - Joeri Sebrechts
显示剩余3条评论

1

从快速浏览中,您可以清理此内容:

if (request.status == 200) {
    if (request.responseText == "available") {
        document.getElementById("txt_username").innerHTML = "NAME AVAILABLE!";
        document.images['img_username'].src=img_smile;
        document.getElementById("continue").disabled = false;
        document.getElementById("field_username").className = 'validated';
    } else if (request.responseText == "taken") {
        document.getElementById("txt_username").innerHTML = "NAME TAKEN!";
        document.images['img_username'].src=img_sad;
        document.getElementById("field_username").className = 'error';
    } else if (request.responseText == "invalid_too_short") {
        document.getElementById("txt_username").innerHTML = "TOO SHORT!";
        document.images['img_username'].src=img_sad;
        document.getElementById("field_username").className = 'error';
    } else {
        document.getElementById("txt_username").innerHTML = "AJAX ERROR!";
                document.images['img_username'].src=img_sad;
        document.getElementById("field_username").className = 'error';
    }
  }

to:

// I prefer triple equals
// Read more at http://javascript.crockford.com/style2.html
if (request.status === 200) {
        // use variables!
        var txtUsername = document.getElementById('txt_username');
        var fieldUsername = document.getElementById('field_username');
        var imgUsername = document.getElementById('img_username');

        var response = request.responseText;

        var error = true;

        // you can do a switch statement here too, if you prefer
        if (response === "available") {
            txtUsername.innerHTML = "NAME AVAILABLE!";

            document.getElementById("continue").disabled = false;

            error = false;

        } else if (response === "taken") {
            txtUsername.innerHTML = "NAME TAKEN!";

        } else if (response === "invalid_too_short") {
            txtUsername.innerHTML = "TOO SHORT!";

        } else {
            txtUsername.innerHTML = "AJAX ERROR!";
        }

        // refactor error actions
        if (error) {
            imgUsername.src = img_sad;
            fieldUsername.className = 'error';
        } else {
            imgUsername.src = img_smile;
            fieldUsername.className = 'validated';
        }
}

谢谢!你的方法更清晰。我会整理我的代码!你能帮忙处理一下字符串长度(strlen)部分吗? - PerryCS
我读了你提供的那篇文章。从未想过这种方式!谢谢你的提示!我尽力练习良好的编程,但是在学习时,哇...要掌握的东西太多了!哈哈 我以前用过 ===,但不知道那篇文章里面有什么。谢谢! - PerryCS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接