복붙노트

참고 : MySQL 확장을 사용하는 완벽한 코드 샘플은 무엇입니까? [닫은]

PHP

참고 : MySQL 확장을 사용하는 완벽한 코드 샘플은 무엇입니까? [닫은]

매일 Stack Overflow에서 mysql_ * 패밀리를 사용하여 코드 스 니펫이 정말 큰 문제가 있습니다. 보통 PDO로 향하는 것이 가장 좋지만 때로는 가능하지도 않으며 (예를 들어, 계승 된 레거시 소프트웨어) 현실적인 기대 (사용자는 이미 자신의 프로젝트에서이 소프트웨어를 사용하고 있습니다).

mysql_ * 라이브러리를 사용하는 코드의 일반적인 문제점은 다음과 같습니다.

함수의 mySQL_ * 패밀리를 사용하여 다음을 수행하는 PHP 코드 샘플을 작성해 보겠습니다.

그리고 위에 열거 된 약점은 보여주지 않습니다.

가능한 한 간단해야합니다. 이상적으로는 함수 나 클래스를 포함하지 않습니다. 목표는 복사 / 붙여 넣기가 가능한 라이브러리를 만드는 것이 아니라 데이터베이스 질의를 안전하게하기 위해해야 ​​할 일을 최소한으로 보여주는 것입니다.

좋은 코멘트를위한 보너스 포인트.

목표는이 질문을 사용자가 잘못된 코드가있는 질문 작성자와 만날 때 (물론 질문의 초점이 아니더라도) 링크 할 수있는 리소스로 만들거나 실패한 쿼리와 마주하고 있지 않은 리소스를 만드는 것입니다. 그것을 고치는 법을 알아라.

PDO 토론을 선점하려면 다음을 수행하십시오.

그렇습니다. 개인을 PDO에 보내는 것이 바람직합니다. 선택 사항 일 때 그렇게해야합니다. 그러나 언제나 가능하지는 않습니다 - 때로는 묻는 질문자가 기존 코드를 작업 중이거나 이미이 라이브러리로 먼 길을왔다 갔다하고 지금은 변경하지 않을 수도 있습니다. 또한, mysql_ * 함수 군은 적절히 사용된다면 완벽하게 안전하다. 따라서 "PDO 사용"답변은 여기에 없습니다.

해결법

  1. ==============================

    1.

    내 찌르기. 현실적인 편의를 유지하면서 가능한 한 간단하게 유지하려고했습니다.

    유니 코드를 처리하고 가독성을 위해 느슨한 비교를 사용합니다. 착하게 굴 어라 ;-)

    <?php
    
    header('Content-type: text/html; charset=utf-8');
    error_reporting(E_ALL | E_STRICT);
    ini_set('display_errors', 1);
    // display_errors can be changed to 0 in production mode to
    // suppress PHP's error messages
    
    /*
    Can be used for testing
    $_POST['id'] = 1;
    $_POST['name'] = 'Markus';
    */
    
    $config = array(
        'host' => '127.0.0.1', 
        'user' => 'my_user', 
        'pass' => 'my_pass', 
        'db' => 'my_database'
    );
    
    # Connect and disable mysql error output
    $connection = @mysql_connect($config['host'], 
        $config['user'], $config['pass']);
    
    if (!$connection) {
        trigger_error('Unable to connect to database: ' 
            . mysql_error(), E_USER_ERROR);
    }
    
    if (!mysql_select_db($config['db'])) {
        trigger_error('Unable to select db: ' . mysql_error(), 
            E_USER_ERROR);
    }
    
    if (!mysql_set_charset('utf8')) {
        trigger_error('Unable to set charset for db connection: ' 
            . mysql_error(), E_USER_ERROR);
    }
    
    $result = mysql_query(
        'UPDATE tablename SET name = "' 
        . mysql_real_escape_string($_POST['name']) 
        . '" WHERE id = "' 
        . mysql_real_escape_string($_POST['id']) . '"'
    );
    
    if ($result) {
        echo htmlentities($_POST['name'], ENT_COMPAT, 'utf-8') 
            . ' updated.';
    } else {
        trigger_error('Unable to update db: ' 
            . mysql_error(), E_USER_ERROR);
    }
    
  2. ==============================

    2.

    나는 총을 뛰어 다니기로 결심했다. 그것은 무엇부터 시작해야합니다. 오류가 발생하면 예외가 발생합니다.

    function executeQuery($query, $args) {
        $cleaned = array_map('mysql_real_escape_string', $args);
    
        if($result = mysql_query(vsprintf($query, $cleaned))) {
            return $result;
        } else {
            throw new Exception('MySQL Query Error: ' . mysql_error());
        }
    }
    
    function updateTablenameName($id, $name) {
        $query = "UPDATE tablename SET name = '%s' WHERE id = %d";
    
        return executeQuery($query, array($name, $id));
    }
    
    try {
        updateTablenameName($_POST['id'], $_POST['name']);
    } catch(Exception $e) {
        echo $e->getMessage();
        exit();
    }
    
  3. ==============================

    3.

    /**
     * Rule #0: never trust users input!
     */
    
    //sanitize integer value
    $id = intval($_GET['id']);
    //sanitize string value;
    $name = mysql_real_escape_string($_POST['name']);
    //1. using `dbname`. is better than using mysql_select_db()
    //2. names of tables and columns should be quoted by "`" symbol
    //3. each variable should be sanitized (even in LIMIT clause)
    $q = mysql_query("UPDATE `dbname`.`tablename` SET `name`='".$name."' WHERE `id`='".$id."' LIMIT 0,1 ");
    if ($q===false)
    {
        trigger_error('Error in query: '.mysql_error(), E_USER_WARNING);
    }
    else
    {
        //be careful! $name contains user's data, remember Rule #0
        //always use htmlspecialchars() to sanitize user's data in output
        print htmlspecialchars($name).' updated';
    }
    
    ########################################################################
    //Example, how easily is to use set_error_handler() and trigger_error()
    //to control error reporting in production and dev-code
    //Do NOT use error_reporting(0) or error_reporting(~E_ALL) - each error
    //should be fixed, not muted
    function err_handler($errno, $errstr, $errfile, $errline)
    {
        $hanle_errors_print = E_ALL & ~E_NOTICE;
    
        //if we want to print this type of errors (other types we can just write in log-file)
        if ($errno & $hanle_errors_print)
        {
            //$errstr can contain user's data, so... Rule #0
            print PHP_EOL.'Error ['.$errno.'] in file '.$errfile.' in line '.$errline
                  .': '.htmlspecialchars($errstr).PHP_EOL;
        }
        //here you can write error into log-file
    }
    
    set_error_handler('err_handler', E_ALL & ~E_NOTICE & E_USER_NOTICE & ~E_STRICT & ~E_DEPRECATED);
    

    그리고 코멘트에 대한 설명 :

  4. ==============================

    4.

    <?  
    mysql_connect(); 
    mysql_select_db("new"); 
    $table = "test"; 
    if($_SERVER['REQUEST_METHOD']=='POST') {
      $name = mysql_real_escape_string($_POST['name']); 
      if ($id = intval($_POST['id'])) { 
        $query="UPDATE $table SET name='$name' WHERE id=$id"; 
      } else { 
        $query="INSERT INTO $table SET name='$name'"; 
      } 
      mysql_query($query) or trigger_error(mysql_error()." in ".$query); 
      header("Location: http://".$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF']);  
      exit;  
    }  
    if (!isset($_GET['id'])) {
      $LIST=array(); 
      $query="SELECT * FROM $table";  
      $res=mysql_query($query); 
      while($row=mysql_fetch_assoc($res)) $LIST[]=$row; 
      include 'list.php'; 
    } else {
      if ($id=intval($_GET['id'])) { 
        $query="SELECT * FROM $table WHERE id=$id";  
        $res=mysql_query($query); 
        $row=mysql_fetch_assoc($res); 
        foreach ($row as $k => $v) $row[$k]=htmlspecialchars($v); 
      } else { 
        $row['name']=''; 
        $row['id']=0; 
      } 
      include 'form.php'; 
    }  
    ?>
    

    form.php

    <? include 'tpl_top.php' ?>
    <form method="POST">
    <input type="text" name="name" value="<?=$row['name']?>"><br>
    <input type="hidden" name="id" value="<?=$row['id']?>">
    <input type="submit"><br>
    <a href="?">Return to the list</a>
    </form>
    <? include 'tpl_bottom.php' ?>
    

    list.php

    <? include 'tpl_top.php' ?>
    <a href="?id=0">Add item</a>
    <? foreach ($LIST as $row): ?>
    <li><a href="?id=<?=$row['id']?>"><?=$row['name']?></a>
    <? endforeach ?>
    <? include 'tpl_bottom.php' ?>
    
  5. ==============================

    5.

    내 다른 대답이 질문의 목적을 놓친 것처럼 보입니다. (이 중 하나는 일부 요구 사항을 충족시키지 못하지만 볼 수 있듯이 안전한 쿼리의 초석이되는 자리 표시자를 처리하는 기능을 구현하지 않으면 안전 솔루션을 얻을 수 없습니다.

    그래서, mysql 질의를 안전하면서도 편리하게 만드는 간결한 해결책을 게시하려는 또 다른 시도가 있습니다.

    오랜 시간 전에 작성한 함수로 기업 표준 OOP 기반 솔루션으로 옮길 때까지 잘 서비스했습니다. 보안 및 사용 편의성이라는 두 가지 목표를 추구했습니다.

    첫 번째는 자리 표시자를 구현하여 얻을 수 있습니다. 두 번째는 자리 표시 자 및 다른 결과 유형을 구현하여 얻을 수 있습니다.

    기능은 반드시 이상적인 것이 아닙니다. 몇 가지 단점은 다음과 같습니다.

    그러나 그것은 안전하고 간결하며, 전체 라이브러리를 설치할 필요가 없습니다.

    function dbget() {
      /*
      usage: dbget($mode, $query, $param1, $param2,...);
      $mode - "dimension" of result:
      0 - resource
      1 - scalar
      2 - row
      3 - array of rows
      */
      $args = func_get_args();
      if (count($args) < 2) {
        trigger_error("dbget: too few arguments");
        return false;
      }
      $mode  = array_shift($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);
      if (!$query) return false;
    
      $res = mysql_query($query);
      if (!$res) {
        trigger_error("dbget: ".mysql_error()." in ".$query);
        return false;
      }
    
      if ($mode === 0) return $res;
    
      if ($mode === 1) {
        if ($row = mysql_fetch_row($res)) return $row[0];
        else return NULL;
      }
    
      $a = array();
      if ($mode === 2) {
        if ($row = mysql_fetch_assoc($res)) return $row;
      }
      if ($mode === 3) {
        while($row = mysql_fetch_assoc($res)) $a[]=$row;
      }
      return $a;
    }
    ?>
    

    사용 예제

    $name = dbget(1,"SELECT name FROM users WHERE id=%d",$_GET['id']);
    $news = dbget(3,"SELECT * FROM news WHERE title LIKE %s LIMIT %d,%d",
                  "%$_GET[search]%",$start,$per_page);
    

    Stackoverflow에 게시 된 모든 코드와 주요 차이점 인 위의 예제에서 볼 수 있듯이 안전 및 데이터 검색 루틴은 모두 기능 코드에 캡슐화되어 있습니다. 따라서 수동 바인딩, 이스케이프 / 인용 또는 캐스팅은 물론 수동 데이터 검색도 없습니다.

    다른 도우미 기능과 결합

    function dbSet($fields,$source=array()) {
      $set = '';
      if (!$source) $source = &$_POST;
      foreach ($fields as $field) {
        if (isset($source[$field])) {
          $set.="`$field`='".mysql_real_escape_string($source[$field])."', ";
        }
      }
      return substr($set, 0, -2); 
    }
    

    이런 식으로 사용했습니다.

    $fields = explode(" ","name surname lastname address zip phone regdate");
    $_POST['regdate'] = $_POST['y']."-".$_POST['m']."-".$_POST['d'];
    $sql = "UPDATE $table SET ".dbSet($fields).", stamp=NOW() WHERE id=%d";
    $res = dbget(0,$sql, $_POST['id']);
    if (!$res) {
      _503;//calling generic 503 error function
    }
    

    OP의 예제를 포함하여 거의 모든 필요를 충족시킬 수 있습니다.

  6. from https://stackoverflow.com/questions/6198104/reference-what-is-a-perfect-code-sample-using-the-mysql-extension by cc-by-sa and MIT lisence