Как правильно настроить 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 и выполняю другие глобальные настройки для сайта.

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моим 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 , я просто ссылаюсь на него и готов к работе. Оно работает. Я получаю ожидаемый результат ...

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

Я очень хочу учиться ...

ThomasK
источник
9
Вам следует использовать автозагрузчик вместо того, чтобы включать каждый файл в ваше приложение сразу.
Lusitanian
4
Этот вопрос, вероятно, лучше всего подходит для Code Review
Призрак Мадары

Ответы:

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 в конце
Strawberry
1
@teecee, вы должны начать с изучения того, как использовать PDO. Я бы порекомендовал этот учебник: wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers , потому что он создан специально для людей, которые хотят перейти с mysql_*на PDO. Затем вы можете вернуться и посмотреть на это решение, которое предназначено для тех, кто уже использует PDO, но нуждается в способе совместного использования соединения с БД между несколькими классами.
терешко
24

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

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

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

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

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

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

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

Ян Унру
источник
Итак, я получаю доступ к своему db-соединению глобально, когда я устанавливаю свой sqlQueryкласс в сеанс, поскольку он расширяется connect_pdo?
ThomasK 06
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();
}
привет-код
источник