ここには 2 つの基本的な問題があります。JSONP の制限を理解していないことと、PDO を正しく使用していないことです。
PDO
PDO の使用にはいくつかのパターンがあります。(これらのパターンを明確にしてコードを再利用するために抽象化することはできますが、基本的にはこの順序でオブジェクトを使用する必要があります。)
単純なクエリ
// 1. Get a database handle
$dh = new PDO($DSN, $USERNAME, $PASSWORD, array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
// 2. Issue a string query, no bindings!
$cursor = $dh->query('SELECT 1');
// 3. read results. There are many ways to do this:
// 3a. Iteration
foreach ($cursor as $row) {
//...
}
// 3b. *fetch*
// You can use any one of multiple fetch modes:
// http://php.net/manual/en/pdostatement.fetch.php
while ($row = $cursor->fetch()) {
//...
}
// 3c. *fetchAll*
// *fetchAll* can also do some aggregation across all rows:
// http://php.net/manual/en/pdostatement.fetchall.php
$results = $cursor->fetchAll();
// 3d. *bindColumn*
$cursor->bindColumn(1, $id, PDO::PARAM_INT);
while ($cursor->fetch(PDO::FETCH_BOUND)) {
//$id == column 1 for this row.
}
// 4. close your cursor
$cursor->closeCursor();
準備されたステートメント
// 1. Get a database handle
$dh = new PDO($DSN, $USERNAME, $PASSWORD, array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
// 2. Prepare a statement, with bindings
$cursor = $dh->prepare('SELECT id, name FROM mytable WHERE name = :name');
// 3. Bind parameters to the statement. There are three ways to do this:
// 3a. via *execute*:
$cursor->execute(array(':name'=>$_GET['name']));
// 3b. via *bindValue*
$cursor->bindValue(':name', $_GET['name']);
// 3c. via *bindParam*. In this case the cursor receives a *reference*.
$name = 'name1';
$cursor->bindParam(':name', $name); // name sent to DB is 'name1'
$name = 'name2'; // name sent to DB is now 'name2'!
$name = 'name3'; // now it's 'name3'!
// 4. Execute the statement
$cursor->execute();
// 5. Read the results
// You can use any of the methods shown above.
foreach ($cursor as $row) { // Iteration
// ...
}
// 6. Don't forget to close your cursor!
// You can execute() it again if you want, but you must close it first.
$cursor->closeCursor();
JSONP
コードには他にも多くの問題があり、ブラウザーとサーバーの間で何が転送されているかが不明確になっているように思われます。
JSONP は、クロスドメイン リクエストに対するブラウザの制限を回避するための手法です。script
これは、url とcallback=
クエリ パラメータを使用して現在のページに要素を追加することで機能します。サーバーは JSON を使用して応答を準備し、JSON をコールバック文字列でラップして、応答を関数呼び出しに変換します。
例:
function doSomething(response) { response.name === 'ボブ'; response.callback === 'doSomething'; }
サーバー上:
header('Content-Type: text/javascript;charset=utf-8'); // NOT application/json!
echo $_GET['callback'], '(', $json_encode($_GET), ')';
ブラウザに戻ると、返されるスクリプトは次のとおりです。
doSomething({"name":"bob","callback","doSomething"})
ご覧のとおり、JSONP は基本的にハックです。XMLHttpRequest を使用しません。jQuery は関数内でそれを偽造するためにいくつかのことを行い$.ajax()
ますが、それでも逃れられない制限があります。
- 可能な唯一のメソッドは GET (no POST) です
script src=
。
- サーバーにデータを渡す唯一の方法は、クエリ文字列を使用することです。
- 応答の「コールバック」は、グローバル スコープからアクセスできる必要があります。
- これは巨大なセキュリティ ホールです。エンド サーバーは必要なスクリプトを出力できるため、エンド サーバーを完全に信頼する必要があります。
可能であれば、JSONP の代わりにCORSを使用してください。
推奨される解決策
これは、テストされていない、推奨される方法です。
いくつかのメモ:
- 登録 URL はhttp://example.org/registerです。エラーの場合でも、常に JSON を返します (これは変更できます)。CORS ヘッダーも発行するため、他のドメインから XHR を使用して POST できます。
- サーバーコードには少し抽象化されています。
serviceRegisterRequest()
URL のアクションを実行するメイン関数です。適切な例外処理で PDO を使用する方法を示します。HTTP 応答の抽象化を返します。
userExists()
createUser()
PDO 準備済みステートメントの使用方法を示します。
createUser()
は、パスワードを暗号化するcrypt()
方法の適切な使用法を示しています。(プレーンテキストのパスワードを保存しないでください!)
emitResponse()
CORS ヘッダーを設定する方法と JSON 出力を生成する方法を示します。
ブラウザーでhttp://example.COM/register :
<!DOCTYPE html>
<html>
<head>
<title>test registration</title>
<script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
</head>
<body>
<form id="theform">
<input name="u">
<input name="p" type="password">
</form>
<script>
$('#theform').submit(function(e){
$.ajax({
url: 'http://example.org/register',
type: 'POST',
data: $(e.target).serialize()
}).done(function(response){
console.log('SUCCESS: ');
console.log(response);
}).fail(function(jqXHR, textStatus){
console.log('FAILURE: ');
if (jqXHR.responseText) {
console.log(JSON.parse(jqXHR.responseText));
}
});
});
</script>
</body>
サーバー上:
function userExists($dbh, $name) {
$ps = $dbh->prepare('SELECT id, Username FROM user WHERE Username = ?');
$ps->execute(array($name));
$user = $ps->fetch(PDO::FETCH_ASSOC);
$ps->closeCursor();
return $user;
}
function createUser($dbh, $name, $pass, $salt) {
$ps = $dbh->prepare('INSERT INTO user (Username, Password) VALUES (?,?)';
$crypt_pass = crypt($pass, $salt);
$ps->execute(array($name, $crypt_pass));
$user_id = $dbh->lastInsertId();
$ps->closeCursor();
return array('id'=>$user_id, 'name'=>$name);
}
function serviceRegisterRequest($method, $data, $salt, $DBSETTINGS) {
if ($method==='POST') {
$dbh = new PDO($DBSETTINGS['dsn'],$DBSETTINGS['username'],$DBSETTINGS['password']);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$response = array('status'=>200,'header'=>array(),'body'=>array());
$dbh->beginTransaction(); // if using MySQL, make sure you are using InnoDB tables!
try {
$user = userExists($dbh, $data['u']);
if ($user) {
$response['status'] = 409; // conflict
$response['body'] = array(
'error' => 'User exists',
'data' => $user,
);
} else {
$user = createUser($dbh, $data['u'], $data['p'], $salt);
$response['status'] = 201; //created
$response['header'][] = "Location: http://example.org/users/{$user['id']}";
$response['body'] = array(
'success' => 'User created',
'data' => $user,
);
}
$dbh->commit();
} catch (PDOException $e) {
$dbh->rollBack();
$response['status'] = 500;
$response['body'] = array(
'error' => 'Database error',
'data' => $e->errorInfo(),
);
} catch (Exception $e) {
$dbh->rollBack();
throw $e; // rethrow errors we don't know about
}
return $response;
}
}
function emitResponse($response) {
// restrict allowed origins further if you can
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST');
foreach ($response['header'] as $header) {
header($header);
}
header('Content-Type: application/json', true, $response['status']);
$output = json_encode($response['body']);
header('Content-Length: '.strlen($output));
echo $output;
exit();
}
$DBSETTINGS = array(
'dsn'=>'mysql:...',
'username' => 'USERNAME',
'password' => 'PASSWORD',
);
$salt = '$6$rounds=5000$MyCr4zyR2nd0m5tr1n9$';
$response = serviceRegisterRequest($_SERVER['REQUEST_METHOD'], $_POST, $salt, $DBSETTINGS);
emitResponse($response);