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



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 객체를 발생 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. Execute : 나중에 응용 프로그램이 매개 변수 값을 제공 (또는 바인드)하고 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 /*"));
Question

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

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

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

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




It's possible to define almost all mysql_* functions using mysqli or PDO. Just include them on top of your old PHP application, and it will work on PHP7. My solution 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_* functions were depreciated (as of php 5.5 ) given the fact that better functions and code structures were developed. The fact that the function was depreciated means that no more effort will be placed into improving it in terms of performance and security, which means it is less future proof .

If you need more reasons:

  • mysql_* functions do not support prepared statements.
  • mysql_ * 함수는 매개 변수의 바인딩을 지원하지 않습니다.
  • mysql_ * functions lack functionality for Object Oriented Programming.
  • 그 목록은 계속된다.



The MySQL extension is the oldest of the three and was the original way that developers used to communicate with MySQL. This extension is now being deprecated in favor of the other mysqli_ PDO because of improvements made in newer releases of both PHP and MySQL.

  • mysqli_ is the 'improved' extension for working with MySQL databases. It takes advantage of features that are available in newer versions of the MySQL server, exposes both a function-oriented and an object-oriented interface to the developer and a does few other nifty things.

  • PDO offers an API that consolidates most of the functionality that was previously spread across the major database access extensions, ie MySQL, PostgreSQL, SQLite, MSSQL, etc. The interface exposes high-level objects for the programmer to work with database connections, queries and result sets, and low-level drivers perform communication and resource handling with the database server. A lot of discussion and work is going into PDO and it's considered the appropriate method of working with databases in modern, professional code.




Because (amongst other reasons) it's much harder to ensure the input data is sanitized. If you use parametrized queries, as one does with PDO or mysqli you can entirely avoid the risk.

As an example, someone could use "enhzflep); drop table users" as a username. The old functions will allow executing multiple statements per query, so something like that nasty bugger can delete a whole table.

If one were to use PDO of mysqli, the user-name would end-up being "enhzflep); drop table users" .

See bobby-tables.com .




Speaking of technical reasons, there are only a few, extremely specific and rarely used. Most likely you will never ever use them in your life.
Maybe I am too ignorant, but I never had an opportunity to use them things like

  • non-blocking, asynchronous queries
  • stored procedures returning multiple resultsets
  • Encryption (SSL)
  • Compression

If you need them - these are no doubt technical reasons to move away from mysql extension toward something more stylish and modern-looking.

Nevertheless, there are also some non-technical issues, which can make your experience a bit harder

  • further use of these functions with modern PHP versions will raise deprecated-level notices. They simply can be turned off.
  • in a distant future, they can be possibly removed from the default PHP build. Not a big deal too, as mydsql ext will be moved into PECL and every hoster will be happy to compile PHP with it, as they don't want to lose clients whose sites were working for decades.
  • strong resistance from community. Еverytime you mention these honest functions, you being told that they are under strict taboo.
  • being an average PHP user, most likely your idea of using these functions is error-prone and wrong. Just because of all these numerous tutorials and manuals which teach you the wrong way. Not the functions themselves - I have to emphasize it - but the way they are used.

This latter issue is a problem.
But, in my opinion, the proposed solution is no better either.
It seems to me too idealistic a dream that all those PHP users will learn how to handle SQL queries properly at once. Most likely they would just change mysql_* to mysqli_* mechanically, leaving the approach the same . Especially because mysqli makes prepared statements usage incredible painful and troublesome.
Not to mention that native prepared statements aren't enough to protect from SQL injections, and neither mysqli nor PDO offers a solution.

So, instead of fighting this honest extension, I'd prefer to fight wrong practices and educate people in the right ways.

Also, there are some false or non-significant reasons, like

  • Doesn't support Stored Procedures (we were using mysql_query("CALL my_proc"); for ages)
  • Doesn't support Transactions (same as above)
  • Doesn't support Multiple Statements (who need them?)
  • Not under active development (so what? does it affect you in any practical way?)
  • Lacks an OO interface (to create one is a matter of several hours)
  • Doesn't support Prepared Statements or Parametrized Queries

The last one is an interesting point. Although mysql ext do not support native prepared statements, they aren't required for the safety. We can easily fake prepared statements using manually handled placeholders (just like PDO does):

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);

voila , everything is parameterized and safe.

But okay, if you don't like the red box in the manual, a problem of choice arises: mysqli or PDO?

Well, the answer would be as follows:

  • If you understand the necessity of using a database abstraction layer and looking for an API to create one, mysqli is a very good choice, as it indeed supports many mysql-specific features.
  • If, like vast majority of PHP folks, you are using raw API calls right in the application code (which is essentially wrong practice) - PDO is the only choice , as this extension pretends to be not just API but rather a semi-DAL, still incomplete but offers many important features, with two of them makes PDO critically distinguished from mysqli:

    • unlike mysqli, PDO can bind placeholders by value , which makes dynamically built queries feasible without several screens of quite messy code.
    • unlike mysqli, PDO can always return query result in a simple usual array, while mysqli can do it only on mysqlnd installations.

So, if you are an average PHP user and want to save yourself a ton of headaches when using native prepared statements, PDO - again - is the only choice.
However, PDO is not a silver bullet too and has its hardships.
So, I wrote solutions for all the common pitfalls and complex cases in the PDO tag wiki

Nevertheless, everyone talking about extensions always missing the 2 important facts about Mysqli and PDO:

  1. Prepared statement isn't a silver bullet . There are dynamical identifiers which cannot be bound using prepared statements. There are dynamical queries with an unknown number of parameters which makes query building a difficult task.

  2. Neither mysqli_* nor PDO functions should have appeared in the application code.
    There ought to be an abstraction layer between them and application code, which will do all the dirty job of binding, looping, error handling, etc. inside, making application code DRY and clean. Especially for the complex cases like dynamical query building.

So, just switching to PDO or mysqli is not enough. One has to use an ORM, or a query builder, or whatever database abstraction class instead of calling raw API functions in their code.
And contrary - if you have an abstraction layer between your application code and mysql API - it doesn't actually matter which engine is used. You can use mysql ext until it goes deprecated and then easily rewrite your abstraction class to another engine, having all the application code intact.

Here are some examples based on my safemysql class to show how such an abstraction class ought to be:

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

Compare this one single line with amount of code you will need with PDO .
Then compare with crazy amount of code you will need with raw Mysqli prepared statements. Note that error handling, profiling, query logging already built in and running.

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

Compare it with usual PDO inserts, when every single field name being repeated six to ten times - in all these numerous named placeholders, bindings, and query definitions.

Another example:

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

You can hardly find an example for PDO to handle such practical case.
And it will be too wordy and most likely unsafe.

So, once more - it is not just raw driver should be your concern but abstraction class, useful not only for silly examples from beginner's manual but to solve whatever real-life problems.




사용의 용이성

분석 및 합성 이유는 이미 언급되었습니다. 초보자들에게는 데이트 된 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 , 그런 다음 trim , 이후 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을 사용하지 않는 것처럼 요즘 원시 데이터베이스 인터페이스를 돌보지 않아도됩니다.




Related