วิธีตั้งค่าการเชื่อมต่อ PDO อย่างถูกต้อง


92

บางครั้งฉันเห็นคำถามเกี่ยวกับการเชื่อมต่อกับฐานข้อมูล
คำตอบส่วนใหญ่ไม่ใช่วิธีที่ฉันทำหรือฉันอาจจะไม่ได้รับคำตอบที่ถูกต้อง อย่างไรก็ตาม; ฉันไม่เคยคิดเกี่ยวกับเรื่องนี้เพราะวิธีที่ฉันทำมันเหมาะกับฉัน

แต่นี่เป็นความคิดที่บ้าคลั่ง บางทีฉันอาจจะทำผิดทั้งหมดและถ้าเป็นเช่นนั้น ฉันต้องการทราบวิธีการเชื่อมต่อกับฐานข้อมูล MySQL อย่างถูกต้องโดยใช้ PHP และ PDO และทำให้เข้าถึงได้ง่าย

นี่คือวิธีที่ฉันทำ:

ก่อนอื่นนี่คือโครงสร้างไฟล์ของฉัน(ถอดออก) :

public_html/

* index.php  

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

index.php
ที่ด้านบนสุดฉันมี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');

ฉันรู้ว่ามีวิธีที่ดีกว่าหรือถูกต้องมากกว่าในการรวมชั้นเรียน แต่จำไม่ได้ว่าคืออะไร ยังไม่ได้มีเวลาตรวจสอบ แต่ฉันคิดว่ามันเป็นสิ่งที่เกิดขึ้นautoloadขึ้น อะไรแบบนั้น...

config.php ที่
นี่โดยพื้นฐานแล้วฉันแค่แทนที่php.ini -properties และทำบางการกำหนดค่าอื่น ๆ ทั่วโลกสำหรับเว็บไซต์

connect.php
ฉันได้ใส่การเชื่อมต่อเข้ากับคลาสเพื่อให้คลาสอื่น ๆ สามารถขยายคลาสนี้ได้ ...

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

ที่นี่ฉันเชื่อว่ามีช่องว่างสำหรับการปรับปรุงครั้งใหญ่ตั้งแต่ฉันเพิ่งเริ่มเรียนรู้ OOP และใช้ PDO แทน mysql
ดังนั้นฉันเพิ่งทำตามบทแนะนำสำหรับผู้เริ่มต้นสองสามบทและลองใช้สิ่งต่างๆ ...

sessions.php
นอกจากการจัดการเซสชันปกติแล้วฉันยังเริ่มต้นคลาสบางคลาสในเซสชันเช่นนี้:

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

ด้วยวิธีนี้คลาสนี้มีอยู่ทั่วทุกที่ นี่อาจไม่ใช่แนวทางปฏิบัติที่ดี (?) ...
อย่างไรก็ตามนี่คือสิ่งที่วิธีนี้ทำให้ฉันทำได้จากทุกที่:

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

ภายในของฉันsqlQuery- ชั้นซึ่งextendsฉันconnect_pdo- ชั้นผมมีฟังก์ชั่นที่เรียกว่าประชาชนgetAreaNameซึ่งมีหน้าที่จัดการการร้องขอไปยังฐานข้อมูลของฉัน
ฉันคิดว่าสวยเรียบร้อย

ทำงานได้อย่างมีเสน่ห์
นั่นคือสิ่งที่ฉันทำ
นอกจากนี้เมื่อใดก็ตามที่ฉันต้องการดึงข้อมูลบางอย่างจากฐานข้อมูลของฉันจากที่ไม่ได้อยู่ในชั้นเรียนฉันก็ทำสิ่งที่คล้ายกับสิ่งนี้:

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

เนื่องจากฉันใส่การเชื่อมต่อเป็นตัวแปรภายใน connect_pdo.phpฉันแค่อ้างถึงมันและฉันก็พร้อมที่จะไป มันได้ผล. ฉันได้ผลลัพธ์ที่คาดหวัง ...

แต่ไม่คำนึงถึงสิ่งนั้น ฉันจะขอบคุณมากถ้าพวกคุณสามารถบอกฉันได้ว่าฉันจะออกจากที่นี่ สิ่งที่ฉันควรทำแทนพื้นที่ที่ฉันทำได้หรือควรเปลี่ยนเพื่อปรับปรุง ฯลฯ ...

ฉันกระตือรือร้นที่จะเรียนรู้ ...


9
คุณควรใช้ตัวโหลดอัตโนมัติแทนที่จะรวมทุกไฟล์ในแอปพลิเคชันของคุณพร้อมกัน
Lusitanian

4
คำถามนี้น่าจะดีที่สุดในCode Review
Madara's Ghost

คำตอบ:


105

เป้าหมาย

อย่างที่ฉันเห็นเป้าหมายของคุณในกรณีนี้คือสองเท่า:

  • สร้างและรักษาการเชื่อมต่อเดียว / ใช้ซ้ำได้ต่อฐานข้อมูล
  • ตรวจสอบให้แน่ใจว่าได้ตั้งค่าการเชื่อมต่ออย่างถูกต้อง

วิธีการแก้

ฉันอยากจะแนะนำให้ใช้ทั้งฟังก์ชันที่ไม่ระบุชื่อและรูปแบบโรงงานเพื่อจัดการกับการเชื่อมต่อ PDO การใช้งานจะมีลักษณะดังนี้:

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

จากนั้นในไฟล์อื่นหรือต่ำกว่าในไฟล์เดียวกัน:

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

โรงงานควรมีลักษณะดังนี้:

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

}

วิธีนี้จะช่วยให้คุณมีโครงสร้างแบบรวมศูนย์ซึ่งทำให้แน่ใจได้ว่าการเชื่อมต่อถูกสร้างขึ้นเมื่อจำเป็นเท่านั้น นอกจากนี้ยังทำให้กระบวนการทดสอบหน่วยและการบำรุงรักษาง่ายขึ้นมาก

ผู้ให้บริการในกรณีนี้จะพบที่ไหนสักแห่งในขั้นตอนการบูต วิธีนี้จะให้ตำแหน่งที่ชัดเจนว่าจะกำหนดการกำหนดค่าที่คุณใช้ในการเชื่อมต่อกับ DB

โปรดทราบว่านี่เป็นตัวอย่างที่เรียบง่ายมาก คุณอาจได้รับประโยชน์จากการดูวิดีโอสองรายการต่อไปนี้:

นอกจากนี้ฉันขอแนะนำอย่างยิ่งให้อ่านบทแนะนำที่เหมาะสมเกี่ยวกับการใช้ PDO (มีบันทึกการสอนออนไลน์ที่ไม่ดี)


3
เนื่องจากแม้ PHP5.3 จะเข้าใกล้ EOL ไซต์ส่วนใหญ่ที่มีเวอร์ชัน PHP ที่ล้าสมัยนั้นเป็นเพียงโฮสติ้งราคาถูกสำหรับ Wordpress ผลกระทบของสภาพแวดล้อมก่อน 5.3 ต่อการพัฒนาวิชาชีพ (ซึ่งน่าจะได้รับประโยชน์จากตัวอย่างเช่นนี้) มีน้อยมากในการประมาณของฉัน
tereško

5
@thelolcat ฉันเห็นด้วยกับคุณ มันเป็นคำตอบเดียวกันไม่มากก็น้อย นั่นคือถ้าคุณไม่เห็นความจริงที่ว่ามันแตกต่างกันอย่างสิ้นเชิง
PeeHaa

1
@thelolcat คุณควรเรียนรู้ว่าการฉีดยาแบบพึ่งพาคืออะไร แทนที่จะทำให้ตัวเองลำบากใจต่อไป. น่าสนุกพอสมควรวิดีโอที่สองในโพสต์ด้านบน (หัวข้อ: "Don't Look For Things" ) อธิบายว่า DI คืออะไรและจะใช้อย่างไร ... แต่แน่นอนว่าคุณก้าวหน้าเกินไปสำหรับสิ่งเล็กน้อยเช่นนี้
tereško

2
นี่เป็นคำตอบเก่า ๆ แต่เป็นคำตอบที่ดีและเป็นลิงค์ที่ดีไปยัง Mysql pdo ในตอนท้าย
สตรอเบอร์รี่

1
@teecee คุณควรเริ่มจากการเรียนรู้วิธีใช้ PDO ก่อน ฉันอยากจะแนะนำบทช่วยสอนนี้: wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developersเพราะสร้างขึ้นสำหรับผู้ที่ต้องการย้ายข้อมูลจากmysql_*ไปยัง PDO จากนั้นคุณสามารถกลับมาดูโซลูชันนี้ซึ่งมุ่งเป้าไปที่ผู้ที่ใช้ PDO อยู่แล้ว แต่ต้องการวิธีแชร์การเชื่อมต่อ DB ระหว่างคลาสต่างๆ
tereško

24

ฉันขอแนะนำว่าอย่าใช้$_SESSIONเพื่อเข้าถึงการเชื่อมต่อ DB ทั่วโลก

คุณสามารถดำเนินการอย่างใดอย่างหนึ่ง (ตามแนวทางปฏิบัติที่เลวร้ายที่สุดไปจนถึงแนวทางปฏิบัติที่ดีที่สุด ):

  • เข้าถึง$dbhโดยใช้global $dbhภายในฟังก์ชันและคลาสของคุณ
  • ใช้การลงทะเบียนซิงเกิลตันและเข้าถึงทั่วโลกดังนี้:

    $registry = MyRegistry::getInstance();
    $dbh = $registry->getDbh();
    
  • ใส่ตัวจัดการฐานข้อมูลลงในคลาสที่ต้องการดังนี้:

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

ฉันขอแนะนำอย่างสุดท้าย เป็นที่รู้จักกันในชื่อการฉีดแบบพึ่งพา (DI) การผกผันของการควบคุม (IoC) หรือหลักการของฮอลลีวูด (อย่าเรียกเราเราจะเรียกคุณว่าคุณ)

อย่างไรก็ตามมันเป็นขั้นสูงกว่าเล็กน้อยและต้องใช้ "สายไฟ" มากขึ้นโดยไม่มีกรอบ ดังนั้นหากการฉีดพึ่งพานั้นซับซ้อนเกินไปสำหรับคุณให้ใช้การลงทะเบียนแบบซิงเกิลตันแทนตัวแปรส่วนกลางจำนวนมาก


ดังนั้นฉันจึงเข้าถึงการเชื่อมต่อฐานข้อมูลของฉันทั่วโลกเมื่อฉันตั้งค่าsqlQuery-class ของฉันเป็นเซสชันเนื่องจากมันขยายออกไปconnect_pdo?
ThomasK

7

เมื่อเร็ว ๆ นี้ฉันได้รับคำตอบ / คำถามที่คล้ายกันด้วยตัวเอง นี่คือสิ่งที่ฉันทำเผื่อว่าใครสนใจ:

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

หากต้องการเรียกว่าคุณต้องแก้ไขบรรทัดนี้เท่านั้น:

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

และคำใบ้ประเภทหากคุณใช้เพื่อ (\ Library \ PDO $ DB)

มันคล้ายกับทั้งคำตอบที่ยอมรับและของคุณ อย่างไรก็ตามมันมีข้อได้เปรียบที่โดดเด่น พิจารณารหัสนี้:

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

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

แม้ว่ามันอาจจะดูเหมือน PDO ปกติ (มันเปลี่ยนไปตามนั้น\Library\เท่านั้น) แต่จริงๆแล้วมันจะไม่เริ่มต้นอ็อบเจ็กต์จนกว่าคุณจะเรียกใช้เมธอดแรกไม่ว่าจะเป็นวิธีใดก็ตาม ทำให้ได้รับการปรับให้เหมาะสมมากขึ้นเนื่องจากการสร้างวัตถุ PDO มีราคาแพงเล็กน้อย มันเป็นระดับความโปร่งใสหรือสิ่งที่มันเรียกว่าผี , รูปแบบของการขี้เกียจโหลด คุณสามารถถือว่า $ DB เป็นอินสแตนซ์ PDO ปกติส่งผ่านไปรอบ ๆ ดำเนินการแบบเดียวกันเป็นต้น


เรียกว่า "ลายมัณฑนากร"
ยาง

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();
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.