如何正确设置PDO连接

时间:2012-07-06 20:37:42

标签: php mysql sql class pdo

我不时会看到有关连接数据库的问题 大多数答案不是我这样做的方式,或者我可能没有正确得到答案。无论如何;我从来没有想过这个,因为我这样做对我有用。

但这是一个疯狂的想法;也许我这样做是错的,如果是这样的话;我真的想知道如何使用PHP和PDO正确连接到MySQL数据库,并使其易于访问。

以下是我的做法:

首先,这是我的文件结构(剥离)

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

在这里,我相信自从我最近开始学习OOP,并使用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 - ,我有一个名为{{的公共函数1}}处理对我的数据库的请求 我觉得很整洁。

像魅力一样工作
所以我基本上就是这样做的。
此外,每当我需要从我的数据库中取出某些内容时,我就会做类似的事情:

getAreaName

由于我将连接放入 connect_pdo.php 中的变量,我只是引用了它,我很高兴。有用。我得到了预期的结果......

但不管那个;如果你们能告诉我,如果我离开这里,我真的很感激。我应该做些什么,我可以或应该改变哪些方面进行改进......

我渴望学习......

5 个答案:

答案 0 :(得分:103)

目标

在我看来,你在这种情况下的目标是双重的:

  • 为每个数据库创建和维护单个/可重用连接
  • 确保连接已正确设置

解决方案

我建议使用匿名函数和工厂模式来处理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使用的a proper tutorial(在线有一个不良教程日志)。

答案 1 :(得分:23)

我建议不要使用$_SESSION全局访问您的数据库连接。

你可以做一些事情(按照最差到最佳做法的顺序):

  • 使用功能和类内的$dbh访问global $dbh
  • 使用单例注册表,并全局访问,如下所示:

    $registry = MyRegistry::getInstance();
    $dbh = $registry->getDbh();
    
  • 将数据库处理程序注入需要它的类中,如下所示:

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

我强烈推荐最后一个。它被称为依赖注入(DI),控制反转(IoC),或简称好莱坞原则(不要打电话给我们,我们会打电话给你)。

然而,它更先进,需要更多的“布线”而没有框架。因此,如果依赖注入过于复杂,请使用单例注册表而不是一堆全局变量。

答案 2 :(得分: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对象创建稍微昂贵。它是一个透明的类,或称为GhostLazy Loading形式。您可以将$ DB视为普通的PDO实例,传递它,执行相同的操作等。

答案 3 :(得分: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();
}

或阅读http://ask.hcig.co.za/?p=179

答案 4 :(得分:0)

您的设置存在一些基本缺陷:

  1. configure.php 文件不应位于网络服务器的文档根目录中 - 如果服务器配置错误,您可能会向公众公开凭据。你可能认为它不会发生在你身上,但这是一种你不需要承担的风险。类也不应该在那里......这并不重要,但任何不需要公开的东西都不应该公开。
  2. 除非您正在处理一个特别大的项目,否则您不应该有“初始化”目录。加载一个大文件比加载具有相同内容的十个小文件快大约 10 倍。随着项目的发展,这往往会加起来,并且会真正降低 PHP 网站的速度。
  3. 除非您确实需要,否则尽量不要加载内容。例如,除非您确实需要,否则不要与 PDO 连接。不要session_start()您实际上读/写会话。除非您创建类的实例,否则不要包含类定义文件。您可以拥有的连接数量是有限的。诸如会话之类的 API 会建立“锁”,可以为使用相同资源的其他人暂停代码执行。
  4. 据我所知,您没有使用 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

init.php 文件可能类似于:

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.

index.php 可能看起来像:

require '../init.php';

$pdo = $pdoConnect();

// go from there

other.php 可能类似,但可能没有连接到数据库,因此不会执行 $pdoConnect。

您应该尽可能将大部分代码写入 classes 目录。保持 index.phpother.php 等尽可能简短和亲切。

相关问题