Cara mengatur koneksi PDO dengan benar

92

Dari waktu ke waktu saya melihat pertanyaan tentang koneksi ke database.
Kebanyakan jawaban bukanlah cara saya melakukannya, atau saya mungkin tidak mendapatkan jawaban dengan benar. Bagaimanapun; Saya tidak pernah memikirkannya karena cara saya melakukannya berhasil untuk saya.

Tapi inilah pemikiran gila; Mungkin saya melakukan ini semua salah, dan jika itu masalahnya; Saya benar-benar ingin tahu cara menyambung ke database MySQL dengan benar menggunakan PHP dan PDO dan membuatnya mudah diakses.

Begini cara saya melakukannya:

Pertama, inilah struktur file saya (dipreteli) :

public_html/

* index.php  

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

index.php
Di bagian paling atas, saya punya 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');

Saya tahu ada cara yang lebih baik, atau lebih tepat, untuk memasukkan kelas, tetapi tidak dapat mengingat apa itu. Belum punya waktu untuk memeriksanya, tapi saya pikir ada sesuatu dengan autoload. sesuatu seperti itu...

configure.php
Di sini saya pada dasarnya hanya mengganti beberapa php.ini properti dan melakukan beberapa konfigurasi global lainnya untuk situs tersebut

connect.php
Saya telah menempatkan koneksi ke kelas sehingga kelas lain dapat memperluas yang satu ini ...

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

Di sini saya percaya ada ruang untuk perbaikan besar-besaran sejak saya baru mulai belajar OOP, dan menggunakan PDO daripada mysql.
Jadi saya baru saja mengikuti beberapa tutorial pemula dan mencoba berbagai hal ...

session.php
Selain menangani sesi reguler, saya juga menginisialisasi beberapa kelas menjadi sesi seperti ini:

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

Dengan cara ini kelas ini tersedia di semua tempat. Ini mungkin bukan praktik yang baik (?) ...
Bagaimanapun, pendekatan ini memungkinkan saya melakukannya dari mana saja:

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

Di dalam my sqlQuery- class , yaitu extendsmy connect_pdo- class , saya memiliki fungsi publik yang disebut getAreaNameyang menangani permintaan ke database saya.
Cukup rapi menurutku.

Bekerja seperti pesona
Jadi pada dasarnya itulah cara saya melakukannya.
Juga, setiap kali saya perlu mengambil sesuatu dari DB saya dari tidak dalam kelas, saya hanya melakukan sesuatu yang mirip dengan ini:

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

Karena saya memasukkan koneksi ke dalam variabel di dalamnya connect_pdo.php , saya hanya merujuknya dan saya siap melakukannya. Berhasil. Saya mendapatkan hasil yang saya harapkan ...

Tapi terlepas dari itu; Saya akan sangat menghargai jika kalian bisa memberi tahu saya jika saya jauh dari sini. Apa yang harus saya lakukan, area yang dapat atau harus saya ubah untuk perbaikan, dll ...

Saya ingin sekali belajar ...

ThomasK
sumber
9
Anda harus menggunakan pemuat otomatis daripada menyertakan setiap file dalam aplikasi Anda sekaligus.
Lusitanian
4
Pertanyaan ini mungkin yang terbaik pada Review Kode
Hantu Madara

Jawaban:

105

Hasil

Menurut saya, tujuan Anda dalam kasus ini ada dua:

  • membuat dan memelihara koneksi tunggal / yang dapat digunakan kembali per database
  • pastikan koneksi telah diatur dengan benar

Larutan

Saya akan merekomendasikan untuk menggunakan fungsi anonim dan pola pabrik untuk menangani koneksi PDO. Penggunaannya akan terlihat seperti ini:

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

Kemudian di file yang berbeda atau lebih rendah di file yang sama:

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

Pabrik itu sendiri akan terlihat seperti ini:

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

}

Dengan cara ini akan memungkinkan Anda memiliki struktur terpusat, yang memastikan bahwa koneksi dibuat hanya jika diperlukan. Ini juga akan membuat proses pengujian dan pemeliharaan unit lebih mudah.

Penyedia dalam hal ini akan ditemukan di suatu tempat pada tahap bootstrap. Pendekatan ini juga akan memberikan lokasi yang jelas di mana untuk mendefinisikan konfigurasi, yang Anda gunakan untuk menghubungkan ke DB.

Ingatlah bahwa ini adalah contoh yang sangat disederhanakan . Anda juga mungkin mendapat manfaat dari menonton dua video berikut:

Juga, saya sangat menyarankan membaca tutorial yang tepat tentang penggunaan PDO (ada log tutorial online yang buruk).

tereško
sumber
3
Karena bahkan PHP5.3 mendekati EOL. Mayoritas situs dengan versi PHP yang sudah ketinggalan zaman sebenarnya hanya hosting murah untuk Wordpress. Dampak lingkungan pra-5.3 pada pengembangan profesional (mereka agak mendapat manfaat dari cuplikan seperti ini) dapat diabaikan, menurut perkiraan saya.
tereško
5
@thelolcat Saya setuju dengan Anda. Ini adalah kurang lebih jawaban yang sama. Itu jika Anda tidak melihat fakta bahwa itu sama sekali berbeda.
PeeHaa
1
@thelolcat, maka Anda harus mempelajari apa itu injeksi ketergantungan . Daripada terus mempermalukan diri sendiri. Lucunya, video kedua pada postingan di atas (berjudul: "Don't Look For Things" ) sebenarnya menjelaskan apa itu DI dan bagaimana cara menggunakannya ... tapi tentunya kalian terlalu mahir untuk hal-hal sepele seperti itu.
tereško
2
Ini adalah jawaban lama, tapi bagus - dan tautan bagus ke MySQL pdo di bagian akhir
Strawberry
1
@ teecee Anda harus mulai dengan mempelajari cara menggunakan PDO. Saya akan merekomendasikan tutorial ini: wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers , karena dibuat tepat untuk orang-orang yang ingin bermigrasi dari mysql_*ke PDO. Kemudian Anda dapat kembali dan melihat solusi ini, yang ditujukan untuk orang-orang, yang sudah menggunakan PDO, tetapi membutuhkan cara untuk membagi koneksi DB antara beberapa kelas.
tereško
24

Saya menyarankan untuk tidak menggunakan $_SESSIONuntuk mengakses koneksi DB Anda secara global.

Anda dapat melakukan salah satu dari beberapa hal (dalam urutan dari yang terburuk hingga praktik terbaik ):

  • Akses $dbhmenggunakan global $dbhbagian dalam fungsi dan kelas Anda
  • Gunakan registri tunggal, dan akses itu secara global, seperti:

    $registry = MyRegistry::getInstance();
    $dbh = $registry->getDbh();
  • Masukkan pengendali database ke dalam kelas yang membutuhkannya, seperti:

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

Saya akan sangat merekomendasikan yang terakhir. Ini dikenal sebagai injeksi ketergantungan (DI), inversi kendali (IoC), atau hanya prinsip Hollywood (Jangan hubungi kami, kami akan menghubungi Anda).

Namun, ini sedikit lebih maju dan membutuhkan lebih banyak "kabel" tanpa kerangka. Jadi, jika injeksi ketergantungan terlalu rumit untuk Anda, gunakan registri tunggal daripada sekumpulan variabel global.

Ian Unruh
sumber
Jadi saya mengakses koneksi db saya secara global ketika saya menyetel sqlQuery-kelas saya menjadi sesi karena diperpanjang connect_pdo?
ThomasK
7

Saya baru-baru ini menemukan jawaban / pertanyaan serupa sendiri. Inilah yang saya lakukan, jika ada yang tertarik:

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

Untuk memanggilnya Anda hanya perlu mengubah baris ini:

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

Dan tipe-mengisyaratkan jika Anda menggunakannya untuk (\ Library \ PDO $ DB).

Ini sangat mirip dengan jawaban yang diterima dan jawaban Anda; Namun, hal itu memiliki keuntungan yang menonjol. Pertimbangkan kode ini:

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

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

Meskipun mungkin terlihat seperti PDO normal (hanya berubah dengan itu \Library\), sebenarnya tidak menginisialisasi objek sampai Anda memanggil metode pertama, mana pun itu. Itu membuatnya lebih optimal, karena pembuatan objek PDO sedikit mahal. Ini adalah kelas transparan, atau yang disebut Ghost , sebuah bentuk dari Lazy Loading . Anda dapat memperlakukan $ DB sebagai instance PDO biasa, menyebarkannya, melakukan operasi yang sama, dll.

Francisco Presencia
sumber
Ini disebut "Pola dekorator"
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();
}
hi-kode
sumber