So richten Sie eine PDO-Verbindung ordnungsgemäß ein


92

Von Zeit zu Zeit sehe ich Fragen zur Verbindung mit der Datenbank.
Die meisten Antworten sind nicht so, wie ich es mache, oder ich bekomme die Antworten einfach nicht richtig. Wie auch immer; Ich habe nie darüber nachgedacht, weil die Art und Weise, wie ich es mache, für mich funktioniert.

Aber hier ist ein verrückter Gedanke; Vielleicht mache ich das alles falsch, und wenn das der Fall ist; Ich würde wirklich gerne wissen, wie man mit PHP und PDO eine Verbindung zu einer MySQL-Datenbank herstellt und diese leicht zugänglich macht.

So mache ich das:

Hier ist zunächst meine Dateistruktur (abgespeckte) :

public_html/

* index.php  

* initialize/  
  -- load.initialize.php  
  -- configure.php  
  -- sessions.php   

index.php
Ganz oben habe ich require('initialize/load.initialize.php');.

load.initialize.php

#   site configurations
    require('configure.php');
#   connect to database
    require('root/somewhere/connect.php');  //  this file is placed outside of public_html for better security.
#   include classes
    foreach (glob('assets/classes/*.class.php') as $class_filename){
        include($class_filename);
    }
#   include functions
    foreach (glob('assets/functions/*.func.php') as $func_filename){
        include($func_filename);
    }
#   handle sessions
    require('sessions.php');

Ich weiß, dass es eine bessere oder korrektere Möglichkeit gibt, Klassen einzuschließen, kann mich aber nicht erinnern, was es war. Ich habe noch nicht die Zeit bekommen, mich damit zu beschäftigen, aber ich denke, es war etwas mit autoload. sowas in der Art...

configure.php
Hier überschreibe ich im Grunde nur einige php.ini- Eigenschaften und mache eine andere globale Konfiguration für die Site

connect.php
Ich habe die Verbindung zu einer Klasse hinzugefügt, damit andere Klassen diese erweitern können ...

class connect_pdo
{
    protected $dbh;

    public function __construct()
    {
        try {
            $db_host = '  ';  //  hostname
            $db_name = '  ';  //  databasename
            $db_user = '  ';  //  username
            $user_pw = '  ';  //  password

            $con = new PDO('mysql:host='.$db_host.'; dbname='.$db_name, $db_user, $user_pw);  
            $con->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
            $con->exec("SET CHARACTER SET utf8");  //  return all sql requests as UTF-8  
        }
        catch (PDOException $err) {  
            echo "harmless error message if the connection fails";
            $err->getMessage() . "<br/>";
            file_put_contents('PDOErrors.txt',$err, FILE_APPEND);  // write some details to an error-log outside public_html  
            die();  //  terminate connection
        }
    }

    public function dbh()
    {
        return $this->dbh;
    }
}
#   put database handler into a var for easier access
    $con = new connect_pdo();
    $con = $con->dbh();
//

Hier glaube ich, dass es Raum für massive Verbesserungen gibt, seit ich vor kurzem angefangen habe, OOP zu lernen und PDO anstelle von MySQL zu verwenden.
Also habe ich gerade ein paar Anfänger-Tutorials befolgt und verschiedene Sachen ausprobiert ...

session.php
Neben der Behandlung regulärer Sitzungen initialisiere ich auch einige Klassen in einer Sitzung wie der folgenden:

if (!isset($_SESSION['sqlQuery'])){
    session_start();
    $_SESSION['sqlQuery'] = new sqlQuery();
}

Auf diese Weise ist diese Klasse überall verfügbar. Dies ist möglicherweise keine gute Praxis (?) ...
Wie auch immer, mit diesem Ansatz kann ich dies von überall aus tun:

echo $_SESSION['sqlQuery']->getAreaName('county',9);  // outputs: Aust-Agder (the county name with that id in the database)

In meinem sqlQuery- Klasse , die extendsmeine connect_pdo- Klasse , habe ich eine genannte öffentliche Funktion , getAreaNamedie die Anfrage an meine Datenbank behandelt.
Ziemlich ordentlich finde ich.

Funktioniert wie ein Zauber
So mache ich es also im Grunde.
Wenn ich etwas aus meiner Datenbank abrufen muss, das nicht in einer Klasse enthalten ist, mache ich einfach etwas Ähnliches:

$id = 123;

$sql = 'SELECT whatever FROM MyTable WHERE id = :id';
$qry = $con->prepare($sql);
$qry -> bindParam(':id', $id, PDO::PARAM_INT);
$qry -> execute();
$get = $qry->fetch(PDO::FETCH_ASSOC);

Da ich die Verbindung in eine Variable in connect_pdo.php eingefügt habe , beziehe ich mich nur darauf und kann loslegen . Es klappt. Ich bekomme meine erwarteten Ergebnisse ...

Aber unabhängig davon; Ich würde mich sehr freuen, wenn ihr mir sagen könnt, ob ich hier weit weg bin. Was ich stattdessen tun sollte, Bereiche, die ich zur Verbesserung ändern könnte oder sollte usw.

Ich bin gespannt zu lernen ...


9
Sie sollten einen Autoloader verwenden, anstatt jede einzelne Datei gleichzeitig in Ihre Anwendung aufzunehmen.
Lusitanian

4
Diese Frage ist wahrscheinlich am besten auf Code Review
Madara's Ghost

Antworten:


105

Das Ziel

Aus meiner Sicht haben Sie in diesem Fall zwei Ziele:

  • Erstellen und Verwalten einer einzelnen / wiederverwendbaren Verbindung pro Datenbank
  • Stellen Sie sicher, dass die Verbindung ordnungsgemäß eingerichtet wurde

Lösung

Ich würde empfehlen, sowohl die anonyme Funktion als auch das Factory-Muster für die PDO-Verbindung zu verwenden. Die Verwendung würde so aussehen:

$provider = function()
{
    $instance = new PDO('mysql:......;charset=utf8', 'username', 'password');
    $instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $instance->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    return $instance;
};

$factory = new StructureFactory( $provider );

Dann in einer anderen Datei oder niedriger in derselben Datei:

$something = $factory->create('Something');
$foobar = $factory->create('Foobar');

Die Fabrik selbst sollte ungefähr so ​​aussehen:

class StructureFactory
{
    protected $provider = null;
    protected $connection = null;

    public function __construct( callable $provider )
    {
        $this->provider = $provider;
    }

    public function create( $name)
    {
        if ( $this->connection === null )
        {
            $this->connection = call_user_func( $this->provider );
        }
        return new $name( $this->connection );
    }

}

Auf diese Weise erhalten Sie eine zentralisierte Struktur, die sicherstellt, dass die Verbindung nur bei Bedarf hergestellt wird. Dies würde auch den Prozess des Unit-Tests und der Wartung erheblich vereinfachen.

Der Anbieter befindet sich in diesem Fall irgendwo in der Bootstrap-Phase. Dieser Ansatz würde auch einen klaren Ort angeben, an dem die Konfiguration definiert werden kann, die Sie für die Verbindung mit der Datenbank verwenden.

Beachten Sie, dass dies ein extrem vereinfachtes Beispiel ist . Sie können auch von zwei folgenden Videos profitieren:

Außerdem würde ich dringend empfehlen, ein geeignetes Tutorial über die Verwendung von PDO zu lesen (es gibt ein Protokoll mit schlechten Online-Tutorials).


3
Da nähert sich sogar PHP5.3 EOL. Die meisten Websites mit veralteten PHP-Versionen sind eigentlich nur billiges Hosting für Wordpress. Die Auswirkungen von Umgebungen vor 5.3 auf die berufliche Entwicklung (die von solchen Ausschnitten profitieren würden) sind meiner Einschätzung nach vernachlässigbar.
Tereško

5
@ thelolcat Ich stimme dir zu. Es ist mehr oder weniger die gleiche Antwort. Das ist, wenn Sie nicht die Tatsache sehen, dass es völlig anders ist.
PeeHaa

1
@thelolcat, dann sollten Sie lernen, was Abhängigkeitsinjektion ist. Anstatt sich weiterhin in Verlegenheit zu bringen. Lustigerweise erklärte das zweite Video im obigen Beitrag (mit dem Titel: "Nicht nach Dingen suchen " ) tatsächlich, was DI ist und wie man es verwendet ... aber natürlich sind Sie für solche trivialen Dinge zu fortgeschritten.
Tereško

2
Dies ist eine alte Antwort, aber eine gute - und eine gute Verbindung zu MySQL pdo am Ende
Strawberry

1
@teecee Sie sollten zunächst lernen, wie man PDO verwendet. Ich würde dieses Tutorial empfehlen: wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers , da es genau für Personen gedacht ist , die von mysql_*PDO migrieren möchten . Dann können Sie sich diese Lösungen ansehen, die sich an diejenigen richten, die bereits PDO verwenden, aber eine Möglichkeit benötigen, die DB-Verbindung zwischen mehreren Klassen zu teilen.
Tereško

24

Ich würde vorschlagen, nicht $_SESSIONfür den globalen Zugriff auf Ihre DB-Verbindung zu verwenden.

Sie können eines der folgenden Dinge tun (in der Reihenfolge der schlechtesten bis besten Praktiken):

  • Zugriff $dbhüber global $dbhinnerhalb Ihrer Funktionen und Klassen
  • Verwenden Sie eine Singleton-Registrierung und greifen Sie wie folgt global darauf zu:

    $registry = MyRegistry::getInstance();
    $dbh = $registry->getDbh();
  • Fügen Sie den Datenbankhandler wie folgt in die Klassen ein, die ihn benötigen:

    class MyClass {
        public function __construct($dbh) { /* ... */ }
    }

Ich kann das letzte sehr empfehlen. Es ist bekannt als Abhängigkeitsinjektion (DI), Inversion der Kontrolle (IoC) oder einfach das Hollywood-Prinzip (Rufen Sie uns nicht an, wir rufen Sie an).

Es ist jedoch etwas fortgeschrittener und erfordert mehr "Verkabelung" ohne Rahmen. Wenn Ihnen die Abhängigkeitsinjektion zu kompliziert ist, verwenden Sie eine Singleton-Registrierung anstelle einer Reihe globaler Variablen.


Also sqlQuerygreife ich global auf meine Datenbankverbindung zu, wenn ich meine -class in session setze, da sie erweitert wird connect_pdo?
ThomasK

7

Ich bin kürzlich selbst zu einer ähnlichen Antwort / Frage gekommen. Dies ist, was ich getan habe, falls jemand interessiert ist:

<?php
namespace Library;

// Wrapper for \PDO. It only creates the rather expensive instance when needed.
// Use it exactly as you'd use the normal PDO object, except for the creation.
// In that case simply do "new \Library\PDO($args);" with the normal args
class PDO
  {
  // The actual instance of PDO
  private $db;

  public function __construct() {
    $this->args = func_get_args();
    }

  public function __call($method, $args)
    {
    if (empty($this->db))
      {
      $Ref = new \ReflectionClass('\PDO');
      $this->db = $Ref->newInstanceArgs($this->args);
      }

    return call_user_func_array(array($this->db, $method), $args);
    }
  }

Um es aufzurufen, müssen Sie nur diese Zeile ändern:

$DB = new \Library\PDO(/* normal arguments */);

Und die Typhinweise, wenn Sie sie verwenden, um (\ Library \ PDO $ DB).

Es ist sowohl der akzeptierten als auch Ihrer Antwort sehr ähnlich. es hat jedoch einen bemerkenswerten Vorteil. Betrachten Sie diesen Code:

$DB = new \Library\PDO( /* args */ );

$STH = $DB->prepare("SELECT * FROM users WHERE user = ?");
$STH->execute(array(25));
$User = $STH->fetch();

Es sieht zwar wie ein normales PDO aus (es ändert sich \Library\nur dadurch), initialisiert das Objekt jedoch erst, wenn Sie die erste Methode aufrufen, je nachdem, um welche es sich handelt. Dies macht es optimierter, da die Erstellung von PDO-Objekten etwas teuer ist. Es ist eine transparente Klasse oder ein sogenannter Ghost , eine Form des Lazy Loading . Sie können die $ DB als normale PDO-Instanz behandeln, sie weitergeben, dieselben Vorgänge ausführen usw.


Dies wird "Dekorationsmuster" genannt
Yang

0
$dsn = 'mysql:host=your_host_name;dbname=your_db_name_here'; // define host name and database name
    $username = 'you'; // define the username
    $pwd='your_password'; // password
    try {
        $db = new PDO($dsn, $username, $pwd);
    }
    catch (PDOException $e) {
        $error_message = $e->getMessage();
        echo "this is displayed because an error was found";
        exit();
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.