整ったコードの7か条

「ビューティフルコード」 32章 働くコードの「整ったコードの7か条」を紹介する。
#は、投稿者(青木)の意見。

32章の著者 Laura Wingerd、Christopher Seiwald

原文
http://www.perforce.com/resources/white-papers/seven-pillars-pretty-code

サンプルコード
ftp://ftp.perforce.com/jam/src/make.c

■既存のスタイルに溶け込むこと
コードを変更するとき、既存のスタイルで書くこと。

「本のよう」であること
1行の長さを短くする。短い行ほど理解しやすい。 経験的に80文字がよい。

コードをブロックに分けること
コードにコメントをつけること

# サンプルコードはstep 1、step 2と大ブロックに分け、step 1の中をstep 1a、step 1b と小ブロックに分けている。

ごちゃごちゃにしないこと

繰り返し参照される変数名を短くする。
元のコードをコメントで残すのではなく削除する。元のコードはSCMを見ればよい。
何を変更したかをコメントしない。変更履歴はSCMを見ればよい。

# gitやバグ管理ツールを使うと、不要コードや不要コメントを思い切って削除できるようになる。

似たものは似ているように見えるようにすること

インデントを克服すること
インデントが深いと読みにくくなる。
しかし、最大の落とし穴は「入れ子」であり、「入れ子」になったコードは理解しにくい。

# 「入れ子」が深いと、頭のスタックがオーバーフローしてしまう。

Another HTML-lint htmllint.cgiをCentOS/5.5に設置する

Another HTML-lint
http://openlab.ring.gr.jp/k16/htmllint/htmllinte.html

Another HTML-lintはHTML文法チェッカーの老舗であり、Perlソースが公開されている。VMWare上のCentOS/5.5(32bit版)への設置手順を説明する。

[動作環境]

・Windows7上のVMWareにCentOSをインストールした。
・CentOSのIPアドレスを 192.168.0.4 とする。
・WindowsのWebブラウザで、http://192.168.0.4/ にアクセスすると「Apache 2 Test page powered by CentOS」と表示されること。

(1) Windows上の作業

●htmllint.zipをダウンロードする。(記事投稿時2011-11-28 16:17版)

●zipファイルを解凍すると、htmllint/ の下にディレクトリ階層なしで全ファイルが展開される。

●htmllintenv を htmllint.env へコピーまたはリネームする。

●htmllint.cgi、taglist.cgi の先頭行(perlパス)を編集する。

編集前

#!/usr/local/bin/perl

編集後

#!/usr/bin/perl

(2) WindowsのWinSCPで、CentOSへアップロード

●/var/www/cgi-bin/htmllint/htmllint.cgi の位置へ全ファイルをアップロードする。

●htmllin.cgi、taglist.cgi のアクセス権限を755 に設定する。

○WindowのWebブラウザで、http://192.168.0.4/cgi-bin/htmllint/htmllint.cgi を開き、「Another HTML-lint error!」と表示されるか?

サーバエラー → (1) と (2) へ

○WindowsのWebブラウザで、http://192.168.0.4/cgi-bin/htmllint/htmllint.html を表示できるか?

できない →(3)へ

○htmllint.htmlのURL欄に「http://www.yahoo.co.jp」を入力し[check]する。結果が表示されたか?

「調整中です」 →(4)へ

(3) CentOSでsshログイン作業

CentOSインストール直後は、/cgi-bin/ 下は *.cgi、*.pl以外は表示できない。htmlやcssを表示できるように/etc/httpd/conf/httpd.confを編集する。

編集前

<Directory "/var/www/cgi-bin">
    AllowOverride None
    Options None
    Order allow,deny
    Allow from all
</Directory>

編集後

<Directory "/var/www/cgi-bin">
    AllowOverride None
    Options None
    Order allow,deny
    Allow from all
    AddHandler text/css css
    AddHandler text/html html
    AddHandler image/gif gif
</Directory>

●webサーバを再起動する。

[root@192.168.0.4]# service httpd restart

○WindowのWebブラウザで、http://192.168.0.4/cgi-bin/htmllint/htmllint.html を表示できるはず。

(4) CentOSでsshログイン作業

gccとPerlモジュールをインストールする。いろいろ聞かれたら yes または y を入力して進める。

[root@192.168.0.4]# yum -y install gcc
[root@192.168.0.4]# cpan
cpan> install Jcode
cpan> install CGI
cpan> install LWP
cpan> exit

○WindowsのWebブラウザで、http://192.168.0.4/cgi-bin/htmllint/htmllint.html を表示し、URL欄に「http://www.yahoo.co.jp」を入力し[check]する。結果を表示できるはず。

デプロイ先を切り替えてgit aws.pushする

【環境】

PC: Windows7、git、AWS Elastic Beanstalk Command Line Tool
サーバー: AWS ElasticBeanstalk

AWS ElasticBeanstalk(以下EBT)はロードバランサーとオートスケーリングサーバのパッケージである。コマンドプロンプトのgit aws.configでデプロイ先を設定し、git aws.pushでデプロイする。

デプロイ先が一つだけなら最初に1回だけgit aws.configをすればいいのだが、デプロイ先として本番環境、ステージング環境、開発環境など複数ある場合、毎回aws.configするのは面倒である。ここではデプロイ先を切り替えてgit aws.pushする方法を説明する。

環境1
AWSアカウント1>EBT>Application=hoge1-app>Enevironment=hoge1-env
環境2
AWSアカウント2>EBT>Application=hoge2-app>Enevironment=hoge2-env

【方法1】

環境1用のgitフォルダD:\hoge1、環境2用のgitフォルダD:\hoge2と別々に用意する。リモートリポジトリを経由してD:\hoge1とD:\hoge2を同期する。

D:\hoge1で開発作業、commit
D:\hoge1で環境1へgit aws.push

D:\hoge1でリモートリポジトリへpush

D:\hoge2でリモートリポジトリからpull
D:\hoge2で環境2へgit aws.push

※リモートリポジトリ
インターネットを介して複数人で作業する場合はgithubが便利である。無料プランは非公開リポジトリを作成できないが、有料プランにすると非公開リポジトリを作ることができる。
一人gitでLAN内作業の場合は、共有ドライブ(ローカルドライブでもよい)が手軽である。エクスプローラ右クリック>Git Create repository here>[Make it Bare]のチェックを付けると、リモートリポジトリが作成される。Git syncのRemote URLに共有フォルダのパス(Z:\gitrepos\hogeなど)を指定すればよい。

【方法2】

1つのgitフォルダ(以下、hogeとする)でデプロイ先を切り替える。

git aws.configは、hoge\.elasticbenstalk\configに設定内容を保存し、git aws.pushはこのconfigにしたがってデプロイする。

hoge\.elasticbenstalk\config

[global]
ApplicationName=hoge1-app
CredentialFile=C:\Users\taro\aws_credential_file
DevToolsEndpoint=git.elasticbeanstalk.ap-northeast-1.amazonaws.com
EnvironmentName=hoge1-env
Region=ap-northeast-1

そこで環境ごとのconfigファイルを用意しておき、バッチファイルでconfigにコピーし、git aws.push する。

まずconfigをコピペして、環境1のconfig-hoge1-env、環境2のconfig-hoge2-envを作成する。AWSアカウントが複数ある場合は、それぞれのaws_credential_fileも用意しておく。

hoge\.elasticbenstalk\config-hoge1-env

[global]
ApplicationName=hoge1-app
CredentialFile=C:\Users\taro\aws\account1\aws_credential_file
DevToolsEndpoint=git.elasticbeanstalk.ap-northeast-1.amazonaws.com
EnvironmentName=hoge1-env
Region=ap-northeast-1

hoge\.elasticbenstalk\config-hoge2-env

[global]
ApplicationName=hoge2-app
CredentialFile=C:\Users\taro\aws\account2\aws_credential_file
DevToolsEndpoint=git.elasticbeanstalk.ap-northeast-1.amazonaws.com
EnvironmentName=hoge2-env
Region=ap-northeast-1

次にバッチファイルを作成する。

hoge\git_awspush_1_hoge1-env.bat

copy .elasticbeanstalk\config-hoge1-env .elasticbeanstalk\config
git aws.push

hoge\git_awspush_2_hoge2-env.bat

copy .elasticbeanstalk\config-hoge2-env .elasticbeanstalk\config
git aws.push

環境1へデプロイしたいときは、hoge\git_awspush_1_hoge1-env.batを実行(またはダブルクリック)し、環境2へデプロイしたいときは、hoge\git_awspush_2_hoge2-env.batを実行(またはダブルクリック)する。

PHP5.3でMySQL接続エラー(old_passwords=1)

【環境】

接続元: PHP 5.3.20 (WebサーバA、新規)
接続先: MySQL 5.0.95 (MySQLサーバB、運用中)

【現象】

次のエラーが表示されてMySQLサーバに接続できない。
Warning (2): mysql_connect() [function.mysql-connect]: mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will store a new, and more secure, hash value in mysql.user. If this user is used in other scripts executed by PHP 5.2 or earlier you might need to remove the old-passwords flag from your my.cnf file [CORE\cake\libs\model\datasources\dbo\dbo_mysql.php, line 374]

xampp_1.5.4a(PHP5.1.4) → MySQLサーバB 接続OK
xampp_1.7.7(PHP5.3.8) → MySQLサーバB 接続ERR
コマンドプロンプトのmysqlコマンド → MySQLサーバB 接続OK
WebサーバA → 別のMySQLサーバ(MySQL5.5) 接続OK
つまりPHP5.3とMySQLサーバBの組み合わせで接続できない。新規のWebサーバAをPHP5.2やPHP5.1にすることはできない。

【原因】

PHP5.2から5.3になったとき、mysqlドライバ部分がmysqlndライブラリに変更された。mysqlndライブラリは41バイトのパスワードフォーマットを使用し、古い16バイトのパスワードフォーマットを使うとエラーを生成する。
http://php.net/manual/ja/migration53.incompatible.php

接続先のMySQLサーバのmy.cnfには old_passwords=1 が設定してあった。SELECT PASSWORD();を実行してみると16バイトのハッシュが表示された。またmysql.UserテーブルのPasswordカラムはchar(41)だが、保存されているデータは16バイトであった。

【検討】

新規のMySQLサーバの場合は、my.cnfのold_passwords=0にしてMySQLサーバを再起動し、DBユーザを新規作成したりパスワードを再設定すればよい。

ところがMySQLサーバBは運用中であり、DBユーザも登録されている。なるべく再起動せずにすむ方法、既存のDBユーザに影響しない方法を実施したい。本番サーバで操作する前に実験サーバで次の2つの確認をした。以下、WebサーバAから接続するDBユーザを ‘taro’@’%’、パスワード ‘1234’ とする。

【実験1】

1-1. old_passwords=0にしてMySQLを再起動し、既存のDBユーザ(16バイトハッシュ)で接続できるか?

mysqlコマンド 接続OK
PHP 5.2 接続OK
PHP 5.3 接続ERR

1-2. DBユーザ taro’@’%’のパスワードを設定する。

SET PASSWORD FOR taro@'%' = PASSWORD('1234');

1-3. DBユーザ ‘taro’@’%’ (41バイトハッシュ)で接続できるか?

mysqlコマンド 接続OK
PHP 5.2 接続OK
PHP 5.3 接続OK

【実験2】

2-1. 別のMySQLサーバ(old_passwords=0)を用意し、41バイトハッシュを生成した。

SELECT PASSWORD('1234');
*A4B6157319038724E3560894F7F932C8886EBFCF

2-2. 対象のMySQLサーバはold_passwords=1のままで、DBユーザのパスワードを41バイトハッシュにするために次のSQLを実行した。

SET PASSWORD FOR taro@'%' = '*A4B6157319038724E3560894F7F932C8886EBFCF';
を実行する。

2-3. mysql.userテーブルを表示して41バイトハッシュになっていることを確認した。

SELECT * FROM `user` WHERE `Host`='%' AND `User`='taro';

2-4. DBユーザ ‘taro’@’%’ (41バイトハッシュ)で接続できるか?

mysqlコマンド 接続OK
PHP 5.2 接続OK
PHP 5.3 接続OK

【まとめ】

実験1では、old_passwords=0 にしても既存のDBユーザの接続に影響がないことがわかった。
実験2では、old_passwords=0にしなくても、41バイトハッシュを設定することができた。

本番のMySQLサーバでは実験2の方法を実施した。

検索ワードをUTF-8からEUC-JP変換してnamazu検索する

namazu.cgiはUTF-8の検索ワードに対応していないので、UTF-8ページの検索フォームからnamazu検索すると、検索ワードが文字化けしてしまい、正しく検索できない。http://www.namazu.org/FAQ.html#set-server-encoding に、namazu.cgiはUTF-8の入力に対応していないと記述がある。

次の案1と案2を作成した。案2はphp.iniでallow_url_fopen = Onの必要があり、必要条件の少ない案1を採用した。

案1: 検索フォームからnamazu.phpへ送信し、namazu.phpで検索ワードをUTF-8からEUC-JPに変換し、namazu.cgiへリダイレクトする。
案2: 検索フォームからnamazu.phpへ送信し、namazu.phpで検索ワードをUTF-8からEUC-JPに変換し、namazu.cgiへhttpリクエストし、内容を返す。

他に次のような記事もあった。試していないが、参考のため残しておく。

php用のnamazu.so拡張モジュールを使う。
・2013-02-14 RHEL6でnamazuをインストールしてPHPから使う
http://www.mogumagu.com/wp/wordpress/?p=1217
・2010-02-06 namazu.soの作り方
http://zkangaroo.blogspot.jp/2009/03/namazuso.html

formタグのaccept-charset指定、jsでdocument.charset指定。
・2009-01-12 UTF-8のページでnamazuを使う
http://blog.digital-assist.net/?p=21

以下、案1について説明する。

(1) namazu.phpに保存してください。

$path(赤太字)を本来のnamazu.cgi の設置パスに修正してください。

<?php
/*
namazu.php

■1.処理の流れ
(1) utf8ページから「ほげ」を検索
↓フォーム送信
(2) namazu.php?query=%E3%81%BB%E3%81%92&whence=0&max=20&result=normal&sort=score
↓リダイレクト
(3) namazu.cgi?query=%A4%DB%A4%B2&whence=0&max=20&result=normal&sort=score

■2.動作条件
(1) 5 <= PHP_VERSION(http_build_query関数があること)
(2) iconv拡張モジュールが有効であること。(通常はデフォルトで有効になっている)
*/
ini_set( 'display_errors', 1 );
ini_set( 'error_reporting', E_ALL );
ini_set( 'assert.bail', 1 );
assert( '5' <= PHP_VERSION );
assert( function_exists('iconv') );

if ( isset($_GET['query']) ) {
	$_GET['query'] = iconv( 'UTF-8', 'EUC-JP', $_GET['query'] );
}

$scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
$host   = $_SERVER['HTTP_HOST'];
$path   = '/cgi-bin/namazu/namazu.cgi';
$query  = http_build_query($_GET);
$url    = sprintf( '%s://%s%s?%s', $scheme, $host, $path, $query );

header( "Location: $url" );
?>

(2) namazu.phpをサーバに設置してください。

動くならnamazu.cgiと同じディレクトリがわかりやすい。cgi-bin下でphpが動かない場合は、/namazu.php などに設置してください。

(3) utf-8ページの検索フォームのaction(赤太字)をnamazu.phpの設置パスに編集してください。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>namazu</title>
</head>
<body>
<h1>検索ボックス/UTF-8</h1>
<form method="get" action="/cgi-bin/namazu/namazu.php">
  <input type="text" name="query" />
  <input type="hidden" name="whence" value="0" />
  <input type="hidden" name="max" value="20" />
  <input type="hidden" name="result" value="normal" />
  <input type="hidden" name="sort" value="score" />
  <input type="submit" value="search" />
</form>
</body>
</html>

AWS RDS MySQLのtime zoneをcakePHPで設定する

【環境】

AWS RDS MySQL 5.5

【現象と要望】

AWSのRDSでMySQLサーバを起動し、日本時間2013-04-02 15:01 :00に “SELECT NOW();” を実行すると、”2013-04-02 06:01:00″と表示される。これを日本向けに”2013-04-02 15:01:00″と表示させたい。

【対応】

phpMyAdmin>変数を表示すると、time zoneはUTCであり、上記の「06:01:00」は正しい表示結果である。

time zoneを’Asia/Tokyo’または’+09:00’に設定すれば解決しそうだが、AWS Management Console>RDS>DB Parameter Groupsを見ると、default_time_zoneは変更不可と表示されている。

対応方法の一つとして、MySQLサーバ接続ごとに、またはSQL実行ごとに「SET @@time_zone=’+09:00′ 」を実行する方法がある。

cakePHP 1.2での対応例を紹介する。

※ソースそのままではなく記事用に編集してあるので、もしエラーが発生したら自分で対応してほしい。

【対応例】

(1) app/config/database.php

DATABASE_CONFIGのdriverの値をmysql_customに変更し、time_zoneを追加する。

class DATABASE_CONFIG {
    var $default = array(
        'driver'	=> 'mysql_custom',
        'persistent'	=> false	,
        'host'		=> 'localhost'	,
        'login'		=> 'user'	,
        'password'	=> 'pass'	,
        'database'	=> 'cake'	,
        'prefix'	=> ''		,
        'encoding'	=> 'utf8'	,
        'time_zone'	=> '+09:00'	,
    );

(2) app/models/datasources/dbo_mysql_custom.php

ファイルを新規作成する。

<?php
uses( 'model' . DS . 'datasources' . DS . 'dbo' . DS . 'dbo_mysql' ); 

class DboMysqlCustom extends DboMysql{
    function connect() {
        $r = parent::connect();
        if ( $r ) {
            if ( isset($this->config['time_zone']) ) {
                $qry = sprintf( "SET @@time_zone = '%s'", $this->config['time_zone'] );
                $this->query( $qry );
            }
        }
        return $r;
    }
}

?>

AWS ElasticBeansTalkでのcakePHPのFULL_BASE_URL定数

【環境】

AWS ElasticBeansTalkのPHP 5.3サーバ
LoadBalancerでSSLを設定し、80→80、443→80とした。
cakePHP 1.2 アプリケーション。

【現象】

https://???.elasticbeanstalk.com/blogs/first にアクセス

アクション内で $this->redirect(‘/blogs/second’);

https://???.elasticbeanstalk.com/blogs/second へリダイレクトするはずが、
http://???.elasticbeanstalk.com/blogs/second へリダイレクトしてしまう。

【原因】

レスポンスヘッダを確認すると、
Location: /blogs/second
ではなく、
Location: http://???.elasticbeanstalk.com/blogs/second
であった。
webブラウザからLoadBalancerへ443ポートアクセスしても、LoadBalancer配下のサーバには80ポートアクセスであり、環境変数 HTTPS は設定されない。そのためcakePHPはhttpアクセスと判断し、httpから始まるURLをFULL_BASE_URL定数を定義してしまう。

【対策例1】

cakePHPがFULL_BASE_URL定数を定義してしまう前に、アプリケーションでFULL_BASE_URL定数を定義する。

app/webroot/index.php の
「if (!include(CORE_PATH . ‘cake’ . DS . ‘bootstrap.php’)) {」
より先頭側に次のスクリプトを挿入する。

function is_ssl_and_forwarded() {
	if ( isset($_SERVER['HTTP_X_FORWARDED_PORT']) && 443 == $_SERVER['HTTP_X_FORWARDED_PORT'] ) {
		$f = true;
	} else {
		$f =false;
	}
	return $f;
} // is_ssl_and_forwarded

	if ( is_ssl_and_forwarded() ) {
		define( 'FULL_BASE_URL', 'https://' . $_SERVER['HTTP_HOST'] );
		//var_dump( FULL_BASE_URL );
	}

【対策例2】

.htaccess で、環境変数HTTPS を設定する。アプリケーション内で環境変数HTTPSを参照している全ての箇所でHTTPSと判断させることができる。

SetEnvIf x-forwarded-port  “^443$” HTTPS=on
SetEnvIf x-forwarded-proto “^https$” HTTPS=on

本番サイトとテストサイトで同一の.htaccessを使い、テストサイトだけBASIC認証をかけたい。

本番サイトとテストサイトで同一の.htaccessを使い、テストサイトだけBASIC認証をかけたい。

本番サイトを www.example.jp
テストサイトを test.example.jp
とすると、次のように .htaccess を記述する。

SetEnvIf Host "^www.example.jp$" server_production
SetEnvIf Host "^test.example.jp$" server_test

satisfy any

order deny,allow
deny from all
allow from env=server_production

AuthUserFile "/var/www/htpasswd/.htpasswd"
AuthGroupFile /dev/null
AuthName "Input Id and Password."
AuthType BASIC
requrie valid-user

HP pavilionでIEがブルースクリーン

今朝からノートPCのHP pavilionでIE9を表示するとブルースクリーンで落ちるようになった。ブルースクリーンには、PAGE_FAULT_IN_NONPAGED_AREAやエラーコード 0x0000005、igdpmd64.sysなどが表示されていた。

memtest86+はパス。ついでのディスク検査もパス。仮想メモリをオフにしてみたが、改善されない。

intel driver updateを試してみたらというアドバイスを見つけ、いくつかをアップデートしたが、相変わらずIE9でブルースクリーン。

検索しているうちにKB2670838が原因らしいとのことで、KB2670838をアンインストールしてみたら、IE9でブルースクリーンにならなくなった。

そのうちMicrosoft Communityのこんな質問を発見、日付は3月1日と今日。「KB2670838 を適用後 Aero や IE や Windows Live メールがおかしくなります」。 このページの回答に「Internet Explore だけで現象が起こる場合、GPU レンダリングを無効にすると改善する場合があります。」とある。

そこでKB2670838をインストールして、IEのGPUレンダリングを無効にしてみたところ、IE9でブルースクリーンにならなくなった。

cakePHPのcode coverageとxamppのxDebug

cakePHP2.x TestSuitのAnalyze Code Coverageを利用するためには、xDebugのインストールが必要です。xamppのファイル群はxDebugも含みますが、php.iniで有効にする必要があります。

xampp/php/php.ini をテキストエディタで開き、最後のほうのXDebugセクションを探してください。

[XDebug]
;zend_extension = “D:\xampp\php\ext\php_xdebug.dll”
;xdebug.profiler_append = 0
;xdebug.profiler_enable = 1
;xdebug.profiler_enable_trigger = 0
;xdebug.profiler_output_dir = “D:\xampp\tmp”
;xdebug.profiler_output_name = “cachegrind.out.%t-%s”
;xdebug.remote_enable = 0
;xdebug.remote_handler = “dbgp”
;xdebug.remote_host = “127.0.0.1”
;xdebug.trace_output_dir = “D:\xampp\tmp”

cakePHP TestSuitのCode Coverageを使いたい場合、次の太字の行のコメントをはずしてください。

[XDebug]
zend_extension = “D:\xampp\php\ext\php_xdebug.dll”
;xdebug.profiler_append = 0
;xdebug.profiler_enable = 1
;xdebug.profiler_enable_trigger = 0
;xdebug.profiler_output_dir = “D:\xampp\tmp”
;xdebug.profiler_output_name = “cachegrind.out.%t-%s”
;xdebug.remote_enable = 0
;xdebug.remote_handler = “dbgp”
;xdebug.remote_host = “127.0.0.1”
;xdebug.trace_output_dir = “D:\xampp\tmp”