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

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

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


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();
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.