Movatterモバイル変換


[0]ホーム

URL:


低価格・高品質・最速のHTMLコーディングはクロノドライブへ

Chrono Drive(クロノドライブ)

wordpressの$query_stringからデータベース接続までの三千里

このエントリーをはてなブックマークに追加

エンジニアリング部エンジニアリング課のひよっこエンジニアKと申します。最近私はwordpressについて勉強しているのですが、ふとその中で$query_stringという変数に出会いました。今回の記事では、$query_stringがどんな変数なのかと、それを知るにあたって私が調べた、$query_stringを使って記事を取得するコードを書いてから、データベースから$query_stringで指定された記事が引っ張ってこられて表示されるまでの三千里(嘘)の道のりをご紹介しようと思います。

 

今回$query_stringを使ったコード

今回私が$query_stringのコードを使ったページはarchive.phpのページでした。

archive.php
PHP
1
2
3
4
parse_str($query_string,$args);
$args['posts_per_page']=5;
$args['order']='DESC';
$query=newWP_Query($args);

$query_stringをここでvar_dumpすると、

var_dump($query_string);

出力は以下の通りになりました。
[そのページがカテゴリーのアーカイブページの場合]

string(75)"category_name=%25e3%2582%25ab%25e3%2583%2586%25e3%2582%25b4%25e3%2583%25aa1"

[そのページが月別アーカイブページの場合]
string(21)"year=2019&monthnum=09"
そのページがカテゴリーのアーカイブページの時は、category_nameというキーにその値が、そのページが月間アーカイブページの時は、yearというキーとmonthnumというキーにそれぞれ値が入っていることがわかります。

結局$query_stringでやってたことってなんだったの?

今回$query_stringを使ったコードで、$query_stringを使って何をやっていたのか?を簡単に言うと、wp_title関数から取得した値を配列に変換し、表示する記事の条件を指定する一部を担っていた、となります。上記のページではparse_str関数を使い、$query_stringで指定した条件と$argsで指定した条件を$argsにまとめ、その$argsによってWP_Queryのインスタンスを生成することでデータベースから希望の条件をすべて満たした記事を引っ張ってきて表示していました。

今回の旅のゴール地点

今回の旅の最終地点は、どうやって変数$query_stringからSQL文が形成され、データベースからデータを持ってきているかの全貌を明らかにすることです。なので、以下の順に処理を追って、データベースに接続している箇所とデータベースにどうクエリを渡しているかを明らかにします。
①まず取得された値を文字列として変数に格納している箇所を探す
②文字列と文字列をくっつけてSQL文の文字列にしている箇所を探す
③データベース接続して、SQL文をデータベース側に渡している箇所を探す

$query_stringからデータベースへの接続までの三千里

①$query_stringに入っていた値を変数に格納する

まず$query_stringに入っていた値を変数に格納します。今回WP_Queryのインスタンスを生成しているときにデータベースに接続がなされていたようだったので(SQLログを調べました)、WP_Queryのクラス定義をしているファイルの中で、$query_stringに入っていた値を変数に渡している箇所がないか探しました。すると、

$q
PHP
1
2
3
4
if($q['year'])
$date_parameters['year']=$q['year'];
if($q['monthnum'])
$date_parameters['monthnum']=$q['monthnum'];

という辺りで年月の値が渡っていることが明らかになりました。どうやらここでまずは値を変数に格納しているようです。

②文字列と文字列をくっつけてSQL文にする

その後、$data_parametersという変数の定義を見てみると、

$data_parameters
PHP
1
2
3
4
if($date_parameters){
    $date_query=newWP_Date_Query(array($date_parameters));
    $where.=$date_query->get_sql();
}

というような処理を見つけます。ここで$whereをvar_dumpしてみると、なんとSQLログで見たことのある文字列の一部が…!どうやらここでSQL文の切れ端が作られていたみたいです。

この$whereを使ってSQL文の形の文字列を作っている箇所がないか探すと…

$this->request=$old_request="SELECT $found_rows $distinct $fields FROM {$wpdb->posts} $join WHERE 1=1 $where $groupby $orderby $limits";

なんと、めちゃくちゃSQLのSELECT文の文字列を作ってそうな場所にめぐりあいました。

③データベース接続して、SQL文をデータベース側に渡す

その後、この$this->requestを使ってデータベース接続してそうな場所がないか探します。すると、ちょっと気になる箇所が出てきました。

$ids=$wpdb->get_col($this->request);

この$idsをvar_dumpすると、記事更新日時が新しい順の、$query_stringで指定した条件に当てはまる記事IDが5件出力されます。ということは、$idsに代入された時点では、もうデータベース接続に成功しています。つまり、それ以前にデータベース接続をしているということがわかります。$idsに代入される直前にあたるのは、$wpdbか、get_col()になります。まずは$wpdbの方を見てみましょう。

 $wpdbとデータベース接続

$wpdbはWordPressのグローバル変数の一つで、今までのファイルとは違うload.phpに定義があります。さっそく定義箇所を見てみましょう。

function require_wp_db()
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
functionrequire_wp_db(){
 
    global$wpdb;
 
    require_once(ABSPATH.WPINC.'/wp-db.php');
 
    if(file_exists(WP_CONTENT_DIR.'/db.php'))
 
    require_once(WP_CONTENT_DIR.'/db.php');
 
    if(isset($wpdb)){
 
        return;
 
    }
 
    $wpdb=newwpdb(DB_USER,DB_PASSWORD,DB_NAME,DB_HOST);
 
}

最後でデータベース接続におなじみのDB_USERなどを使ったインスタンスが生成されていることがわかります。あともう一息!

wpdbというインスタンスを生成していたので、今度はwpdbというクラスの定義を探してみます。すると、wp-db.phpというファイルの、wpdbクラスのコンストラクタでこんな記述を見つけました。

function __construct()
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
publicfunction__construct($dbuser,$dbpassword,$dbname,$dbhost){
 
    register_shutdown_function(array($this,'__destruct'));
 
    if(WP_DEBUG&&WP_DEBUG_DISPLAY)
 
    $this->show_errors();
 
    // Use ext/mysqli if it exists unless WP_USE_EXT_MYSQL is defined as true
 
    if(function_exists('mysqli_connect')){
 
        $this->use_mysqli=true;
 
       if(defined('WP_USE_EXT_MYSQL')){
 
            $this->use_mysqli=!WP_USE_EXT_MYSQL;
 
        }
 
    }
 
    $this->dbuser=$dbuser;
 
    $this->dbpassword=$dbpassword;
 
    $this->dbname=$dbname;
 
    $this->dbhost=$dbhost;
 
    // wp-config.php creation will manually connect when ready.
 
    if(defined('WP_SETUP_CONFIG')){
 
        return;
 
    }
 
    $this->db_connect();
 
}

どうやら、$this->db_connect();の部分でデータベース接続を行っていそうです!db_connect関数の定義の一部を見てみると、

function db_connect
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
publicfunctiondb_connect($allow_bail=true){
 
    // 中略
 
    $this->has_connected=true;
 
    $this->set_charset($this->dbh);
 
    $this->ready=true;
 
    $this->set_sql_mode();
 
    $this->select($this->dbname,$this->dbh);
 
    returntrue;
 
}

このselectという関数を使って、データベースを選択していそうなことがわかります。

実際、select関数の定義を調べてみると、mysqli_select_db関数でデータベースの選択を行っています。

function select
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
publicfunctionselect($db,$dbh=null){
 
    if(is_null($dbh))
 
    $dbh=$this->dbh;
 
    if($this->use_mysqli){
 
        $success=mysqli_select_db($dbh,$db);
 
    }else{
 
        $success=mysql_select_db($db,$dbh);
 
    }
 
    //以下略
 
}

ということは、やはりこの段階でデータベースに接続を行っていそうです。これでWordPressがどういう経緯でデータベースを接続していたか、全貌が明らかになりましたね。

get_col()とクエリ

めでたくデータベース接続の流れを解き明かせた次は、get_col()を使ってどうクエリを渡しているのかを見ていきましょう。さっそくget_col()の定義を確認してみます。

function get_col
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
publicfunctionget_col($query=null,$x=0){
 
    if($this->check_current_query&&$this->check_safe_collation($query)){
 
        $this->check_current_query=false;
 
    }
 
    if($query){
 
        $this->query($query);
 
    }
 
    $new_array=array();
 
    // Extract the column values
 
    for($i=0,$j=count($this->last_result);$i<$j;$i++){
 
        $new_array[$i]=$this->get_var(null,$x,$i);
 
    }
 
    return$new_array;
 
}

この中で、$queryをvar_dumpしてみると、②の段階で見つけたSQLのSELECT文の切れ端を含むSELECT文が出力されました。一方で$this->last_resultや$new_arrayをvar_dumpしてみると、指定の記事IDが取得されてきていることがわかります。つまり、query関数を実行している辺りでデータベースにクエリが渡っていそうなことがわかります。さっそくquery関数の定義を見てみましょう。

function query
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
publicfunctionquery($query){
 
    // 中略
 
    $query=apply_filters('query',$query);
 
    //ここでvar_dumpするとすべてのSQL文が表示される
 
    $this->flush();
 
    // Log how the function was called
 
    $this->func_call="\$db->query(\"$query\")";
 
    // If we're writing to the database, make sure the query will write safely.
 
    if($this->check_current_query&&!$this->check_ascii($query)){
 
        $stripped_query=$this->strip_invalid_text_from_query($query);
 
       // strip_invalid_text_from_query() can perform queries, so we need
 
        // to flush again, just to make sure everything is clear.
 
        $this->flush();
 
        if($stripped_query!==$query){
 
            $this->insert_id=0;
 
            returnfalse;
 
        }
 
    }
 
    $this->check_current_query=true;
 
    // Keep track of the last query for debug.
 
    $this->last_query=$query;
 
    $this->_do_query($query);
 
    // 中略
 
    if(preg_match('/^\s*(create|alter|truncate|drop)\s/i',$query)){
 
        $return_val=$this->result;
 
    }elseif(preg_match('/^\s*(insert|delete|update|replace)\s/i',$query)){
 
        if($this->use_mysqli){
 
            $this->rows_affected=mysqli_affected_rows($this->dbh);
 
        }else{
 
            $this->rows_affected=mysql_affected_rows($this->dbh);
 
        }
 
        // Take note of the insert_id
 
        if(preg_match('/^\s*(insert|replace)\s/i',$query)){
 
            if($this->use_mysqli){
 
                $this->insert_id=mysqli_insert_id($this->dbh);
 
            }else{
 
                $this->insert_id=mysql_insert_id($this->dbh);
 
            }
 
        }
 
        //中略
 
        // Log number of rows the query returned
 
        // and return number of rows selected
 
        $this->num_rows=$num_rows;
 
        $return_val  =$num_rows;
 
    }
 
    return$return_val;
 
}

$this->last_query = $query;直後でvar_dumpしてみると、まだすべてのSQL文が文字列として表示されています。 一方で、その後は$this->insert_id = mysqli_insert_id( $this->dbh );など、クエリを実行した結果に対しての返り値を変数に代入している部分が存在します。ということは、$this->last_query = $query;以下で少なくとも文字列がデータベース上に入り、実行されていることがわかります。さっそく$this->last_query = $query;直後に実行される、_do_query関数を見てみましょう。

function _do_query
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
privatefunction_do_query($query){
 
    if(defined('SAVEQUERIES')&&SAVEQUERIES){
 
        $this->timer_start();
 
    }
 
    if(!empty($this->dbh)&&$this->use_mysqli){
 
        $this->result=mysqli_query($this->dbh,$query);
 
    }elseif(!empty($this->dbh)){
 
        $this->result=mysql_query($query,$this->dbh);
 
    }
 
    $this->num_queries++;
 
    if(defined('SAVEQUERIES')&&SAVEQUERIES){
 
        $this->queries[]=array($query,$this->timer_stop(),$this->get_caller());
 
    }
 
}

定義の中をよく見てみると、mysqli_query関数がありました。この関数はデータベース上でクエリを実行する関数です。つまり、この時点でまさにクエリが実行され、データベースからデータを取得してきたということがわかりました。

今回のまとめ

①$query_stringでは、取ってくる記事を指定する条件の一部を担っている

②$query_stringからデータベース接続までの流れは、値を変数に代入→値を使った文字列をどんどん他の文字列とつなぎ合わせていく→データベースにそのクエリを引き渡してデータを貰ってくるの3段階になっている

③グローバル変数$wpdbによってデータベース接続が、(今回は)get_col関数によってデータベースにクエリが引き渡されている

 

「フロントエンドエンジニアの教科書」を出版しました!HTML・CSS・JavaScript+α 次世代コーダーのための仕事術

HTMLコーダーからフロントエンドエンジニアにステップするために必要な知識と技術を解説。
現場で求められる人材となるためには、何を知っていて、何ができなければいけないのか。
Web制作の最先端にいるクロノドライブだからこそ教えられるノウハウが満載です。

出版社名:ソフトバンククリエイティブ
著者:クロノドライブ

Amazon.co.jp詳細ページへ

Pick upコンテンツ

  • コーディングノウハウコラム
  • HTML辞典
  • CSS辞典
  • コーディングガイドライン
  • お客様の声

[8]ページ先頭

©2009-2025 Movatter.jp