PHP

PHPで配列の行列を入れ替える

配列をテーブルレイアウト用に変えたりする時に、行列入れ替えたいなという事があります。
array_mapを使うと、foreachでループ処理しなくても、入れ替えることができます。


$before[] = array('0', 'aaa', '090-0000-0000');
$before[] = array('1', 'bbb', '090-1111-1111');
$before[] = array('2', 'ccc', '090-2222-2222');



$after = array_map(null, $before[0], before[1], before[2]);

さくらVPSでメールサーバー構築(Postfix+Dovecot2+ClamAV+SpamAssassin+Amavisd-new)

メールサーバーについて気になった事が有ったので、さくらVPS環境で、メールサーバー構築した際の備忘録です。

  • バーチャルドメインで運用を想定している為、MySQLを利用して、管理はPostfixAdminで行えるようにします。
  • 通信はTSLを利用します。
  • アンチウィルス、スパムフィルタを利用します。
メールサーバー Postfix 2.6.6
POP、IMAPサーバー Dovecot 2.0.9
アンチウィルスソフトウェア ClamAV 0.97
スパムフィルタソフトウェア SpamAssassin 3.3.1
メールサーバー、アンチウィルスソフトウェア、スパムフィルタソフトウェアのインターフェースソフトウェア Amavised-new 2.8.0
メールアカウント管理 PostfixAdmin 2.3.6

PostfixAdminのインストールと設定

PostfixAdmin からソースをダウンロードして、解凍後、公開ディレクトリにFTPなどでアップロードします。今回は公開ディレクトリを下記のような形で設定し、PostfixAdminを配置しました。

/home/webadmin/public_html ← ドキュメントルート 
/home/webadmin/public_html/postfixadmin 

PostfixAdminで使用するDB、Userの作成

データベースをpostfix、ユーザーをpostfixで作成しました。

create database postfix GRANT ALL PRIVILEGES ON *.* TO 'postfix'@'localhost' IDENTIFIED BY 'XXXXXXXX' WITH GRANT OPTION; 

PostfixAdminの設定

設定ファイルは、config.inc.phpですが、config.local.phpというファイルがある場合は、設定を上書きするので、config.local.phpを作成して、設定内容を記述します。

 
vi /home/webadmin/public_html/postfixadmin/config.local.php 

<?php $CONF['configured'] = true; 
$CONF['setup_password'] = 'changeme'; // ブラウザからアクセスした後に設定する 
$CONF['default_language'] = 'ja'; 
$CONF['database_type'] = 'mysqli'; 
$CONF['database_host'] = 'localhost'; 
$CONF['database_user'] = 'postfix'; 
$CONF['database_password'] = 'XXXXXXX'; 
$CONF['database_name'] = 'postfix'; 
$CONF['database_prefix'] = ''; 
$CONF['admin_email'] = 'postmaster@ドメイン名'; 
$CONF['encrypt'] = 'md5crypt'; // パスワードの暗号化方式 
$CONF['page_size'] = '100'; 
$CONF['domain_path'] = 'YES'; // メールボックス用に、ドメインごとにディレクトリを作成する /home/virtual/virtualdomain.com/ 
$CONF['aliases'] = '100'; 
$CONF['mailboxes'] = '100'; 
$CONF['maxquota'] = '100'; 
$CONF['quota'] = 'YES'; 
$CONF['backup'] = 'NO'; 
$CONF['sendmail'] = 'NO'; 
$CONF['fetchmail'] = 'NO'; 
$CONF['new_quota_table'] = 'YES'; // dovecotのバージョンが1.2以下の場合はNOにする。 
$CONF['user_footer_link'] = 'http://ドメイン名/postfixadmin/users/main.php'; 

// procmail使用時の設定 
// メールディレクトリ自動作成 
$CONF['mailbox_postcreation_script'] = 'sudo /usr/local/bin/postfixadmin/postfixadmin-mailbox-postcreation.sh'; 
// メールディレクトリ自動削除 
$CONF['mailbox_postdeletion_script'] = 'sudo /usr/local/bin/postfixadmin/postfixadmin-mailbox-postdeletion.sh'; 
// ドメインディレクトリ自動削除 
$CONF['domain_postdeletion_script'] = 'sudo /usr/local/bin/postfixadmin/postfixadmin-domain-postdeletion.sh'; 
?>

バーチャルドメインメールを保存しておくディレクトリ、ユーザーの作成

バーチャルドメインでは、メールボックスとUNIXユーザーは切り離されて、メールは指定したバーチャルドメイン用のメールボックスに配送されます。そのユーザーを作成します。

 #groupadd -g 5000 virtual #useradd -g 5000 -u 5000 virtual 

設定後、http://ドメイン/postfixadmin/setup.phpにアクセスします。

imap関数がないとWarningが出たので、

 
# yum install php-imap 

としてインストールしました。インストール後はapacheを再起動します。

 
# /sbin/service/httpd restart 

その後、http://ドメイン/postfixadmin/setup.phpにアクセスし、Warningが出ないことを確認したら、パスワードの設定をして、管理画面からドメイン、ユーザーを追加します。

セットアップが完了したら、セットアップ用のファイルを削除します。

 
# rm -f /home/webadmin/public_html/postfixadmin/setup.php 
# rm -f /home/webadmin/public_html/postfixadmin/motd* 

Postfixのインストールと設定

さくらVPSにははじめからPostfixがインストールされています。

インストール済みのPostfixが対応しているルックアップテーブルを表示して、MySQLに対応しているか確認します。

 
# postconf -m btree cidr environ hash ldap mysql nis pcre proxy regexp static unix 

mysqlと表示されているのでMySQLに対応していることが確認できます。

PostfixからMySQLへアクセスする為のファイルを作成します。

 
# vi /etc/postfix/mysql_virtual_alias_maps.cf 

user = postfix 
password = XXXXXX 
hosts = localhost 
dbname = postfix 
table = alias 
select_field = goto 
where_field = address 

# vi /etc/postfix/mysql_virtual_domains_maps.cf 

user = postfix 
password = XXXXXX 
hosts = localhost 
dbname = postfix 
table = domain 
select_field = domain 
where_field = domain 
additional_conditions = and active = '1' 

# vi /etc/postfix/mysql_virtual_mailbox_maps.cf 

user = postfix 
password = XXXXXX 
hosts = localhost 
dbname = postfix 
table = mailbox 
select_field = maildir 
where_field = username 

# vi /etc/postfix/mysql_virtual_mailbox_limit_maps.cf 

user = postfix 
password = XXXXXX 
hosts = localhost 
dbname = postfix 
table = mailbox 
select_field = quota 
where_field = username 
#additional_conditions = and active = '1' 

Postfixの設定ファイルを編集します。

設定ファイルを変更する場合は全て、オリジナルのバックアップをとってから行いました。

 
# cp /etc/postfix/{main.cf,main.cf.org} 
 
# vi /etc/postfix/main.cf 

#inet_interfaces = localhost #コメントアウト 
#inet_protocols = all #コメントアウト 
#mydestination = $myhostname, localhost.$mydomain, localhost #コメントアウト 

#最終行に追加 
myhostname = wwwXXXXX.sakura.ne.jp #さくらのホスト名 
inet_interfaces = all # 外部からのメールも受信する 
inet_protocols = ipv4 mydestination = $myhostname, localhost 
relay_domains = $mydestination 
home_mailbox = Maildir/ 
smtpd_banner = $myhostname ESMTP unknown 

# Virtual Domain w/MySQL 
local_transport = local 
virtual_transport = procmail # メールの配送はprocmail 

procmail_destination_recipient_limit = 1 
virtual_mailbox_base = /home/virtual 
virtual_alias_maps = mysql:/etc/postfix/mysql_virtual_alias_maps.cf 
virtual_alias_domains = $virtual_alias_maps 
virtual_mailbox_domains = mysql:/etc/postfix/mysql_virtual_domains_maps.cf 
virtual_mailbox_maps = mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf 
virtual_minimum_uid = 5000 
virtual_uid_maps = static:5000 
virtual_gid_maps = static:5000 

maximal_backoff_time = 800s 
minimal_backoff_time = 100s 
bounce_queue_lifetime = 60m 
maximal_queue_lifetime = 60m 
message_size_limit = 20480000 
#virtual_mailbox_limit = 51200000 
virtual_mailbox_limit_maps = mysql:/etc/postfix/mysql_virtual_mailbox_limit_maps.cf 
virtual_mailbox_limit_override = yes 
virtual_overquota_bounce = yes 
virtual_mailbox_limit_inbox = yes 

smtpd_sasl_auth_enable = yes 
smtpd_sasl_type = dovecot 
smtpd_sasl_path = private/auth 
smtpd_sasl_local_domain = $myhostname 
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination 

# SpamAssasin 
# procmailと連携するようにする。 
mailbox_command = /usr/bin/procmail 
# End SpamAssasin 

# amavisd-new 
# amavisd-newと連携するように設定 
content_filter=smtp-amavis:[127.0.0.1]:10024 
# End amavisd-new 

新規ユーザーが追加されたらホームディレクトリに Mairdir/ を自動で作成されるようにする。

 
# mkdir -p /etc/skel/Maildir/{new,cur,tmp} 
# chmod -R 700 /etc/skel/Maildir/ 

サブミッションポート587番ポートを利用する。

 
# cp /etc/postfix/{master.cf,master.cf.org} 
# vi /etc/postfix/master.cf 

#最終行に追加 
submission inet n - n - - smtpd 
# amavisd-new
smtp-amavis unix -    -    n    -    2  smtp
    -o smtp_data_done_timeout=1200
    -o smtp_send_xforward_command=yes
    -o disable_dns_lookups=yes
127.0.0.1:10025 inet n    -    n    -    -  smtpd
    -o content_filter=
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o smtpd_restriction_classes=
    -o smtpd_client_restrictions=
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o mynetworks=127.0.0.0/8
    -o strict_rfc821_envelopes=yes
    -o smtpd_error_sleep_time=0
    -o smtpd_soft_error_limit=1001
    -o smtpd_hard_error_limit=1000
# End amavisd-new

# procmail
procmail unix - n n - - pipe
  flags=R user=virtual argv=/usr/bin/procmail -t -m USER=${user} DOMAIN=${nexthop} /etc/procmailrc
# End procmail

最後に自動起動の設定をして終了です。

 
# /sbin/chkconfig postfix on 

dovecot2のインストールと設定

yumでインストールする。

 
# yum install dovecot dovecot-mysql 

dovecot2の設定ファイルは/etc/dovecot/dovecot.confですが、/etc/dovecot/conf.d/以下に.confファイルを作成すれば自動で読みこんでくれるので、00-base.confというファイルを作成し設定を行います。

 
# vi /etc/dovecot/conf.d/00-base.conf 
# debug
auth_debug_passwords = yes
auth_verbose = yes
auth_debug = yes

listen = *
disable_plaintext_auth = no
auth_mechanisms = plain login digest-md5 cram-md5

# mail dir
mail_location = maildir:/home/virtual/%d/%u
first_valid_uid = 5000
first_valid_gid = 5000
mail_plugins = quota

protocol imap {
  imap_client_workarounds = delay-newmail tb-extra-mailbox-sep
  mail_plugins = $mail_plugins imap_quota
}
protocol pop3 {
  pop3_client_workarounds = outlook-no-nuls oe-ns-eoh
}
passdb {
  driver = sql
  args = /etc/dovecot/dovecot-postfixadmin-mysql.conf
}
userdb {
  driver = sql
  args = /etc/dovecot/dovecot-postfixadmin-mysql.conf
}
service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
  }
}
plugin {
  quota = maildir:User quota
}

次に、/etc/dovecot/conf.d/10-auth.confを編集します。

 
# cp /etc/dovecot/conf.d/{10-auth.conf,10-auth.conf.org} 
# vi /etc/dovecot/conf.d/10-auth.conf 

#auth_mechanisms = plain #コメントアウト 
#!include auth-system.conf.ext #コメントアウト 

次に、MySQLへの接続ファイルを作成します。

 
# vi /etc/dovecot/dovecot-postfixadmin-mysql.conf 
driver = mysql 
connect = host=/var/run/mysqld/mysqld.sock 
dbname=postfix 
user=postfix 
password=XXXXXX 
default_pass_scheme = MD5-CRYPT 
password_query = SELECT password FROM mailbox WHERE username = '%u' AND active = '1' 
user_query = SELECT concat('/home/virtual/', maildir) AS home, 5000 AS uid, 5000 AS gid FROM mailbox WHERE username = '%u' AND active = '1' 

次に、認証用ライブラリをインストールします。

 
# yum install cyrus-sasl-plain cyrus-sasl-md5 

最後自動起動の設定をして終了です。

 
# /sbin/chkconfig dovecot on 

ClamAVのインストールと設定

yumでインストール

# yum install clamd

バックアップを取って、設定(実行ユーザー(root)にする)

# cp /etc/{clamd.conf,clamd.conf.org} 
# vi /etc/clamd.conf

#User clam #コメントアウト

起動と自動起動設定

# /etc/init.d/clamd start
# chkconfig clamd on

ウイルス定義ファイル更新機能を有効化と、定義ファイルの更新

# sed -i 's/Example/#Example/g' /etc/freshclam.conf
# freshclam

ClamAVとPostfixの連携設定

# vi /etc/clamd.conf

#TCPSocket 3310 #コメントアウト

/etc/init.d/clamd restart

Amavised-newのインストールと設定

yumでインストール

# yum install amavisd-new

バックアップを取って、設定

# cp /etc/amavisd/{amavisd.conf,amavisd.conf.org} 
# vi /etc/amavisd/amavisd.conf

# COMMONLY ADJUSTED SETTINGS:
$undecipherable_subject_tag = ''; #←追加(パスワード付ZIPファイル受信時メール件名に「***UNCHECKED***」と付加されないようにする。)

@bypass_spam_checks_maps  = (1);  # controls running of anti-spam code #←コメントアウト解除(amavisd-newではスパムチェックは行なわないようにする。スパムチェックはprocmail経由でspamcコマンドで行なう)

$mydomain = 'wwwXXXXX.sakura.ne.jp';   # a convenient default for other settings #←コメントアウト解除してホスト名に変更する

#$QUARANTINEDIR = undef;      # -Q #←コメントアウト(ウィルスメールは隔離しない)

#$virus_admin               = undef; #←コメントアウト(ウィルス検知メールを管理者宛に通知しない)

### BLOCKED ANYWHERE
# qr'^UNDECIPHERABLE$',  # is or contains any undecipherable components
# qr'^\.(exe-ms|dll)$',                   # banned file(1) types, rudimentary #←コメントアウト(exeファイルを受信できるようにする)
# qr'^\.(exe|lha|cab|dll)$',              # banned file(1) types


# #   this entry; when running chrooted one may prefer socket "$MYHOME/clamd".
# ↓追加(/var/run/clamav/clamd.sockはソケット名変更)
['ClamAV-clamd',
  \&ask_daemon, ["CONTSCAN {}\n", "/var/run/clamav/clamd.sock"],
  qr/\bOK$/m, qr/\bFOUND$/m,
  qr/^.*?: (?!Infected Archive)(.*) FOUND$/m ],

起動と自動起動設定

# /etc/init.d/amavisd start
# chkconfig amavisd on

SpamAssassin設定

起動と自動起動設定

# /etc/init.d/spamassassin start
# chkconfig spamassassin on

バックアップを取って設定

# cp /etc/mail/spamassassin/{v310.pre,v310.pre.org}
# vi /etc/mail/spamassassin/v310.pre


loadplugin Mail::SpamAssassin::Plugin::TextCat #←コメントアウト解除

rootユーザーでSpamAssassin設定ファイル最新化スクリプト作成

# cd ~
# vi spamassassin-update
#!/bin/bash

# SpamAssassin設定ファイル最新版ダウンロード
cd /etc/mail/spamassassin
wget -qN http://www.flcl.org/~yoh/user_prefs

# 設定ファイル更新時のみSpamAssassin再起動
diff user_prefs user_prefs.org > /dev/null 2>&1
if [ $? -ne 0 ]; then
    cp user_prefs local.cf

    # スパム判断したメールを添付形式にしないように設定
    echo "report_safe 0" >> local.cf

    # スパム判断したメールの件名に「***SPAM***」を付加するように設定※受信メールサーバーがPOPの場合のみ
    echo "rewrite_header Subject ***SPAM***" >> local.cf

    # SpamAssassin再起動
    /etc/rc.d/init.d/spamassassin restart > /dev/null
fi
cp user_prefs user_prefs.org

実行権限追加

# chmod +x spamassassin-update

一度実行して、最新にする

# ./spamassassin-update

cronに登録する

# mv spamassassin-update /etc/cron.daily/

procmailの設定ファイルを新規作成する

# vi /etc/procmailrc
SHELL=/bin/bash
PATH=/usr/bin:/bin
DEFAULT=/home/virtual/$DOMAIN/$USER\@$DOMAIN/
LOCKFILE=/home/virtual/procmail.lock
LOGFILE=/home/virtual/procmail.log
#VERBOSE=ON

# SpamAssassin Spam Check
:0fw
|/usr/bin/spamc


:0
*^X-Spam-Flag: YES
$SPAM

ログの設定

# vi /etc/logrotate.d/procmail
/home/virtual/procmail.log {
    missingok
    nocreate
    notifempty
}

メールディレクトリの自動作成・削除

PostfixAdminを使ってアカウント追加・削除する際に、ディレクトリを自動で作成・削除するようにスクリプトを作成する。

メールボックス作成・削除スクリプト用ディレクトリ作成

# mkdir /usr/local/bin/postfixadmin

PostfixAdminに有るスクリプトをコピー

# cp /home/webadmin/public_html/postfixadmin/ADDITIONS/postfixadmin-domain-postdeletion.sh /usr/local/bin/postfixadmin
# cp /home/webadmin/public_html/postfixadmin/ADDITIONS/postfixadmin-mailbox-postcreation.sh /usr/local/bin/postfixadmin
# cp /home/webadmin/public_html/postfixadmin/ADDITIONS/postfixadmin-mailbox-postdeletion.sh /usr/local/bin/postfixadmin

削除したメールボックスを保存するディレクトリを作成

# mkdir /home/virtual/.deleted-maildirs
# chmod 0700 /home/virtual/.deleted-maildirs
# chown virtual:virtual /home/virtual/.deleted-maildirs

スクリプトの編集(メールボックス作成)

# vi /usr/local/bin/postfixadmin/postfixadmin-mailbox-postcreation.sh
#basedir=/var/spool/maildirs #←コメントアウト
basedir=/home/virtual        #←追加
# ↓コメントアウト
#maildirmake "$maildir"
#if [ ! -d "$maildir" ]; then
#    echo "$0: maildirmake didn't produce a directory; bailing out."
#    exit 1
#fi

exit 0の前に追加

mkdir "$maildir"
if [ ! -d "$maildir" ]; then
    echo "$0: mkdir didn't produce a directory; bailing out."
    exit 1
fi

mkdir "${maildir}cur"
if [ ! -d "${maildir}cur" ]; then
    echo "$0: mkdir didn't produce a directory; bailing out."
    exit 1
fi

mkdir "${maildir}new"
if [ ! -d "${maildir}new" ]; then
    echo "$0: mkdir didn't produce a directory; bailing out."
    exit 1
fi

mkdir "${maildir}tmp"
if [ ! -d "${maildir}tmp" ]; then
    echo "$0: mkdir didn't produce a directory; bailing out."
    exit 1
fi

chown -R virtual:virtual "$maildir"
chmod -R 700 "$maildir"

スクリプトの編集(メールボックス削除)

# vi /usr/local/bin/postfixadmin/postfixadmin-mailbox-postdeletion.sh
#basedir=/var/spool/maildirs
basedir=/home/virtual
#trashbase=/var/spool/deleted-maildirs
trashbase=/home/virtual/.deleted-maildirs
#subdir=`echo "$1" | sed 's/@.*//'`
subdir=$1

スクリプトの編集(ドメイン削除用)

# vi /usr/local/bin/postfixadmin/postfixadmin-domain-postdeletion.sh
#basedir=/var/spool/maildirs
basedir=/home/virtual
#trashbase=/var/spool/deleted-maildirs
trashbase=/home/virtual/.deleted-maildirs

実行権限追加

# chmod 0700 /usr/local/bin/postfixadmin/postfixadmin*

apacheがsudo出来るように設定する

# visudo
Defaults    requiretty
Defaults:apache    !requiretty #←追加


root    ALL=(ALL)       ALL
apache ALL=(ALL) NOPASSWD: /usr/local/bin/postfixadmin/postfixadmin-mailbox-postcreation.sh #←追加
apache ALL=(ALL) NOPASSWD: /usr/local/bin/postfixadmin/postfixadmin-mailbox-postdeletion.sh #←追加
apache ALL=(ALL) NOPASSWD: /usr/local/bin/postfixadmin/postfixadmin-domain-postdeletion.sh #←追加

再起動して確認

/etc/init.d/postfix restart
/etc/init.d/dovecot restart
/etc/init.d/spamassassin restart
/etc/init.d/clamd restart
/etc/init.d/amavisd restart

TSLの設定追記…

PHP4環境でのTwitter API1.1対応

PHP4の環境でTwitter API1.1対応した時の備忘録です。

現状

http://api.twitter.com/1/statuses/followers.format
  • ↑APIを使ってアカウント毎(都道府県の)に、フォローワーを取得
  • ユーザーのscreen_nameが有るかチェック
  • screen_nameが有ったら特定の処理

という事をやっていました。

今後

まずはoauth対応が必要

developerサイトでコンシューマ登録(Consumer key、Consumer secret、Access token、Access token secret取得)し、oauthが使えるようにする。

自分のアカウント情報わかればいいので、コールバックは設定なし(developerサイトで取得したAccess tokenのみでOK)

どのAPIを使うか

もともと、statuses/followers.formatは非推奨だったようで且つAPI1.1のドキュメントには使えると書いてないし、
API1のドキュメントにも GET followers/ids and GET users/lookup. 使ってねって書いてある。

https://api.twitter.com/1.1/friends/ids.json?cursor=-1&screen_name=twitterapi

↑からid一覧が取得できるのでそこから

https://api.twitter.com/1.1/users/lookup.json?screen_name=twitterapi,twitter

↑を使って、screen_nameを取得する。
※100ユーザーまで一度に調べれる(カンマ区切りでパラメーター)

疑問

APIのリクエストリミット制限に引っかかるのでは?
  • 今までのリクエストリミット
    • OAuthなしの場合はIPアドレス当たり1時間150回、OAuthありの場合は1アカウント当たり1時間350回
  • 新しいAPIのリクエストリミット
    • ・API 1.1では、1アカウント当たり15分で“エンドポイントごとに(API毎に)”15回または180回
https://dev.twitter.com/docs/api/1.1/get/application/rate_limit_status

↑で各エンドポイント毎のリミッター分かる。

結局

制限は仕方ないと割りきって、各アカウントでfollowers/ids(リミット:15回/15分)でフォローされているidを取得して、users/lookup(リミット:180回/15分)でscreen_nameを取得する。

users/lookupはパラメーターが長くなるのでPOSTがよさそう。
OAuthのパーミッションがRead-only だとPOST出来ないよ。

Utilクラス

<?php
require_once 'HTTP/Request.php';
require_once 'Jsphon/Decoder.php';
require_once 'twitter_config.php';
error_reporting(-1);
define('REQUEST_DOMAIN',       'https://api.twitter.com');

class TwitterUtil {

    // ---------------------------------------------
    // friendships/lookup 
    // ---------------------------------------------
    function friendshipsLookup($follow_account, $screen_name) {

        if (!isset($follow_account) || !isset($screen_name)) {
            return;
        }

        // APIパラメーター
        $endPointParam = array('screen_name' => $screen_name);

        // アカウント情報取得
        $accountConfig = $GLOBALS['TWITTER_CONFIG']['ACCOUNT'][$follow_account];

        // 投げる変数類設定
        $requestParam = $this->_initOAuthParam($accountConfig);
        $requestParam += $endPointParam;

        // Signatureベーステキストの作成
        $requestUri = REQUEST_DOMAIN . '/1.1/friendships/lookup.json';
        $signatureBase = $this->_createSignatureBase($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET);

        // 最初に定義したパラメータリストからscreen_name撤去
        unset($requestParam['screen_name']);

        // Signatureの作成
        $keys = $this->_rawurlencode($accountConfig['CONSUMER_SECRET']) . '&' . $this->_rawurlencode($accountConfig['ACCESS_TOKEN_SECRET']);
        $requestParam['oauth_signature'] = base64_encode($this->_hmacsha1($keys, $signatureBase));

        // リクエスト
        $result = $this->_authRequest($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET, $endPointParam);
        return $result;
    }

    // ---------------------------------------------
    // followers/ids
    // ---------------------------------------------
    function followersIds($follow_account, $cursor) {

        if (!isset($follow_account) || !isset($cursor)) {
            return;
        }

        // APIパラメーター
        $endPointParam = array('cursor' => $cursor, 'screen_name' => $follow_account,);

        // アカウント情報取得
        $accountConfig = $GLOBALS['TWITTER_CONFIG']['ACCOUNT'][$follow_account];

        // 投げる変数類設定
        $requestParam = $this->_initOAuthParam($accountConfig);
        $requestParam += $endPointParam;

        // Signatureベーステキストの作成
        $requestUri = REQUEST_DOMAIN . '/1.1/followers/ids.json';
        $signatureBase = $this->_createSignatureBase($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET);

        // 最初に定義したパラメータリストからcursor,screen_name撤去
        unset($requestParam['cursor'], $requestParam['screen_name']);

        // Signatureの作成
        $keys = $this->_rawurlencode($accountConfig['CONSUMER_SECRET']) . '&' . $this->_rawurlencode($accountConfig['ACCESS_TOKEN_SECRET']);
        $requestParam['oauth_signature'] = base64_encode($this->_hmacsha1($keys, $signatureBase));

        // リクエスト
        $result = $this->_authRequest($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET, $endPointParam);
        return $result;

    }

    // ---------------------------------------------
    // users/lookup
    // ---------------------------------------------
    function usersLookup($follow_account, $user_id ) {

        if (!isset($follow_account) || !isset($user_id)) {
            return;
        }

        // APIパラメーター
        $endPointParam = array('user_id' => $user_id);

        // アカウント情報取得
        $accountConfig = $GLOBALS['TWITTER_CONFIG']['ACCOUNT'][$follow_account];

        // 投げる変数類設定
        $requestParam = $this->_initOAuthParam($accountConfig);
        $requestParam += $endPointParam;

        // Signatureベーステキストの作成
        $requestUri = REQUEST_DOMAIN . '/1.1/users/lookup.json';
        $signatureBase = $this->_createSignatureBase($requestUri, $requestParam, HTTP_REQUEST_METHOD_POST);

        // 最初に定義したパラメータリストからscreen_name撤去
        unset($requestParam['user_id']);

        // Signatureの作成
        $keys = $this->_rawurlencode($accountConfig['CONSUMER_SECRET']) . '&' . $this->_rawurlencode($accountConfig['ACCESS_TOKEN_SECRET']);
        $requestParam['oauth_signature'] = base64_encode($this->_hmacsha1($keys, $signatureBase));

        // リクエスト
        $result = $this->_authRequest($requestUri, $requestParam, HTTP_REQUEST_METHOD_POST, $endPointParam);
        return $result;

    }

    // ---------------------------------------------
    // application/rate_limit_status
    // ---------------------------------------------
    function status($follow_account, $resources) {

        if (!isset($follow_account) || !isset($resources)) {
            return;
        }

        // APIパラメーター
        $endPointParam = array('resources' => $resources);

        // アカウント情報取得
        $accountConfig = $GLOBALS['TWITTER_CONFIG']['ACCOUNT'][$follow_account];

        // 投げる変数類設定
        $requestParam = $this->_initOAuthParam($accountConfig);
        $requestParam += $endPointParam;

        // Signatureベーステキストの作成
        $requestUri = REQUEST_DOMAIN . '/1.1/application/rate_limit_status.json';
        $signatureBase = $this->_createSignatureBase($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET);

        // 最初に定義したパラメータリストからresources撤去
        unset($requestParam['resources']);

        // Signatureの作成
        $keys = $this->_rawurlencode($accountConfig['CONSUMER_SECRET']) . '&' . $this->_rawurlencode($accountConfig['ACCESS_TOKEN_SECRET']);
        $requestParam['oauth_signature'] = base64_encode($this->_hmacsha1($keys, $signatureBase));

        // リクエスト
        $result = $this->_authRequest($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET, $endPointParam);
        return $result;

    }

    // ---------------------------------------------
    // リクエスト(GET,POST)
    // ---------------------------------------------
    function _authRequest($uri, $param, $method, $addData) {

        // HTTPヘッダーに追加するAuthorizationパラメータ作成
        $requestHeaders = array();
        foreach ($param as $k => $v) {
            $requestHeaders[] = $k . '="' . $this->_rawurlencode($v) . '"';
        }

        $requestHeader = 'OAuth ' . implode(', ', $requestHeaders);

        // PEARのRequestを使ってポスト
        $request = &new HTTP_Request($uri);

        $request->setMethod($method);
        $request->addHeader('Authorization', $requestHeader);
        if ($method == HTTP_REQUEST_METHOD_GET) {
            foreach ($addData as $k => $v) {
                $request->addQueryString($k, $v);
            }
        } else {
            foreach ($addData as $k => $v) {
                $request->addPostData($k, $v);
            }
        }
        $result = $request->sendRequest();
        if (PEAR::isError($result)) {
            return;
        }

        $json = new Jsphon_Decoder();
        return $json->decode($request->getResponseBody());

    }

    // ---------------------------------------------
    // OAuth認証共通パラメーター初期化
    // ---------------------------------------------
    function _initOAuthParam($config) {
        return array(
            'oauth_consumer_key'     => $config['CONSUMER_KEY'],
            'oauth_nonce'            => md5(uniqid(mt_rand(), TRUE)),
            'oauth_signature_method' => 'HMAC-SHA1',
            'oauth_timestamp'        => time(),
            'oauth_token'            => $config['ACCESS_TOKEN'],
            'oauth_version'          => '1.0',
        );
    }

    // ---------------------------------------------
    // Signatureベーステキスト作成
    // ---------------------------------------------
    function _createSignatureBase($uri, $param, $method = 'GET') {

        if (!isset($uri)) {
            return;
        }

        // 変数名でソート
        ksort($param);

        // Sigunatureのベーステキストを作成
        $queries = array();
        foreach ($param as $k => $v) {
            $queries[] = $k . '=' . $this->_rawurlencode($v);
        }
        $query = implode('&', $queries);

        // それぞれをURLエンコードして接続
        $signatureBase = "{$method}&";
        $signatureBase .= $this->_rawurlencode($uri) . '&';
        $signatureBase .= $this->_rawurlencode($query);

        return $signatureBase;
    }

    // ---------------------------------------------
    // URLエンコード RFC3986版
    // ---------------------------------------------
    function _rawurlencode($str) {
        $str = rawurlencode($str);
        $str = str_replace('+', ' ', $str);
        $str = str_replace('%7E', '~', $str);
        return $str;
    }

    // ---------------------------------------------
    // hash_hmac('sha1', $data, $key, true)の代わり
    // ---------------------------------------------
    function _hmacsha1($key, $data) {
        $blocksize = 64;
        $hashfunc = 'sha1';
        if (strlen($key) > $blocksize) {
            $key = pack('H*', $hashfunc($key));
        }
        $key = str_pad($key, $blocksize, chr(0x00));
        $ipad = str_repeat(chr(0x36), $blocksize);
        $opad = str_repeat(chr(0x5c), $blocksize);
        $hmac = pack('H*', $hashfunc(($key ^ $opad) . pack('H*', $hashfunc(($key ^ $ipad) . $data))));
        return $hmac;
    }

}

PEARのHTTP_REQUESTを使ってリクエストを送って、jsonはJsphonを使ってデコードしています。
PHP4の環境なので

PHP4でoAuth。Twitter APIでつぶやく。「マチルダさ~ん」 | motooLogue.

を参考に作りました。
twitter_config.phpには

<?php
$GLOBALS['TWITTER_CONFIG']['ACCOUNT']['aaa'] = array(
    'CONSUMER_KEY'        => 'asdfasdfeii83jeoasdfasd',
    'CONSUMER_SECRET'     => 'asdfase90ia3aksjdflkasdlfasdfasdfas',
    'ACCESS_TOKEN'        => '12103981-asdfasdJOE+Spassaspdoep',
    'ACCESS_TOKEN_SECRET' => 'asdfDFIELKJF38293lsdsf89DKKKLL'
);

のような感じでアカウント情報を保持するようにしました。

呼び出し元は

<?php

require_once 'twitter_util.php';

$twitterUtil = new TwitterUtil();
$data = $twitterUtil->followersIds('aaa', '-1');

echo '<pre>';
print_r($data);
echo '</pre>';
exit;

のようにリクエスト送信、レスポンス取得ができるので、各アカウントでfollowers/ids(リミット:15回/15分)でフォローされているidを取得して、users/lookup(リミット:180回/15分)でscreen_nameを取得、チェックして特定の処理を行えば対応完了となりました。

PHP4用のOAuth対応ブログが大変参考になりました。

PHPのバージョンを管理するphpbrewを使ってみた

バージョンの異なる実行環境を切り替えたり、インストールしたりと管理できるツールとしてはrvm(ruby)perlbrew(perl)nodebrew(node.js)と言ったものが有名所ですが、PHPにもphpbrewというツールがある事を知ったので使ってみました。

c9s/phpbrew · GitHub.

インストール

環境はMac OS X Lionです。
元々、入っているPHPのバージョンは5.3.8でPHP4.4.9はコンパイルして入れたものが入っています。

[suusuke@macbook ~]$ php -v
PHP 5.3.8 with Suhosin-Patch (cli) (built: Nov 15 2011 15:33:15) 
Copyright (c) 1997-2011 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2011 Zend Technologies
[suusuke@macbook ~]$ php4 -v
PHP 4.4.9 (cli) (built: Mar 26 2011 10:08:22)
Copyright (c) 1997-2008 The PHP Group
Zend Engine v1.3.0, Copyright (c) 1998-2004 Zend Technologies

phpbrewインストール

c9s/phpbrew · GitHub に書いてある手順どおりインストールします。

[suusuke@macbook ~]$ sudo pear channel-discover pear.corneltek.com
Password:
Adding Channel "pear.corneltek.com" succeeded
Discovery of channel "pear.corneltek.com" succeeded
[suusuke@macbook ~]$ sudo pear install -a -f corneltek/PhpBrew
Password:
downloading PhpBrew-1.0.2.tgz ...
Starting to download PhpBrew-1.0.2.tgz (43,519 bytes)
............done: 43,519 bytes
downloading Universal-1.0.2.tgz ...
Starting to download Universal-1.0.2.tgz (10,805 bytes)
...done: 10,805 bytes
downloading CLIFramework-1.0.6.tgz ...
Starting to download CLIFramework-1.0.6.tgz (8,432 bytes)
...done: 8,432 bytes
downloading GetOptionKit-1.0.0.tgz ...
Starting to download GetOptionKit-1.0.0.tgz (10,801 bytes)
...done: 10,801 bytes
install ok: channel://pear.corneltek.com/Universal-1.0.2
install ok: channel://pear.corneltek.com/GetOptionKit-1.0.0
install ok: channel://pear.corneltek.com/CLIFramework-1.0.6
install ok: channel://pear.corneltek.com/PhpBrew-1.0.2
[suusuke@macbook ~]$ cd ~/
[suusuke@macbook ~]$ git clone git@github.com:c9s/phpbrew.git
Cloning into phpbrew...
Identity added: /Users/suusuke/ssh/github/id_rsa (/Users/suusuke/ssh/github/id_rsa)
remote: Counting objects: 1130, done.
remote: Compressing objects: 100% (436/436), done.
remote: Total 1130 (delta 676), reused 1014 (delta 560)
Receiving objects: 100% (1130/1130), 926.93 KiB | 244 KiB/s, done.
Resolving deltas: 100% (676/676), done.
[suusuke@macbook ~]$ cd phpbrew
[suusuke@macbook phpbrew]$ sudo pear install -f package.xml
install ok: channel://pear.corneltek.com/PhpBrew-1.1.1
[suusuke@macbook ~]$ ./phpbrew init
Phpbrew environment is initialized, required directories are created under

    ~/.phpbrew

Paste the following line(s) to the end of your ~/.bashrc and start a
new shell, phpbrew should be up and fully functional from there:

    source ~/.phpbrew/bashrc

For further instructions, simply run `phpbrew` to see the help message.

Enjoy phpbrew at $HOME!!
[suusuke@macbook ~]$ source ~/.phpbrew/bashrc 

インストールできるバージョンの確認

[suusuke@macbook ~]$ ./phpbrew/phpbrew known
Available stable versions:
	php-5.4.0
	php-5.3.10
	php-5.3.9
	php-5.3.8
	php-5.3.7
	php-5.3.2
	php-5.3.1
Available svn versions:
	php-svn-head
	php-svn-5.3
	php-svn-5.4
Available versions from PhpStas:
	php-5.4.0
	php-5.4.0RC1
	php-5.4.0RC2
	php-5.4.0RC3
	php-5.4.0RC4
	php-5.4.0RC5
	php-5.4.0RC6
	php-5.4.0RC7
	php-5.4.0RC8
	php-5.4.0alpha1
	php-5.4.0alpha2
	php-5.4.0alpha3
	php-5.4.0beta1
	php-5.4.0beta2

インストールできるオプションの確認

[suusuke@macbook ~]$ ./phpbrew/phpbrew variants
Variants
    pdo
    pear
    gd
    openssl
    mysql
    sqlite
    pgsql
    cli
    ftp
    sockets
    apxs2
    debug
    cgi
    soap
    pcntl
    intl
    imap
    kerberos

Example:

    phpbrew install php-5.3.10 +mysql +pdo
    phpbrew install php-5.3.10 +mysql +pdo +apxs2
    phpbrew install php-5.3.10 +mysql +pdo +apxs2=/usr/bin/apxs2

PHP5.4.0をインストールして使ってみる

[suusuke@macbook ~]$ ./phpbrew/phpbrew -d install --no-test php-5.4.0
===> Downloading http://www.php.net/distributions/php-5.4.0.tar.bz2
######################################################################## 100.0%
===> Extracting...
===> Configuring php-5.4.0...
./configure '--prefix=/Users/suusuke/.phpbrew/php/php-5.4.0' '--with-config-file-path=/Users/suusuke/.phpbrew/php/php-5.4.0/etc' '--with-config-file-scan-dir=/Users/suusuke/.phpbrew/php/php-5.4.0/var/db' '--with-pear=/Users/suusuke/.phpbrew/php/php-5.4.0/lib/php' '--disable-all' '--enable-bcmath' '--enable-ctype' '--enable-dom' '--enable-exif' '--enable-fileinfo' '--enable-filter' '--enable-hash' '--enable-fpm' '--with-xsl' '--with-tidy' '--with-xmlrpc' '--enable-json' '--enable-libxml' '--enable-mbregex' '--enable-mbstring' '--enable-phar' '--enable-session' '--enable-short-tags' '--enable-simplexml' '--enable-sockets' '--enable-tokenizer' '--enable-xml' '--enable-xmlreader' '--enable-xmlwriter' '--enable-zip' '--with-bz2' '--with-mhash' '--with-pcre-regex' '--with-zlib=/opt/local' '--with-libxml-dir' '--with-curl=/opt/local' '--with-gettext=/opt/local' '--with-libedit=/usr' '--with-readline' '--enable-pdo' '--enable-cli'
configure: WARNING: You will need re2c 0.13.4 or later if you want to regenerate PHP parsers.
configure: error: bz2 module requires libbz2 >= 1.0.0
===> Building php-5.4.0...
make: *** No targets specified and no makefile found.  Stop.
Skip tests
Build finished: 0 minutes.
===> Installing...
---> Copying php.ini-development 
Source directory: 
Done!
To use the newly built PHP, try the line(s) below:

    $ phpbrew use php-5.4.0

Or you can use switch command to switch your default php version to php-5.4.0:

    $ phpbrew switch php-5.4.0

あれ!?

configure: error: bz2 module requires libbz2 >= 1.0.0

configureでエラーになる。。。

with-bz2オプションでlibbz2のパス指定すればいいのかなと思って、パラメータで指定してみたがnot definedとなって、パラメータでは指定できないみたい。。。

[suusuke@macbook ~]$ phpbrew -d install php-5.4.0 --with-bz2=/usr/lib/libbz2.dylib 
===> Downloading http://www.php.net/distributions/php-5.4.0.tar.bz2
######################################################################## 100.0%
===> Extracting...
Variant --with-bz2 is not defined.

わからない。。。I don’t know…

追記

libbz2がインストールされてないんじゃないの?とコメント頂いたので、調べてみる。

[suusuke@macbook ~]$ ll /usr/lib/libbz2.dylib 
lrwxr-xr-x  1 root  wheel  16  9 20 02:36 /usr/lib/libbz2.dylib -> libbz2.1.0.dylib

あるよね。。。
ホントに!?

macportsで調べる

[suusuke@macbook ~]$ sudo port installed
Password:
Warning: port definitions are more than two weeks old, consider using selfupdate
The following ports are currently installed:
  apache2 @2.2.10_0+darwin_9 (active)
  ...

macportsには無かった、取り合えずmacportsが古いといっているのでselfupdateしてmacportsで入れてみる。

[suusuke@macbook ~]$ sudo port -d selfupdate
[suusuke@macbook ~]$ sudo port upgrade outdated
[suusuke@macbook ~]$ sudo port upgrade installed

※途中port upgradeでmysqlのアップデートに失敗してmysqlがcleanされてしまい、コンパイルするのに一日費やしてしまった。。。

[suusuke@macbook ~]$ sudo port clean mysql5-devel
[suusuke@macbook ~]$ sudo port install mysql5-devel configure.compiler=llvm-gcc-4.2

コンパイラーをllvm-gcc-4.2するとコンパイルに成功した。

macports自信とライブラリをアップデートしたら、問題なくphpbrew -d install php-5.4.0に成功した。
bz2関連のライブラリが古かったのだろうか。。。。

phpでxmlを出力して受け取る方法

phpでxml関連のライブラリーは結構ありますが、一番手軽なのはPEARのXML_Serializerだと思います。
※Cakeとか、フルスッタクのフレームワークに入ってるxmlのコンポーネントがある場合はそちらのほうが手軽です。

目次

  • XML_Serializerインストール
  • XMLの出力
  • XMLの受け取り

XML_Serializerインストール

PEARでインストール

pear install XML_Serializer

手動でインストール

XML_Serializer.

からライブラリーをダウンロード、解凍して、適当な場所に保存する。

XML_UtilXML_Parserと依存しているので、合わせてダウンロードしてインストールしておく。
※PEARはインストール済みを想定。されてない場合は、インストールする必要がある。

XMLの出力

require_once 'XML/Serializer.php';

$data = array('aaa' => 'bbb', 'ccc' => 'ddd');

$options = array(
                 "indent"         => " ",
                 "linebreak"      => "\n",
                 "typeHints"      => false,
                 "addDecl"        => true,
                 "encoding"       => "EUC-JP",
                 "rootName"       => "result",
                 "defaultTagName" => "item",
                 );
$serializer = new XML_Serializer($options);
$status = $serializer->serialize($data);

if( $status === true ) {
    $xml = $serializer->getSerializedData();
    header ("Content-Type: text/xml; charset=EUC-JP");
    echo $xml;
    exit;
}
exit;

文字コードはEUC-JPに指定しています。

XMLの受け取り

require_once 'XML/Unserializer.php';

$header = array(
    "User-Agent: ".$_SERVER["HTTP_USER_AGENT"]
);
$options = array(
    "http" => array(
        "method"  => "GET",
        "content" => http_build_query(array()),
        "header"  => implode("\r\n", $header)
    )
);

$api_uri = 'http://example.com/'; // xmlを出力するURL
$xml = file_get_contents($api_uri, false, stream_context_create($options));
$obj = new XML_Unserializer();

$obj->setOption("parseAttributes", true);

$status = $obj->unserialize($xml);
if ($status === true) {
    $xml_data = $obj->getUnserializedData();
    mb_convert_variables('EUC-JP', 'UTF-8', $xml_data);
    print_r($xml_data['item']);
}

exit;

こんな感じで、xmlを読み込むことができます。
オプションは

XML_Unserializer Options.

が参考になります。
ちなみに、file_get_contentsでGETでパラメータ付きリクエスト送りたい場合は、

file_get_contents() で GET リクエストを送る場合の値は URL に持たせる | ウェブル.

xmlを出力する方でBASIC認証を指定したい場合もあると思いますが、file_get_contentsではBASIC認証されたディレクトリのファイルも取得できます。

$header = array(
    "Authorization: Basic ".base64_encode("User:Pass"),
    "User-Agent: ".$_SERVER["HTTP_USER_AGENT"]
);

VMWare Server の開発環境作り直し

インストールしたもののメモです。

OSインストール

Index of /Linux/centos/5/isos/i386. CentOS5.7を今回は使用しました。
ISOファイルをダウンロードして、最小構成でインストール。

VMWare Toolsインストール

VMWare上のCentOSは時間がずれるのでVMWare Toolsをインストール。
VMWare Serverの管理画面から[Install VMware Tools…]を選択します。
選択すると、CDドライブにインストール用のファイルがセットされます。

[root@localhost ~]# mount /dev/cdrom /mnt/
[root@localhost ~]# cd /mnt/
[root@localhost ~]# /etc/yum.conf
gpgcheck=0 ← 0に変更

[root@localhost ~]# cp VMwareTools-7.7.6-203138.i386.rpm /tmp/
[root@localhost ~]# cd /tmp/
[root@localhost ~]# yum localinstall VMwareTools-7.7.6-203138.i386.rpm
[root@localhost ~]# /etc/yum.conf
gpgcheck=1 ← 元に戻す

一旦、サーバを停止しvmxファイルを変更します。

tools.syncTime = "TRUE" ← TRUEに変更

サーバを再度起動し、grubの設定を変更します。
このパラメータの追加(divider=10)はCentOS5.7では合った方が精度が良いよ?とナレッジに記載あったので追加しました。
VMware KB: Timekeeping best practices for Linux guests.

[root@localhost ~]# vi /boot/grub/grub.conf
kernel /vmlinuz-2.6.18-274.el5 ro root=/dev/VolGroup00/LogVol00 divider=10

ntpdインストール

[root@localhost ~]# yum install ntp
[root@localhost ~]# vi /etc/ntp.conf 
tinker panic 0
restrict 127.0.0.1
restrict default kod nomodify notrap
server 0.vmware.pool.ntp.org
server 1.vmware.pool.ntp.org
server 2.vmware.pool.ntp.org
driftfile /var/lib/ntp/drift

[root@localhost ~]# vi /etc/ntp/step-tickers 
0.vmware.pool.ntp.org
1.vmware.pool.ntp.org

[root@localhost ~]# service ntpd start
[root@localhost ~]# chkconfig ntpd on

インストールと設定

まずはifconfigでipを確認して、puttyまたはpoderosaで接続する。

viコマンドでvimを使うようにする

何かとvimの方が便利なので、aliasで設定

[root@localhost ~]# vi /etc/bashrc 

alias vi='vim' ← 追加

[root@localhost ~]# source /etc/bashrc 

不要なサービス停止

yum自動アップデート停止

[root@localhost ~]# /etc/rc.d/init.d/yum-updatesd stop
yum-updates を停止中:                                      [  OK  ]
[root@localhost ~]# yum remove yum-updatesd

SELinuxを無効化

[root@localhost ~]# vi /etc/sysconfig/selinux
#SELINUX=enforcing
SELINUX=disabled

iptables停止・自動起動無効

[root@localhost ~]# service iptables stop
[root@localhost ~]# chkconfig iptables off

RPMリポジトリを追加

[root@localhost ~]# rpm -import http://www.jasonlitka.com/media/RPM-GPG-KEY-jlitka
[root@localhost ~]# vi /etc/yum.repos.d/utterramblings.repo

[utterramblings]
name=Jason's Utter Ramblings Repo
baseurl=http://www.jasonlitka.com/media/EL$releasever/$basearch/
enabled=0
gpgcheck=1
gpgkey=http://www.jasonlitka.com/media/RPM-GPG-KEY-jlitka

enabled=0にしているので、通常のyumコマンドではこのリポジトリは使われない。

使う場合は

[root@localhost ~]# yum --enablerepo=utterramblings install パッケージ
[root@localhost ~]# yum --enablerepo=utterramblings update パッケージ

とする。

再起動

[root@localhost ~]# reboot

Apache,PHP,MySQLインストール

[root@localhost ~]# yum --enablerepo=utterramblings install httpd httpd-devel php php-mbstring php-devel php-mcrypt php-pear php-mysql php-gd mysql-server

Apache,MySQLの起動と自動起動設定

[root@localhost ~]# chkconfig httpd on
[root@localhost ~]# chkconfig mysqld on
[root@localhost ~]# service httpd start
[root@localhost ~]# service mysqld start

MySQLのユーザ設定

rootのパスワードを設定して、パスワードがブランクのユーザを削除

[root@localhost ~]# mysql -u root
mysql> set password for root@localhost=password('password');
mysql> exit

[root@localhost ~]# mysql -u root -p
mysql> DELETE FROM mysql.user WHERE Password = '';
mysql> exit

Apacheの設定

sampleユーザ追加

[root@localhost ~]# adduser sample
[root@localhost ~]# su - sample
[root@localhost ~]# chmod 755 /home/sample/

[sample@localhost ~]$ mkdir ~/public_html

[sample@localhost ~]$ vi ~/public_html/phpinfo.php ← 確認用
<?php
phpinfo();


&#91;sample@localhost ~&#93;$ exit
&#91;/bash&#93;

<h4>バーチャルホスト設定</h4>

[root@localhost ~]# vi /etc/httpd/conf.d/vhosts.conf

NameVirtualHost *:80# sample<VirtualHost *:80>
        DocumentRoot /home/sample/public_html
        ServerName test.sample.com
        <Directory "/home/sample/public_html">
                Options Indexes FollowSymLinks MultiViews Includes ExecCGI
                AddType text/html .shtml
                AddHandler server-parsed .shtml
                AddHandler cgi-script .cgi .pl
                AllowOverride All
                Order allow,deny
                Allow from all
        </Directory>
</VirtualHost>

確認

[root@localhost ~]# httpd -S
VirtualHost configuration:
wildcard NameVirtualHosts and _default_ servers:
*:80                   is a NameVirtualHost
         default server test.sample.com (/etc/httpd/conf.d/vhosts.conf:5)
         port 80 namevhost test.sample.com (/etc/httpd/conf.d/vhosts.conf:5)
Syntax OK

Windowsのhostsファイル変更

C:\Windows\System32\drivers\etc\hosts

192.168.1.3       test.sample.com

http://test.sample.com/phpinfo.phpにアクセスして確認.

ここまでで一通り開発環境が出来ました。
ソースのアップはrsync等で同期すると簡単にできると思います。

Windowsからrsyncを利用する方法も書きました。
suusuke – blog – Windows で rsync を使用する.

追加

ここからは有ると便利な設定等を追記していきます。

PHP Xdebugインストール

gcc,gcc-c++インストール

[root@localhost ~]# yum -y install gcc gcc-c++

Xdebugインストール

既にphp-devel,php-pearはインストールしてある状態なのでpeclコマンドでインストール。

[root@localhost ~]# pecl install xdebug

....


Build process completed successfully
Installing '/usr/lib/php/modules/xdebug.so'
install ok: channel://pecl.php.net/xdebug-2.1.2
configuration option "php_ini" is not set to php.ini location
You should add "extension=xdebug.so" to php.ini

/usr/lib/php/modules/xdebug.soにxdebug.soがインストールされました。

php.iniに追記

/etc/php.ini

zend_extension="/usr/lib/php/modules/xdebug.so"

PEAR::DBでUTF-8のMySQLを使う

コネクションストリングにオプションで書けば出来んじゃないかと思ったのですが、UTF-8使う場合はqueryメソッドでSET NAMESを書くみたいです。。。

<?php
require_once('DB.php');

class DatabaseConnection {

    var $_handle = null;

    function &get () {
        static $db;
        if (!isset($db)) {
            $db = new DatabaseConnection();
        }
        return $db;
    }

    function DatabaseConnection () {
        $phptype = $GLOBALS&#91;'DSN'&#93;&#91;'phptype'&#93;;
        $hostspec = $GLOBALS&#91;'DSN'&#93;&#91;'hostspec'&#93;;
        $database = $GLOBALS&#91;'DSN'&#93;&#91;'database'&#93;;
        $username = $GLOBALS&#91;'DSN'&#93;&#91;'username'&#93;;
        $password = $GLOBALS&#91;'DSN'&#93;&#91;'password'&#93;;

        $dsn = "$phptype://$username:$password@$hostspec/$database";
        $this->_handle = DB::Connect($dsn);
        if (DB::isError($this->_handle)) {
            die($this->_handle->getMessage());
        }
        $this->_handle->setFetchMode(DB_FETCHMODE_ASSOC);
        $this->_handle->query("SET NAMES 'utf8'");
    }

    function handle () {
        return $this->_handle;
    }
}

いまだに、PEAR::DB使ってるのにびっくりしますが | |д・) ソォーッ…

PHPで多次元配列をソートする

$data = array(
    [0] => array(
        "foo" => 10,
        "bar" => 5,
        "hoge" => 7
    ),
    [1] => array(
        "foo" => 8,
        "bar" => 6,
        "hoge" => 1
    ),
    [2] => array(
        "foo" => 10,
        "bar" => 5,
        "hoge" => 7
    )
);

foo列でソートする場合

foreach($data as $key => $row){
    $foo[$key] = $row["foo"];
}

array_multisort($foo,SORT_DESC,$data);

foo,bar,hogeでソートしたい場合

foreach($data as $key => $row){
    $foo[$key] = $row["foo"];
    $bar[$key] = $row["bar"];
    $hoge[$key] = $row["hoge"];
}

array_multisort($foo,SORT_DESC,$bar,SORT_DESC,$hoge,SORT_DESC,$data);

素晴らしすぎる!

DotCloud で PHP を動かしてみた

DotCloudとは

クラウドサービスの中でPaaSに分類されるもので、GoogleのApp Engineのように開発プラットホームを提供するサービスです。

2011.6.20現在、DotCloudはベータ版で公開されててinviteコードを貰えば無料で利用する事ができます。
inviteコードはメールアドレスを登録すると3,4日で届きます。

ちなみに、Webサーバーはnginx(エンジンエックス)というものを使っているそうです。

DotCloud

DotCloudを使ってみる

inviteコードが届いたので早速phpinfo()を表示させてみたいと思います。(Mac環境にて)
届いたメール内に登録用のURLが記載されているのでそこから、アカウントを作成します。

作成するとCheatsheetが表示されるのでそれを参考に設定します。

dotcloudコマンドのインストール

Pythonのeasy_installを使ってインストールします。

$ sudo easy_install dotcloud

これでdotcloudコマンドが使用できるようになります。

Applicationを作成する

$ dotcloud create suusuke

api keyの入力が求められるのでDotCloudの設定ページからapi keyをコピペします。

ここで決めた名前がURLの一部になります。
suusukeの部分がアプリケーション名になります。

サービス名を決めてデプロイする(PHP)

$ dotcloud deploy -t php suusuke.www

PHPで作成するのでタイプをphpにして、サービス名はwwwにしました。

phpinfoを出力するファイルを作成しpush

$ vi phpinfo.php
<?php phpinfo(); ?>

$ dotcloud push suusuke.www .

URLはhttp://..dotcloud.com/となるので、http://www.suusuke.dotcloud.com/phpinfo.phpにアクセスするとphpinfoが表示されます。

その他のコマンド等

sshで接続するときは

$ dotcloud ssh suusuke.www

で接続できます。

また、gitの環境がある場合はそのまま、DotCloudにpushできるようです。

DotCloud Document


追記
仙台駅から3Km圏内のツイートを表示するの作ってみた。作ってみたってほどでもないのですが…
http://www.suusuke.dotcloud.com/

TwitterとかInstagramもそうだけど、認証を必要としないAPI使うのは本当簡単だなと思う。
OAuthとか認証使うとちょっと大変になってくる。

LimonadeでURLに/?/を付けないでもアクセスできるようにする

InstagramのOAuth認証を試していたらコールバックURLに/?/が入っていると、設定したコールバックURLと異なるURLにリクエストが飛んできてました。

Limonadeではデフォルトで/?/callback/のようなURLになるので、ドキュメントに変更方法書いてあるはずと思ってみてみたら、READMEに書いてましたw

アプリケーションのルートディレクトリに次のような.htaccessを置くだけです。

<IfModule mod_rewrite.c>
  Options +FollowSymlinks
  Options +Indexes
  RewriteEngine on

  # if your app is in a subfolder
  # RewriteBase /my_app/ 

  RewriteCond %{SCRIPT_FILENAME} !-f
  RewriteCond %{SCRIPT_FILENAME} !-d

  RewriteRule ^(.*)$   index.php?uri=/$1    [NC,L,QSA]
</IfModule>

アプリケーションルートがドキュメントルート以外(例えば/my_app/)の場合は、RewriteBase /my_app/のコメントを解除するとhttp://www.example.com/my_app/からhttp://www.example.com/でアクセスできるようになる。

参考URL:README