English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
이 튜토리얼에서는 PHP를 사용하여 MySQL에서 предподготовленные операторы를 사용하는 방법을 배웁니다.
предподготовленный оператор(도명화된 문장으로도 알려짐)은 실제 파라미터 값이 아닌 대체자를 포함하는 SQL 쿼리 템플릿입니다. 문장을 실행할 때, 이 대체자들은 실제 값으로 대체됩니다.
MySQLi는 익명 위치 대체자(?)를 지원합니다. 예를 들어:
INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?);
PDO는 익명 위치 대체자(?), 그리고 이름 지정된 대체자를 지원합니다. 이름 지정된 대체자는 컴마와 함께 시작하는 콜론(:)으로 시작하는 식별자입니다. 예를 들어:
INSERT INTO persons (first_name, last_name, email) VALUES (:first_name, :last_name, :email);
предподготовленный оператор 실행은 두 단계로 구성됩니다: 준비 및 실행.
준비 - 준비 단계에서는 SQL 문장 템플릿을 생성하고 데이터베이스 서버로 전송합니다. 서버는 문장 템플릿을 해석하고 문법 검사 및 쿼리 최적화를 수행한 후, 이를 저장하여 나중에 사용할 준비를 합니다.
실행 - 실행 중에 파라미터 값을 서버로 전송합니다. 서버는 문장 템플릿과 이 값들을 사용하여 실행할 문장을 생성합니다.
предподготовленные операторы는 매우 유용하며, 특히 특정 INSERT 문장을 여러 번 실행하는 동안 다른 값들을 여러 번 사용할 때 특히 그렇습니다. 이 부분에서는 이를 사용하는 몇 가지 주요 장점을 설명합니다.
하나의 предподготовленный оператор는 동일한 문장을 효율적으로 반복적으로 실행할 수 있습니다. 이 문장은 한 번만 해석되지만 여러 번 실행할 수 있습니다. 각 번째 실행에서는 데이터베이스 서버로 전송해야 할 대신, 전체 SQL 문장 대신 대체占位符 값을 전송하므로, 대역폭 사용을 최대한 줄일 수 있습니다.
предподготовленные операторы는 강력한 보호를 제공하여 방지할 수 있습니다SQL注入파라미터 값이 직접 SQL 쿼리 문자열에 포함되지 않기 때문에. 다른 프로토콜을 사용하여 파라미터 값을 쿼리와 분리하여 데이터베이스 서버로 전송하면 그를 방해하지 않습니다. 문장 템플릿을 해석한 후, 서버는 실행 시에 이 값들을 사용합니다. 이것이 предподготовленные операторы가 오류가 잘 발생하지 않고, 데이터베이스 보안에서 가장 중요한 요소 중 하나로 생각되는 이유입니다.
다음 예제에서는 предподготовленные операторы의 실제 작동 방식을 보여드립니다:
<?php /* 尝试MySQL服务器连接。 假设您正在运行MySQL。 具有默认设置的服务器(没有密码的用户“root”) */ $link = mysqli_connect("localhost", "root", "", "demo"); //연결 확인 if($link === false){ die("에러: 연결할 수 없음. ". mysqli_connect_error()); } //使用预处理语句 $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)"; if($stmt = mysqli_prepare($link, $sql)){ //변수를 предзаполненной команде 바인딩으로 설정 mysqli_stmt_bind_param($stmt, "sss", $first_name, $last_name, $email); /* 파라미터 값을 설정하고 실행합니다, 이 문장은 다른 행을 다시 입력합니다。 */ $first_name = "Hermione"; $last_name = "Granger"; $email = "[email protected]"; mysqli_stmt_execute($stmt); /* 파라미터 값을 설정하고 INSERT 행의 문장을 실행합니다。 */ $first_name = "Ron"; $last_name = "Weasley"; $email = "[email protected]"; mysqli_stmt_execute($stmt); echo "记录插入成功。"; } else{ echo "에러: 쿼리 준비 실패: $sql." . mysqli_error($link); } //关闭语句 mysqli_stmt_close($stmt); //关闭连接 mysqli_close($link); ?>
<?php /* 尝试MySQL服务器连接。 假设您正在运行MySQL。 具有默认设置的服务器(没有密码的用户“root”) */ $mysqli = new mysqli("localhost", "root", "", "demo"); //연결 확인 if($mysqli === false){ die("에러: 연결할 수 없음. ". $mysqli->connect_error); } // 使用预处理语句 $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)"; if($stmt = $mysqli->prepare($sql)){ // 변수를 предзаполненной команде 바인딩으로 설정 $stmt->bind_param("sss", $first_name, $last_name, $email); /* 파라미터 값을 설정하고 실행합니다。 다시 이 문장을 실행하여 다른 행을 삽입 */ $first_name = "Hermione"; $last_name = "Granger"; $email = "[email protected]"; $stmt->execute(); /* 매개변수 값을 설정하고 실행 삽입할 행의 문장 */ $first_name = "Ron"; $last_name = "Weasley"; $email = "[email protected]"; $stmt->execute(); echo "성공적으로 레코드가 입력되었습니다."; } else{ echo "에러: 쿼리 준비 불가: $sql. " . $mysqli"->error; } //关闭语句 $stmt->close(); //关闭连接 $mysqli->close(); ?>
<?php /* 尝试MySQL服务器连接。 假设您正在运行MySQL。 具有默认设置的服务器(没有密码的用户“root”) */ try{ $pdo = new PDO("mysql:host=localhost;dbname=demo", "root", ""); // 将PDO错误模式设置为异常 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch(PDOException $e){ die("错误:无法连接。 " . $e->getMessage()); } //尝试执行插入查询 try{ //使用预处理语句 $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (:first_name, :last_name, :email)"; $stmt = $pdo->prepare($sql); //将参数绑定到语句 $stmt->bindParam(':first_name', $first_name, PDO::PARAM_STR); $stmt->bindParam(':last_name', $last_name, PDO::PARAM_STR); $stmt->bindParam(':email', $email, PDO::PARAM_STR); /* 매개변수 값을 설정하고 실행, 다시 이 문장을 실행하여 다른 행을 삽입 */ $first_name = "Hermione"; $last_name = "Granger"; $email = "[email protected]"; $stmt->execute(); /* 매개변수 값을 설정하고 실행 삽입할 행의 문장 */ $first_name = "Ron"; $last_name = "Weasley"; $email = "[email protected]"; $stmt->execute(); echo "记录插入成功。"; } catch(PDOException $e){ die("错误:无法准备/쿼리 실행: $sql. " . $e->getMessage()); } // 关闭语句 unset($stmt); //关闭连接 unset($pdo); ?>
위의 예제에서 볼 수 있듯이, 우리는 INSERT 문을 한 번만 준비했지만, 다른 매개변수 집합을 통해 여러 번 실행했습니다.
위의 SQL INSERT 문에서, 물음표는first_name،last_name그리고email필드 값 대체 문자
mysqli_stmt_bind_param() 함수는 변수를 SQL 문 템플릿의 대체 문자(?)에 바인딩합니다. 대체 문자(?)는 실행 시 변수에 저장된 실제 값을 대체합니다. 두 번째 매개변수로 제공된 데이터 타입 정의 문자열은 'sss' 문자열로 각 바인딩 변수의 데이터 타입을 string(문자열)로 지정합니다.
데이터 타입 정의 문자열은 해당 바인딩 변수의 데이터 타입을 지정하며, 다음 네 가지 유형의 매개변수가 있습니다:
i - integer(정수형)
d - double(이진 정밀형)
s - string(문자열)
b - BLOB(바이너리 라지 오브젝트: 이진 대형 객체)
데이터 타입 정의 문자열의 바인딩 변수 수와 문자 수는 SQL 문 템플릿의 대체 문자 수와 일치해야 합니다.
지금까지 학습한 것을 기억해보세요. 우리는 다음과 같은 HTML 텍스트 필드를 생성했습니다.데이터를 데이터베이스에 삽입여기서 우리는 예제를 확장하기 위해 전처리 문장을 실행하겠습니다. 다음 추가 스크립트 예제를 테스트할 수 있지만, 텍스트 필드의 action 속성에 올바른 파일 이름을 사용해야 합니다.
이는 데이터 입력을 위해 업데이트된 PHP 코드입니다. 예제를 잘 살펴보면, mysqli_real_escape_string()와 같이 사용되지 않음을 발견할 수 있습니다. предзаполненной команд에서 사용자 입력은 직접 쿼리 문자열로 대체되지 않으므로, 그들을 올바르게 Escaping하는 필요가 없습니다.
<?php /* 尝试MySQL服务器连接。 假设您正在运行MySQL。 具有默认设置的服务器(没有密码的用户“root”) */ $link = mysqli_connect("localhost", "root", "", "demo"); //연결 확인 if($link === false){ die("에러: 연결할 수 없음. ". mysqli_connect_error()); } //使用预处理语句 $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)"; if($stmt = mysqli_prepare($link, $sql)){ //변수를 준비된 문장으로 매개변수로 바인딩 mysqli_stmt_bind_param($stmt, "sss", $first_name, $last_name, $email); //매개변수 설정 $first_name = $_REQUEST['first_name']; $last_name = $_REQUEST['last_name']; $email = $_REQUEST['email']; //尝试执行预处理语句 if(mysqli_stmt_execute($stmt)){ echo "记录插入成功。"; } else{ echo "에러: 쿼리 실행 불가. ". $sql. " ". mysqli_error($link); } } else{ echo "에러: 쿼리 실행 불가. ". $sql. " ". mysqli_error($link); } // 关闭语句 mysqli_stmt_close($stmt); //关闭连接 mysqli_close($link); ?>
<?php /* 尝试MySQL服务器连接。 假设您正在运行MySQL。 具有默认设置的服务器(没有密码的用户“root”) */ $mysqli = new mysqli("localhost", "root", "", "demo"); //연결 확인 if($mysqli === false){ die("에러: 연결할 수 없음. ". $mysqli->connect_error); } //使用预处理语句 $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)"; if($stmt = $mysqli->prepare($sql)){ //변수를 предзаполненной команде 바인딩으로 설정 $stmt->bind_param("sss", $first_name, $last_name, $email); //설정 매개변수 $first_name = $_REQUEST['first_name']; $last_name = $_REQUEST['last_name']; $email = $_REQUEST['email']; //尝试执行预处理语句 if($stmt->execute()){ echo "记录插入成功。"; } else{ echo "错误:无法执行查询: $sql. " . $mysqli->error; } } else{ echo "错误:无法执行查询: $sql. " . $mysqli->error; } //关闭语句 $stmt->close(); //关闭连接 $mysqli->close(); ?>
<?php /* 尝试MySQL服务器连接。 假设您正在运行MySQL。 具有默认设置的服务器(没有密码的用户“root”) */ try{ $pdo = new PDO("mysql:host=localhost;dbname=demo", "root", ""); //将PDO错误模式设置为异常 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch(PDOException $e){ die("错误:无法连接。 " . $e->getMessage()); } //尝试执行插入查询 try{ //使用预处理语句 $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (:first_name, :last_name, :email)"; $stmt = $pdo->prepare($sql); // 将参数绑定到语句 $stmt->bindParam(':first_name', $_REQUEST['first_name'], PDO::PARAM_STR); $stmt->bindParam(':last_name', $_REQUEST['last_name'], PDO::PARAM_STR); $stmt->bindParam(':email', $_REQUEST['email'], PDO::PARAM_STR); // 执行预处理语句 $stmt->execute(); echo "记录插入成功。"; } catch(PDOException $e){ die("错误:无法准备/执行查询 $sql. " . $e->getMessage()); } //关闭语句 unset($stmt); //关闭连接 unset($pdo); ?>
注意:尽管在预处理语句中不需要转义用户输入,但是您应始终验证从外部源接收到的数据的类型和大小,并实施适当的限制以防止系统资源的利用。