Як правильно налаштувати з’єднання 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. щось схоже...

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

Тут я впевнений, що є місце для значного вдосконалення, оскільки нещодавно я почав вивчати ООП та використовувати PDO замість mysql.
Отже, я щойно прослухав кілька підручників для початківців і випробував різні речі ...

session.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
Вам слід використовувати автозавантажувач замість того, щоб включати кожен файл у свою програму одночасно.
Лузитаній

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

}

Таким чином ви отримаєте централізовану структуру, яка гарантує, що з'єднання створюється лише тоді, коли це потрібно. Це також значно полегшить процес модульного тестування та обслуговування.

У цьому випадку постачальника можна знайти десь на етапі завантаження. Цей підхід також дасть чітке місце для визначення конфігурації, яка використовується для підключення до БД.

Майте на увазі, що це надзвичайно спрощений приклад . Ви також можете скористатися переглядом двох таких відео:

Крім того, я настійно рекомендую прочитати правильний підручник про використання PDO (в Інтернеті є журнал поганих підручників).


3
Оскільки навіть PHP5.3 наближається до EOL. Більшість сайтів із застарілими версіями PHP насправді є просто дешевим хостингом для Wordpress. Вплив середовищ, що передують версії 5.3, на професійний розвиток (вони, напевно, виграють від подібних фрагментів), за моєю оцінкою, незначний.
tereško

5
@thelolcat Я з вами згоден. Це є більш-менш той же самий відповідь. Тобто, якщо ви не бачите того, що це зовсім інше.
PeeHaa

1
@thelolcat, тоді ви повинні дізнатися, що таке ін'єкція залежності . Замість того, щоб продовжувати бентежити себе. Як не дивно, друге відео у дописі вище (під назвою: "Не шукайте речей" ) насправді пояснило, що таке DI і як ним користуватися ... але, звичайно, ви занадто просунуті для таких дрібниць.
tereško

2
Це стара відповідь, але хороша - і чудове посилання на Mysql pdo наприкінці
Полуниця

1
@teecee вам слід почати з того, що спочатку навчитесь користуватися PDO. Я б порекомендував цей підручник: wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers , оскільки він створений саме для людей, які хочуть перейти з mysql_*PDO. Тоді ви можете повернутися і подивитися на ці рішення, які орієнтовані на тих, хто вже використовує PDO, але потребує способу спільного використання з'єднання БД між кількома класами.
tereško

24

Я б радив не використовувати $_SESSIONдля глобального доступу до з’єднання з БД.

Ви можете зробити одну з кількох речей (у порядку, що відповідає найкращим з найкращих практик):

  • Доступ, $dbhвикористовуючи global $dbhвсередині ваших функцій та класів
  • Використовуйте синглтон-реєстр і отримуйте доступ до нього в усьому світі, наприклад:

    $registry = MyRegistry::getInstance();
    $dbh = $registry->getDbh();
  • Введіть обробник бази даних у класи, яким це потрібно, наприклад:

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

Дуже рекомендую останню. Він відомий як ін’єкція залежності (DI), інверсія управління (IoC) або просто голлівудський принцип (не дзвоніть нам, ми зателефонуємо вам).

Однак він трохи вдосконалений і вимагає більше «проводки» без каркаса. Отже, якщо ін’єкція залежностей занадто складна для вас, використовуйте синглтон-реєстр замість групи глобальних змінних.


Отже, я отримую доступ до мого з’єднання db глобально, коли я встановлюю свій sqlQueryклас у сеанс, оскільки він розширюється 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();
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.