参考:什么是使用MySQL扩展的完美代码示例?

这是为了创建一个社区学习资源 。 我们的目标是要有好的代码示例,它们不会重复在复制/粘贴的PHP代码中经常发现的可怕错误。 我已经要求它成为社区Wiki。

并不意味着编码比赛。 这不是要找到最快或最简单的方式来进行查询,而是为新手提供一个很好的,可读的参考。

每天,使用Stack Overflow上的mysql_*系列函数都会产生大量问题,并带有非常糟糕的代码片段。 尽管通常最好将这些人引导到PDO,但有时候这是不可能的(例如继承遗留软件),也不是现实的期望(用户已经在他们的项目中使用它)。

使用mysql_*库的代码的常见问题包括:

  • 值中的SQL注入
  • SQL注入LIMIT子句和动态表名
  • 没有错误报告(“为什么这个查询不起作用?”)
  • 损坏的错误报告(即即使在代码投入生产时也会出现错误)
  • 跨站脚本(XSS)注入输出值
  • 我们来编写一个PHP代码示例,它使用mySQL_ *系列函数执行以下操作:

  • 接受两个POST值, id (数字)和name (字符串)
  • 对表tablename执行UPDATE查询,使用ID id更改行中的name
  • 失败时,请客气地退出,但仅在生产模式下显示详细的错误。 trigger_error()就足够了; 或者使用您选择的方法
  • 输出消息“ $name updated”。
  • 没有显示上面列出的任何弱点。

    它应该尽可能简单 。 它理想情况下不包含任何函数或类。 目标不是创建复制/可粘贴的库,而是要显示为确保数据库查询安全所需完成的工作。

    奖励积分以获得好评。

    目标是让这个问题成为一个资源,用户在遇到问题提问者时可以链接到一个代码错误的代码(尽管它根本不是问题的焦点),或者遇到失败的查询并且不会知道如何解决它。

    为了抢先进行PDO讨论:

    是的,最好指示将这些问题写入PDO的人员。 当它是一种选择时,我们应该这样做。 然而,这并不总是可行的 - 有时候,问题提供者正在处理遗留代码,或者已经对这个库有很长的路要走,现在不太可能改变它。 另外,如果正确使用, mysql_*系列函数是完全安全的。 所以请不要在这里使用PDO。


    我刺伤它。 试图尽可能保持简单,同时仍然保持一些现实世界的便利。

    处理unicode并使用宽松的比较来提高可读性。 对人好点 ;-)

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

    我决定跳枪,把东西放好。 这是一开始。 引发错误时发生异常。

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

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

    以及一些评论的解释:

    //1. using `dbname`. is better than using mysql_select_db()
    

    通过使用mysql_select_db,您可以创建错误,并且找到并修复错误并不容易。
    例如,在某些脚本中,将db1设置为数据库,但在某些功能中,需要将db2设置为数据库。
    调用此函数后,数据库将被切换,并且脚本中的所有后续查询都将被破坏,或者会破坏某些错误数据库中的数据(如果表和列的名称一致)。

    //2. names of tables and columns should be quoted by "`" symbol 
    

    某些列的名称也可以是SQL关键字,使用“ ` ”符号将有助于此。
    另外,插入查询的所有字符串值都应该用'符号引用。

    //always use htmlspecialchars() to sanitize user's data in output
    它会帮助你防止XSS攻击。

    链接地址: http://www.djcxy.com/p/93699.html

    上一篇: Reference: What is a perfect code sample using the MySQL extension?

    下一篇: Is this safe from SQL Injection?