Как правильно настроить соединение PDO

Время от времени я вижу вопросы о подключении к базе данных.
Большинство ответов - это не то, как я это делаю, или я просто не могу правильно ответить. Так или иначе; Я никогда не думал об этом, потому что способ, которым я это делаю, работает для меня.

Но здесь сумасшедшая мысль; Возможно, я делаю все это неправильно, и если это так; Мне бы очень хотелось знать, как правильно подключиться к базе данных 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 и выполняю некоторые другие глобальные конфигурации для сайта

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.
Поэтому я только что последовал за несколькими учебниками для начинающих и опробовал разные вещи...

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 my connect_pdo -class, у меня есть открытая функция 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, я просто ссылаюсь на нее, и мне хорошо идти. Оно работает. Я получаю ожидаемые результаты...

Но независимо от этого; Я был бы очень признателен, если бы вы, ребята, могли сказать мне, если я уйду отсюда. То, что я должен делать, области, которые я мог или должен изменить для улучшения и т.д....

Я очень хочу узнать...

Ответ 1

Цель

Как я вижу, ваша цель в этом случае двоякая:

  • создать и поддерживать одно/многоразовое соединение для каждой базы данных
  • убедитесь, что соединение настроено правильно

Решение

Я бы рекомендовал использовать как анонимную функцию, так и шаблон factory для работы с 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');

Сам factory должен выглядеть примерно так:

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 (есть журнал плохого учебника онлайн).

Ответ 2

Я бы предложил не использовать $_SESSION для доступа к вашему соединению с БД глобально.

Вы можете сделать одну из нескольких вещей (в порядке худших и лучших):

  • Доступ $dbh с помощью global $dbh внутри ваших функций и классов
  • Используйте одноэлементный реестр и получите доступ к нему глобально, например:

    $registry = MyRegistry::getInstance();
    $dbh = $registry->getDbh();
    
  • Внесите обработчик базы данных в нужные ему классы:

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

Я бы очень рекомендовал последний. Он известен как инъекция зависимостей (DI), инверсия контроля (IoC) или просто принцип Голливуда (не называйте нас, мы вам позвоним).

Однако он немного более продвинутый и требует больше "проводов" без рамки. Итак, если инъекция зависимостей слишком сложна для вас, используйте единый реестр, а не кучу глобальных переменных.

Ответ 3

Недавно я пришел к аналогичному ответу/вопросу самостоятельно. Это то, что я сделал, в случае, если кто-то заинтересован:

<?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 немного дорого. Это прозрачный класс или то, что он назвал Призрак, форма Lazy Loading. Вы можете рассматривать $DB как обычный PDO-экземпляр, передавать его, выполнять те же операции и т.д.

Ответ 4

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

или читать http://ask.hcig.co.za/?p=179