Come impostare correttamente una connessione PDO


92

Di tanto in tanto vedo domande riguardanti la connessione al database.
La maggior parte delle risposte non è il modo in cui lo faccio, o potrei semplicemente non ottenere le risposte correttamente. Comunque; Non ci ho mai pensato perché il modo in cui lo faccio funziona per me.

Ma ecco un pensiero folle; Forse sto sbagliando tutto, e se è così; Mi piacerebbe davvero sapere come connettermi correttamente a un database MySQL utilizzando PHP e PDO e renderlo facilmente accessibile.

Ecco come lo sto facendo:

Prima di tutto, ecco la struttura del mio file (ridotta) :

public_html/

* index.php  

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

index.php
In cima, ho 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');

So che esiste un modo migliore, o più corretto, per includere le lezioni, ma non ricordo cosa fosse. Non ho ancora avuto il tempo di esaminarlo, ma penso che fosse qualcosa con autoload. qualcosa del genere...

configure.php
Qui fondamentalmente sovrascrivo solo alcune proprietà php.ini e faccio qualche altra configurazione globale per il sito

connect.php
Ho messo la connessione su una classe in modo che altre classi possano estendere questa ...

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

Qui credo che ci sia spazio per enormi miglioramenti da quando ho iniziato di recente a imparare OOP e utilizzare PDO invece di mysql.
Quindi ho appena seguito un paio di tutorial per principianti e ho provato cose diverse ...

sessioni.php
Oltre a gestire sessioni regolari, inizializzo anche alcune classi in una sessione come questa:

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

In questo modo questa classe è disponibile ovunque. Questa potrebbe non essere una buona pratica (?) ...
Comunque, questo è ciò che questo approccio mi permette di fare ovunque:

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

All'interno della mia sqlQuery- classe , che della extendsmia connect_pdo- classe , ho una funzione pubblica chiamata getAreaNameche gestisce la richiesta al mio database.
Abbastanza pulito penso.

Funziona come un fascino
Quindi è fondamentalmente come lo sto facendo.
Inoltre, ogni volta che ho bisogno di recuperare qualcosa dal mio DB da non all'interno di una classe, faccio qualcosa di simile a questo:

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

Dal momento che ho inserito la connessione in una variabile all'interno di connect_pdo.php , mi sto solo riferendo ad essa e sono a posto. Funziona. Ottengo i risultati attesi ...

Ma indipendentemente da quello; Apprezzerei davvero se voi ragazzi poteste dirmi se sono lontano da qui. Cosa dovrei fare invece, aree che potrei o dovrei cambiare per migliorare ecc ...

Sono ansioso di imparare ...


9
È necessario utilizzare un caricatore automatico invece di includere ogni singolo file nell'applicazione in una volta.
Lusitana

4
Questa domanda è probabilmente la migliore su Code Review
Madara's Ghost

Risposte:


105

L'obiettivo. il gol

Per come la vedo io, il tuo obiettivo in questo caso è duplice:

  • creare e mantenere una connessione singola / riutilizzabile per database
  • assicurarsi che la connessione sia stata impostata correttamente

Soluzione

Suggerirei di utilizzare sia la funzione anonima che il modello di fabbrica per gestire la connessione PDO. L'uso di esso sarebbe simile a questo:

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

Quindi in un file diverso o inferiore nello stesso file:

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

La fabbrica stessa dovrebbe avere un aspetto simile a questo:

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

}

In questo modo avresti una struttura centralizzata, che assicura che la connessione venga creata solo quando richiesto. Inoltre, renderebbe molto più semplice il processo di test e manutenzione dell'unità.

Il provider in questo caso si troverebbe da qualche parte nella fase di bootstrap. Questo approccio fornirebbe anche una posizione chiara in cui definire la configurazione da utilizzare per la connessione al DB.

Tieni presente che questo è un esempio estremamente semplificato . Potresti anche trarre vantaggio dalla visione di due video seguenti:

Inoltre, consiglio vivamente di leggere un tutorial adeguato sull'uso di PDO (ci sono un registro di cattivi tutorial online).


3
Dal momento che anche PHP5.3 si sta avvicinando a EOL. La maggior parte dei siti con versioni PHP obsolete in realtà sono solo hosting economico per Wordpress. L'impatto degli ambienti precedenti alla 5.3 sullo sviluppo professionale (che trarrebbero vantaggio da frammenti come questo) è trascurabile, secondo la mia stima.
tereško

5
@thelolcat sono d'accordo con te. Si tratta più o meno la stessa risposta. Questo se non vedi il fatto che è completamente diverso.
PeeHaa

1
@thelolcat, dovresti imparare cos'è l' iniezione di dipendenza . Invece di continuare a metterti in imbarazzo. Stranamente, il secondo video nel post sopra (intitolato: "Don't Look For Things" ) in realtà ha spiegato cosa è DI e come usarlo ... ma ovviamente sei troppo avanzato per cose così banali.
tereško

2
Questa è una vecchia risposta, ma buona - e alla fine un ottimo collegamento con Mysql dop
Strawberry

1
@teecee dovresti iniziare imparando prima come usare PDO. Consiglierei questo tutorial: wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers , perché è fatto proprio per le persone che vogliono migrare da mysql_*a PDO. Quindi puoi tornare a guardare questa soluzione, che è rivolta a coloro che già utilizzano PDO, ma hanno bisogno di un modo per condividere la connessione DB tra più classi.
tereško

24

Suggerirei di non utilizzare $_SESSIONper accedere alla connessione DB a livello globale.

Puoi fare una delle seguenti cose (in ordine di peggiore rispetto alle migliori pratiche):

  • Accedi $dbhutilizzando l' global $dbhinterno delle tue funzioni e classi
  • Usa un registro singleton e accedi a quello a livello globale, in questo modo:

    $registry = MyRegistry::getInstance();
    $dbh = $registry->getDbh();
    
  • Iniettare il gestore del database nelle classi che ne hanno bisogno, in questo modo:

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

Consiglio vivamente l'ultimo. È noto come iniezione di dipendenza (DI), inversione di controllo (IoC) o semplicemente il principio di Hollywood (non chiamarci, ti chiameremo).

Tuttavia, è un po 'più avanzato e richiede più "cablaggio" senza un framework. Quindi, se l'inserimento delle dipendenze è troppo complicato per te, usa un registro singleton invece di un mucchio di variabili globali.


Quindi accedo alla mia connessione db a livello globale quando imposto la mia sqlQuery-class in sessione poiché si estende connect_pdo?
ThomasK

7

Recentemente sono arrivato a una risposta / domanda simile da solo. Questo è quello che ho fatto, nel caso qualcuno fosse interessato:

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

Per chiamarlo è sufficiente modificare questa riga:

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

E il suggerimento sul tipo se lo stai usando per (\ Library \ PDO $ DB).

È molto simile sia alla risposta accettata che alla tua; tuttavia ha un notevole vantaggio. Considera questo codice:

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

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

Sebbene possa sembrare un normale PDO (cambia \Library\solo in base a quello ), in realtà non inizializza l'oggetto fino a quando non chiami il primo metodo, qualunque esso sia. Ciò lo rende più ottimizzato, poiché la creazione di oggetti PDO è leggermente costosa. È una classe trasparente, o quello che viene chiamato Ghost , una forma di Lazy Loading . Puoi trattare $ DB come una normale istanza PDO, passarla in giro, eseguire le stesse operazioni, ecc.


Questo si chiama "motivo Decoratore"
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();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.