2012年12月19日水曜日

国会図書館のすすめ [#vgadvent2012]

※この記事はAdvent Calendar 2012 - Voyage Group Engineer blog - の一環として書かれています。

いやはや、Advent Calenderに向けて放置してたブログを書いたりデザインを一新したりする野望を抱えていたのですが、あえなく当日を迎えてしまいました@takkyuuplayerです。

土日は意外とやることが少ない


社会人になって土日の有難さが分かる、とは言いますが私が社会人になって感じたのは規則正しい生活の心地よさと「土日は意外とやることが少ない」ってことです。普段ずっと椅子に座りっぱなしだから運動でもするか!と意気込んでいてもいざ土日を迎えると体を動かす気力を失ってしまいます。そこでお勧めなのが国会図書館です。

自転車で国会図書館へ行こう


もし自宅から国会図書館への距離が10km程度なら、是非自転車で国会図書館へ乗り込みましょう。片道40分ほどでなまった体にはちょうど良い運動量です。




自転車置き場から眺めた国会図書館


なぜ国会図書館なのか?


図書館は近くにあるのに、なぜわざわざ遠い国会図書館まで足を運ぶのか?それは日本で出版された全ての本が読めるからです。国会図書館には納本制度というものがあります。
 「納本制度」とは、図書等の出版物をその国の責任ある公的機関に納入することを発行者等に義務づける制度のことです。わが国では、国立国会図書館法(昭和23年法律第5号)により、国内で発行されたすべての出版物を、国立国会図書館に納入することが義務づけられています。
納本制度|国立国会図書館―National Diet Library http://www.ndl.go.jp/jp/aboutus/deposit.html

国会図書館の本は館外へ持ち出すことはできません。そのため普通の図書館のように借りて家で読むということもできないのですが、代わりに朝一で行けばどんな人気の本でも読めるというメリットがあります。私の場合平日はエンジニアリングの本しか読みませんので、国会図書館では営業の人とかの日報で挙がった非エンジニアリング関係の本や、学生時代に打ち込んだ理数関係の本を読むことにしています。

また国会図書館では一切外に出ることなく丸一日遊べます。なんと館内には「食堂・コンビニ・カフェ」が存在します。食事をしに外へ出る必要は無く、値段も学食と同等の安さです。空調も完璧、電源もあります。さすがに無線LANはありませんが、むしろ気付いたらネットサーフィンにならなくて済むのでよいのではないでしょうか?

もちろん運動のきっかけを作れることも国会図書館へ行く理由の1つです。週に一度とはいえ、往復90分弱のサイクリングはデスクワークで衰えた体を引き締める効果があります。

国会図書館に弱点ってあるの?


一見完璧に見えますが、この世に完璧はありません。国会図書館にももちろん弱点があります。まず致命的なのは日曜日が休館日なことです。図書館は月曜日が休館日と相場が決まっていそうなものを、国会図書館は日曜日に休みます。日曜日もやっていれば土日の使い方は確定したのですが・・・。

もうひとつたまに傷になるのが立地条件です。隣に国会議事堂があるということは、当然そこでデモが開催される可能性があります。図書館という性質上基本は静かなのですが、デモが始まるとやや騒々しくなります。

雨が降ったらOASISへ


いかがでしたでしょうか?国会図書館の魅力は伝わりましたでしょうか?私はそんな国会図書館の魅力に惹かれ、雨の日も風の日も毎週欠かさず行っている、と言いたいところですがさすがに雨が降ると自転車では行けません。かといって電車で行く感じでもないのです。そんな時は会社の図書館OASISへ行きます(盛った)。OASISにはビジネス・テクノロジー・アイデア・マンガという4つのジャンルの本が取り揃えられています。同じIT業界に興味を持った同士、本の趣味も似ていて楽しいです。


社内用ライブラリ OASIS

次回予告

次回のAdvent Calender担当は@hagino3000さんです。

2012年7月4日水曜日

Slimを読む(1)

今週からは Home - Slim Framework for PHP 5 を読み始める。symfony2はどうなったのか?重い。やっぱり軽いOSSから読んでいかないと。

はじめに


環境: Amazon EC2, Amazon Linux AMI x86_64 EBS
ソースコード:Symfony2.0.15

まずはコードを動かしてみる


なにはともあれ、動かしてみるところから。

ソースコードの配置


[bash]
~$ cd /var/www/
/var/www$ wget https://github.com/codeguy/Slim/zipball/master
/var/www$ mv codeguy-Slim-29d662d/ slim
[/bash]

バーチャルホストの設定


/etc/httpd/conf.d/slim.conf
[text]

ServerName slim.wordpress.takkyuuplayer.vagrant
DocumentRoot /var/www/html/slim/
ErrorLog logs/slim-error_log
CustomLog logs/slim-access_log combined env=!no_log

[/text]

[bash]
/var/www$ sudo service httpd restart
[/bash]

http://slim.wordpress.takkyuuplayer.vagrant/にアクセスすればSlimのトップ画面が表示されるはずだ。

2012年6月21日木曜日

symfony2を読む(3)

さて、前回に引き続きsymfony2を読み解く。今日は以下の箇所について調べる。

./web/app_dev.php


[php]
Request::createFromGlobals()
[/php]

vendor/symfony/src/Symfony/Component/HttpFoundation/Request.php


[php]
static public function createFromGlobals()
{
$request = new static($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER);

if (0 === strpos($request->server->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
&& in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE'))
) {
parse_str($request->getContent(), $data);
$request->request = new ParameterBag($data);
}

return $request;
}
[/php]
1行目, new static ってなんだ!? new self と new static の違い - Sarabande.jp によると、記述元のクラスでメソッド呼び出された風になるのが、new self, 呼び出し元のクラスでメソッド呼び出し風になるのが new static とのことだが、今回は記述元も呼び出し元も同じである。例えばJavaでstatic int といえばすべてのインスタンスで共有したいintを指すが、今回は他のクラスからRequestインスタンスを呼び出すときに同一のものを扱いたい、ということなのだろうか?そういうことにしておこう。何はともあれ、コンストラクタの呼び出しがあり、そこで初期化のための関数が呼ばれる。
[php]
public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
{
$this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
}
public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
{
$this->request = new ParameterBag($request);
$this->query = new ParameterBag($query);
$this->attributes = new ParameterBag($attributes);
$this->cookies = new ParameterBag($cookies);
$this->files = new FileBag($files);
$this->server = new ServerBag($server);
$this->headers = new HeaderBag($this->server->getHeaders());

$this->content = $content;
$this->languages = null;
$this->charsets = null;
$this->acceptableContentTypes = null;
$this->pathInfo = null;
$this->requestUri = null;
$this->baseUrl = null;
$this->basePath = null;
$this->method = null;
$this->format = null;
}
[/php]
ここではキーと値を格納するクラスParameterBagのインスタンスが内部変数に格納される。createFromGlobalsに戻ろう。
[php]
if (0 === strpos($request->server->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
&& in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE'))
) {
parse_str($request->getContent(), $data);
$request->request = new ParameterBag($data);
}
[/php]
if文の中には

  1. ファイルアップロードで使われるようなCONTENT_TYPEではない。

  2. PUT OR DELETE メソッド


の2つを満たす時にだけ入る。このとき、リクエストの内容はrequestBodyに書き換えられる。

最後に Requestクラスのstaticインスタンスを返して終了だ。

2012年6月18日月曜日

symfony2を読む(2)

昨日に引き続き、symfony2を読む。カレントディレクトリをsymfony2のインストールディレクトリとする。

web/app_dev.php


[php]
require_once __DIR__.'/../app/bootstrap.php.cache';
require_once __DIR__.'/../app/AppKernel.php';

use Symfony\Component\HttpFoundation\Request;

$kernel = new AppKernel('dev', true);
$kernel->loadClassCache();
$kernel->handle(Request::createFromGlobals())->send();
[/php]

useにより、$SYMFONY2_DIR/vendor/symfony/src/Symfony/Component/HttpFoundation/Request.php を読み出している。
この $SYMFONY2_DIR/vendor/symfony/src の位置はどうやって伝えられたのだろうか?それは bootstrap.php.cache 内で読み込まれた autoload.php に秘密があった。

app/autoload.php


[php]
$loader = new UniversalClassLoader();
$loader->registerNamespaces(array(
'Symfony' => array(__DIR__.'/../vendor/symfony/src', __DIR__.'/../vendor/bundles'),
[/php]

loadClassCache(), handle()はともにAppKernelの親クラスであるKernelクラスの下記メソッドが呼ばれている。

vendor/symfony/src/Symfony/Component/HttpKernel/Kernel.php


[php]
public function loadClassCache($name = 'classes', $extension = '.php')
{
if (!$this->booted && file_exists($this->getCacheDir().'/classes.map')) {
ClassCollectionLoader::load(include($this->getCacheDir().'/classes.map'), $this->getCacheDir(), $name, $this->debug, false, $extension);
}
}
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
if (false === $this->booted) {
$this->boot();
}

return $this->getHttpKernel()->handle($request, $type, $catch);
}
[/php]
今日は loadClassCache()の挙動をよく調べてみよう。まず
[php]
$this->booted # false
$this->getCacheDir().'/classes.map' # app/cache/dev/classes.map
[/php]
となっている。bootedの値ついては__constructメソッドを読めば分かる。
[php]
ClassCollectionLoader::load
[/php]
では、下記クラスのloadメソッドが呼ばれている。

vendor/symfony/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php


[php]
static public function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php')
{
// each $name can only be loaded once per PHP process
if (isset(self::$loaded[$name])) {
return;
}

self::$loaded[$name] = true;
[/php]
$nameという名前で、$classesに渡されたクラス群を読み込むらしい。$nameと$classesは一対一対応となっている。
[php]
if ($adaptive) {
// don't include already declared classes
$classes = array_diff($classes, get_declared_classes(), get_declared_interfaces());

// the cache is different depending on which classes are already declared
$name = $name.'-'.substr(md5(implode('|', $classes)), 0, 5);
}
[/php]
ここでは $adaptive = false なのでif文の中には入らない。

[php]
// auto-reload
$reload = false;
if ($autoReload) {
$metadata = $cacheDir.'/'.$name.$extension.'.meta';
if (!file_exists($metadata) || !file_exists($cache)) {
$reload = true;
} else {
$time = filemtime($cache);
$meta = unserialize(file_get_contents($metadata));

if ($meta[1] != $classes) {
$reload = true;
} else {
foreach ($meta[0] as $resource) {
if (!file_exists($resource) || filemtime($resource) > $time) {
$reload = true;

break;
}
}
}
}
}

if (!$reload && file_exists($cache)) {
require_once $cache;

return;
}
[/php]
$autoReload変数には今回true(AppKernel->debug)が渡されているのでif文の中に入る。ここでは$classesを再度読み込み直すか?それともキャッシュされたクラスをそのまま使うのかが指定されている。再読み込みする条件は3つ。

  1. そもそもキャッシュが存在しない

  2. キャッシュされているクラス群と読み込もうとしているクラス群が異なる

  3. キャッシュ元のクラスが更新されている


[php]

$files = array();
$content = '';
foreach ($classes as $class) {
if (!class_exists($class) && !interface_exists($class) && (!function_exists('trait_exists') || !trait_exists($class))) {
throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class));
}

$r = new \ReflectionClass($class);
$files[] = $r->getFileName();

$c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', file_get_contents($r->getFileName()));

// add namespace declaration for global code
if (!$r->inNamespace()) {
$c = "\nnamespace\n{\n".self::stripComments($c)."\n}\n";
} else {
$c = self::fixNamespaceDeclarations(' $c = preg_replace('/^\s*<\?php/', '', $c);
}

$content .= $c;
}

// cache the core classes
if (!is_dir(dirname($cache))) {
mkdir(dirname($cache), 0777, true);
}
self::writeCacheFile($cache, '[/php]
$classesを1つずつ読み込み、最終的にすべてのクラスを1つのファイル($cacheDir.'/'.$name.$extension)にまとめている。ここで$nameと$classesを一対一対応にしたことが効いてくる。

[php]
if ($autoReload) {
// save the resources
self::writeCacheFile($metadata, serialize(array($files, $classes)));
}
[/php]
オブジェクトを直列化してファイルを保存する。

dev環境、prod環境でのautoReloadの違い


prod環境では$autoReload=falseとなり、キャッシュを消すまで古いクラスが使われ続ける。これは安定した速い運用が求められるprod環境に適している。対してdev環境では$autoReload=trueとなり、常に最新のソースコードでSymfonyを動かすことになる。多少ブラウザ表示が遅くなるとはいえ、開発効率を上げるための工夫である。

2012年6月17日日曜日

symfony2を読む(1)

はじめに


環境: Amazon EC2, Amazon Linux AMI x86_64 EBS
ソースコード:Symfony2.0.15

まずはコードを動かしてみる


参考:The Big Picture (current) - Symfony

ソースコードの配置


[bash]
~$wget http://symfony.com/get/Symfony_Standard_Vendors_2.0.15.tgz
~$tar xvf Symfony_Standard_Vendors_2.0.15.tgz
~$mv Symfony /var/www/html/symfony2
[/bash]

バーチャルホストの設定


参考:バーチャルホスト設定 - CentOSで自宅サーバー構築
DocumentRoot /var/www/html/symfony2/web
に対してバーチャルホストを設定。

config.phpを表示


アクセス情報【使用中のIPアドレス確認】にて自分の現在のIPアドレスを確認。
/var/www/html/symfony2/web/ を開いて
[php]
if (!in_array(@$_SERVER['REMOTE_ADDR'], array(
'127.0.0.1',
'::1',
'Your.Current.Ip.Address',
))) {
header('HTTP/1.0 403 Forbidden');
exit('This script is only accessible from localhost.');
}
[/php]
のように編集。その後 http://symfony2.yourdomain.com/config.php アクセス。表示に従い必要なライブラリのインストールや、フォルダの作成などを行う。

app_dev.phpを表示


config.phpと同様に
[php]
if (isset($_SERVER['HTTP_CLIENT_IP'])
|| isset($_SERVER['HTTP_X_FORWARDED_FOR'])
|| !in_array(@$_SERVER['REMOTE_ADDR'], array(
'127.0.0.1',
'::1',
'Your.Current.Ip.Address',
))
) {
header('HTTP/1.0 403 Forbidden');
exit('You are not allowed to access this file. Check '.basename(__FILE__).' for more information.');
}
[/php]
と編集。その後 http://symfony2.yourdomain.com/app_dev.php にアクセスすると以下のような画面が表示される。
app_dev

2012年5月1日火曜日

Amazon EC2入門

Amazon EC2の登録からssh接続までの作業ログ。

環境:Ubuntu12.04 x32

EC2の仮想サーバー起動まで

Amazon Web Services (日本語)にて、「今すぐ申し込む」からAWSを利用するための登録を行う。
登録が完了したらログインして「アカウント/コンソール」からAWS Management Consoleを開く 
 EC2タブへ移動し、Launch Instanceをクリック

①ssh公開鍵を作成しダウンロード
②サーバーを選んで
③Continue

 Launch
 
 ①NavigationタブからInstancesを表示 ②Status Check欄が準備完了となるのを待つ 

sshでログイン

ssh公開鍵の移動
[bash]
~$ mkdir .ssh
~$ mv
~/ダウンロード/takkyuuplayer.pem .ssh
[/bash]
公開鍵のパーミション変更
[bash]
~$ chmod 400 .ssh/takkyuuplayer.pem
[/bash]
Public DNSの確認

ssh接続
[bash]
~$ ssh -i ~/.ssh/takkyuuplayer.pem ec2-user@PublicDNS
[/bash]
これでssh接続は完了する。

らくらくssh接続

PublicDNSをいちいち覚えていられないので、簡単にAWSに接続できるようにconfigファイルを作る。
[bash]
~$ vi ~/.ssh/config
[/bash]

[text]
Host ec2
User ec2-user
HostName PublicDNS
Port 22
IdentityFile ~/.ssh/takkyuuplayer.pem
[/text]

すると以下の簡略コマンドで接続可能になる

[bash]
~$ ssh ec2
[/bash]