PDO接続を正しく設定する方法


92

データベースへの接続に関する質問がときどきあります。
ほとんどの答えは私がそれをする方法ではありません、または私はちょうど答えを正しく得ないかもしれません。とにかく; 自分のやり方がうまくいくので、私はそれについて考えたことはありません。

しかし、これはおかしな考えです。多分私はこれをすべて間違っています、そしてそれが事実なら。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を学び始め、mysqlの代わりにPDOを使用して以来、大幅な改善の余地があると確信しています。
だから私はいくつかの初心者向けチュートリアルをたどって、さまざまなものを試しました...

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)

my sqlQuery- classの内側にあるextendsmy connect_pdo- classにはgetAreaName、データベースへの要求を処理するというパブリック関数が呼び出されています。
かなりきちんと思います。

チャームのように機能する
ので、基本的にはそれが私のやり方です。
また、クラス内からではなくDBから何かをフェッチする必要があるときはいつでも、次のようなことを行います。

$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
アプリケーションにすべてのファイルを一度に含めるのではなく、オートローダーを使用する必要があります。
ルシタニア

4
この質問は、おそらくコードレビューで
マダラのゴースト

回答:


105

目標

ご覧のとおり、この場合の目的は2つあります。

  • データベースごとに単一の/再利用可能な接続を作成して維持する
  • 接続が正しく設定されていることを確認してください

解決

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

}

この方法により、必要な場合にのみ接続が確実に作成されるようにする集中型構造が可能になります。また、単体テストとメンテナンスのプロセスがはるかに簡単になります。

この場合のプロバイダーは、ブートストラップ段階のどこかにあります。このアプローチは、DBへの接続に使用する構成を定義するための明確な場所も提供します。

これは非常に単純化された例であることを覚えておいてください。また、次の2つのビデオを見ると効果がある場合があります。

また、PDOの使用に関する適切なチュートリアルを読むことを強くお勧めします(オンラインには不適切なチュートリアルのログがあります)。


3
PHP5.3もEOLに近づいているので。古いバージョンのPHPを使用しているサイトの大部分は、実際にはWordpressのホスティングが安いだけです。私の見積もりでは、5.3より前の環境が専門能力開発に与える影響(このようなスニペットの恩恵を受けるようなもの)はごくわずかです。
tereško

5
@thelolcat私はあなたに同意します。それ多かれ少なかれ同じ答えです。それはあなたがそれが完全に異なるという事実を見ない場合です。
PeeHaa

1
@thelolcat、依存関係の注入とは何かを学ぶ必要があります。自分を困らせ続ける代わりに。おかしなことに、上記の投稿の2番目のビデオ(タイトル:"Do n't Look For Things")は、実際にはDIとは何か、およびその使用方法を説明しています。
テレシュコ

2
これは古い答えですが、良い答えです-そして最後にMysql pdoへの素晴らしいリンク
Strawberry

1
@teeceeあなたはまずPDOの使い方を学ぶことから始めるべきです。このチュートリアルはwiki.hashphp.org/PDO_Tutorial_for_MySQL_Developersをお勧めします。これはmysql_*、PDO からの移行を希望する人のために作られたものだからです。次に、戻ってこのソリューションを見てみましょう。このソリューションは、すでにPDOを使用しているが、複数のクラス間でDB接続を共有する方法が必要なソリューションを対象としています。
テレシュコ2016年

24

を使用$_SESSIONしてDB接続にグローバルにアクセスしないことをお勧めします。

いくつかのことの1つを実行できます(ワーストからベストプラクティスの順に)。

  • 関数とクラスの内部を$dbh使用しglobal $dbhてアクセスする
  • シングルトンレジストリを使用して、次のようにグローバルにアクセスします。

    $registry = MyRegistry::getInstance();
    $dbh = $registry->getDbh();
  • 次のように、データベースハンドラーを必要とするクラスに挿入します。

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

私は最後のものを強くお勧めします。これは、依存性注入(DI)、制御の反転(IoC)、または単にハリウッドの原則(私たちに電話しないで、私たちはあなたに電話します)として知られています。

ただし、これは少し高度であり、フレームワークなしでより多くの「配線」が必要です。したがって、依存性注入が複雑すぎる場合は、一連のグローバル変数の代わりにシングルトンレジストリを使用します。


それで、自分のsqlQuery-classを拡張してセッションに設定すると、私のdb接続にグローバルにアクセスしconnect_pdoます
ThomasK

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オブジェクトの作成には少しコストがかかるため、これにより、より最適化されます。これは透過的なクラス、またはGhostと呼ばれるもので、レイジーローディングの一種です。$ 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();
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.