예제 pdo PHP에서 mysql_ * 함수를 사용하면 안되는 이유는 무엇입니까?




7 Answers

PHP는 MySQL에 연결할 수있는 세 가지 API를 제공합니다. 이것들은 mysql (PHP 7에서 제거됨), mysqliPDO 확장입니다.

mysql_* 함수는 매우 인기가 있었지만 사용은 더 이상 권장되지 않습니다. 문서화 팀은 데이터베이스 보안 상황에 대해 논의하고 있으며 사용자가 일반적으로 사용되는 ext / mysql 확장자에서 벗어나도록 교육하는 것은이 과정의 일부입니다 ( php.internals : ext / mysql 사용 중단 ).

나중에 PHP 개발자 팀은 사용자가 mysql_connect() , mysql_pconnect() 또는 ext/mysql 내장 된 암시 적 연결 기능을 통해 MySQL에 연결할 때 E_DEPRECATED 오류를 생성하기로 결정했습니다.

ext/mysqldeprecated 이후로 폐지되었으며 PHP 7 부터 삭제되었습니다 .

레드 박스를 보시겠습니까?

어떤 mysql_* 함수 페이지로 가면, 더 이상 사용해서는 안되는 빨간색 상자가 보인다.

ext/mysql 에서 벗어나면 보안뿐 아니라 MySQL 데이터베이스의 모든 기능에 액세스 할 수 있습니다.

ext/mysqlMySQL 3.23을 위해 만들어졌으며 유지 보수하기가 좀 더 어렵게 만든이 오래된 버전과의 호환성을 유지하면서 거의 추가되지 않았다. ext/mysql 지원하지 않는 누락 된 기능은 다음과 같습니다 ( deprecated ).

mysql_* 함수를 사용하지 않는 이유 :

  • 활발한 개발 중이 아님
  • PHP 7에서 제거됨
  • OO 인터페이스가 없다.
  • 비 차단, 비동기 쿼리를 지원하지 않습니다.
  • 준비된 문이나 매개 변수가있는 쿼리를 지원하지 않습니다.
  • 저장 프로 시저를 지원하지 않습니다.
  • 여러 문장을 지원하지 않습니다.
  • transactions 지원하지 않습니다.
  • MySQL 5.1의 모든 기능을 지원하지는 않는다.

위의 질문은 Quentin의 답변에서 인용 한 것입니다.

준비된 문에 대한 지원 부족은 별도의 함수 호출을 사용하여 수동으로 이스케이프 처리하는 것보다 외부 데이터를 이스케이프하고 인용하는 오류가 발생하기 쉬운 명확하고 명확한 방법을 제공하므로 특히 중요합니다.

SQL 확장비교를 참조하십시오.

지원 중단 경고 표시 안 함

코드가 MySQLi / PDO 로 변환되는 동안, E_DEPRECATED 에러는 php.ini의 error_reportingE_DEPRECATED: 를 제외하도록 설정함으로써 억제 될 수 있습니다 E_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

그러나 이것은 또한 MySQL과 다른 것들에 대한 것일 수있는 다른 deprecation 경고를 숨 깁니다. ( PHP 매뉴얼에서 )

PDO 대 MySQLi 문서 : 어느 것을 사용해야합니까? Dejan Marjanovic의 도움을 받아 선택할 수 있습니다.

더 좋은 방법은 PDO 이며, 이제는 간단한 PDO 자습서를 작성하고 있습니다.

간단하고 짧은 PDO 튜토리얼

질문. "PDO"란 무엇인가?

A. " PDO - PHP Data Objects - 여러 데이터베이스에 대한 일관된 액세스 방법을 제공하는 데이터베이스 액세스 계층입니다."

MySQL에 연결하기

mysql_* 함수를 사용하거나 이전 방식으로 말할 수 있습니다 (PHP 5.5 이상에서 사용되지 않음)

$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);

PDO : 새로운 PDO 객체를 생성하기 PDO 됩니다. 생성자는 데이터베이스 소스를 지정하기위한 매개 변수를 사용합니다. PDO 의 생성자는 대부분 DSN (데이터 소스 이름)과 선택적으로 username , password 인 네 개의 매개 변수를 사용 password .

여기에서는 DSN 제외한 모든 것에 익숙하다고 생각합니다. 이것은 PDO 에서 새로운 것입니다. DSN 은 기본적으로 PDO 에게 어떤 드라이버를 사용할 지와 연결 세부 사항을 알려주는 일련의 옵션입니다. 자세한 내용은 PDO MySQL DSN을 확인하십시오.

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

참고 : charset=UTF-8 사용할 수도 있지만 오류가 발생하는 경우가 있으므로 utf8 을 사용하는 것이 좋습니다.

접속 에러가있는 경우는 Exception 를 처리하기 위해서 캐치 할 수있는 PDOException 오브젝트를 슬로우합니다.

좋은 읽기 : 연결 및 연결 관리 ¶

네 번째 매개 변수에 대한 배열로 여러 드라이버 옵션을 전달할 수도 있습니다. PDO 를 예외 모드로 만드는 매개 변수를 전달하는 것이 좋습니다. 일부 PDO 드라이버는 원시 준비 문을 지원하지 않으므로 PDO 는 준비 작업의 에뮬레이션을 수행합니다. 또한이 에뮬레이션을 수동으로 활성화 할 수 있습니다. 기본 서버 측 준비 문을 사용하려면 명시 적으로 false 설정해야합니다.

다른 하나는 기본적으로 MySQL 드라이버에서 사용할 수있는 에뮬레이션 준비를 해제하는 것이지만 PDO 안전하게 사용하려면 에뮬레이션을 꺼야합니다.

나중에 에뮬레이션 준비를 해제해야하는 이유를 설명합니다. 이유를 찾으려면 이 게시물을 확인 하십시오 .

권장하지 않는 이전 버전의 MySQL 을 사용하는 경우에만 사용할 수 있습니다.

다음은이를 수행 할 수있는 방법의 예입니다.

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password',
              array(PDO::ATTR_EMULATE_PREPARES => false,
              PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

PDO 구축 후 속성을 설정할 수 있습니까?

, setAttribute 메소드를 사용하여 PDO를 생성 한 후 몇 가지 속성을 설정할 수도 있습니다.

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

오류 처리

오류 처리는 mysql_* 보다 PDO 에서 훨씬 쉽습니다.

mysql_* 사용할 때 일반적으로 사용되는 방법은 다음과 같습니다.

//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));

OR die() 는 오류를 처리하는 좋은 방법이 아닙니다. 왜냐하면 우리가 die 에서 처리 할 수 ​​없기 때문입니다. 스크립트를 갑작스럽게 종료 한 다음 화면에 오류를 표시하여 최종 사용자에게 표시하지 않으려 고 피의 해커가 귀하의 스키마를 발견하게하십시오. 또는 mysql_* 함수의 반환 값을 mysql_error() 와 함께 사용하여 오류를 처리 할 수 ​​있습니다.

PDO 는 더 나은 해결책을 제공합니다 : 예외. PDO 수행하는 모든 작업은 try - catch 블록에 래핑되어야합니다. 오류 모드 속성을 설정하여 PDO 를 세 가지 오류 모드 중 하나로 강제 설정할 수 있습니다. 다음 세 가지 오류 처리 모드가 있습니다.

  • PDO::ERRMODE_SILENT . 그것은 단지 에러 코드를 설정하고 mysql_* 과 거의 비슷하게 동작합니다. 여기서 각 결과를 확인한 다음 $db->errorInfo(); 오류 세부 사항을 얻으십시오.
  • PDO::ERRMODE_WARNING E_WARNING 시킵니다. (실행 중 경고 (치명적이지 않은 오류). 스크립트의 실행이 중지되지 않았습니다.)
  • PDO::ERRMODE_EXCEPTION : 예외를 던집니다. PDO가 제기 한 오류를 나타냅니다. 자신의 코드에서 PDOException 을 던져서는 PDOException . PHP의 예외에 대한 자세한 정보는 예외 를 참조하십시오. 그것은 매우 비슷 or die(mysql_error()); , 잡히지 않을 때. 그러나 달리 or die() 와 달리 PDOException 을 잡아서 정상적으로 처리 할 수 ​​있습니다.

좋은 읽을 거리 :

처럼:

$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

그리고 아래처럼 try - catch 감쌀 수 있습니다 :

try {
    //Connect as appropriate as above
    $db->query('hi'); //Invalid query!
} 
catch (PDOException $ex) {
    echo "An Error occured!"; //User friendly message/message you want to show to user
    some_logging_function($ex->getMessage());
}

지금 당장은 try - catch 로 처리 할 필요가 없습니다. 적절한 시간에 잡을 수는 있지만 try - catch 를 사용하는 것이 좋습니다. 또한 PDO 를 호출하는 함수 밖에서 그것을 잡는 것이 더 합리적 일 수 있습니다.

function data_fun($db) {
    $stmt = $db->query("SELECT * FROM table");
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

//Then later
try {
    data_fun($db);
}
catch(PDOException $ex) {
    //Here you can handle error and show message/perform action you want.
}

또한 by or die() 처리 할 수도 있고 mysql_* 과 같이 말할 수도 mysql_* 정말 다양 할 것입니다. display_errors off 오류 로그를 읽음으로써 프로덕션 환경에서 위험한 오류 메시지를 숨길 수 있습니다.

위의 모든 것을 읽은 후, 아마도 생각하고있을 것입니다. 단순한 SELECT , INSERT , UPDATE 또는 DELETE 문을 기대고 싶을 때 무엇입니까? 걱정마. 여기 우리가 간다.

데이터 선택

mysql_* 에서 무엇을하고 있는가?

<?php
$result = mysql_query('SELECT * from table') or die(mysql_error());

$num_rows = mysql_num_rows($result);

while($row = mysql_fetch_assoc($result)) {
    echo $row['field1'];
}

이제 PDO 에서 다음과 같이 할 수 있습니다.

<?php
$stmt = $db->query('SELECT * FROM table');

while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo $row['field1'];
}

또는

<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

//Use $results

참고 : 아래 메서드 ( query() )를 사용하는 경우이 메서드는 PDOStatement 객체를 반환합니다. 따라서 결과를 가져 오려면 위와 같이 사용하십시오.

<?php
foreach($db->query('SELECT * FROM table') as $row) {
    echo $row['field1'];
}

PDO 데이터에서는 명령문 핸들의 메소드 인 ->fetch() 를 통해 얻어집니다. fetch를 호출하기 전에 가장 좋은 방법은 데이터를 가져 오는 방법을 PDO에 알려주는 것입니다. 아래 섹션에서 나는 이것을 설명하고있다.

모드 불러 오기

위의 fetch()fetchAll() 코드에서 PDO::FETCH_ASSOC 을 사용합니다. 이것은 필드 이름을 키로하는 연관 배열로 행을 반환하도록 PDO 에 지시합니다. 하나씩 설명 할 다른 많은 가져 오기 모드도 있습니다.

우선, 가져 오기 모드를 선택하는 방법을 설명합니다.

 $stmt->fetch(PDO::FETCH_ASSOC)

위의 fetch() 있습니다. 다음을 사용할 수도 있습니다.

이제 모드 가져 오기 :

  • PDO::FETCH_ASSOC : 결과 집합에 반환 된 열 이름으로 인덱싱 된 배열을 반환합니다.
  • PDO::FETCH_BOTH (기본값) : 결과 집합에 반환 된 열 이름과 0 인덱싱 된 열 번호로 인덱싱 된 배열을 반환합니다.

더 많은 선택권이 있습니다! PDOStatement Fetch 문서 PDOStatement 모든 것을 읽어보십시오 . .

행 수 얻기 :

반환되는 행의 수를 얻기 위해 mysql_num_rows 를 사용하는 대신 다음과 같이 PDOStatement 가져 PDOStatement rowCount() 수행 할 수 있습니다.

<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';

마지막으로 삽입 된 ID 가져 오기

<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();

삽입 및 업데이트 또는 삭제 문

mysql_* 함수에서 우리가하는 일은 다음과 같습니다 :

<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);

그리고 pdo에서 다음과 같이이 동일한 작업을 수행 할 수 있습니다.

<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;

위의 쿼리에서 PDO::exec 는 SQL 문을 실행하고 영향을받은 행 수를 반환합니다.

삽입 및 삭제에 대해서는 나중에 설명합니다.

위의 방법은 쿼리에서 변수를 사용하지 않는 경우에만 유용합니다. 그러나 쿼리에서 변수를 사용할 필요가있을 때 위와 같이 준비된 문이나 매개 변수가있는 문에 대해 시도하지 마십시오.

준비된 진술

Q. 준비된 성명서 란 무엇이며 왜 필요합니까?
A. 준비된 명령문은 미리 컴파일 된 SQL 문으로서 서버에만 데이터를 보내 여러 번 실행할 수 있습니다.

준비된 문장을 사용하는 일반적인 워크 플로우는 다음과 같습니다 ( Wikipedia 3 3 포인트에서 인용 ) :

  1. 준비 : 명령문 템플리트는 응용 프로그램에 의해 작성되고 데이터베이스 관리 시스템 (DBMS)으로 송신됩니다. 특정 값은 매개 변수, 자리 표시 자 또는 바인드 변수라고하는 지정되지 않은 상태로 남습니다 (아래에 ? 라고 표시됨).

    INSERT INTO PRODUCT (name, price) VALUES (?, ?)

  2. DBMS는 구문 템플릿에 대한 구문 분석, 컴파일 및 쿼리 최적화를 수행하고 결과를 저장하지 않고 저장합니다.

  3. 실행 : 나중에 응용 프로그램이 매개 변수 값을 제공 (또는 바인드)하고 DBMS가 명령문을 실행합니다 (결과를 반환 할 수 있음). 응용 프로그램은 다른 값으로 원하는만큼 여러 번 명령문을 실행할 수 있습니다. 이 예에서는 첫 번째 매개 변수에 '빵'을, 두 번째 매개 변수에 1.00 을 제공 할 수 있습니다.

SQL에 플레이스 & 더를 포함 시켜서 준비된 명령문을 사용할 수 있습니다. 기본적으로 placeholder가없는 세 가지가 있습니다 (위의 변수로 시도하지 마십시오). 하나는 명명되지 않은 자리 표시자가 있고, 다른 하나는 명명 된 자리 표시자가 있습니다.

Q. 이제는 이름이 지정된 자리 표시자가 무엇이며 어떻게 사용합니까?
A. 명명 된 자리 표시 자. 물음표 대신 콜론으로 시작하는 설명적인 이름을 사용하십시오. 우리는 이름 자리 홀더에서 가치의 위치 / 질서에 관심이 없다 :

 $stmt->bindParam(':bla', $bla);

bindParam(parameter,variable,data_type,length,driver_options)

또한 execute 배열을 사용하여 바인드 할 수 있습니다.

<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

OOP 친구를위한 또 다른 멋진 기능은 명명 된 자리 표시자는 속성이 명명 된 입력란과 일치한다고 가정 할 때 데이터베이스에 개체를 직접 삽입 할 수 있다는 것입니다. 예 :

class person {
    public $name;
    public $add;
    function __construct($a,$b) {
        $this->name = $a;
        $this->add = $b;
    }

}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);

Q. 이제 명명되지 않은 자리 표시자는 무엇이고 어떻게 사용합니까?
A. 예를 들어 보겠습니다.

<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();

$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));

위의 내용을 볼 수 ? 이름 자리 표시 자처럼 이름 대신. 이제 첫 번째 예에서 변수를 다양한 자리 표시 자 ( $stmt->bindValue(1, $name, PDO::PARAM_STR); )에 $stmt->bindValue(1, $name, PDO::PARAM_STR); 합니다. 그런 다음 해당 자리 표시 자에 값을 할당하고 문을 실행합니다. 두 번째 예제에서 첫 번째 배열 요소는 첫 번째 배열 요소로 이동합니다 ? 두 번째에서 두 번째까지 ? .

참고 : 명명되지 않은 자리 표시 자에서 우리는 PDOStatement::execute() 메서드에 전달할 배열의 적절한 순서를 처리해야합니다.

준비된 쿼리를 SELECT , INSERT , UPDATE , DELETE

  1. SELECT :

    $stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
    $stmt->execute(array(':name' => $name, ':id' => $id));
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
  2. INSERT :

    $stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
    $stmt->execute(array(':field1' => $field1, ':field2' => $field2));
    $affected_rows = $stmt->rowCount();
    
  3. DELETE :

    $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
    $stmt->bindValue(':id', $id, PDO::PARAM_STR);
    $stmt->execute();
    $affected_rows = $stmt->rowCount();
    
  4. UPDATE :

    $stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
    $stmt->execute(array($name, $id));
    $affected_rows = $stmt->rowCount();
    

노트:

그러나 PDO 및 / 또는 MySQLi 는 완전히 안전하지 않습니다. 해답을 확인하십시오. PDO 준비 문이 SQL 삽입을 방지하기에 충분한가요? ircmaxell . 또한, 나는 그의 대답에서 일부를 인용하고있다 :

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));
pdo 사용법

mysql_* 함수를 사용해서는 안되는 기술적 이유는 무엇입니까? (예 : mysql_query() , mysql_connect() 또는 mysql_real_escape_string() )?

내 사이트에서 일하는 경우에도 왜 다른 것을 사용해야합니까?

그들이 내 사이트에서 작동하지 않으면 왜 오류가 발생합니까?

경고 : mysql_connect () : 그런 파일이나 디렉토리가 없습니다.




사용의 용이성

분석 및 합성 이유는 이미 언급되었습니다. 초보자들에게는 데이트 된 mysql_ 함수 사용을 중단하는 더 큰 동기가 있습니다.

최신 데이터베이스 API는 사용 하기더 쉽습니다 .

대부분 코드를 단순화 할 수있는 바운드 매개 변수 입니다. 그리고 share 하면 PDO 로 전환하는 것이 너무 힘들지 않습니다.

한 번에 더 큰 코드 기반을 다시 작성하는 데는 시간이 걸립니다. 이 중간체 대안을위한 Raison d' être :

mysql_ * 대신에 동일한 기능을하는 pdo_ *

<pdo_mysql.php> 를 사용하면 최소한의 노력으로 기존의 mysql_ 기능에서 전환 할 수 있습니다. mysql_ 를 대체하는 pdo_ 함수 래퍼를 추가한다.

  1. 단순히 include_once( <pdo_mysql.php> ); 각 호출 스크립트에서 데이터베이스와 상호 작용해야합니다.

  2. 모든 곳 에서 mysql_ 함수 접두사를 제거하고 pdo_ 바꾸십시오.

    • mysql_ connect()pdo_ connect() 가된다.
    • mysql_ query()pdo_ query() 가됩니다.
    • mysql_ num_rows()pdo_ num_rows()
    • mysql_ insert_id()pdo_ insert_id()
    • fetch_array()fetch_array() 가된다.
    • fetch_assoc()fetch_assoc() 가된다.
    • real_escape_string()real_escape_string() 이됩니다.
    • 등등...

  3. 코드는 비슷하게 작동하며 대부분 동일하게 보입니다.

    include_once("pdo_mysql.php"); 
    
    pdo_connect("localhost", "usrABC", "pw1234567");
    pdo_select_db("test");
    
    $result = pdo_query("SELECT title, html FROM pages");  
    
    while ($row = pdo_fetch_assoc($result)) {
        print "$row[title] - $row[html]";
    }
    

Et voilà.
귀하의 코드는 PDO를 사용하고 있습니다.
이제 실제로 활용할 차례입니다.

바운드 매개 변수는 사용하기 쉽습니다.

방금 다루기 힘든 API가 필요합니다.

pdo_query() 는 바운드 매개 변수를 매우 쉽게 지원합니다. 이전 코드를 변환하는 것은 간단합니다.

SQL 문자열에서 변수를 이동하십시오.

  • pdo_query() 쉼표로 구분 된 함수 매개 변수로 추가하십시오.
  • 물음표를 넣으 ? 변수가 있던 자리 표시 자로
  • 이전에 문자열 값 / 변수를 묶은 작은 따옴표를 제거하십시오.

길이가 긴 코드의 경우 이점이 더 분명 해집니다.

종종 문자열 변수는 SQL에 보간되는 것이 아니라 그 사이에있는 이스케이프 호출과 연결됩니다.

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title='" . pdo_real_escape_string($title) . "' OR id='".
   pdo_real_escape_string($title) . "' AND user <> '" .
   pdo_real_escape_string($root) . "' ORDER BY date")

? 자리 표시자를 적용해도 걱정할 필요가 없습니다.

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)

pdo_ *는 여전히 또는을 허용한다는 것을 기억하십시오.
변수를 벗어나 동일한 쿼리에서 바인드하지 마십시오.

  • 자리 표시 자 기능은 그 뒤에있는 실제 PDO가 제공합니다.
  • 따라서 또한 허용됩니다. 나중에 :named 자리 표시 자 목록.

더 중요한 것은 모든 쿼리 뒤에 $ _REQUEST [] 변수를 안전하게 전달할 수 있다는 것입니다. 제출 된 <form> 필드가 데이터베이스 구조와 정확히 일치하면 더 짧아집니다.

pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);

너무 단순합니다. 그러나 mysql_ 제거하고 이스케이프 처리해야하는 이유에 대한 더 많은 재 작성 조언과 기술적 인 이유를 생각해 보겠습니다.

oldschool sanitize() 함수를 수정하거나 제거하십시오.

바인드 된 params를 사용하여 모든 mysql_ 호출을 pdo_query 로 변환했으면 모든 중복 pdo_real_escape_string 호출을 제거하십시오.

특히 날짜가 지정된 자습서에 의해 광고 된대로 sanitize 또는 clean 또는 filterThis 또는 clean_data 함수를 수정해야합니다.

function sanitize($str) {
   return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}

여기서 가장 눈에 띄는 버그는 문서가 부족하다는 것입니다. 더 중요한 것은 필터링 순서가 정확히 잘못된 순서였습니다.

  • 올바른 순서는 다음과 같습니다. 가장 안쪽의 호출로 deprecatedly stripslashes , 그 다음에 _escape_string , strip_tags , 출력 컨텍스트에 대한 htmlentities , 마지막으로 _escape_string 은 응용 프로그램에서 직접 SQL 구문 분석을 수행해야합니다.

  • 그러나 첫 단계로 _real_escape_string 호출을 제거 하십시오.

  • 데이터베이스 및 응용 프로그램 흐름에서 HTML 컨텍스트에 안전한 문자열이 필요한 경우 나머지 sanitize() 함수를 지금 유지해야 할 수도 있습니다. 이후 HTML 만 적용되는 주석을 추가하십시오.

  • 문자열 / 값 처리는 PDO 및 해당 매개 변수가있는 명령문에 위임됩니다.

  • sanitize 함수에 stripslashes() 에 대한 언급이 있으면 더 높은 수준의 감독을 나타낼 수 있습니다.

    • 그것은 일반적으로 사용되지 않는 magic_quotes 에서 손상 (이중 이스케이프)을 실행 취소하기위한 것입니다. 그러나 어느 것이 문자열에 의한 문자열이 아니라 중앙 에서 가장 잘 고정되어 있습니다.

    • 사용자 영역 반전 접근법 중 하나를 사용하십시오. 그런 다음 sanitize 함수에서 stripslashes() 를 제거하십시오.

    magic_quotes에 대한 역사적인 주석. 이 기능은 올바르게 사용되지 않습니다. 그러나 종종 보안 기능이 실패한 것으로 잘못 묘사됩니다. 그러나 magic_quotes는 테니스 공이 영양 공급원으로 실패한만큼 실패한 보안 기능입니다. 그것은 단순히 그들의 목적이 아니 었습니다.

    PHP2 / FI의 원래 구현은 " 따옴표가 자동으로 이스케이프되어 양식 데이터를 직접 msql 쿼리로 전달하는 것이 더 쉬워졌습니다 "라고 명시 적으로 도입했습니다. 특히, 지원 된 ASCII만으로 mSQL 을 사용하는 것이 우연히 안전했습니다.
    그렇다면 PHP3 / Zend는 MySQL을 위해 magic_quotes를 다시 소개하고 그것을 잘못 문서화했습니다. 그러나 원래는 편의를위한 기능 이 아니며 보안을 염두에 두지 않았습니다.

준비된 진술의 차이점

문자열 변수를 SQL 쿼리로 스크램블하면 더 복잡해지지 않고 따라갈 수 있습니다. 또한 MySQL이 코드와 데이터를 다시 분리하기위한 외부적인 노력이기도합니다.

SQL 주입은 단순히 데이터가 코드 컨텍스트 로 추출 되는 경우입니다. 데이터베이스 서버는 나중에 PHP가 원래 붙인 변수를 쿼리 절 사이에서 구분할 수 없습니다.

바운드 매개 변수를 사용하면 PHP 코드에서 SQL 코드와 SQL 컨텍스트 값을 구분할 수 있습니다. 그러나 PDO :: EMULATE_PREPARES를 제외하고는 장면 뒤에서 다시 섞이지는 않습니다. 데이터베이스는 변환되지 않은 SQL 명령과 1 : 1 변수 값을받습니다.

이 답변은 mysql_ 때의 가독성에 대해주의를 기울여야 함을 강조합니다. 이 가시적이고 기술적 인 데이터 / 코드 분리로 인해 종종 성능상의 이점 (값이 다른 INSERT가 반복됨)이 있습니다.

매개 변수 바인딩이 모든 SQL 주입에 대한 마법의 원 스톱 솔루션이 아니라는 점에 유의하십시오. 데이터 / 값에 대한 가장 일반적인 사용을 처리합니다. 그러나 열 이름 / 테이블 식별자를 허용하거나, 동적 절 작성을 도와 주거나, 일반 배열 값 목록을 허용 할 수는 없습니다.

하이브리드 PDO 사용

이러한 pdo_* wrapper 함수는 코딩 친화적 인 stop-gap API를 만듭니다. (그것은 특이한 함수 서명 변경이 아니라면 MYSQLI 할 수 있었던 것입니다). 그들은 또한 대부분의 시간에 실제 PDO를 노출합니다.
재 작성은 새로운 pdo_ 함수 이름을 사용할 때 멈추지 않아도됩니다. 하나씩 순차적으로 각 pdo_query ()를 일반 $ pdo-> prepare () -> execute () 호출로 전환 할 수 있습니다.

그러나 다시 단순화하는 것이 가장 좋습니다. 예를 들어 일반적인 결과 가져 오기 :

$result = pdo_query("SELECT * FROM tbl");
while ($row = pdo_fetch_assoc($result)) {

foreach 반복으로 바꿀 수 있습니다.

foreach ($result as $row) {

아니면 직접적이고 완전한 어레이 검색 :

$result->fetchAll();

PDO 나 mysql_이 대개 실패한 쿼리 후에 제공하는 것보다 더 유용한 경고를 얻을 수 있습니다.

다른 옵션

따라서 이것은 mysql_ 을 삭제하기위한 실질적인 이유와 가치있는 경로를 시각적으로 보여줍니다.

단지 pdo 전환해도 pdo 되지 않습니다. pdo_query() 도 프론트 엔드에 있습니다.

매개 변수 바인딩을 도입하거나 더 멋진 API의 다른 것을 활용할 수 없다면, 그것은 무의미한 스위치입니다. 초보자들에게 실망감을 안겨 줄만큼 간단하게 묘사되기를 바랍니다. (교육은 금지 된 것보다 효과적입니다.)

가장 단순한 일이 될 수있는 범주에 속하지만, 여전히 실험적인 코드이기도합니다. 방금 주말에 썼습니다. 그러나 대안의 과다가 있습니다. PHP 데이터베이스 추상화를 위한 google과 조금 탐색. 그러한 작업을 위해 항상 훌륭한 라이브러리가 많이 있었고 앞으로도 많이있을 것입니다.

데이터베이스 상호 작용을 더 단순하게하고 싶다면 Paris/Idiorm 과 같은 맵퍼를 사용해 볼 가치가 있습니다. 아무도 자바 스크립트에서 더 이상 사용하지 않는 DOM을 사용하지 않는 것처럼 요즘 원시 데이터베이스 인터페이스를 보모 할 필요가 없습니다.




기술적 인 이유에 대해서 말하자면 극히 특이하고 거의 사용되지 않는 몇 가지가 있습니다. 가장 가능성이 당신의 인생에서 절대 사용하지 않습니다.
어쩌면 나는 너무 무식하지만, 나는 그것들을 사용할 기회를 결코 얻지 못했다.

  • 비 블로킹, 비동기 쿼리
  • 여러 결과 집합을 반환하는 저장 프로 시저
  • 암호화 (SSL)
  • 압축

필요한 경우 mysql 확장에서 더 세련되고 현대적인 것으로 이동하려는 기술적 인 이유는 의심의 여지가 없습니다.

그럼에도 불구하고 기술적이지 않은 몇 가지 문제가있어 경험을 조금 더 어렵게 만들 수 있습니다.

  • 현대 PHP 버전에서 이러한 함수를 더 사용하면 더 이상 사용되지 않는 수준의 알림이 표시됩니다. 그들은 단순히 끌 수 있습니다.
  • 먼 미래에는 기본 PHP 빌드에서 제거 될 수 있습니다. mydsql ext가 PECL로 옮겨지고 모든 hoster는 수십 년 동안 사이트를 운영하는 고객을 잃고 싶지 않기 때문에 PHP를 컴파일하면 행복합니다.
  • 커뮤니티의 강력한 저항. 이 정직한 기능을 언급 할 때마다 엄격한 금기 사항에 처해 있다고 들었습니다.
  • 평균적인 PHP 사용자이기 때문에 이러한 함수를 사용하려는 생각은 오류가 발생하기 쉽고 잘못되었습니다. 단지 당신에게 잘못된 길을 가르치는 모든 수많은 자습서와 매뉴얼 때문입니다. 함수 자체가 아니라 강조해야합니다. 그러나 사용되는 방식입니다.

이 후자의 문제는 문제입니다.
그러나 제 의견으로는 제안 된 해결책이 더 좋지 않습니다.
그것은 나에게 보인다 너무 이상주의 모든 PHP 사용자가 제대로 한 번에 SQL 쿼리를 처리하는 방법을 배울 것입니다 꿈을. 대부분 mysql_ *을 mysqli_ *로 기계적으로 변경 하여 접근 방식을 동일하게 유지 합니다. 특히 mysqli가 prepared statement 사용법을 놀라 울 정도로 고통스럽고 귀찮게 만들기 때문에.
언급하지 않기 위하여 기본 준비가 문을 보호하기에 충분하지 않은 SQL 주입에서, 어느 쪽도 아니 mysqli 나 PDO는 솔루션을 제공합니다.

그래서,이 정직한 확장자와 싸우는 대신, 나는 그릇된 습관과 싸우고 올바른 방법으로 사람들을 교육하는 것을 선호합니다.

또한 일부 거짓 또는 중요하지 않은 이유로

  • 저장 프로 시저를 지원하지 않습니다 (우리는 mysql_query("CALL my_proc");여러 연령대 에서 사용 하고 있었습니다 )
  • 트랜잭션을 지원하지 않습니다 (위와 동일).
  • 다중 선언문을 지원하지 않습니다 (누가 필요합니까?).
  • 활발한 개발 단계에 있지 않습니다 (그래서 실제적인면에서 당신 에게 어떤 영향 미칩니 까?).
  • 객체 지향 인터페이스 (OO Interface)가 부족하다. (몇 시간 만에 생성된다.)
  • Prepared Statements 또는 Parametrized Queries를 지원하지 않습니다.

마지막 하나는 흥미로운 점입니다. mysql ext는 네이티브 prepared statement를 지원하지 않지만 , 안전을 위해 요구되지는 않는다. 수동으로 처리 된 자리 표시자를 사용하여 준비된 구문을 쉽게 위장 할 수 있습니다 (PDO와 동일).

function paraQuery()
{
    $args  = func_get_args();
    $query = array_shift($args);
    $query = str_replace("%s","'%s'",$query); 

    foreach ($args as $key => $val)
    {
        $args[$key] = mysql_real_escape_string($val);
    }

    $query  = vsprintf($query, $args);
    $result = mysql_query($query);
    if (!$result)
    {
        throw new Exception(mysql_error()." [$query]");
    }
    return $result;
}

$query  = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);

짜잔는 , 모든 매개 변수와 안전합니다.

그러나 설명서의 빨간색 상자가 마음에 들지 않으면 mysqli 또는 PDO 중에서 선택 문제가 발생합니까?

글쎄, 대답은 다음과 같습니다 :

  • 데이터베이스 추상화 레이어 를 사용하고 API를 만들 필요성을 이해한다면 , mysqli 는 많은 mysql 특정 기능을 실제로 지원하므로 매우 좋은 선택이다.
  • 대다수의 PHP 사용자와 마찬가지로 애플리케이션 코드에서 원시 API 호출을 사용하는 경우 (본질적으로 잘못된 관행) - PDO 만이 유일한 선택입니다 .이 확장은 API가 아니라 세미 DAL 인 것처럼 가장합니다. 여전히 불완전하지만 많은 중요한 기능을 제공합니다. 그 중 두 가지는 PDO를 mysqli와 비판적으로 구분합니다.

    • mysqli와는 달리 PDO는 자리 표시자를 값으로 바인딩 할 수 있기 때문에 동적으로 생성 된 쿼리를 여러 화면의 코드없이 구현할 수 있습니다.
    • mysqli와 달리 PDO는 쿼리 결과를 항상 일반적인 배열로 반환 할 수 있지만 mysqli는 mysqlnd 설치에서만 수행 할 수있다.

그래서 평범한 PHP 사용자이고 기본 준비된 명령문을 사용할 때 많은 두통을 피하려는 경우 PDO가 유일한 선택입니다.
그러나 PDO는은 총알이 아니며 어려움이 있습니다.
그래서 PDO 태그 위키 에있는 모든 일반적인 함정과 복잡한 사례에 대한 해결책을 작성했습니다.

그럼에도 불구하고, 확장에 대해 이야기하는 모든 사람들 은 Mysqli와 PDO에 관한 2 가지 중요한 사실을 항상 놓쳤습니다 .

  1. 준비된 진술 은은 총알이 아닙니다 . 준비된 문을 사용하여 바인딩 할 수없는 동적 식별자가 있습니다. 알 수없는 매개 변수가있는 동적 쿼리가있어 쿼리 작성이 어려운 작업이됩니다.

  2. mysqli_ * 나 PDO 함수가 애플리케이션 코드에 나타나서는 안된다. 바인딩과 루핑, 오류 처리 등의 모든 더러운 작업을 내부에서 수행하여 애플리케이션 코드를 DRY하고 깨끗하게 만드는 애플리케이션 코드와 추상화 레이어 사이에 추상화 레이어
    가 있어야합니다 . 특히 동적 쿼리 작성과 같은 복잡한 경우.

따라서 PDO 또는 mysqli로 전환하는 것으로는 충분하지 않습니다. 하나는 코드에서 원시 API 함수를 호출하는 대신 ORM, 쿼리 빌더 또는 데이터베이스 추상화 클래스를 사용해야합니다.
반대로 - 애플리케이션 코드와 mysql API 사이에 추상화 레이어가있는 경우 실제로 어떤 엔진이 사용되는지는 중요하지 않습니다. MySQL의 ext가 더 이상 사용되지 않을 때까지 사용할 수 있으며, 모든 애플리케이션 코드가 그대로 유지 된 상태에서 추상화 클래스를 다른 엔진으로 쉽게 다시 작성할 수 있습니다.

다음은 내 safemysql 클래스 를 기반으로하는 몇 가지 예제로 그러한 추상 클래스가 있어야하는 방식을 보여줍니다.

$city_ids = array(1,2,3);
$cities   = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);

이 한 줄 을 PDO에 필요한 코드의 양과 비교하십시오 .
그런 다음 원시 Mysqli 준비 문과 함께 필요한 코드의 미친 양과 비교하십시오 . 오류 처리, 프로파일 링, 쿼리 로깅이 이미 빌드되어 실행 중입니다.

$insert = array('name' => 'John', 'surname' => "O'Hara");
$db->query("INSERT INTO users SET ?u", $insert);

이러한 모든 수많은 명명 된 자리 표시 자, 바인딩 및 쿼리 정의에서 모든 단일 필드 이름이 6-10 번 반복 될 때 일반적인 PDO 삽입과 비교하십시오.

다른 예시:

$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);

PDO가 이러한 실용적인 사례를 처리 할 수있는 예는 거의 찾을 수 없습니다.
그리고 그것은 너무 어리 석고 안전하지 않을 것입니다.

다시 한번 말하자면, 초보자 용 매뉴얼이 아닌 바보 같은 예제뿐만 아니라 실제 문제를 해결하는 데에도 유용합니다.




(다른 이유로) 입력 데이터가 소독되도록하는 것이 훨씬 어렵 기 때문에. PDO 또는 mysqli에서와 같이 매개 변수화 된 쿼리를 사용하면 위험을 완전히 피할 수 있습니다.

예를 들어 누군가가 "enhzflep); drop table users"사용자 이름으로 사용할 수 있습니다 . 이전 함수는 쿼리 당 여러 개의 명령문을 실행할 수 있으므로, 그 불쾌한 놈이 전체 테이블을 삭제할 수 있습니다.

mysqli의 PDO를 사용한다면 사용자 이름이 끝날 것이다 "enhzflep); drop table users".

bobby-tables.com 참조하십시오 .




MySQL 확장은 3 가지 중 가장 오래된 것이고 개발자들이 MySQL과 통신하는 데 사용 된 원래의 방식이었습니다. 이 확장은 이제 PHP와 MySQL의 최신 릴리스에서 개선 된 점 때문에 다른 mysqli_PDO 을 선호하여 deprecated .mysqli_ PDO

  • mysqli_ 는 MySQL 데이터베이스 작업을위한 '향상된'확장 기능입니다. 새로운 버전의 MySQL 서버에서 사용할 수있는 기능을 이용하고, 기능 지향적이며 객체 지향적 인 인터페이스를 개발자에게 제공하며, 다른 멋진 기능은 제공하지 않습니다.

  • PDO 는 이전에 주요 데이터베이스 액세스 확장 (예 : MySQL, PostgreSQL, SQLite, MSSQL 등)에 분산되어 있던 대부분의 기능을 통합하는 API를 제공합니다. 인터페이스는 프로그래머가 데이터베이스 연결, 쿼리 및 결과 세트 및 하위 레벨 드라이버는 데이터베이스 서버와 통신 및 자원 처리를 수행합니다. 많은 토론과 작업이 PDO에 들어가고 있으며 현대의 전문적인 코드로 데이터베이스 작업을하는 적절한 방법으로 간주됩니다.




mysql_*mysqli 나 PDO를 사용하여 거의 모든 함수 를 정의 할 수있다 . 이전 PHP 응용 프로그램에 추가하면 PHP7에서도 작동합니다. 내 솔루션은 here .

<?php

define('MYSQL_LINK', 'dbl');
$GLOBALS[MYSQL_LINK] = null;

function mysql_link($link=null) {
    return ($link === null) ? $GLOBALS[MYSQL_LINK] : $link;
}

function mysql_connect($host, $user, $pass) {
    $GLOBALS[MYSQL_LINK] = mysqli_connect($host, $user, $pass);
    return $GLOBALS[MYSQL_LINK];
}

function mysql_pconnect($host, $user, $pass) {
    return mysql_connect($host, $user, $pass);
}

function mysql_select_db($db, $link=null) {
    $link = mysql_link($link);
    return mysqli_select_db($link, $db);
}

function mysql_close($link=null) {
    $link = mysql_link($link);
    return mysqli_close($link);
}

function mysql_error($link=null) {
    $link = mysql_link($link);
    return mysqli_error($link);
}

function mysql_errno($link=null) {
    $link = mysql_link($link);
    return mysqli_errno($link);
}

function mysql_ping($link=null) {
    $link = mysql_link($link);
    return mysqli_ping($link);
}

function mysql_stat($link=null) {
    $link = mysql_link($link);
    return mysqli_stat($link);
}

function mysql_affected_rows($link=null) {
    $link = mysql_link($link);
    return mysqli_affected_rows($link);
}

function mysql_client_encoding($link=null) {
    $link = mysql_link($link);
    return mysqli_character_set_name($link);
}

function mysql_thread_id($link=null) {
    $link = mysql_link($link);
    return mysqli_thread_id($link);
}

function mysql_escape_string($string) {
    return mysql_real_escape_string($string);
}

function mysql_real_escape_string($string, $link=null) {
    $link = mysql_link($link);
    return mysqli_real_escape_string($link, $string);
}

function mysql_query($sql, $link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, $sql);
}

function mysql_unbuffered_query($sql, $link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, $sql, MYSQLI_USE_RESULT);
}

function mysql_set_charset($charset, $link=null){
    $link = mysql_link($link);
    return mysqli_set_charset($link, $charset);
}

function mysql_get_host_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_host_info($link);
}

function mysql_get_proto_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_proto_info($link);
}
function mysql_get_server_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_server_info($link);
}

function mysql_info($link=null) {
    $link = mysql_link($link);
    return mysqli_info($link);
}

function mysql_get_client_info() {
    $link = mysql_link();
    return mysqli_get_client_info($link);
}

function mysql_create_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "CREATE DATABASE `$db`");
}

function mysql_drop_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "DROP DATABASE `$db`");
}

function mysql_list_dbs($link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, "SHOW DATABASES");
}

function mysql_list_fields($db, $table, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    $table = str_replace('`', '', mysqli_real_escape_string($link, $table));
    return mysqli_query($link, "SHOW COLUMNS FROM `$db`.`$table`");
}

function mysql_list_tables($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "SHOW TABLES FROM `$db`");
}

function mysql_db_query($db, $sql, $link=null) {
    $link = mysql_link($link);
    mysqli_select_db($link, $db);
    return mysqli_query($link, $sql);
}

function mysql_fetch_row($qlink) {
    return mysqli_fetch_row($qlink);
}

function mysql_fetch_assoc($qlink) {
    return mysqli_fetch_assoc($qlink);
}

function mysql_fetch_array($qlink, $result=MYSQLI_BOTH) {
    return mysqli_fetch_array($qlink, $result);
}

function mysql_fetch_lengths($qlink) {
    return mysqli_fetch_lengths($qlink);
}

function mysql_insert_id($qlink) {
    return mysqli_insert_id($qlink);
}

function mysql_num_rows($qlink) {
    return mysqli_num_rows($qlink);
}

function mysql_num_fields($qlink) {
    return mysqli_num_fields($qlink);
}

function mysql_data_seek($qlink, $row) {
    return mysqli_data_seek($qlink, $row);
}

function mysql_field_seek($qlink, $offset) {
    return mysqli_field_seek($qlink, $offset);
}

function mysql_fetch_object($qlink, $class="stdClass", array $params=null) {
    return ($params === null)
        ? mysqli_fetch_object($qlink, $class)
        : mysqli_fetch_object($qlink, $class, $params);
}

function mysql_db_name($qlink, $row, $field='Database') {
    mysqli_data_seek($qlink, $row);
    $db = mysqli_fetch_assoc($qlink);
    return $db[$field];
}

function mysql_fetch_field($qlink, $offset=null) {
    if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    return mysqli_fetch_field($qlink);
}

function mysql_result($qlink, $offset, $field=0) {
    if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    $row = mysqli_fetch_array($qlink);
    return (!is_array($row) || !isset($row[$field]))
        ? false
        : $row[$field];
}

function mysql_field_len($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->length : false;
}

function mysql_field_name($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field))
        return false;
    return empty($field->orgname) ? $field->name : $field->orgname;
}

function mysql_field_table($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field))
        return false;
    return empty($field->orgtable) ? $field->table : $field->orgtable;
}

function mysql_field_type($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->type : false;
}

function mysql_free_result($qlink) {
    try {
        mysqli_free_result($qlink);
    } catch (Exception $e) {
        return false;
    }
    return true;
}



mysql_ * 함수는 더 나은 함수와 코드 구조가 개발되었다는 사실을 감안할 때 ( PHP 5.5부터 ) 가치가 하락 했다. 함수가 감가 상각되었다는 사실은 성능과 보안면에서 더 이상 개선되지 않는다는 것을 의미합니다 . , 미래의 증거가 적다는 것을 의미합니다 .

더 많은 이유가 필요한 경우 :

  • mysql_ * 함수는 prepared statement를 지원하지 않는다.
  • mysql_ * 함수는 매개 변수의 바인딩을 지원하지 않습니다.
  • mysql_ * 함수는 객체 지향 프로그래밍을위한 기능이 부족합니다.
  • 그 목록은 계속된다.



Related