[php] PHP에서 mysql_ * 함수를 사용하지 않아야하는 이유는 무엇입니까?

mysql_*함수를 사용하지 않아야하는 기술적 이유는 무엇입니까 ? (예를 들어 mysql_query(), mysql_connect()또는 mysql_real_escape_string())?

사이트에서 작업하는 경우에도 다른 것을 사용해야하는 이유는 무엇입니까?

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

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



답변

MySQL 확장 :

  • 활발한 개발 중이 아님
  • 되어 공식적으로 사용되지 않는 PHP 5.5로 (6 월 2013 년 발표).
  • PHP 7.0 부터 완전히 제거 되었습니다 (2015 년 12 월 출시)
    • 이는 2018 년 12 월 31 일 현재 지원되는 PHP 버전에 존재하지 않음을 의미합니다. 지원하는 PHP 버전을 사용하는 경우 보안 문제가 해결되지 않은 버전을 사용하고 있습니다.
  • OO 인터페이스 부족
  • 지원하지 않습니다 :
    • 비 차단 비동기식 쿼리
    • 준비된 명령문 또는 매개 변수화 된 쿼리
    • 저장 프로 시저
    • 여러 문장
    • 업무
    • “새로운”패스워드 인증 방법 (MySQL 5.6에서는 기본적으로; 5.7에서는 필수)
    • MySQL 5.1 이상의 새로운 기능

더 이상 사용되지 않으므로 사용하면 코드의 미래 증거가 줄어 듭니다.

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

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


답변

PHP는 MySQL에 연결하기 위해 세 가지 다른 API를 제공합니다. 이것들은 mysql(PHP 7에서 제거) mysqli, 그리고 PDO확장입니다.

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

그리고 나중에 PHP 개발자 팀 생성하기로 결정 취한 E_DEPRECATED통해 여부를 사용자가 MySQL을 연결할 때 오류 mysql_connect(), mysql_pconnect()또는에 내장 된 암시 적 연결 기능을 ext/mysql.

ext/mysql공식적으로 PHP 5.5으로 사용되지 하고있다 PHP 7로 제거 .

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

mysql_*기능 매뉴얼 페이지 로 이동하면 더 이상 사용해서는 안되는 빨간색 상자가 표시됩니다.


멀어지면 ext/mysql보안뿐만 아니라 MySQL 데이터베이스의 모든 기능에 액세스해야합니다.

ext/mysqlMySQL 3.23을 위해 만들어졌으며 그 이후로 거의 추가되지 않았지만 대부분이 이전 버전과의 호환성을 유지하여 코드를 유지하기가 조금 더 어려워졌습니다. 지원되지 않는 기능 ext/mysql은 다음 을 포함합니다 : ( PHP manual에서 ).

mysql_*기능을 사용하지 않는 이유 :

  • 적극적 개발 중이 아님
  • PHP 7에서 제거
  • OO 인터페이스 부족
  • 비 차단 비동기 쿼리를 지원하지 않습니다
  • 준비된 명령문 또는 매개 변수화 된 쿼리를 지원하지 않습니다
  • 저장 프로 시저를 지원하지 않습니다
  • 여러 문장을 지원하지 않습니다
  • 거래를 지원하지 않습니다
  • MySQL 5.1의 모든 기능을 지원하지는 않습니다

Quentin의 답변에서 인용 한 위의 내용

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

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


지원 중단 경고 억제

코드로 변환되는 동안 MySQLi/ PDO, E_DEPRECATED설정하여 오류를 억제 할 수 error_reporting에서 php.ini 파일 제외E_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

이것은 또한 다른 지원 중단 경고를 숨길 것 입니다. 그러나 이것은 MySQL 이외의 것들에 대한 것일 수도 있습니다. ( PHP 매뉴얼에서 )

PDO vs. MySQLi 기사 : 어느 것을 사용해야합니까? 에 의해 Dejan Marjanovic 당신이 선택하는 데 도움이 될 것입니다.

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


간단하고 짧은 PDO 튜토리얼


Q. 내 마음의 첫 번째 질문은 ‘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);

With PDO: 새 PDO객체를 생성하기 만하면됩니다. 생성자는 데이터베이스 소스 생성자를 지정하기위한 매개 변수를 승인합니다. (데이터 소스 이름) 및 선택적으로 , PDO네 개의 매개 변수를 사용 합니다.DSNusernamepassword

여기서 나는 당신이 제외한 모든 것을 잘 알고 있다고 생각합니다 DSN. 이것은의 새로운 기능입니다 PDO. A DSN는 기본적으로 PDO사용할 드라이버와 연결 세부 정보 를 알려주는 일련의 옵션입니다 . 자세한 내용은 PDO MySQL DSN을 확인하십시오 .

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

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

연결 오류가 있으면 더 PDOException처리하기 위해 잡을 수 있는 객체가 발생합니다 Exception.

읽기 : 연결 및 연결 관리

여러 드라이버 옵션을 배열로 네 번째 매개 변수에 전달할 수도 있습니다. 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 생성 후 속성을 설정할 수 있습니까?

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

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

오류 처리


오류 처리에서 훨씬 더 쉽게 PDO보다 mysql_*.

사용할 때 일반적인 관행 mysql_*은 다음과 같습니다.

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

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

PDO더 나은 솔루션을 제공합니다 : 예외. 우리가 함께 할 아무것도 PDOA의 포장되어야한다 trycatch블록. PDOerror mode 속성을 설정하여 세 가지 오류 모드 중 하나로 강제 설정할 수 있습니다 . 다음은 세 가지 오류 처리 모드입니다.

  • PDO::ERRMODE_SILENT. 그것은 단지 오류 코드를 설정하고 mysql_*각 결과를 확인한 다음 $db->errorInfo();오류 세부 정보를 얻는 위치 와 거의 동일하게 작동 합니다 .
  • PDO::ERRMODE_WARNING올립니다 E_WARNING. 런타임 경고 (치명적이지 않은 오류). 스크립트 실행이 중지되지 않습니다.
  • PDO::ERRMODE_EXCEPTION: 예외를 던집니다. PDO에 의해 발생한 오류를 나타냅니다. 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());
}

당신은 취급 할 필요가 없습니다 trycatch지금. 언제든지 적절한 시간에 잡을 수 있지만 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.
}

또한 처리 할 수 or die()있거나처럼 말할 수 mysql_*있지만 실제로는 다양합니다. display_errors off오류 로그를 돌려서 읽으면 프로덕션에서 위험한 오류 메시지를 숨길 수 있습니다 .

자, 위의 모든 것들을 읽고 나면, 당신은 아마 생각 : 도대체 무엇인지 그 난 그냥 간단하게 기대어 시작하려고 할 때 SELECT, INSERT, UPDATE, 또는 DELETE문을? 걱정하지 마십시오.


데이터 선택

PDO 이미지 선택

그래서 당신이하고있는 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 Data에서는 ->fetch()명령문 핸들의 메소드 인를 통해 얻습니다 . 가져 오기를 호출하기 전에 가장 좋은 방법은 PDO에게 데이터를 가져 오는 방법을 알려주는 것입니다. 아래 섹션에서 이것을 설명합니다.

페치 모드

PDO::FETCH_ASSOCfetch()fetchAll()코드 에서 의 사용에 유의하십시오 . 이것은 PDO필드 이름을 키로 사용하여 행을 연관 배열로 리턴하도록 지시 합니다. 다른 페치 모드도 많이 있는데, 하나 하나 설명하겠습니다.

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

 $stmt->fetch(PDO::FETCH_ASSOC)

위에서는을 사용하고 fetch()있습니다. 다음을 사용할 수도 있습니다.

이제 가져 오기 모드가되었습니다.

  • PDO::FETCH_ASSOC: 결과 세트에 리턴 된대로 열 이름으로 색인화 된 배열을 리턴합니다.
  • PDO::FETCH_BOTH (기본값) : 결과 집합에 반환 된 열 이름과 0 색인 열 번호로 색인 된 배열을 반환합니다.

더 많은 선택이 있습니다! PDOStatementFetch 문서 에서 이에 대해 모두 읽으십시오 . .

행 개수 얻기 :

mysql_num_rows반환 된 행 수를 얻는 데 사용 하는 대신 a PDOStatement와 do를 얻을 수 있습니다 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();

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

PDO 이미지 삽입 및 업데이트

우리가 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 point에서 인용 ).

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

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

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

  3. Execute : 나중에 어플리케이션은 매개 변수에 값을 제공 (또는 바인드)하고 DBMS는 명령문을 실행합니다 (결과를 리턴 할 수 있음). 응용 프로그램은 다른 값으로 원하는만큼 명령문을 실행할 수 있습니다. 이 예에서는 첫 번째 매개 변수와 1.00두 번째 매개 변수에 ‘Bread’를 제공 할 수 있습니다 .

SQL에 자리 표시자를 포함시켜 준비된 명령문을 사용할 수 있습니다. 기본적으로 자리 표시자가없는 세 가지가 있습니다 (위의 변수를 사용하여 위의 변수로 시도하지 마십시오).

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

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

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

실행 배열을 사용하여 바인딩 할 수도 있습니다.

<?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);)에 변수를 할당합니다 . 그런 다음 해당 자리 표시 자에 값을 할당하고 문을 실행합니다. 두 번째 예에서, 첫 번째 배열 요소는 첫 번째로 가고 ?두 번째는 두 번째로 ?갑니다.

참고 : 명명되지 않은 자리 표시 자 에서는 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 /*"));


답변

먼저 모든 사람에게 제공되는 표준 의견으로 시작하겠습니다.

mysql_*새 코드 에서는 함수를 사용하지 마십시오 . 더 이상 유지 관리되지 않으며 공식적으로 더 이상 사용되지 않습니다 . 참고 항목 빨간색 상자 ? 에 대해 알아 준비된 문 대신 사용할 PDO 또는 MySQLi를 이 기사는 당신이 어떤을 결정하는 데 도움이됩니다. PDO를 선택하면 다음 은 좋은 튜토리얼 입니다.

이것을 통해 문장별로 문장을 살펴보고 설명해 드리겠습니다.

  • 더 이상 유지 관리되지 않으며 공식적으로 더 이상 사용되지 않습니다.

    이는 PHP 커뮤니티가 이러한 매우 오래된 기능에 대한 지원을 점차적으로 중단하고 있음을 의미합니다. 향후 버전의 PHP에는 존재하지 않을 것입니다! 이 함수를 계속 사용하면 코드가 먼 미래에 깨질 수 있습니다.

    새로운! -ext / mysql은 이제 PHP 5.5부터 공식적으로 사용되지 않습니다!

    최신! ext / mysql 은 PHP 7에서 제거되었습니다 .

  • 대신 준비된 진술에 대해 배워야합니다

    mysql_*확장은 준비된 명령문을 지원하지 않으며 , 이는 SQL 주입 에 대한 매우 효과적인 대책 입니다. 공격자가 스크립트에 액세스하고 데이터베이스에서 가능한 쿼리 를 수행 할 수 있도록하는 MySQL 종속 응용 프로그램의 매우 심각한 취약점을 수정했습니다 .

    자세한 내용 은 PHP에서 SQL 삽입을 방지하는 방법을 참조하십시오 .

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

    mysql기능 매뉴얼 페이지 로 이동하면 더 이상 사용해서는 안되는 빨간색 상자가 나타납니다.

  • PDO 또는 MySQLi 사용

    데이터베이스 상호 작용에 대한 완벽한 OOP 접근 방식을 제공하는 PDO-PHP 데이터베이스 객체 (PDO-PHP Database Object )와 MySQL 별 개선 기능인 MySQLi가 더 강력하고 잘 구축 된 대안 이 있습니다.


답변

사용의 용이성

분석 및 합성 이유는 이미 언급되었습니다. 신규 이민자에게는 날짜가 지정된 mysql_ 함수 사용을 중단하는 것이 더 큰 동기가됩니다.

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

주로 코드를 단순화 할 수 있는 바운드 매개 변수 입니다. 그리고 훌륭한 튜토리얼을 통해 (위에서 볼 수 있듯이) PDO 로의 전환 이 지나치게 힘들지는 않습니다.

그러나 더 큰 코드 기반을 한 번에 다시 작성하려면 시간이 걸립니다. 이 중간 대안을위한 Raison d’ être :

mysql_ * 대신 동등한 pdo_ * 함수

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

  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()
    • mysql_fetch_array() 된다 pdo_fetch_array()
    • mysql_fetch_assoc() 된다 pdo_fetch_assoc()
    • mysql_real_escape_string() 된다 pdo_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_하고 탈출 하고 싶을 지에 대한 더 많은 재 작성 조언과 기술적 이유로 돌아가 봅시다 .

구식 sanitize()기능 수정 또는 제거

바운드 매개 변수 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자사의 응용 프로그램으로 직접 intersparsing SQL을보다 선행해야한다.

  • 그러나 첫 번째 단계로 전화를 제거_real_escape_string 하십시오.

  • sanitize()데이터베이스 및 애플리케이션 플로우에 HTML 컨텍스트 안전 문자열이 필요한 경우 나머지 기능 을 유지해야 할 수도 있습니다 . HTML 이스케이프 처리에만 적용된다는 의견을 추가하십시오.

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

  • stripslashes()살균 기능 에 대해 언급 한 내용이 있으면 더 높은 수준의 감독을 의미 할 수 있습니다.

    • 그것은 일반적으로 더 이상 사용되지 않는 피해 (두 배 탈출)를 취소하기 위해있었습니다 magic_quotes. 그러나 문자열별로가 아니라 중앙 에서 가장 잘 고정 됩니다.

    • 유저 랜드 반전 접근법 중 하나를 사용하십시오 . 그런 다음 기능 stripslashes()에서를 제거하십시오 sanitize.

    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_*래퍼 함수는 코딩 친화적 인 스톱 갭 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_query()또한 그것에 대한 프론트 엔드입니다.

매개 변수 바인딩을 도입하거나 더 좋은 API의 다른 것을 활용할 수 없다면, 그것은 무의미한 스위치입니다. 나는 새로운 이민자들에게 낙담하지 않을 정도로 간단하게 묘사되기를 바랍니다. (교육은 일반적으로 금지보다 효과적입니다.)

그것은 가장 간단한 것-아마도 일할 수있는 범주에 적합하지만 여전히 매우 실험적인 코드입니다. 나는 방금 주말에 그것을 썼다. 그러나 수많은 대안이 있습니다. PHP 데이터베이스 추상화를 위해 Google을 검색하고 조금 탐색하십시오. 그러한 작업을위한 훌륭한 라이브러리는 항상 존재하고있을 것입니다.

데이터베이스 상호 작용을 더 단순화하려면 Paris / Idiorm 과 같은 매퍼를 사용해보십시오. 더 이상 JavaScript에서 블랜드 DOM을 사용하지 않는 것처럼 요즘에는 원시 데이터베이스 인터페이스를 보모 할 필요가 없습니다.


답변

mysql_기능 :

  1. 오래되었습니다-더 이상 유지되지 않습니다
  2. 다른 데이터베이스 백엔드로 쉽게 이동할 수 없습니다
  3. 준비된 진술을 지원하지 않으므로
  4. 프로그래머가 연결을 사용하여 쿼리를 작성하도록 장려하여 SQL 주입 취약점으로 연결

답변

기술적 인 이유에 관해서는 극도로 구체적이며 거의 사용되지 않는 몇 가지가 있습니다. 아마 당신은 결코 당신의 인생에서 그들을 사용하지 않을 것입니다.
어쩌면 나는 너무 무지하지만, 나는 그들과 같은 것들을 사용할 기회가 없었습니다.

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

필요한 경우-이것이 mysql 확장에서 더 세련되고 현대적인 것으로 이동해야 할 기술적 이유입니다.

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

  • 최신 PHP 버전에서 이러한 기능을 추가로 사용하면 더 이상 사용되지 않는 레벨의 알림이 발생합니다. 그들은 단순히 끌 수 있습니다.
  • 먼 미래에는 기본 PHP 빌드에서 제거 될 수 있습니다. mydsql ext가 PECL로 이동하고 모든 호스팅 업체는 수십 년 동안 사이트를 운영하는 클라이언트를 잃고 싶지 않기 때문에 PHP를 컴파일하는 것을 기뻐할 것이므로 큰 문제는 아닙니다.
  • Stackoverflow 커뮤니티의 강력한 저항. 당신이 정직한 기능을 언급 할 때마다, 당신은 그것들이 엄격한 금기 상태에 있다고 들었습니다.
  • 일반적인 PHP 사용자 인 경우,이 함수를 사용하려는 아이디어는 오류가 발생하기 쉽습니다. 당신에게 잘못된 길을 가르치는 수많은 튜토리얼과 매뉴얼 때문에 말입니다. 기능 자체가 아니라 강조해야합니다. 그러나 사용 방식.

후자의 문제는 문제입니다.
그러나 내 의견으로는 제안 된 솔루션도 더 나쁘지 않습니다. 모든 PHP 사용자가 SQL 쿼리를 한 번에 올바르게 처리하는 방법을 배우는
것이 너무 이상적 입니다. 대부분의 경우 접근 방식은 동일하게 남겨두고 기계적으로 mysql_ *를 mysqli_ *로 변경했을 것입니다 . 특히 mysqli는 준비된 명령문 사용법을 엄청나게 고통스럽고 번거롭기 때문에.
말할 것도없이 네이티브 준비된 명령문으로 SQL 인젝션 을 보호하기에 충분 하지 않으며 mysqli 나 PDO도 솔루션을 제공하지 않습니다.

따라서이 정직한 확장과 싸우는 대신 잘못된 관행에 맞서 싸우고 올바른 방법으로 사람들을 교육하는 것을 선호합니다.

또한 다음과 같은 허위 또는 중요하지 않은 이유가 있습니다.

  • 저장 프로 시저를 지원하지 않습니다 ( mysql_query("CALL my_proc");연령 으로 사용 중임)
  • 거래를 지원하지 않습니다 (위와 동일)
  • 여러 문장을 지원하지 않습니다 (누가 필요합니까?)
  • 적극적 개발 중이 아님 (실제로 어떤 영향 미칩니 까?)
  • OO 인터페이스가 부족합니다 (하나를 만드는 데 몇 시간이 걸립니다)
  • 준비된 문장 또는 매개 변수화 된 쿼리를 지원하지 않습니다

마지막은 흥미로운 점입니다. mysql ext는 기본 준비된 명령문을 지원하지 않지만 안전을 위해 필요하지는 않습니다. 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);

voila , 모든 것이 매개 변수화되고 안전합니다.

그러나 매뉴얼의 빨간색 상자가 마음에 들지 않으면 mysqli 또는 PDO?

대답은 다음과 같습니다.

  • 데이터베이스 추상화 계층 을 사용하고 하나를 생성하기위한 API를 찾아야 하는 필요성을 이해한다면 , mysqli 는 실제로 많은 mysql 관련 기능을 지원하기 때문에 매우 좋은 선택입니다.
  • 대다수의 PHP 사용자와 마찬가지로 응용 프로그램 코드에서 원시 API 호출을 사용하는 경우 (실제로 잘못된 관행 임)- 이 확장은 API뿐만 아니라 semi-DAL 인 것처럼 가장하기 때문에 PDO가 유일한 선택입니다 . 여전히 불완전하지만 많은 중요한 기능을 제공합니다. 그중 두 가지는 PDO를 mysqli와 크게 구별합니다.

    • mysqli와 달리 PDO는 값으로 자리 표시자를 바인딩 할 수 있으므로 매우 복잡한 코드의 여러 화면없이 동적으로 작성된 쿼리를 실행할 수 있습니다.
    • mysqli와 달리 PDO는 항상 간단한 일반 배열로 쿼리 결과를 반환 할 수 있지만 mysqli는 mysqlnd 설치에서만 수행 할 수 있습니다.

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

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

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

  2. mysqli_ * 또는 PDO 함수는 애플리케이션 코드에 나타나지 않아야합니다. 응용 프로그램 코드와 응용 프로그램 코드 사이
    에는 추상화 계층 이 있어야하며 , 바인딩, 루핑, 오류 처리 등의 모든 더러운 작업을 수행하여 응용 프로그램 코드를 건조하고 깨끗하게 만듭니다. 특히 동적 쿼리 작성과 같은 복잡한 경우에 적합합니다.

따라서 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의 예는 거의 없습니다.
그리고 너무 장황하고 안전하지 않을 것입니다.

따라서 한 번 더-그것은 원시 드라이버 일뿐 만 아니라 추상화 클래스입니다. 초보자 매뉴얼의 어리석은 예제뿐만 아니라 실제 문제를 해결하는 데 유용합니다.


답변

여러 가지 이유가 있지만 아마도 가장 중요한 이유는 이러한 함수가 준비된 명령문을 지원하지 않기 때문에 안전하지 않은 프로그래밍 방식을 장려한다는 것입니다. 준비된 명령문은 SQL 삽입 공격을 방지하는 데 도움이됩니다.

mysql_*기능을 사용할 때는 다음을 통해 사용자 제공 매개 변수를 실행해야합니다.mysql_real_escape_string() . 한곳에서 잊어 버렸거나 입력의 일부만 탈출하면 데이터베이스가 공격받을 수 있습니다.

PDO또는 준비된 문을 사용하면 mysqli이러한 종류의 프로그래밍 오류를 만들기가 더 어려워집니다.