Как правильно настроить соединение 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
мой 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, я просто ссылаюсь на него, и я готов к работе. Оно работает. Я получаю ожидаемые результаты...
Но независимо от этого; Я был бы очень признателен, если бы вы, ребята, могли сказать мне, если я далеко отсюда. Что я должен сделать вместо этого, области, которые я мог или должен изменить для улучшения и т.д...
Я хочу учиться...
5 ответов
Цель
На мой взгляд, ваша цель в этом случае двоякая:
- создать и поддерживать одно / многоразовое соединение для каждой базы данных
- убедитесь, что соединение установлено правильно
Решение
Я бы рекомендовал использовать как анонимную функцию, так и фабричный шаблон для работы с 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 (в Интернете есть журнал плохих учебников).
Я бы предложил не использовать $_SESSION
для доступа к вашему соединению с БД в глобальном масштабе.
Вы можете сделать одну из нескольких вещей (в порядке от худшего до лучших практик):
- Доступ
$dbh
с помощьюglobal $dbh
внутри ваших функций и классов Используйте единый реестр и получите доступ к нему глобально, например так:
$registry = MyRegistry::getInstance(); $dbh = $registry->getDbh();
Вставьте обработчик базы данных в классы, которые в нем нуждаются, например, так:
class MyClass { public function __construct($dbh) { /* ... */ } }
Я очень рекомендую последний. Это известно как внедрение зависимостей (DI), инверсия управления (IoC) или просто принцип Голливуда (не звоните нам, мы вам позвоним).
Тем не менее, он немного более продвинутый и требует больше "проводки" без каркаса. Поэтому, если внедрение зависимостей слишком сложно для вас, используйте одноэлементный реестр вместо набора глобальных переменных.
Я недавно пришел к аналогичному ответу / вопросу самостоятельно. Это то, что я сделал, если кому-то интересно:
<?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 немного дороже. Это прозрачный класс или, как его называют, Ghost, форма Lazy Loading. Вы можете рассматривать $ DB как обычный экземпляр PDO, передавать его, выполнять те же операции и т. Д.
В вашей настройке есть несколько основных недостатков:
- В
Файл не должен находиться в корне документа веб-сервера - если сервер когда-либо будет неправильно настроен, вы можете раскрыть учетные данные для общественности. Вы можете подумать, что с вами этого не случится, но вам просто не нужно идти на такой риск. Классов тоже не должно быть ... это не так важно, но все же все, что не должно быть публичным, не должно быть публичным. - Если вы не работаете с особенно большим проектом, у вас не должно быть каталога «инициализации». Загрузка одного большого файла примерно в 10 раз быстрее, чем загрузка десяти маленьких файлов с таким же содержимым. Это имеет тенденцию действительно накапливаться по мере роста проекта и может действительно замедлять работу PHP-сайтов.
- Старайтесь не загружать вещи, если это действительно не нужно. Например, не подключайтесь к PDO, если в этом нет необходимости. Не надо
вы фактически читаете / пишете в сессию. Не включайте файл определения класса, если вы не создаете экземпляр класса. У вас есть ограничения на количество подключений. И API, такие как сеанс, устанавливают «блокировки», которые могут приостанавливать выполнение кода для других людей, использующих тот же ресурс. - Насколько я могу судить, вы не используете Composer. Вы должны использовать его - это значительно упростит жизнь как для вашего собственного кода, так и для сторонних зависимостей.
Вот предлагаемая мной структура каталогов, аналогичная той, что я использую для проектов среднего размера:
init.php Replaces public_html/initialize. Your PDO connection details
are held here.
classes/ Replaces public_html/classes
vendor/autoload.php Your class autoload script generated using the
industry standard Composer command line tool
composer.json The file where you describe how autoload.php
operates among other things. For example if you
don't use namespaces (maybe you should) it might be:
{"autoload": {"psr-4": { "": "classes/" }}}
public_html/index.php Your landing page
public_html/other.php Some other page
public_html/css/foobar.css ...and so on for all static resources
В
date_default_timezone_set('Etc/UTC');
require 'vendor/autoload.php';
$pdoConnect = function() {
static $pdo = false;
if (!$pdo) {
$pdo = new PDO('mysql:dbname=db;host=localhost', 'user', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
}
return $pdo;
};
// similar anonymous functions for session_start(), etc.
может выглядеть так:
require '../init.php';
$pdo = $pdoConnect();
// go from there
может быть похожим, но, возможно, он не подключается к базе данных, поэтому не выполняет $pdoConnect.
Насколько это возможно, вы должны записать большую часть вашего кода в каталог классов. Хранить
$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