Nov 30

こんにちは。takei-hです。

ちょっと時間が経ってしまったのですが、アシアル株式会社KLab株式会社の合同勉強会でMessagePackとPHP Extensionについて発表しましたので、資料を公開します。



また、PHP Extensionもだいたい形になったので、公開します。

MessagePack PHP Extensionのダウンロード

Continue reading »

Nov 20

tadaka-atです。

最近Haskellの記事ばかり書いていますが、PHPも書いてます!ということでPHPのライブラリを公開しました。

PictgramConverter

サポートする機能は

  • 絵文字sjisバイナリをutf-8バイナリに変換
  • キャリア間の絵文字変換

「ShiftJISからutf-8への文字コード変換とともに、絵文字もutf-8エンコーディングに変換」「異なるキャリアの絵文字も表示キャリアに合わせて変換」という処理が手軽にしかも高速にできるすぐれものです。

インストールは以下のコマンドで完了します。

$ pear channel-discover openpear.org
$ pear install openpear/PictgramConverter-alpha

詳しく知りたい方は以下の勉強会資料もごらんください。
アシアルKLab合同勉強会で発表しました

Sep 28

ども、amo-kです。

先日(9/7)にsymfony開発者Fabien PotencierさんがKLabにお越し下さりました!
▼ 握手!(あんまり若手エンジニアが写ってないw)

▼ ディスカッションの様子

symfonyの使い方から思想まで、色々なことについて話す事ができました。
世界のトップレベルのエンジニアとのディスカッションに、KLab若手エンジニア達も非常に喜んでおりました。
来年のSymfony Conference Japanで何かお役に立てる様にがんばりま〜す。

また、Fabien Potencierさんを連れて来て頂いた秋元さん、月宮さん、大野さんありがとうございました!!
今後ともよろしくお願い致します〜

Mar 01

ども、amo-kです。

先日デプロイ時に実況中継するIRCボットを作ったのでこれについて。

KLabでは通常、
テスト/本番環境に最新のプログラムソースコードを反映する際に
以下のような手順を踏む。

1.踏み台サーバにログイン
2.SVN Export
3.アーカイヴ作成
4.テスト/本番環境にアーカイヴをアップロード
5.テスト/本番環境のコマンドを実行し、アーカイヴ解凍、各Webサーバに同期
6.お掃除

通常はこれを1コマンドで実現するためにデプロイスクリプトを書いて
そのスクリプトを実行する事でデプロイすることとなる。

その際に、デプロイした人はターミナルをチェックしていれば
スクリプトの標準出力で進捗を把握できるが、たの案件メンバは解らない。
そこで、デプロイ実況中継をするIRCボットを作ってみた。

今回は特定のキーワードに反応したり、待機する必要がないので
各ステータス毎にIRCサーバに接続、チャンネルJOIN、レポート、切断ということをする
非常に単純なボットを作った。

以下、IRCクライアント画面サンプルとサンプルソースコード
工夫した点は特に無いが、ライブラリを使わずに
TCPコネクションを張ってIRCサーバと通信している点が若干特徴的かもしれない。
IRCクライアント画面キャプチャサンプル
デプロイ時に実況中継するIRCボット

サンプルコード

#!/usr/bin/ruby

require 'socket'

chan = ARGV[0]
msg  = ARGV[1]

sock = TCPSocket.open("localhost", 6667)
sock.send("NICK DeployBot\r\n", 0);
sock.send("USER DeployBot localhost localhost :DeployBot\r\n", 0);
sock.send(sprintf("JOIN #%s\r\n", chan), 0);
sock.send(sprintf("NOTICE #%s :<-------------- %s -------------->\r\n", chan, msg), 0);
sock.send("QUIT\r\n", 0);
sock.readlines
sock.close

最初はphpでsocket関数を使って実装しようと思ったんだけど
実は踏み台サーバでsocket関数が有効になっていなかったので、設定するのが面倒でもうRubyで杜撰に書いちゃおうと思ってRubyで書きましたww

Feb 22

ども、amo-kです。

今回は、先日RIA的なアプリを作っていて遭遇した
問題について書きます。

IE × SSL × リソースDL

上記が重なった際に生じる問題です。
どうなるかといいますと、
HTTPレスポンスヘッダのキャッシュコントロール系で
意図した動作をしないという問題です。

例えば、

Pragma: no-cache

というレスポンスヘッダを定義していると
逆にcacheファイルを参照しようとするようです。

今回は、HTTPサーバ側でレスポンスの全てを構築していた訳ではありませんでした。
サーバサイドはphpアプリケーションでレスポンスヘッダ/メッセージボディを生成するのですが
phpはデフォルトでレスポンスヘッダを生成してくれます。
その際に

Pragma: no-cache

を入れてくれます。

これは嬉しいのですが、
上記IEのバグに対応するには、ヘッダを書き換える必要があります。
ということで以下のようにヘッダを上書きして対応しました。

header("Pragma: ");

一般的には、Webサイトの管理ツールなどで
SSL通信による集計ファイルのDLサービス等で
同様の現象が再現し得るでしょう。

JavaScriptじゃないんだし、
phpソースコード中にIEの対応を書くなんてと思いましたが
よくよく調べてみると、Microsoftサポートでも公開していたので
まあいっかと思った次第です。

参考: Microsoft サポート オンライン

Dec 08

どうも、相変わらず暴君なyoshida-kです。

今日はMeCabというオープンソースな形態素解析エンジンをphpから利用して見ましょうというお話です。
MeCabを使うと文章を単語に分割したり、分割された単語がどういったものなのかという情報を取得したりできます。

まずは、MeCabをインストールしてみましょう。
リンク先を見ればインストール方法は明らかですが、とりあえず順を追って説明します。
1.sourceforge.netよりMeCabのソースをDownload
2.任意のディレクトリにDownloadしてきたアーカイブを展開
tar -zxvf ./mecab-x.y.tar.gz 的なw
3.mecabのコンパイルを行う。
普通のアプリと同じでconfigureしてmake , make installな感じです。
ディレクトリ内にあるINSTALLファイルを見るととりあえずやり方が書いてあったりします。

ここまでやるととりあえずmecabがインストールされるわけですが、コレだけではmecabはちゃんと動きません。
mecabを動かすには辞書が必要です。
辞書も先ほどのsourceforgeのところからdownloadできるので同様に展開⇒make installしてください。

さて、この状態でmecabを起動することが可能となりました。
mecabを起動すると形態素解析をしてくれます。

よく使われている例ですが
「すもももももももものうち」と入力すると

すもも  名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も      助詞,係助詞,*,*,*,*,も,モ,モ
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
も      助詞,係助詞,*,*,*,*,も,モ,モ
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
うち    名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ

このような形に出力してくれます。

さて、いよいよPHPでこのMeCabを利用するためにPHPのExtensionをインストールしましょう。
MeCabのPHP ExtensionはこちらのPage2さんが公開してくださってます。

インストール方法は
phpize ⇒ configure ⇒ make ⇒ make installです。
インストールが完了したらphp.iniのモジュール読み込みの設定にmecab.soを追加しましょう。

さて、以下の内容がテストコードです。
うまくいくと、mecabを起動して実行したものと同じ結果が出力されます。

<?php
   dl("mecab.so");

   $mecab = mecab_new();
   $string = "すもももももももものうち";

   print mecab_sparse_tostr( $mecab , $str );

   mecab_destroy();
 ?>

いかがでしょう?
コレだけではまだ実用性にかけるので次回はもうちょっと実用的な内容を紹介させていただきます。

Nov 14

symfonyでアプリケーションを作成していた際に、文字コード絡みで面白い事象に遭遇したので記事にすることにしたw

携帯用のWebアプリケーションを作っていたのだが、
Webサーバ側での出力データの文字コードをShift_JISに統一するため、
以下のように全ての文字コードをShift_JIS/cp932に統一して実験してみた。
MySQL(my.cnf)

[client]
default-character-set=cp932
[mysqld]
default-character-set=cp932

現在の設定状況を確認。

mysql> show variables like 'character_set%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | cp932                      |
| character_set_connection | cp932                      |
| character_set_database   | cp932                      |
| character_set_filesystem | binary                     |
| character_set_results    | cp932                      |
| character_set_server     | cp932                      |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

とここで、、、DBドライバmysqliはmy.cnfを読み込む設定が可能だが、DBドライバmysqlは無理ぽい。。。
要するに、クライアント側(symfony/phpアプリケーション)からはmy.cnfは読み込まれない。

php(php.ini)

[mbstring]
mbstring.language = Japanese
mbstring.internal_encoding = SJIS-WIN
mbstring.http_output = SJIS-WIN

symfony

# config/settings.yml(symfony本体、アプリケーション共に)
charset: Shift_JIS
# databases.yml
encoding: cp932

以上の設定を施したが、0×5c問題が気になったので以下のテストを行った。

symfonyで作成したテスト用アプリケーションで「ソ」一文字を登録。
結果は失敗。
ということでsymfonyが使用しているO/Rマッパや
データベースドライバについて調べてみた。
symfonyはデフォルトではPropelというO/Rマッパを用い、
CreoleというDB接続モジュールでDBドライバに”mysql”を利用している。
ということで、CreoleのDBドライバ:mysqlにおける MySQL接続クラス(MySQLConnection.php)を確認してみた。

if ($encoding) {
    $this->executeUpdate("SET NAMES " . $encoding);
}

むむ、発見。
ここでdatabases.ymlに設定した文字コードを指定している。
SET NAMESクエリを発行している。
試しにここをphpの標準関数mysql_set_charset()に置き換えてみた。

if ($encoding) {
    mysql_set_charset($encoding);
}

再度「ソ」一文字登録テスト。
お!成功!!
しかし何故??
ということでMySQLのクエリログを確認

# 失敗: $this->executeUpdate("SET NAMES " . $encoding);
081114 19:30:21      22 Connect     db_test@localhost on
                     22 Init DB     db_test
                     22 Init DB     db_test
                     22 Query       SET NAMES cp932
                     22 Query       SET AUTOCOMMIT=0
                     22 Query       BEGIN
                     22 Init DB     db_test
                     22 Query       INSERT INTO table_test (TITLE,CREATED_AT,UPDATED_AT) VALUES ('ソ\','2008-11-14 20:27:42','2008-11-14 20:27:42')
                     22 Init DB     db_test
                     22 Query       ROLLBACK
                     22 Query       SET AUTOCOMMIT=1
                     22 Quit

# 成功: mysql_set_charset($encoding);
081114 19:30:48      23 Connect     db_test@localhost on
                     23 Init DB     db_test
                     23 Query       SET NAMES cp932
                     23 Query       SET AUTOCOMMIT=0
                     23 Query       BEGIN
                     23 Init DB     db_test
                     23 Query       INSERT INTO table_test (TITLE,CREATED_AT,UPDATED_AT) VALUES ('ソ','2008-11-14 20:28:09','2008-11-14 20:28:09')
                     23 Init DB     db_test
                     23 Query       COMMIT
                     23 Query       SET AUTOCOMMIT=1

# 失敗: mysql_query("SET NAMES " . $encoding);
081114 19:39:12      27 Connect     db_test@localhost on
                     27 Init DB     db_test
                     27 Query       SET NAMES cp932
                     27 Query       SET AUTOCOMMIT=0
                     27 Query       BEGIN
                     27 Init DB     db_test
                     27 Query       INSERT INTO table_test (TITLE,CREATED_AT,UPDATED_AT) VALUES ('ソ\','2008-11-14 20:36:33','2008-11-14 20:36:33')
                     27 Init DB     db_test
                     27 Query       ROLLBACK
                     27 Query       SET AUTOCOMMIT=1

ン?
成功/失敗時のクエリを確認すると、文字コード指定部分は全く同じである。
なんと、違いはMySQLサーバがデータを受け取った時点で異なることがわかった。
では、php標準関数mysql_set_charset()では何をしているのか?
phpのソースコード(php_mysql.c)を追ってみた。

PHP_FUNCTION(mysql_set_charset)
{
	zval *mysql_link = NULL;
	char *csname;
	int id = -1, csname_len;
	php_mysql_conn *mysql;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|r", &csname, &csname_len, &mysql_link) == FAILURE) {
		return;
	}

	if (ZEND_NUM_ARGS() == 1) {
		id = php_mysql_get_default_link(INTERNAL_FUNCTION_PARAM_PASSTHRU);
		CHECK_LINK(id);
	}

	ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink);

	if (!mysql_set_character_set(&mysql->conn, csname)) {
		RETURN_TRUE;
	} else {
		RETURN_FALSE;
	}
}

おっと、mysql_set_character_set()関数を使用している。
これはMySQLモジュール、libmysqlの関数である。
では、このmysql_set_character_set()では何をしているのか?
MySQLのソースコードに内包されているlibmysqldのclient.cを確認

int STDCALL mysql_set_character_set(MYSQL *mysql, const char *cs_name)
{
  struct charset_info_st *cs;
  const char *save_csdir= charsets_dir;

  if (mysql->options.charset_dir)
    charsets_dir= mysql->options.charset_dir;

  if (strlen(cs_name) < MY_CS_NAME_SIZE &&
     (cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0))))
  {
    char buff[MY_CS_NAME_SIZE + 10];
    charsets_dir= save_csdir;
    /* Skip execution of "SET NAMES" for pre-4.1 servers */
    if (mysql_get_server_version(mysql) < 40100)
      return 0;
    sprintf(buff, "SET NAMES %s", cs_name);
    if (!mysql_real_query(mysql, buff, strlen(buff)))
    {
      mysql->charset= cs;
    }
  }
  else
  {
    char cs_dir_name[FN_REFLEN];
    get_charsets_dir(cs_dir_name);
    mysql->net.last_errno= CR_CANT_READ_CHARSET;
    strmov(mysql->net.sqlstate, unknown_sqlstate);
    my_snprintf(mysql->net.last_error, sizeof(mysql->net.last_error) - 1,
		ER(mysql->net.last_errno), cs_name, cs_dir_name);

  }
  charsets_dir= save_csdir;
  return mysql->net.last_errno;
}

ぉお!

sprintf(buff, "SET NAMES %s", cs_name);
if (!mysql_real_query(mysql, buff, strlen(buff)))
{
    mysql->charset= cs;
}

これだ!
SET NAMESクエリを発行した後で
クライアント側のmysqlコネクションオブジェクトに文字コードをセットしている。

ただ単にSET NAMESクエリを発行するのとphpのmysql_set_charset()にて文字コードを指定するのとでは
この違いがあった。

いくらsymfonyやphpの設定をShift_JISにしても、MySQLクライアント(DBドライバmysql)がShift_JISで無ければ0×5cの2バイト目の5c(\)を2バイト目の5cと認識できず、単にエスケープ文字としてエスケープしてしまう。しかし、MySQLサーバ側はShift_JIS(cp932)で設定されているため2バイト目の5cを2バイト目と認識し、クライアントが付加した5cが余分なエスケープとなってクエリがシンタックスエラーとなる。
Shift_JISの「ソ」を「835c」と表現して以下に当問題の一連の流れを書く。

■ symfony/php
insert into table values '835c';
■ mysql(MySQLクライアント)
# 835cの2バイト目をエスケープ(5c付加)してしまう
# 83 + ここに5cを付加 + 5c ⇒ 835c5c
insert into table values '835c5c';
■ MySQLサーバ
# 835cまでを「ソ」と認識し、残りの5cが余計なエスケープとなり、
# 閉じクオートがエスケープされる。('ソ\')
insert into table values '835c5c';

mysqliなら、my.cnfを読み込むことも可能なのでこの様な事象に遭遇することは少ないのかもしれない。

ん~phpでDBドライバにmysqlを使用する、またはそのような環境においてShift_JISを利用したい場合は
mysql_set_charset()で文字コードを設定するべきですね。

Oct 31

先輩のamo-kさんから、「Symfonyのトランザクション処理時の動作について調べてみて」と言われたので、Symfonyの勉強がてら調べてみました。

Synfony のログからわかること

まず Symfony のログを見てみると、begin -> begin -> category テーブルの update -> commit -> begin -> begin -> bookmark テーブルの update -> commit -> commit という処理の流れになっています。

アクションで明示的に記述したトランザクションとは別に、$category->save では1重、$bookmark->save では2重のトランザクションが走っているように見えます。

モデルクラスの中身を見てみる

$bookmark と $category はそれぞれ Symfony のスクリプトによって自動生成されたモデルクラスである BookMark と Category のインスタンスです。

Bookmark と Category はそれぞれ BaseBookmark と BaseCategory を継承しており、実際の更新処理はこれらのベースクラスに記述されています。

save関数の中でもトランザクションが使われています。

BaseBookmark と BaseCategory の save 関数内に以下のような記述があります。

// BaseBookmark::save 関数の一部(BaseBookmark.php)、BaseCategory::save (BaseCategory.php)も同様
$con->begin();
$affectedRows = $this->doSave($con);
$con->commit();
return $affectedRows;

これを見てわかるとおり、 save 関数内でも $con->begin() ~ $con->commit() によって、トランザクション処理を行っています。

doSave 関数の中では、このインスタンスが新規のレコードなのか、レコードの更新なのかを判定し、新規であれば Peer クラスの doInsert を、更新であれば doUpdate を呼び出すようになっています。

以下、該当部分のソースコード。

// BaseBookmark::doSave 関数の一部(BaseBookmark.php)
  if ($this->isNew()) {
    $pk = BookmarkPeer::doInsert($this, $con);
    $affectedRows += 1;
    $this->setId($pk);
    $this->setNew(false);
  } else {
    $affectedRows += CategoryPeer::doUpdate($this, $con);
  }

doInsert 関数と doUpdate 関数は BookmarkPeer の親クラスである BaseBookmarkPeer クラスで定義されていています。

doInsert関数の中では、以下のように $con->begin() でトランザクションを開始したあと、BasePeer クラスの doInsert 関数を呼び出しています。これが、 $bookmark->save() を実行したときの、最も深い begin ~ commit の正体です。

// BaseBookmarkPeer::doInsert 関数の一部(BaseBookmark.php)
  try {
    $con->begin();
    $pk = BasePeer::doInsert($criteria, $con);
    $con->commit();
  } catch(PropelException $e) {
    $con->rollback();
    throw $e;
  }

なお、doUpdate 関数内ではトランザクションをかけていません。アクション内で記述していた $category->save() はすでにあるレコードの更新操作なので doUpdate が実行され、該当部分のトランザクションは2重までになります。

コネクションオブジェクトに対する関数呼び出しのログ出力処理

アプリケーションをデバッグモードで実行しているときは $con は sfDebugConnection というクラスのインスタンスになっていて、各モデルクラスはこのインスタンスを通して、DB操作を行うようになっています。

このクラスはsymfonyで提供されているもので、コネクションオブジェクトに対して実行された処理のログをとっています。例えば、commit 関数なら以下のようになっています。

 // sfDebugConnection.php から抜粋
  public function commit()
  {
    $this->log("{sfCreole} committing transaction.");
    return $this->childConnection->commit();
  }

関数が呼び出されると無条件にログ出力が行われます。そのため、今回のようにコネクションオブジェクトに対して最大で3重のトランザクションをかけている場合は、ログにもそのように出力されるわけです。

その次に $this->childConnection の同名の関数を呼び出すという処理になっています。
$this->childConnection は実際に使われているDBに応じた、Connectionインタフェースを実装したドライバになります。今回はMySQLを使用しているので、MySQLConnectionというクラスになっています。

MySQLConnection は ConnectionCommon というクラスを継承しています。DBの実装に関係なくコネクションで共通の処理が ConnectionCommon に記述されています。

begin関数、commit関数はConnectionCommonで以下のように定義されています。

// sfConnectionCommon.php から抜粋
    public function begin()
    {
        if ($this->transactionOpcount === 0 || $this->supportsNestedTrans()) {
            $this->beginTrans();
        }
        $this->transactionOpcount++;
    }

    public function commit()
    {
        if ($this->transactionOpcount > 0) {
            if ($this->transactionOpcount == 1 || $this->supportsNestedTrans()) {
                $this->commitTrans();
            }
            $this->transactionOpcount--;
        }
    }

begin関数で transactionOpcount を +1 し、commit関数で -1 するようになっています。

ここで supportNestedTrans 関数はDBがトランザクションの入れ子をサポートしている場合にtrueを返します。今回使用しているMySQLはトランザクションの入れ子をサポートしないので、この関数は false を返します。

ConnectionCommon のサブクラスで beginTrans および commitTrans 関数がオーバーライドされ、実際のトランザクション開始~コミット処理を行っています。

この部分の処理で、MySQLのようにトランザクションの入れ子をサポートしていないDBを使用している場合は最も外側のトランザクションのみが有効になることがわかります。

以上の処理をシーケンス図にすると、以下のようになります。



トランザクション処理時のシーケンス(拡大画像へリンク)

と、ここまでわかったところで、この記事を発見。まさに知りたかったことが書いてあるじゃないか・・・。

まあ、もう少し詳細にコードを追っているので、本記事も参考になるのではないでしょうか。

Oct 31

Symfony(phpのフレームワーク)にて気になる部分があったので若手ホープのhonda-hに調査してもらった。
※データベースはMySQL5、InnoDBを利用。

気になった部分
以下のコードサンプルのように
明示的にトランザクション処理を行ったとする。
Symfonyのログを見ると何故か4回づつbeginやcommitが行われたように
sfCreoleのログが出力されている。
これでは、以下のコードサンプルの更新2の部分で
エラーとなった場合に更新1も含めてロールバックしてほしいのに
更新1はコミットされたように見える。
しかし、SQLログを見るとサンプルコードで期待する結果となっている。
(begin ⇒ 更新1⇒ 更新2 ⇒ commit)

コードサンプル:

    // 実際はもう少し処理をしているがコアな部分以外は割愛する
...
    $bookmark = new Bookmark();
    $bookmark->setTitle($this->getRequestParameter('title'));
    $bookmark->setUrl($this->getRequestParameter('url'));

    if ($this->getRequestParameter('category_id'))
    {
        $bookmark->setCategoryId($this->getRequestParameter('category_id'));
        $category = CategoryPeer::retrieveByPk($this->getRequestParameter('category_id'));
        $category->incrementSize();
    }

    $con = Propel::getConnection();  // DBコネクションオブジェクト取得

    try
    {
        $con->begin();  // トランザクション開始
        if ($this->getRequestParameter('category_id'))
        {
            $this->logMessage('{sfAction} save category.', 'info');
            $category->save($con);  // 更新1
        }

        $this->logMessage('{sfAction} save bookmark.', 'info');
        $bookmark->save($con);  // 更新2

        $this->logMessage('{sfAction} commit transaction.', 'info');
        $con->commit();  // コミット
    }
    catch (Exception $e)
    {
        $con->rollback();  // ロールバック
...

Symfonyログサンプル

Oct 28 11:29:51 symfony [info] {sfCreole} prepareStatement(): SELECT category.ID, category.TITLE, category.SIZE FROM category WHERE category.ID=?
Oct 28 11:29:51 symfony [info] {sfCreole} executeQuery(): [0.47 ms] SELECT category.ID, category.TITLE, category.SIZE FROM category WHERE category.ID=5
Oct 28 11:29:51 symfony [info] {sfCreole} connect(): DSN: array (   'compat_assoc_lower' => NULL,   'compat_rtrim_string' => NULL,   'database' => 'criteria_test',   'encoding' => 'cp932',   'hostspec' => 'localhost',   'password' => 'criteria_test',   'persistent' => NULL,   'phptype' => 'mysql',   'port' => NULL,   'protocol' => NULL,   'socket' => NULL,   'username' => 'criteria_test', ), FLAGS: 0
Oct 28 11:29:51 symfony [info] {sfCreole} beginning transaction.
Oct 28 11:29:51 symfony [info] {sfAction} save category.
Oct 28 11:29:51 symfony [info] {sfCreole} beginning transaction.
Oct 28 11:29:51 symfony [info] {sfCreole} prepareStatement(): UPDATE category SET SIZE = ? WHERE category.ID=?
Oct 28 11:29:51 symfony [info] {sfCreole} executeUpdate(): UPDATE category SET SIZE = 5 WHERE category.ID=5
Oct 28 11:29:51 symfony [info] {sfCreole} committing transaction.
Oct 28 11:29:51 symfony [info] {sfAction} save bookmark.
Oct 28 11:29:51 symfony [info] {sfCreole} beginning transaction.
Oct 28 11:29:51 symfony [info] {sfCreole} beginning transaction.
Oct 28 11:29:51 symfony [info] {sfCreole} prepareStatement(): INSERT INTO bookmark (CATEGORY_ID,TITLE,URL,CREATED_AT,UPDATED_AT) VALUES (?,?,?,?,?)
Oct 28 11:29:51 symfony [info] {sfCreole} executeUpdate(): INSERT INTO bookmark (CATEGORY_ID,TITLE,URL,CREATED_AT,UPDATED_AT) VALUES (5,'koreha hidoi','http://korea.hidoi/','2008-10-28 11:29:51','2008-10-28 11:29:51')
Oct 28 11:29:51 symfony [info] {sfCreole} committing transaction.
Oct 28 11:29:51 symfony [info] {sfCreole} committing transaction.
Oct 28 11:29:51 symfony [info] {sfAction} commit transaction.
Oct 28 11:29:51 symfony [info] {sfCreole} committing transaction.

SQLログサンプル:

# at 3946
#081028 11:29:51 server id 1  end_log_pos 4023 	Query	thread_id=47	exec_time=0	error_code=0
SET TIMESTAMP=1225160991/*!*/;
BEGIN
/*!*/;
# at 4023
#081028 11:29:51 server id 1  end_log_pos 4143 	Query	thread_id=47	exec_time=0	error_code=0
SET TIMESTAMP=1225160991/*!*/;
UPDATE category SET SIZE = 5 WHERE category.ID=5
/*!*/;
# at 4143
#081028 11:29:51 server id 1  end_log_pos 4171 	Intvar
SET INSERT_ID=40/*!*/;
# at 4171
#081028 11:29:51 server id 1  end_log_pos 4401 	Query	thread_id=47	exec_time=0	error_code=0
SET TIMESTAMP=1225160991/*!*/;
INSERT INTO bookmark (CATEGORY_ID,TITLE,URL,CREATED_AT,UPDATED_AT) VALUES (5,'koreha hidoi','http://koreha.hidoi/','2008-10-28 11:29:51','2008-10-28 11:29:51')
/*!*/;
# at 4401
#081028 11:29:51 server id 1  end_log_pos 4428 	Xid = 334
COMMIT/*!*/;
DELIMITER ;

ということで、honda-hの調査結果を次に書きます。
ではhonnda-h、あとはよろしく~
調査結果

Oct 18

amo-kです。phpでSMTPクライアント書きました~

書いてみたきっかけ

PHPだとmail()mb_send_mail()でメール送信できちゃうけどよくよく見てみると、SMTPコマンドのHELOコマンドやMAIL FROMコマンドに値を指定できないぽい。最後の引数に、MTAに渡すコマンドラインオプションを指定可能だが、これはMTAに依存するということだ。MAIL FROMコマンドの値を指定したくても、この最後の引数に指定するしかないということになる。

では、何処でHELOコマンドやMAIL FROMコマンドの値を設定しているかというと、php.iniのSMTPディレクティヴやsendmail_fromディレクティヴの値となる。(Windows版phpのみ)つまり、アプリケーションレベルでこれ等の値を指定したい場合に、明確に指定するIFが無いということだ。

あれやこれやと考える時間がもったいないので、この前HTTPクライアント書いたし、せっかくなのでSMTPクライアントも書いて見たw

コード:
Continue reading »