Cách thiết lập kết nối PDO đúng cách


92

Đôi khi tôi thấy câu hỏi liên quan đến kết nối với cơ sở dữ liệu.
Hầu hết các câu trả lời không phải là cách tôi làm, hoặc tôi có thể không nhận được câu trả lời chính xác. Dù sao; Tôi chưa bao giờ nghĩ về nó bởi vì cách tôi làm nó phù hợp với tôi.

Nhưng đây là một suy nghĩ điên rồ; Có lẽ tôi đang làm sai tất cả, và nếu đúng như vậy; Tôi thực sự muốn biết cách kết nối đúng cách với cơ sở dữ liệu MySQL bằng PHP và PDO và làm cho nó dễ dàng truy cập.

Đây là cách tôi đang thực hiện:

Trước hết, đây là cấu trúc tệp của tôi (đã rút gọn) :

public_html/

* index.php  

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

index.php
Ở trên cùng, tôi có 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');

Tôi biết có một cách tốt hơn, hoặc đúng hơn, để bao gồm các lớp, nhưng không thể nhớ nó là gì. Tôi vẫn chưa có thời gian để xem xét nó, nhưng tôi nghĩ đó là một cái gì đó với autoload. đại loại như vậy ...

configure.php
Ở đây tôi về cơ bản chỉ ghi đè lên một số php.ini -properties và làm một số cấu hình toàn cầu khác cho trang web

connect.php
Tôi đã đặt kết nối vào một lớp để các lớp khác có thể mở rộng lớp này ...

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

Ở đây tôi tin rằng có chỗ cho sự cải tiến lớn vì gần đây tôi đã bắt đầu học OOP và sử dụng PDO thay vì mysql.
Vì vậy, tôi vừa làm theo một vài hướng dẫn dành cho người mới bắt đầu và thử những thứ khác nhau ...

Session.php
Bên cạnh việc xử lý các phiên thông thường, tôi cũng khởi tạo một số lớp thành một phiên như thế này:

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

Bằng cách này, lớp học này có sẵn khắp nơi. Đây có thể không phải là phương pháp hay (?) ...
Dù sao, đây là những gì mà cách tiếp cận này cho phép tôi làm ở mọi nơi:

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

Bên trong tôi sqlQuery- lớp , mà extendstôi connect_pdo- lớp , tôi có một public function gọi getAreaNameđó xử lý các yêu cầu đến cơ sở dữ liệu của tôi.
Tôi nghĩ là khá gọn gàng.

Hoạt động như một sự quyến rũ
Vì vậy, về cơ bản đó là cách tôi đang làm.
Ngoài ra, bất cứ khi nào tôi cần tìm nạp một thứ gì đó từ DB của mình từ không phải trong một lớp, tôi chỉ cần làm điều gì đó tương tự như sau:

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

Vì tôi đã đặt kết nối vào một biến bên trong connect_pdo.php , nên tôi chỉ cần tham khảo nó và tôi đã sẵn sàng. Nó hoạt động. Tôi nhận được kết quả mong đợi của mình ...

Nhưng bất chấp điều đó; Tôi thực sự sẽ đánh giá cao nếu các bạn có thể cho tôi biết nếu tôi rời khỏi đây. Thay vào đó tôi nên làm gì, những lĩnh vực tôi có thể hoặc nên thay đổi để cải thiện, v.v.

Tôi háo hức học ...


9
Bạn nên sử dụng trình tải tự động thay vì đưa từng tệp vào ứng dụng của mình cùng một lúc.
Lusitanian

4
Câu hỏi này có lẽ là tốt nhất về luật xét
ma của Madara

Câu trả lời:


105

Mục đích

Như tôi thấy, mục tiêu của bạn trong trường hợp này là gấp đôi:

  • tạo và duy trì một kết nối duy nhất / có thể tái sử dụng trên mỗi cơ sở dữ liệu
  • đảm bảo rằng kết nối đã được thiết lập đúng cách

Giải pháp

Tôi khuyên bạn nên sử dụng cả hàm ẩn danh và mẫu xuất xưởng để xử lý kết nối PDO. Việc sử dụng nó sẽ như thế này:

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

Sau đó, trong một tệp khác hoặc thấp hơn trong cùng một tệp:

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

Bản thân nhà máy sẽ trông giống như sau:

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

}

Bằng cách này sẽ cho phép bạn có một cấu trúc tập trung, đảm bảo rằng kết nối chỉ được tạo khi được yêu cầu. Nó cũng sẽ làm cho quá trình kiểm tra đơn vị và bảo trì dễ dàng hơn nhiều.

Nhà cung cấp trong trường hợp này sẽ được tìm thấy ở đâu đó ở giai đoạn bootstrap. Cách tiếp cận này cũng sẽ cung cấp một vị trí rõ ràng để xác định cấu hình mà bạn sử dụng để kết nối với DB.

Hãy nhớ rằng đây là một ví dụ cực kỳ đơn giản . Bạn cũng có thể có lợi khi xem hai video sau:

Ngoài ra, tôi thực sự khuyên bạn nên đọc một hướng dẫn thích hợp về cách sử dụng PDO (có một nhật ký hướng dẫn xấu trên mạng).


3
Vì ngay cả PHP5.3 cũng đang tiếp cận EOL. Phần lớn các trang web có phiên bản PHP lỗi thời thực chất chỉ là dịch vụ lưu trữ giá rẻ cho Wordpress. Theo ước tính của tôi, tác động của các môi trường trước 5.3 đối với sự phát triển nghề nghiệp (chúng sẽ được hưởng lợi từ những đoạn trích như thế này).
tereško

5
@thelolcat Tôi đồng ý với bạn. Nó nhiều hơn hoặc ít câu trả lời tương tự. Đó là nếu bạn không nhìn thấy thực tế là nó hoàn toàn khác.
PeeHaa 10/12/13

1
@thelolcat, sau đó bạn nên tìm hiểu tiêm phụ thuộc là gì. Thay vì tiếp tục làm xấu hổ bản thân. Thật thú vị, video thứ hai trong bài đăng ở trên (có tiêu đề: "Don't Look For Things" ) thực sự đã giải thích DI là gì và cách sử dụng nó ... nhưng tất nhiên bạn đã quá nâng cao cho những thứ tầm thường như vậy.
tereško

2
Đây là một câu trả lời cũ, nhưng là một câu trả lời hay - và là một liên kết tuyệt vời đến Mysql pdo ở cuối
Strawberry

1
@teecee, bạn nên bắt đầu bằng cách học cách sử dụng PDO trước. Tôi muốn giới thiệu hướng dẫn này: wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers , vì nó được tạo chính xác cho những người muốn di chuyển từ mysql_*PDO. Sau đó, bạn có thể quay lại và xem xét các giải pháp này, nhằm vào những người đã sử dụng PDO, nhưng cần một cách để chia sẻ kết nối DB giữa nhiều lớp.
tereško

24

Tôi khuyên bạn không nên sử dụng $_SESSIONđể truy cập kết nối DB của bạn trên toàn cầu.

Bạn có thể thực hiện một trong một số điều (theo thứ tự từ tệ nhất đến tốt nhất ):

  • Truy cập $dbhbằng cách sử dụng global $dbhbên trong các hàm và lớp của bạn
  • Sử dụng sổ đăng ký singleton và truy cập trên toàn cầu, như sau:

    $registry = MyRegistry::getInstance();
    $dbh = $registry->getDbh();
  • Chèn trình xử lý cơ sở dữ liệu vào các lớp cần nó, như sau:

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

Tôi rất muốn giới thiệu cái cuối cùng. Nó được gọi là tiêm phụ thuộc (DI), đảo ngược kiểm soát (IoC), hoặc đơn giản là nguyên tắc Hollywood (Đừng gọi cho chúng tôi, chúng tôi sẽ gọi cho bạn).

Tuy nhiên, nó cao cấp hơn một chút và yêu cầu nhiều "dây" hơn mà không có khuôn khổ. Vì vậy, nếu việc chèn phụ thuộc quá phức tạp đối với bạn, hãy sử dụng sổ đăng ký singleton thay vì một loạt các biến toàn cục.


Vì vậy, tôi truy cập kết nối db của mình trên toàn cầu khi tôi đặt sqlQuery-class của mình thành phiên vì nó mở rộng connect_pdo?
ThomasK

7

Gần đây tôi đã tự mình đi đến một câu trả lời / câu hỏi tương tự. Đây là những gì tôi đã làm, trong trường hợp có ai quan tâm:

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

Để gọi nó, bạn chỉ cần sửa đổi dòng này:

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

Và gợi ý kiểu nếu bạn đang sử dụng nó để (\ Library \ PDO $ DB).

Nó thực sự giống với cả câu trả lời được chấp nhận và của bạn; tuy nhiên nó có một lợi thế đáng chú ý. Hãy xem xét mã này:

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

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

Mặc dù nó có thể trông giống như PDO bình thường (nó chỉ thay đổi theo cách đó \Library\), nó thực sự không khởi tạo đối tượng cho đến khi bạn gọi phương thức đầu tiên, bất kể nó là phương thức nào. Điều đó làm cho nó tối ưu hơn, vì việc tạo đối tượng PDO hơi tốn kém. Đó là một lớp trong suốt, hay được gọi là Ghost , một dạng Lazy Loading . Bạn có thể coi $ DB như một cá thể PDO bình thường, truyền nó xung quanh, thực hiện các thao tác tương tự, v.v.


Đây được gọi là "Mẫu trang trí"
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();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.