PHPからAWS(EC2)のAPIを叩いてみたので書いてみる。
AWSの公式SDKがあるけど勉強がてらやってみた。
仕様は、
http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/
に書いてある。
準備
- アクセスキーID
- シークレットアクセスキー
すでに発行しているならばそれでもOK
Endpointを決める
http://docs.amazonwebservices.com/general/latest/gr/index.html?rande.html#ec2_region
東京リージョンの場合はec2.ap-northeast-1.amazonaws.com
叩くAPI(Action)を決める
インスタンスを起動したい!とかタグを打ちたい!とか。
http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?OperationList-query.html
今回は、インスタンス一覧取得(DescribeInstances)を選択したみる。
Action固有のパラメータを決める
DescribeInstancesはパラメータなしでも叩ける。
今回は、マイクロインスタンスで絞ってみる。
| Key | Value |
|---|---|
| Filter.1.Name | instance-type |
| Filter.1.Value.1 | t1.micro |
必須パラメータを追加
http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?Query-Common-Parameters.html
| Key | Value |
|---|---|
| Action | アクション名(今回は、DescribeInstances) |
| Version | APIのバージョン(今回は、2011-11-01) |
| AWSAccessKeyId | アクセスキーID |
| Timestamp | YYYY-MM-DDThh:mm:ssZ(date(‘c’)で取得できる) |
| Signature | 後述する方法で生成 |
| SignatureMethod | HmacSHA256 or HMacSHA1 |
| SignatureVersion | 2 |
これに、Action固有のパラメータを足したものが最終的に送るパラメータ。
オープンソーシャルなAPIも似たような方法で署名してた気がする。
手順は、
- 各Keyを元にソート(ksort)
- Key,ValueをURLEncode(RFC 3986)して&で連結
- 署名に使う文字列を生成(HTTPメソッド名+EndPoint+path+連結したパラメータ)
- 上記文字列とシークレットアクセスキーを使ってハッシュ生成(HMAC-SHA256 or HMAC-SHA1)
- base64エンコード
コードにすると以下のような感じ。
(HTTPメソッドとかベタ書きだけど…)
<?php
class Signer
{
function sign($params, $secretAccessKey)
{
$params = $this->sortByKey($params);
$string = $this->buildCanonicalString($params);
$stringToSign = $this->buildStringToSign($string);
$signature = base64_encode($this->hash($stringToSign, $secretAccessKey));
return $signature;
}
private function sortByKey($params)
{
ksort($params);
return $params;
}
private function buildCanonicalString($params)
{
$strings = array();
foreach ($params as $key => $value) {
$strings[] = $this->urlencode($key) . '=' . $this->urlencode($value);
}
return implode('&', $strings);
}
private function buildStringToSign($string)
{
$stringToSign = "GET\n"
. "ec2.ap-northeast-1.amazonaws.com\n"
. "/\n"
. $string;
return $stringToSign;
}
private function hash($stringToSign, $secretAccessKey)
{
return hash_hmac('sha256', $stringToSign, $secretAccessKey, true);
}
// PHP 5.2.X系はチルダもエンコードするので元に戻す
private function urlencode($value)
{
return str_replace('%7E', '~', rawurlencode($value));
}
}
※PHP5.2系の環境はなかったので、PHP5.3でしか動作確認していない。
HTTPリクエスト送信
結果はXMLとして帰ってくるので、これもまたお好きなように。
まとめ
Signature生成/Httpリクエスト送信/EndPoint/ハッシュアルゴリズム
辺りを分離したクラスを作れば立派な簡易クライアントの出来上がり!
それは、また時間のある時にでも…。
結構前に書いて下書きで眠ってた…。
せっかくなので、公開。
PHP, Ruby, Pythonの比較。
PHPで(個人的に)良く使う関数は他の言語ではどうすればいいんだろう?
という趣旨のエントリー。
ちょこちょこっとググったモノを使っているので、もっといいやり方をご存知の方は教えて下さい。
GET
<?php $url = 'http://google.co.jp'; $string = file_get_contents($url); echo $string;
require 'open-uri'
url = 'http://google.co.jp'
results = ''open(url) {|f| results = f.read}
puts results
import urllib url = 'http://google.co.jp' d = urllib.urlopen(url) results = d.read() d.close() print results
URL encode/decode
<?php $url = 'http://example.com/'; echo urlencode($url)."\n"; echo urldecode(urlencode($url))."\n";
require 'cgi' url = 'http://example.com/' puts CGI::escape(url) puts CGI::unescape(CGI::escape(url))
import urllib url = 'http://example.com/' print urllib.quote(url, '') print urllib.unquote(urllib.quote(url, ''))
HTMLのescape/unescape
<?php $html = <<<EOD <html> <body> body </body> </html> EOD; echo htmlspecialchars($html); echo htmlspecialchars_decode(htmlspecialchars($html));
require 'cgi' html = <<EOD <html> <body> body </body> </html> EOD puts CGI.escapeHTML(html) puts CGI.unescapeHTML(CGI.escapeHTML(html))
from xml.sax.saxutils import * html = """ <html> <body> body </body> </html> """ print escape(html) print unescape(escape(html))
目次
まだ、dotcloudのアカウントを持っていない人は、
から。
ちょっと待っててな!的なメールがすぐに来て、2日もすればアカウント作れるよ!的なメールが届く。
さてさて、環境構築の流れは以下の通り。
- python 2.6.6のインストール
- easy_installのインストール
- dotcloudコマンドのインストール
- 開発用ユーザでいじってみる
ハマった点は
CentOS 5.5デフォルトのpythonのバージョンが低くてeasy_installでdotcloudが入らない
という点。
さくらのVPSで作業した。(sudo面倒だからrootで…)
python 2.6.6のインストール
# mkdir /usr/local/python266 # ln -fns /usr/local/python266 /usr/local/python
# cd /usr/local/src # wget http://www.python.org/ftp/python/2.6.6/Python-2.6.6.tgz # tar -zxvf Python-2.6.6.tgz # cd Python-2.6.6 # ./configure --with-threads --enable-shared --prefix=/usr/local/python # vim Modules/Setup zlib zlibmodule.c -I$(prefix)/include -L$(exec_prefix)/lib -lz # make # make install
# vim /etc/ld.so.conf.d/python2.6.6.conf /usr/local/python266/lib # ldconfig
# /usr/local/python/bin/python Python 2.6.6 (r266:84292, May 13 2011, 10:45:20) [GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> quit()
すでに入っているpythonとの共存の為にaliasにしておく
# vim ~/.bashrc PATH=$PATH:/usr/local/python/bin alias python='python2.6'
# . ~/.bashrc # python Python 2.6.6 (r266:84292, May 13 2011, 10:45:20) [GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> quit()
easy_installのインストール
# cd /usr/local/src # wget http://peak.telecommunity.com/dist/ez_setup.py # python ez_setup.py # easy_install-2.6
dotcloudコマンドのインストール
# easy_install-2.6 dotcloud
開発用ユーザでいじってみる
$ vim ~/.bashrc PATH=$PATH:/usr/local/python/bin alias python='python2.6'
$ . ~/.bashrc $ python Python 2.6.6 (r266:84292, May 13 2011, 10:45:20) [GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> quit()
$ mkdir -p ~/work/dotcloud/
※testの部分には自分の好きな名前を入れる。すでに使われているとエラーになる。
$ dotcloud create test
※初回はAPIキーを聞かれる。これは、dotcloudのWebにログインして、Settingsページを見ると書いてある。
$ dotcloud deploy --type php test.php
$ mkdir -p ~/work/dotcloud/test/php $ cd ~/work/dotcloud/test/php $ vim index.php <?php phpinfo();
$ dotcloud push test.php ~/work/dotcloud/test/php
デプロイは、
~/work/dotcloud/test/php
のディレクトリをrsyncしてるみたい。
http://php.test.dotcloud.com/
にアクセス。
独自ドメインの割り当て
dotcloud alias add test.php ドメイン
で割り振れるらしい。
CNAMEはgateway.dotcloud.com。
まとめ
何よりも、PHPだけじゃなくてRubyやPythonの環境もデプロイできるのは凄い!
次はSinatraでもデプロイしてみようかなぁ。
こちらもたまーに使うのでメモエントリー。
Singleton.class.php
<?php
class Singleton
{
static private $instance = null;
private function __construct()
{
}
static function instance()
{
if (!is_null(self::$instance)) return self::$instance;
self::$instance = new self();
return self::$instance;
}
}
test.php
<?php require_once 'Singleton.class.php'; $obj1 = Singleton::instance(); $obj2 = Singleton::instance(); var_dump($obj1); var_dump($obj2);
サンプル実行
$ php test.php
object(Singleton)#1 (0) {
}
object(Singleton)#1 (0) {
}
インストールして試してみました。
手順的には、
http://github.com/openpne/OpenPNE3/raw/OpenPNE-3.4.1/doc/ja/OpenPNE3_Setup_Guide.txt
の通りで9割OK。
一点だけハマったと点は
ブラウザからアクセス(PC) ------------------------ (1) http://example.com/index.php にアクセス (2) ログインフォームに sns@example.com / password と入力し、ログインできるかどうか確認
のところで、
[DocumentRoot]/member/login/authMode/MailAddress
がNot Foundになってしまう点。
いくつかググってみると、.htaccessの設置を許可していないと起こるらしい。
AllowOverride all
をhttpd.confに追加すればOK。(Directory設定でも可)
Rubyだと普通にできる事が、PHPだとできないという事に最近ぶつかる。
class A
def self.create
return self.new
end
end
class B < A
end
puts A.create # Aクラスのオブジェクトを返す
puts B.create # Bクラスのオブジェクトを返す
とか書くと、createは期待した通りのオブジェクトを返してくれる。
でも、PHP(5.2.x)だと…
<?php
class A
{
static function create() { return new self(); }
}
class B extends A
{
}
var_dump(A::create()); // Aクラスのオブジェクトを返す
var_dump(B::create()); // これもAクラスのオブジェクトを返す
となってしまう…。
PHP 5.3系だと直ってるらしいけど…。
なんとイケてない言語…。
abstract final class God
{
}
と昔、知り合いが書いていました。
PHPだとParse errorです(笑)
作成中です。
Storategyパターンで使用するコマンドは切り替えられるようにする予定。
そうすると、コマンド引数によってそれぞれ条件が変わるから
そこをある程度吸収できる仕組みは必要。
Facadeパターンとかで吸収できるかなぁと思ってる。
でも、最大公約か最小公倍かで悩み中。
取り急ぎ、Webクローラを作ります。
というわけで、この時間までコーディングしていました。
データ変換(PDFからテキストなど)とhttpリクエストのライブラリ作ってました。
あらかた出来上がった(コメント入れとかは全然していないが…)ので
オープンソースとして公開しようと思ってます。
ニーズがあるかどうかはわからないけど、自分は使いたいから作った。
同じ思いの人もいるから公開しておく。
こんな活動できるのも今の会社に入れたおかげだなぁー。
次は、クローラを作る予定。(内部でwgetするだけなんだけどね…)
pdftotextとかwvHtmlとか使えば、テキストにできる!
という事で、PHPからどうやって呼ぼうかなぁと検討してみました。
※オブジェクト指向はまだまだ勉強中なので中途半端だけど…。
まず、各コンバータのテンプレートとなる抽象クラスを作る。
<?php
abstract class Converter_Template
{
abstract function toText($string);
abstract function toHtml($string);
abstract protected function isInstalledUseCommand();
}
次に、各ファイルのコンバータは、拡張子毎にオブジェクト化。
Word(doc)を例にとると
class Converter_Doc extends Converter_Template
{
function toText($string)
{
ごにょごにょ
}
function toHtml($string)
{
ごにょごにょ
}
protected function isInstalledUseCommand()
{
return true;
}
}
Excel(xls)を例にとると
class Converter_Xls extends Converter_Template
{
function toText($string)
{
ごにょごにょ
}
function toHtml($string)
{
ごにょごにょ
}
protected function isInstalledUseCommand()
{
return true;
}
}
このままだと、扱えるファイルが増えるとオブジェクトがどんどん増えるので
Factoryパターンを適用し、ここにお任せする。
class Converter_Factory
{
static function create($extension)
{
if ($extension === 'doc') return new Converter_Doc();
if ($extension === 'xls') return new Converter_Xls();
throw new Exception("Converterer not found. [extension = $extension]");
}
}
こんな感じで、「ごにょごにょ」部分を実装すればOK。
実際には、コマンド実行とかは各コンバータで共有できそう。
内容が見えたら、適切な名前を付けてオブジェクト化。(Converter_Commandとか??)
呼び出しとしては、
$data = file_get_contents('xxxxx.doc');
$converter = Converter_Factory::create('doc');
var_dump($converter->toText($data));
var_dump($converter->toHtml($data));
createに渡す値をxlsにすれば、エクセルに適用できる。
こんな感じでどうだろう?

最新コメント