Mysql
 sql >> Datenbank >  >> RDS >> Mysql

PDO MySQL:PDO::ATTR_EMULATE_PREPARES verwenden oder nicht?

Um Ihre Bedenken zu beantworten:

  1. MySQL>=5.1.17 (oder>=5.1.21 für PREPARE und EXECUTE Anweisungen) können vorbereitete Anweisungen im Abfrage-Cache verwenden . Ihre Version von MySQL+PHP kann also vorbereitete Anweisungen mit dem Abfrage-Cache verwenden. Beachten Sie jedoch sorgfältig die Vorbehalte zum Zwischenspeichern von Abfrageergebnissen in der MySQL-Dokumentation. Es gibt viele Arten von Abfragen, die nicht zwischengespeichert werden können oder die nutzlos sind, obwohl sie zwischengespeichert werden. Meiner Erfahrung nach ist der Abfrage-Cache sowieso nicht oft ein großer Gewinn. Abfragen und Schemas müssen speziell konstruiert werden, um den Cache optimal zu nutzen. Oft wird das Caching auf Anwendungsebene auf lange Sicht ohnehin notwendig.

  2. Native Vorbereitungen machen keinen Unterschied für die Sicherheit. Die pseudo-vorbereiteten Anweisungen werden immer noch Abfrageparameterwerte maskieren, es wird nur in der PDO-Bibliothek mit Strings statt auf dem MySQL-Server unter Verwendung des Binärprotokolls durchgeführt. Mit anderen Worten, derselbe PDO-Code ist unabhängig von Ihrem EMULATE_PREPARES gleichermaßen anfällig (oder nicht anfällig) für Injektionsangriffe Einstellung. Der einzige Unterschied besteht darin, wo die Parameterersetzung erfolgt – mit EMULATE_PREPARES , es kommt in der PDO-Bibliothek vor; ohne EMULATE_PREPARES , es tritt auf dem MySQL-Server auf.

  3. Ohne EMULATE_PREPARES Möglicherweise erhalten Sie Syntaxfehler eher zur Vorbereitungszeit als zur Ausführungszeit. mit EMULATE_PREPARES Sie erhalten nur zur Ausführungszeit Syntaxfehler, da PDO bis zur Ausführungszeit keine Abfrage an MySQL geben muss. Beachten Sie, dass dies den Code beeinflusst, den Sie schreiben werden ! Besonders wenn Sie PDO::ERRMODE_EXCEPTION verwenden !

Eine zusätzliche Überlegung:

  • Für ein prepare() fallen feste Kosten an (unter Verwendung nativer vorbereiteter Anweisungen), also ein prepare();execute() mit nativen vorbereiteten Anweisungen kann etwas langsamer sein als das Ausgeben einer einfachen Textabfrage mit emulierten vorbereiteten Anweisungen. Auf vielen Datenbanksystemen ist der Abfrageplan für ein prepare() wird ebenfalls zwischengespeichert und kann mit mehreren Verbindungen geteilt werden, aber ich glaube nicht, dass MySQL dies tut. Wenn Sie also Ihr vorbereitetes Anweisungsobjekt nicht für mehrere Abfragen wiederverwenden, kann Ihre Gesamtausführung langsamer sein.

Als letzte Empfehlung , denke ich, dass Sie mit älteren Versionen von MySQL+PHP vorbereitete Anweisungen emulieren sollten, aber mit Ihren allerneuesten Versionen sollten Sie die Emulation ausschalten.

Nachdem ich einige Apps geschrieben habe, die PDO verwenden, habe ich eine PDO-Verbindungsfunktion erstellt, die meiner Meinung nach die besten Einstellungen hat. Sie sollten wahrscheinlich so etwas verwenden oder Ihre bevorzugten Einstellungen anpassen:

/**
 * Return PDO handle for a MySQL connection using supplied settings
 *
 * Tries to do the right thing with different php and mysql versions.
 *
 * @param array $settings with keys: host, port, unix_socket, dbname, charset, user, pass. Some may be omitted or NULL.
 * @return PDO
 * @author Francis Avila
 */
function connect_PDO($settings)
{
    $emulate_prepares_below_version = '5.1.17';

    $dsndefaults = array_fill_keys(array('host', 'port', 'unix_socket', 'dbname', 'charset'), null);
    $dsnarr = array_intersect_key($settings, $dsndefaults);
    $dsnarr += $dsndefaults;

    // connection options I like
    $options = array(
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    );

    // connection charset handling for old php versions
    if ($dsnarr['charset'] and version_compare(PHP_VERSION, '5.3.6', '<')) {
        $options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES '.$dsnarr['charset'];
    }
    $dsnpairs = array();
    foreach ($dsnarr as $k => $v) {
        if ($v===null) continue;
        $dsnpairs[] = "{$k}={$v}";
    }

    $dsn = 'mysql:'.implode(';', $dsnpairs);
    $dbh = new PDO($dsn, $settings['user'], $settings['pass'], $options);

    // Set prepared statement emulation depending on server version
    $serverversion = $dbh->getAttribute(PDO::ATTR_SERVER_VERSION);
    $emulate_prepares = (version_compare($serverversion, $emulate_prepares_below_version, '<'));
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, $emulate_prepares);

    return $dbh;
}