HTTP通信とPHPのスーパーグローバル変数の関係

2015/12/30

HTTP

HTTP通信はクライアントからのリクエストに対してApacheサーバーがレスポンスを返すことで成立します。

ジャカルタ

インドネシアのITサービス

インターネット技術の急速な発展と普及により、優秀なIT人材を輩出することで知られるジャカルタのビヌス大学(BINUS)やバンドゥンのバンドゥン工科大学、インドネシアコンピューター大学(UNIKOM)の学生の多くがインターネット・WEB業界やソフトウェア業界を志望するようです。

続きを見る

Basic認証の概要

ApacheなどのWebサーバの動作をディレクトリ単位で制御するためのファイルであり、具体的には、CGIやSSIなどを実行するための宣言(命令)や、拡張子ごとにファイルタイプを指定するMIMEタイプの設定、ユーザ認証、IPアドレスやドメイン単位でのアクセス制限などを書き込むことができます。

.htaccessファイルで設定した内容は、.htaccessファイルがあるディレクトリとそのサブディレクトリに効果があり、効果があるディレクトリに入っているファイルすべてに影響を及ぼします。また、サブディレクトリにも.htaccessファイルを別に置くこともでき、この場合は両方のファイルの効果が発生する(矛盾するときはサブディレクトリの設定を優先)。

名前が「.htaccess」なだけで、その正体は普通のテキストファイルなのでメモ帳で作成可能です。.htaccessで保護されたフォルダにはロボット検索もは入れません。ちなみに検索ロボットは「クローラー」「スパイダー」などとも呼ばれ、 常にWEBを巡回し、巡回したウェブサイトの情報をデータベース化するプログラムです。

.htaccessの書き方

レンタルサーバーのコントロールパネルの「アカウント情報」部分に絶対パスが書いてありますのでこれをファイルパスとして記述します。

AuthUserFile /home/users/1/○○○○○/web/△△△△△/admin/.htpasswd
AuthGroupFile /dev/null
AuthName "Input your ID and Password"
AuthType Basic
require valid-user

.htpasswdの書き方

ユーザーIDとパスワードを書くわけだですが、パスワード部分はインターネット上の暗号化サイトで暗号化するのが一般的です。

hogehoge:4CUbLFOhm/sNg

ちなみにBASIC認証によるログインは普通のログインと違い一度ブラウザを閉じないとログアウトできない仕様になっています。

お手軽にユーザー認証をかけられるのはいいのですが、Basic認証はパスワードがそのままHTTPのネットワークを流れるので、盗聴などセキュリティ上の危険性が高いことは認識しておくべきです。

Apache設定ファイルにディレクティブの値としてHTTP環境変数を設定

HTTPサーバーとしての最大の機能はバーチャルホストであり、物理ディレクトリを仮想サーバーのDocumentRootに指定してURLを割り当てることで、1つのApacheサーバーで複数のドメインや複数のサブドメインの管理することができます。

Apacheサーバーのチューニングをするといういことは、Apache設定ファイルであるhttpd.confに、Apacheディレクティブと値(ON/OFF, 数字, HTTP環境変数, URLなど)を設定することであり、これを親子関係にある.htaccessに設定して、ディレクトリ単位に配置することもできます。

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://bahtera.jp/$1 [R=301,L]
</IfModule>

.htaccessに、Apacheのrewrite_modeモジュールが実装するディレクティブ(コマンド・命令)を記述し、任意のディレクトリに配置することによってhttpd.confによる設定を上書きします。

例えばRewriteCondというディレクティブは、%{xxxx}形式で環境変数やリクエストヘッダの情報を参照するので、%{HTTPS}であれば、サーバー変数であるHTTPSを参照し、SSL通信がOFFであるかどうかを判断しています。

ApacheのHTTP環境変数はサーバー変数とも呼ばれ、変数ごとに固有の情報を記憶する仕組みを実装しており、この情報はログ収集やアクセス制御に使われます。

なおサーバー変数はWindowsの環境変数とは別物です。

ディレクティブの適用範囲を限定して設定するにはコンテナタグ「<>」の中で<ディレクティブ 値>の形式で記述します。

  1. ディレクトリごとの設定は<Directory "D:/xammp/htdocs">
  2. ファイルごとの設定は<Files ".ht*">
  3. URLごとの設定は<Location>
  4. 仮想ホストごとの設定は<ViurtualHost>

.htaccessを有効にするためにhttpd.conf内のAllowOverrideをAllにする必要があります。

<Directory "D:/xammp/htdocs">
AllowOverride All
</Directory>

ちなみにApacheサーバーのディレクトリ単位にBasic認証をかけるための.htaccessファイルにはApacheのディレクティブを記載します。

AuthUserFile /home/users/1/○○○○○/web/△△△△△/admin/.htpasswd
AuthGroupFile /dev/null
AuthName "Input your ID and Password"
AuthType Basic
require valid-user

register_globalディレクティブの機能

WordPress起動時に読み込まれるPHPファイルを追っていくためには、php.ini、httpd.conf、.htaccessファイル、PHPスーパーグローバル変数、サーバー変数、HTTP通信など、WEB通信で登場するキーワードの相関関係を理解する必要があります。

WordPressを起動する際の初期設定モジュールであるwp-settings.phpから、load.phpのwp_unregister_GLOBALS()関数を呼び出して、php.iniのregister_globalディレクティブをOFFにします。

スーパーグローバル変数$_GETや$_POSTが取得するデータは

  • $変数1=$_GET[‘変数1’];
  • $変数2=$_POST[‘変数2’];

のように初期化(代入)することにで、コード中の$変数1と$変数2が利用できるようになりますが、これがregister_globalディレクティブがONのままだと、上記の代入なしで$変数1と$変数2が、デフォルトでグローバル変数として値を持ってしまいます。

またサーバーから返信されるレスポンスヘッダーに含まれる環境変数から、現在実行されているPHPスクリプトのパスを参照するとき、スーパーグローバル変数で$_SERVER["PHP_SELF"]としなくても、$PHP_SELFだけで参照できます。

このようにGET、POST、環境変数などPHPの外部からくる値が$変数名というフォーマットで使用できてしまうと、仮にURLのクエリストリングで「index.php?wp_did_header=dummy」とかやられたら、コード上で$wp_did_header変数にdummyという値が格納されてしまい、WordPressが起動時にwp-blog-header.phpでエラーを起こす可能性があります。

スーパーグローバル変数が勝手にグローバル変数に展開されると手間が省ける反面、セキュリティ上よろしくないということで、register_globalディレクティブのデフォルトがPHP5.4になってからOFFになりました。

register_globalディレクティブと$GLOBALSの関係

$GLOBALSはグローバルスコープで定義済みのすべての変数への参照を持つ特殊なスーパーグローバル変数です。

<?php
function test() {
    $foo = "ローカル変数";

    echo '$foo in global scope: ' . $GLOBALS["foo"] . "\n";
    echo '$foo in current scope: ' . $foo . "\n";
}

$foo = "グローバル変数";
test();
?>

グローバルスコープの変数の値が、ローカルスコープで$GLOBALSによって参照できています。

  • $foo in global scope: グローバル変数
  • $foo in current scope: ローカル変数

仮にregister_globalディレクティブがONの場合、HTTP通信でクライアントからリクエストが送信されるたびに、連想配列であるスーパーグローバル変数$GLOBALSに対して、[キー=>値]として自動的に値がセットされます。

HTTP通信とスーパーグローバル変数の関係

WEBシステムはクライアント(ブラウザ)からのリクエストとサーバー(PHP)からのレスポンスをHTTP通信で行なうことを前提に構築されます。

開発したHTMLもCSSもJavaScriptもPHPも、すべてサーバー上に置かれ、クライアントが送信したリクエストに基づいて、サーバーはPHPで処理を行い、結果をHTML形式でクライアントに送信し、ブラウザが描画(rendering)することでWEBサイトが見られる仕組みです

HTML

HTMLとCSSの位置づけ【DOMの基本操作】

WEBシステム開発とは何かと問われた場合の最大公約数的答えは、多少くどい言い方にはなりますが、ブラウザからの要求をサーバー側で処理したHTMLを、ブラウザが受け取ってスクリプトを実行した結果をレンダリングしてWEBサイトという形で表示する過程を構築することになります。

続きを見る

このHTTP通信の際に、PHPではクライアントからのリクエストヘッダは$_SERVERで、フォームに入力したリクエスト本体は$_GETや$_POSTで参照でき、スーパーグローバル変数としてPHPのコード中で使用します。

リクエスト本体は$_GETや$_POSTで取得できるものが$_REQUESTでも取得できますが、キーが同じだと上書きされてしまうので使いません。

これはコンタクトフォームページへのアクセス時のヘッダ情報ですが、リンクからアクセスする場合でもアドレスバーに直接URLを打ち込む場合でも、指定したコンテンツを取得するためのリクエストGET通信で行なわれます。

(1) HTTPメソッド
GET /contact/ HTTP/1.1 ・・・通信メソッドとパスとプロトコル

(2) リクエストヘッダ $_SERVERで参照
Accept: text/html, application/xhtml+xml, */*
Referer: https://bahtera.jp/profile/  ・・・リンク元のURL
Accept-Language: en-US  ・・・ブラウザ対応言語
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko  ・・・ブラウザの種類
Accept-Encoding: gzip, deflate
Host: bahtera.jp
Connection: Keep-Alive
Cookie: _ga=GA1.2.836530435.1451540923; _gat=1

そしてサーバーから、処理結果としてヘッダー情報がレスポンスされます。

(1) HTTPステータス
HTTP/1.1 200 OK ・・・プロトコルとステータスコードとステータスメッセージ

(2) レスポンスヘッダ $_SERVERで参照
Date: Thu, 31 Dec 2015 05:49:40 GMT
Server: Apache
X-Powered-By: PHP/5.2.17
X-Pingback: https://bahtera.jp/xmlrpc.php
Link: <https://bahtera.jp/wp-json/>; rel="https://api.w.org/", <https://bahtera.jp/?p=7515>; rel=shortlink
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 8074
Content-Type: text/html; charset=UTF-8 ・・・コンテンツの種類
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive

コンタクトフォームからメッセージを送信する際はPOST通信で行なわれます。

(1) HTTPメソッド
POST /contact/ HTTP/1.1 ・・・通信メソッドとパスとプロトコル

(2) リクエストヘッダ $_SERVERで参照
Accept: application/json, text/javascript, */*; q=0.01
Content-Type: application/x-www-form-urlencoded; charset=UTF-8 ・・・コンテンツのタイプ
X-Requested-With: XMLHttpRequest
Referer: https://bahtera.jp/contact/ ・・・リンク元のURL
Accept-Language: en-US ・・・ブラウザ対応言語
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko ・・・ブラウザの種類
Host: bahtera.jp
Content-Length: 282
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: _ga=GA1.2.836530435.1451540923; _gat=1

_wpcf7=6942&_wpcf7_version=4.3.1&_wpcf7_locale=&_wpcf7_unit_tag=wpcf7-f6942-p7515-o1&_wpnonce=231d554ea0&your-name=%E5%B1%B1%E6%9C%AC%E3%80%80%E5%95%93%E4%BA%8C&your-email=pokecart%40gmail.com&your-message=%E3%83%86%E3%82%B9%E3%83%88%E3%81%A7%E3%81%99%E3%80%82&_wpcf7_is_ajax_call=1

そしてサーバーから、処理結果のヘッダーがレスポンスされます。

(1) HTTPステータス
HTTP/1.1 200 OK ・・・プロトコルとステータスコードとステータスメッセージ

(2) レスポンスヘッダ $_SERVERで参照
Date: Thu, 31 Dec 2015 05:57:58 GMT
Server: Apache
X-Powered-By: PHP/5.2.17
Content-Type: application/json; charset=UTF-8 ・・・コンテンツの種類
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked

レスポンスヘッダにPHPコードからヘッダ情報を追加

PHPコード中から、header()関数にて、レスポンスヘッダにLocationヘッダを追加してクライアントに送信すると、それを受けたブラウザは他サイトにリダイレクトします。

//Locationヘッダにリダイレクト先のURLをセット
header('Location:https://bahtera.jp/blog/');

PHPコード中から、header()関数にて、HTTPステータス401を追加してクライアントに送信すると、それを受けたブラウザは認証ダイアログを表示します。

//HTTPステータスに401をセットして認証ダイアログを表示
header('HTTP/1.0 401 Unauthorized');
//WWW-Authenticationヘッダに
header('WWW-Authentication: Basic realm="INDONESIA-JAPAN"');

それ以外にもステータスコードは301(恒久的移動)、401(HTTP認証要求)、404(Not Found)など、目的に応じてヘッダ情報のHTTPステータスとしてクライアントに送信されます。

サーバー変数とApacheディレクティブの関係

連想配列であるスーパーグローバル変数$_SERVERで参照できるリクエストヘッダとレスポンスヘッダのプレフィックスはHTTP_です。

$_SERVER['HTTP_REFERER']; //リンク元のURL(リクエストヘッダ)
$_SERVER['HTTPS']; //SSL通信によるアクセスかどうか(サーバー変数)

それ以外にプレフィックスがHTTP_でないサーバー変数が参照でき、もしphp.ini上のregister_globalディレクティブがONであれば$HTTPSだけで参照できます。

サーバ変数とはサーバが生成する変数であり、ヘッダやパス・スクリプトの位置などにアクセスできる変数です。

.htaccessはApacheで使用されるディレクトリ単位に、Webサーバの動作を制御するために置かれるApacheのディレクティブを記述したファイルであり、ディレクトリにベーシック認証をかけたり、mod_rewriteモジュールが実装するディレクティブでhttpd.confの設定を上書きすることができます。

AuthUserFile /home/users/1/○○○○○/web/△△△△△/admin/.htpasswd
AuthGroupFile /dev/null
AuthName "Input your ID and Password"
AuthType Basic
require valid-user

Apacheのrewrite_modeモジュールが実装するディレクティブで.htaccessによってhttpd.confを上書きしますが、ここでサーバー変数であるHTTPSを見て、SSL通信の有無を判断しています。

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://bahtera.jp/$1 [R=301,L]
</IfModule>

サイトを常時SSL化した後のリダイレクト処理時にHTTPステータスコードを302(一時的な移動)にしてしまうと、クローラーがリダイレクト先をインデックス化してくれませんので、SEOの観点から必ず301(恒久的な移動)にする必要があります。

正規化と正規表現

今はなき第一種情報処理試験のシステム工学で、データベースの正規化の問題がありましたが、この場合の正規化とはテーブル間の冗長性をキーに基づいて省いていくプロセスのことです。

同じように、正規表現も文字列全体の冗長性を、メタ文字(特殊文字)に基づいて省いていく表現方法です。

Apacheのmod_rewriteモジュールに対してURL表記をリライトしてね、という指令を.htaccessファイルに記述して、httpd.conf設定を上書するために正規表現を使います。

.htaccessファイルに記述するディレクティブと正規表現

ディレクティブ(Directive)とはコマンドと同じ意味ですが、Apacheのmod_rewriteモジュールが実装するコマンドの場合はディレクティブと呼んでいます。

#SITEGUARD_PLUGIN_SETTINGS_START
#==== SITEGUARD_RENAME_LOGIN_SETTINGS_START
<IfModule mod_rewrite.c>
 RewriteEngine on
 RewriteBase /
 RewriteRule ^wp-signup\.php 404-siteguard [L]
 RewriteRule ^wp-activate\.php 404-siteguard [L]
 RewriteRule ^login_12345(.*)$ wp-login.php$1 [L]
</IfModule>
#==== SITEGUARD_RENAME_LOGIN_SETTINGS_END
#SITEGUARD_PLUGIN_SETTINGS_END

WordPressのプラグインSiteGuardを有効化するたびに自動的に更新される部分です。

  • 3行目:mod_rewriteが利用できるかどうかを確認
  • 4行目:RewriteEngineをオン
  • 5行目:Rewrite処理のベースになるURLを指定。
  • 6行目:「wp-signup.php」という文字列を「404-siteguard」という文字列を置き換え
  • 7行目:「wp-activate.php」という文字列を「404-siteguard」という文字列を置き換え
  • 8行目:「login_12345」とそれに続く任意の文字列を「wp-login.php」という文字列の後ろに任意の文字列をくっつけて置き換え

実際には8行目の正規表現(.*)はブランクなので後方参照である$1もブランクになり、「login_12345という文字列をwp-login.phpという文字列に置き換え」という意味になります。

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://bahtera.jp/$1 [R=301,L]
</IfModule>
  • 4行目:HTTPSによるアクセスでない場合
  • 5行目:任意の文字列をHTTPS通信のURLに続く後方参照として置き換え

ちなみに[R=301,L]はRewriteRuleのオプションでリダイレクト(R)の301として指定し、RewriteRuleの条件にマッチした場合にはこれが最後(ラストの[L])で以降は処理しない、という意味です。

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

これはWordPressをインストールすると自動的に記述するみたいですね。

  • 5行目:HTTP環境変数に入っているモノがファイルでない(ファイルが存在しない)
  • 6行目:HTTP環境変数に入っているモノがディレクトリでない(ディレクトリが存在しない)

HTTP環境変数とはブラウザがサーバにアクセスする際に、HTTPヘッダに各種の情報を含めて送信するもので、Google Analyticsが参照元を認識できるのは、ブラウザがヘッダー情報としてHTTP_REFERERを送ってくるからです。

REQUEST_FILENAMEはリクエストされたファイルの名前、要は入力された文字列であり、これ以外によく登場するHTTP環境変数が以下のようなものです。

  • HTTP_HOST:閲覧しているサイトのドメイン
  • HTTPS:SSL通信の有無
  • HTTP_REFERER:前にいたページのURL

PHP5.4ビルトインWebサーバーを使う。

PHPの5.4からビルトインWebサーバーが付いており、テスト環境でわざわざApatceをインストールする必要がなくなりました。「開発用としてのみ設計されたものであり、 実運用に使ってはいけません」と思いっきり書いてあるのですが、数台の現場端末から実績収集するくらいなら実運用でも耐えられるんじゃないかとも思います。

というのは一般的に客先サーバー環境でのソフトウェアのインストールは制限が多い。「Webサーバーを立ち上げます」と言うと客側で気持ち的に負担になるのでので、「PCにPHPとMysqlをインストールするだけで、数台のPCから簡単に実績収集と管理ができます」と言えば抵抗がずいぶん少なくなります。

1. PHPプログラムの準備

php-5.4.5-nts-Win32-VC9-x86.zipを解凍してものを適当な場所に作ったフォルダ名C:\php(どこでもOK)の下にコピーする。

2. 環境変数にPHPプログラムのパスを追加

コンピューター (右クリ)-> プロパティー > システムの詳細設定のPathに「c:\php;」を追加。

3. ドキュメントルートの作成

適当な場所にドキュメントルート(C:\www\htdocsみたいなもの)を作成。

4. php.iniの修正

php.iniはwindowsフォルダにコピーする必要なし。
C:\php\php.ini-developmentをphp.iniにリネームしてサーバールートの指定を行う。

; Directory in which the loadable extensions (modules) reside.
; http://php.net/extension-dir
; extension_dir = "./"
; On windows:
extension_dir = "c:\php\ext"

5. php.exeのショートカットを作成

プロパティに以下のオプションを追加

C:\php\php.exe -S 127.0.0.1:80

デフォルトではドキュメントルートは「c:\php」なので「Start in」に「C:\www\htdocs」を追加。
php.exeを常時起動しておく必要があり、タスクバーにphp.exeのアイコンが表示されるのがウザイが、気軽にWebサーバー立ち上げるには便利だと思う。
phpビルトインサーバー

(注1)php.iniを修正してPHPの稼動確認を行う場合はビルトインサーバーを必ずリスタートさせる。php.iniはビルトインサーバー起動時に1度だけ読み込まれるので、php.ini修正の影響を確認するためにはリスタートが必要になる。

PHPビルトインサーバーはApacheなしで完全にPHP5.4単独で稼動します。
問題はPHP5.4はDoS攻撃防止のため受け付けるパラメータ数に上限をかけていること。php.iniの設定を変えても解消されず。うーん。。。

ちなみに>Dos攻撃(hashdos)とは・・・

PHPなど多くの言語では、文字列をキーとする配列(連想配列、ハッシュ)が用意されており、HTTPリクエストのパラメータも連想配列の形で提供されます。PHPの場合、$_GET、$_POSTなどです。

連想配列の実装には、高速な検索が要求されるためハッシュテーブルが用いられます。ハッシュテーブルは、文字列を整数値(ハッシュ値)に変換するハッシュ関数を用いて、平均的には一定時間に検索・挿入・削除が行えるデータ構造です。しかし、ハッシュ値が一致する(衝突する)キー文字列については、通常ハッシュテーブルは順次的な探索となり、検索・挿入などが遅くなります。

hashdosは、ハッシュ値が同じになるキーを多数POSTパラメータに含ませることにより、CPU資源を枯渇させる攻撃です。

方法は.htaccessに「php_value max_input_vars xxxxx」を追記して対応したい。
ところがPHPビルトインサーバーは.htaccessファイルを認識しない・・・。仕方ないのでApacheをインストールしかないなあ。

Apacheを使う。

 

1. Apacheのインストール

httpd-2.2.22-win32-x86-openssl-0.9.8t.msiをダウンロードして実行。Network NameとServer Domainはlocalhostにする。
Apacheのインストール

2. ブラウザからlocalhostにアクセス

Apacheのインストール
Apacheのホームディレクトリはデフォルトではhttpd.confに以下のように設定されている。
C:\Program Files\Apache Software Foundation\Apache2.2\htdocs

3. ApacheとPHPビルトインサーバーの関係

どっちもWebサーバーだが・・・

Apacheを起動するとlocalhostでApacheのホームディレクトリ「C:\Program Files\Apache Software Foundation\Apache2.2\htdocs」を見に行く。

Apacheを停止してPHPビルトインサーバーを起動するとビルトインサーバーのホームディレクトリ「C:\www\htdocs」を見に行く。

ビルトインサーバーのホームディレクトリはphp.exeのショートカットの「Start in」に設定しているだけ。Apacheをインストールしていないのでhttpd.confなんかないし、php.iniに何か指定する必要もない。

4. httpd.confでドキュメントルートを修正(2箇所)

C:\Program Files\Apache Software Foundation\Apache2.2\conf\にあるhttpd.confを修正

# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
# symbolic links and aliases may be used to point to other locations.
#
# DocumentRoot "C:/Program Files/Apache Software Foundation/Apache2.2/htdocs"
DocumentRoot "C:/Users/Hp/Documents/www/htdocs"

# This should be changed to whatever you set DocumentRoot to.
#
# <Directory "C:/Program Files/Apache Software Foundation/Apache2.2/htdocs>
<Directory "C:/Users/Hp/Documents/www/htdocs">

5. 指定のディレクトリがドキュメントルートに変わる

ただしPHPが稼動していないのでフォルダ内のツリーが見える。
Apacheのインストール

6. ApacheとPHPのリンク

ApacheとPHPのバージョンの違いによってhttpd.confの設定が異なっていたり、設定ファイル自体が異なっていたりする。
面倒くさいのでXAMPPをインストールしちゃおうかなー。

XAMPPを使う。

XAMPP(ザンプ)とは、apache friendsが公開しているWebサーバ及びWebアプリケーション環境を構築するためのフリーソフトウェア群を一括してインストールすることができるパッケージです。パッケージにはApache, MySQL, PHP, phpMyAdmin, FileZilla FTP Server, Mercury Mail Transport Systemが含まれています。

1. インストール

xampp-win32-1.8.0-VC9-installer.exeをダウンロードして実行するだけ。

ただしXAMPPのインストール前にMysqlのみ単独でインストールしていたりすると、XAMPPからインストールしたMysqlが起動しなくなるので、事前にMysqlを完全にアンインストールしておく必要がある。残す必要のあるDBはバックアップ&リストアでXAMPPインストール後に復旧させればいいし。

2. localhostを確認

デフォルトのドキュメントルートはC:\xampp\htdocsでxamppフォルダにリダイレクトされる。
XAMPP

3. Control Panelからhttpd.conf(Configボタン)を修正(2箇所)

XAMPP

# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
# symbolic links and aliases may be used to point to other locations.
#
#DocumentRoot "C:/xampp/htdocs"
DocumentRoot "C:/Users/Hp/Documents/www/htdocs"

# This should be changed to whatever you set DocumentRoot to.
#
#
# This should be changed to whatever you set DocumentRoot to.
#
#<Directory "C:/xampp/htdocs">
<Directory "C:/Users/Hp/Documents/www/htdocs">

4. Control PanelからApacheを再起動

ドキュメントルートがC:/Users/Hp/Documents/www/htdocsに変更されPHPも問題なく稼動。Apache2.2とPHP5.4の連携で苦労したのは何だったんだ・・・。ちなみにphp.iniはC:\xampp\phpにあります。

5. .htaccessファイルの作成

php_value max_input_vars 50000を記述し無事パラメータ制限を回避できた。

6. phpMyAdminの初期設定

phpMyAdminは初期状態でログインなしで使えてしまうのでC:\xampp\phpMyAdmin\config.inc.phpのauth_typeをhttpに変更する。

$cfg['Servers'][$i]['auth_type'] = 'http';←ココ
$cfg['Servers'][$i]['user'] = 'root';(デフォルトのまま)
$cfg['Servers'][$i]['password'] = '';(デフォルトのまま)

この後phpMyAdminにroot(PWDなし)でログインすると、ツールバーメニューに「Users」が表示されるので、ここからlocalhostのrootのパスワードを変更し、phpMyAdminを開き直すと該当するPWDが有効になっている。

7. 64bitのWindowsで32bit用MySQL ODBCドライバーを使う

Windowsが64bitでもアプリケーションが32bitであれば当然ながらODBC Connectorは32bit版を使用する。32bit版のODBCドライバを64bit版Winにインストールすると、コントロールパネルからODBC設定画面を起動してもインストールしたはずのMySQL5.1ドライバが表示されない。よってコマンドプロンプトから以下のコマンドで32bit版のODBCデータソースアドミニストレーター画面を表示させ、ホスト名などの設定を行なう。

%windir%\SysWOW64\odbcad32.exe

上記にてシステムDNSを作成しMySQLとのコネクションを確立したら、アプリケーション側に以下のMySQL5.1のConnectionStringを記述(設定)する。

Driver=MySQL ODBC 5.1 Driver;Persist Security Info=False;User ID=root;Data Source=asprova;Extended Properties="DSN=asprova;UID=root;"

httpd.confとhttpd-xampp.confの違い

XAMPPの場合、Apacheの設定ファイルhttpd.confは、PHPに関する設定をhttpd-xampp.confに分離してincludeしています。そしてPHPINIDirにphp.iniが置かれるディレクトリを指定します。

httpd.conf

//#XAMPP settings
Include "conf/extra/httpd-xampp.conf"

Apache用PHP本体であるphp5apache2_4.dllがサーバー上のApacheのハンドラ(イベントで呼び出される関数)としてインストールされている場合、mod_phpというモジュール名でApacheにロードされて起動します。

httpd-xampp.conf

//#
//# PHP-Module setup
//#
LoadFile "D:/xampp/php/php5ts.dll"
LoadFile "D:/xampp/php/libpq.dll"
LoadModule php5_module "D:/xampp/php/php5apache2_4.dll"


<IfModule php5_module>
    PHPINIDir "D:/xampp/php"
</IfModule>

phpinfo()上のapache2handler部分
WEB環境起動時に読み込まれるモジュールをphpinfo()でチェックしてみた。

httpd.conf上のドキュメントルートの設定にあるディレクトリ内のファイルが外部に公開され、クライアントの要求に対してHTTPプロトコルによりHTMLとして返されます。このようにxamppの綴り間違ってインストールしてもちゃんと動きます。

DocumentRoot "D:/xampp/htdocs"
<Directory "D:/xampp/htdocs">

httpd.confから読み込んだhttpd-xampp.conf内のPHPINIDirで指定したディレクトリのphp.iniが有効になりますので、Configuration File Pathが違うディレクトリを指していても問題ありません。逆にPHPINIDirにディレクトリの指定がなければLoaded Configuration FileにC:\Windows\php.iniを指定する必要があります。

WEB環境起動時に読み込まれるモジュールをphpinfo()でチェックしてみた。

PHPの拡張モジュールとして読み込むモジュール達

PHP自体はApacheのハンドラとしてロードされますが、PHPで利用するマルチバイト文字列関数や、MySQLにアクセスするためのドライバはphp.ini中で拡張モジュールとして登録されている必要があります。

//拡張ディレクトリのパス
extension_dir="D:\xampp\php\ext"

extension=php_mysql.dll      //MySQL
extension=php_mbstring.dll   //マルチバイト文字列関数
extension=php_oci8_11g.dll   //Oracle Client 11g
extension=php_pdo_sqlsrv_56_ts.dll      //PHP5.6用PDOのスレッドセーフドライバー
extension=php_sqlsrv_56_ts.dll          //PHP5.6用SQL Serverのスレッドセーフドライバー

仮想環境を構築する

仮想環境構築の流れ

話の流れを整理するとこんな感じです。

  1. Windows内でIISとApache⇒切り替えが面倒
  2. Windows内でIIS、仮想化ソフトでLinux&Apache&MySQL環境(仮想マシン)を構築⇒作業が大変
  3. Windows内でIIS、仮想化ソフトでテンプレート使って環境(仮想マシン)構築⇒楽チン

テンプレート(イメージファイル)で開発環境シェアという発想は、ローカルPC上で開発環境をシェアして、互換性からくるエラーのリスクが減らすということであり、仕組みとして必要になるのは以下の4つになります。

  1. 仮想化ソフト(VM)
  2. Vagrant本体
  3. Vagrantファイル
  4. boxテンプレート

仮想化ソフト(プロバイダー)はOracleのVirtualBoxを使う事例が圧倒的に多いようなので、今は使っていないVM WareをアンインストールしてOracleのVirtualBoxをインストールしました。

またVagrant本体と設定ファイルであるVagrantfileでもって、開発環境のテンプレートであるboxを仮想化ソフトに展開し、仮想マシンを作成します。

WordPress開発環境のboxイメージとして有名なVCCW(Vagrant Chef Centos Wordress)のGITをクローンしますが、実行時にPC側のBIOSの仮想化支援機能をEnableにしておかないと、SSH接続時にエラーが出ます。

$ git clone https://github.com/miya0001/vccw.git

これだけでWindowsのVirtualBox上でCentOSを動かし、Chefを使ってWordPress環境を自動構築するという管理をVagrantで行なうことができます。

Cygwin64 Terminal

ローカルWindows環境から一歩インターネットの世界に出ると、サーバーのほとんどがUNIXであるため、Windowsで使える端末エミュレータ(ターミナル+シェル)と言えば、必然的にUNIX(カーネル)に繋いでCUIターミナル上で操作するもの、ということになります。

emulateは「真似る」という意味であり、一般的にエミュレーションとはWindows上のターミナルから打ち込んだシェルコマンドでUNIXカーネルを操作することであり、操作対象となるUNIXはインターネット上のサーバーにあったり、ローカルPC内の仮想マシンにあったりしますが、リモートであろうとローカルであろうと、SSH通信接続が主流です。

MacはもともとUNIXベースなのでデフォルトでターミナルというUNIX端末エミュレータが付いています。

Windows7にはデフォルトの端末エミュレータとしてPowerShellというのがありますが、如何せんコマンドがWindows専用、UNIX互換コマンドライン環境はありません。

自分の場合、VPSサーバー操作のためにPuTTYはインストールしていますが、いかんせんPuTTYはリモートSSH接続するときのターミナルの通信機能だけ切り取ったようなソフトなので、今回の仮想マシン作成のためにCygwin64をインストールしました。

ただし仮想マシン用のboxテンプレートはGITをクローンするので、Cygwin64でgitコマンドが使えるようにGITをインストールしておく必要があります。

仮想マシンの実行プロセス

まずクローンしたboxを確認します。

$ vagrant box list
miya0001/vccw (virtualbox, 2.19.0)

VCCWのGITをクローンした時点でvagrantfileも同梱されていますので、initで初期化してvagrantfileを作成しようとすると「既に存在します」と怒られます。

$ vagrant init vccw
`Vagrantfile` already exists in this directory. Remove it before
running `vagrant init`.

vagrantの初回起動時に、GITをクローンしたvccw.devというboxイメージをプロバイダーであるVirtualBoxにaddすることにより、virtualbox上で起動するようになります。

またvagrantfileの設定に基づいて必要なリソースをダウンロードするプロビジョニング(Provisioning)が実行されますので、起動するまで時間がかかります。

$ vagrant up
Bringing machine 'vccw.dev' up with 'virtualbox' provider...
==> vccw.dev: Box 'miya0001/vccw' could not be found. Attempting to find and install...
    vccw.dev: Box Provider: virtualbox
    vccw.dev: Box Version: >= 0
==> vccw.dev: Loading metadata for box 'miya0001/vccw'
    vccw.dev: URL: https://atlas.hashicorp.com/miya0001/vccw
==> vccw.dev: Adding box 'miya0001/vccw' (v2.19.0) for provider: virtualbox
    vccw.dev: Downloading: https://atlas.hashicorp.com/miya0001/boxes/vccw/versions/2.19.0/providers/virtualbox.box
==> vccw.dev: Box download is resuming from prior download progress
    vccw.dev:
==> vccw.dev: Successfully added box 'miya0001/vccw' (v2.19.0) for 'virtualbox'!
==> vccw.dev: Importing base box 'miya0001/vccw'...
==> vccw.dev: Matching MAC address for NAT networking...
==> vccw.dev: Checking if box 'miya0001/vccw' is up to date...
==> vccw.dev: Setting the name of the VM: vccw.dev
==> vccw.dev: Auto-generating node name for Chef...

//省略

==> vccw.dev: * template[/etc/motd] action create
==> vccw.dev:  (up to date)
==> vccw.dev: [2016-01-20T16:31:43+00:00] INFO: execute[a2ensite wordpress.conf] sending restart action to service[apache2] (delayed)
==> vccw.dev: Recipe: apache2::default
==> vccw.dev:
==> vccw.dev: * service[apache2] action restart
==> vccw.dev: [2016-01-20T16:31:47+00:00] INFO: service[apache2] restarted
==> vccw.dev:
==> vccw.dev: - restart service service[apache2]
==> vccw.dev:
==> vccw.dev:
==> vccw.dev: [2016-01-20T16:31:47+00:00] INFO: Chef Run complete in 843.305980276 seconds
==> vccw.dev:
==> vccw.dev: Running handlers:
==> vccw.dev: [2016-01-20T16:31:47+00:00] INFO: Running report handlers
==> vccw.dev: Running handlers complete
==> vccw.dev:
==> vccw.dev: [2016-01-20T16:31:47+00:00] INFO: Report handlers complete
==> vccw.dev: Chef Client finished, 37/231 resources updated in 846.788885529 seconds

HP@HP-PC ~/vccw
$

プロビジョニングとは本来、ネットワークやコンピューターの設備などのリソースを提供できるよう予測し準備しておくことであり、供給や設備等の意味を表すプロビジョン(provision)という単語がもととなって派生した言葉です。

これで仮想マシンが起動しましたので、ホストPC上からhttp://192.168.33.10/でWordPressが起動し、物理ファイルはboxディレクトリ内の/www/wordpressに存在します。

ちなみに2回目以降のvagrantの起動はあっさり終わります。

$ vagrant up
Bringing machine 'vccw.dev' up with 'virtualbox' provider...
==> vccw.dev: Checking if box 'miya0001/vccw' is up to date...
==> vccw.dev: Clearing any previously set forwarded ports...
==> vccw.dev: Clearing any previously set network interfaces...
==> vccw.dev: Preparing network interfaces based on configuration...
    vccw.dev: Adapter 1: nat
    vccw.dev: Adapter 2: hostonly
==> vccw.dev: Forwarding ports...
    vccw.dev: 22 (guest) => 2222 (host) (adapter 1)
==> vccw.dev: Running 'pre-boot' VM customizations...
==> vccw.dev: Booting VM...
==> vccw.dev: Waiting for machine to boot. This may take a few minutes...
    vccw.dev: SSH address: 127.0.0.1:2222
    vccw.dev: SSH username: vagrant
    vccw.dev: SSH auth method: private key
    vccw.dev: Warning: Remote connection disconnect. Retrying...
==> vccw.dev: Machine booted and ready!
==> vccw.dev: Checking for guest additions in VM...
==> vccw.dev: Setting hostname...
==> vccw.dev: Configuring and enabling network interfaces...
==> vccw.dev: Mounting shared folders...
    vccw.dev: /vagrant => C:/cygwin64/home/HP/vccw
    vccw.dev: /var/www/wordpress => C:/cygwin64/home/HP/vccw/www/wordpress
    vccw.dev: /tmp/vagrant-chef/c7ebca6d77af9158fff141a6f118ee56/cookbooks => C:/cygwin64/home/HP/vccw/provision/cookbooks
    vccw.dev: /tmp/vagrant-chef/7d5350f7934765ca44a15807f16805d5/cookbooks => C:/cygwin64/home/HP/vccw/provision/site-cookbooks
==> vccw.dev: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> vccw.dev: flag to force provisioning. Provisioners marked to run always will still run.
HP@HP-PC ~/vccw
$

一回仮想マシンを削除した後に再度vagrantを起動するとまた少し時間がかかります。

$ vagrant up
Bringing machine 'vccw.dev' up with 'virtualbox' provider...
==> vccw.dev: Importing base box 'miya0001/vccw'...
==> vccw.dev: Matching MAC address for NAT networking...
==> vccw.dev: Checking if box 'miya0001/vccw' is up to date...
==> vccw.dev: Setting the name of the VM: vccw.dev
==> vccw.dev: Auto-generating node name for Chef...
==> vccw.dev: Clearing any previously set network interfaces...
==> vccw.dev: Preparing network interfaces based on configuration...
    vccw.dev: Adapter 1: nat
    vccw.dev: Adapter 2: hostonly
==> vccw.dev: Forwarding ports...
    vccw.dev: 22 (guest) => 2222 (host) (adapter 1)
==> vccw.dev: Running 'pre-boot' VM customizations...
==> vccw.dev: Booting VM...
==> vccw.dev: Waiting for machine to boot. This may take a few minutes...
    vccw.dev: SSH address: 127.0.0.1:2222
    vccw.dev: SSH username: vagrant
    vccw.dev: SSH auth method: private key
    vccw.dev: Warning: Remote connection disconnect. Retrying...
    vccw.dev:
    vccw.dev: Vagrant insecure key detected. Vagrant will automatically replace
    vccw.dev: this with a newly generated keypair for better security.
    vccw.dev:
    vccw.dev: Inserting generated public key within guest...
    vccw.dev: Removing insecure key from the guest if it's present...
    vccw.dev: Key inserted! Disconnecting and reconnecting using new SSH key...
==> vccw.dev: Machine booted and ready!
==> vccw.dev: Checking for guest additions in VM...
==> vccw.dev: Setting hostname...
==> vccw.dev: Configuring and enabling network interfaces...
==> vccw.dev: Mounting shared folders...
    vccw.dev: /vagrant => C:/cygwin64/home/HP/vccw
    vccw.dev: /var/www/wordpress => C:/cygwin64/home/HP/vccw/www/wordpress
    vccw.dev: /tmp/vagrant-chef/c7ebca6d77af9158fff141a6f118ee56/cookbooks => C:/cygwin64/home/HP/vccw/provision/cookbooks
    vccw.dev: /tmp/vagrant-chef/7d5350f7934765ca44a15807f16805d5/cookbooks => C:/cygwin64/home/HP/vccw/provision/site-cookbooks
==> vccw.dev: Running provisioner: chef_solo...
==> vccw.dev: Detected Chef (latest) is already installed
==> vccw.dev: Generating chef JSON and uploading...
==> vccw.dev: Running chef-solo...

//途中省略

==> vccw.dev: [2016-01-23T16:07:36+00:00] INFO: Chef Run complete in 9594.780249836 seconds
==> vccw.dev:
==> vccw.dev: Running handlers:
==> vccw.dev: [2016-01-23T16:07:36+00:00] INFO: Running report handlers
==> vccw.dev: Running handlers complete
==> vccw.dev:
==> vccw.dev: [2016-01-23T16:07:36+00:00] INFO: Report handlers complete
==> vccw.dev: Chef Client finished, 35/231 resources updated in 9596.505026441 seconds

HP@HP-PC ~/vccw
$

vagrantファイル

ホストPCから仮想マシンのhttpdサーバーにアクセスするためのIPアドレス192.168.33.10というのはvagrantfileから呼び出している設定ファイル/provision/default.yml内に定義されています。

# General Settings
wp_box: miya0001/vccw
chef_cookbook_path: ./provision

# Virtual Machine Settings
memory: 512
cpus: 1

# Network Settings
hostname: vccw.dev
ip: 192.168.33.10
sync_folder: 'www/wordpress'

# WordPress Settings
version: latest
lang: en_US
title: Welcome to the VCCW
multisite: false
rewrite_structure: /archives/%post_id%

# WordPress Path
document_root: '/var/www/wordpress'
wp_siteurl: ''  # Path to the WP_SITEURL like "wp"
wp_home: ''     # Path to the WP_HOME like "wp"

# WordPress User
admin_user: admin
admin_pass: admin
admin_email: vccw@example.com

# WordPress Database
db_prefix: wp_
db_host: localhost

# WordPress Default Plugins
# Plugin's slug or url to the plugin's slug.
plugins:
  - dynamic-hostname
  - wp-total-hacks
  - tinymce-templates

# WordPress Default Theme
# Theme's slug or url to the theme's .zip.
theme: ''

# WordPress Options
options:
  blogdescription: Hello VCCW.

# The values of wp-config.php
force_ssl_admin: false
wp_debug: true
savequeries: false

# Theme unit testing
theme_unit_test: false
theme_unit_test_uri: https://wpcom-themes.svn.automattic.com/demo/theme-unit-test-data.xml
# theme_unit_test_uri: https://raw.githubusercontent.com/jawordpressorg/theme-test-data-ja/master/wordpress-theme-test-date-ja.xml

# DB will be reset when provision
reset_db_on_provision: true

# Are you hurrying a lot?
disable_vccw_cookbook: false

# User and Group
user: vagrant
group: vagrant

# phpenv
php_version: default

# Linked Clone for Vagrant v1.8
linked_clone: false

仮想マシンへの接続

仮想マシンとしてCentOSのApacheサーバー上でのWordPress環境が起動し、ホストPCからアクセスできるようになりましたが、仮想マシンへの接続は、vagrantの専用コマンドvagrant sshがあります。

$ vagrant ssh
Last login: Thu Jan  7 01:56:47 2016 from 10.0.2.2
                   ___           ___           ___
      ___         /  /\         /  /\         /__/\
     /__/\       /  /:/        /  /:/        _\_ \:\
     \  \:\     /  /:/        /  /:/        /__/\ \:\
      \  \:\   /  /:/  ___   /  /:/  ___   _\_ \:\ \:\
  ___  \__\:\ /__/:/  /  /\ /__/:/  /  /\ /__/\ \:\ \:\
 /__/\ |  |:| \  \:\ /  /:/ \  \:\ /  /:/ \  \:\ \:\/:/
 \  \:\|  |:|  \  \:\  /:/   \  \:\  /:/   \  \:\ \::/
  \  \:\__|:|   \  \:\/:/     \  \:\/:/     \  \:\/:/
   \__\::::/     \  \::/       \  \::/       \  \::/
       ~~~~       \__\/         \__\/         \__\/

http://vccw.cc/

Initial code by Takayuki Miyauchi.
https://github.com/miya0001
[vagrant@vccw ~]$

これはVirtualBoxで起動しているウィンドウ内のCentOSからユーザーID:vagrant, パスワード:vagrantでログインしているのと同じです。
仮想マシンへの接続

ステータスの確認

仮想マシン起動前

$ vagrant status
Current machine states:
vccw.dev                  poweroff (virtualbox)
The VM is powered off. To restart the VM, simply run `vagrant up`

仮想マシン起動後

$ vagrant up
$ vagrant status
Current machine states:
vccw.dev                  running (virtualbox)
The VM is running. To stop this VM, you can run `vagrant halt` to
shut it down forcefully, or you can run `vagrant suspend` to simply
suspend the virtual machine. In either case, to restart it again,
simply run `vagrant up`.

仮想マシン削除後

$ vagrant destroy
$ vagrant status
Current machine states:
vccw.dev                  not created (virtualbox)
The environment has not yet been created. Run `vagrant up` to
create the environment. If a machine is not created, only the
default provider will be shown. So if a provider is not listed,
then the machine is not created for that environment.