その際に用意した資料を公開いたします。
基本的には、
- - 構文解析表があれば構文解析ができる
- - DIRECTOR集合があれば構文解析表を生成できる
- - DIRECTOR集合を計算するには所定のアルゴリズムを実行すればよい
という流れで話しました。
前回のエントリでmultipartのバウンダリ文字列を抜き取る処理を記述したけれど、
multipartを分解したいだけなら、Mail/mimeDecode.php の基本処理で事足りたようである...
<?php
require_once('Mail/mimeDecode.php');
$_params['include_bodies'] = true;
$_params['decode_bodies'] = true;
$_params['decode_headers'] = true;
$_params['input'] = file_get_contents('php://stdin');
$_structure = Mail_mimeDecode::decode($_params);
foreach ($_structure->parts as $_part) {
//$_part->body;
}
?>
この例では、foreachの中の
$_part->body;で分解された個々のbodyを取得できる。
!!!!!multipartを分解するだけの処理を知りたい場合は、こちらを参照されたい。!!!!!
multipartのバウンダリ文字列がヘッダで渡ることを知らなくて、少しだけはまった。
本エントリでは、qmailが動いているサーバでメールを受信し、
/tmp/mail_sample.txtにバウンダリ文字列を出力するまでの過程を記述する。
qmailでは、$HOME/.qmailでメール本文を受けたときに起動するスクリプトを記述できる。
私の環境では、.qmailは次のようになっている。
$ cat ~/.qmail | /usr/bin/php /tmp/sample_script.php
この例では、メールを受信したタイミングで/tmp/sample_script.php が起動する。
さて、/tmp/sample_script.php は次のようになっている。
(PEAR のMail_Mimeがインストールされていることを前提としている)
<?php
require_once('Mail/mimeDecode.php');
$_in = fopen("php://stdin", "r");
$_mail_text = '';
while($_line = fgets($_in)) {
$_mail_text .= $_line;
}
$_decoder = & new Mail_mimeDecode( $_mail_text );
$_parts =$_decoder->getSendArray();
list( $_recipients, $_headers, $_body ) = $_parts;
$_ret = preg_match("/boundary=\"([0-9a-zA-Z_\-\+\=\.\:]+)\"/", $_headers['Content-Type'], $_matches);
if($_ret) {
$_fp = fopen("/tmp/mail_sample.txt", "w");
fwrite($_fp, $_matches[1]);
fclose($_fp);
}
?>
ただ単純に、ヘッダからContent-Typeの列を取り出して、正規表現で抜き出すだけ。
本来なら、multipart/mixed かどうかも見るべきかと思うけれど、
multipart/mixed でない限りは"boundary"という文字列は存在しないはずなので、これでよしとしている。
なお、multipart/mixed の場合は、次のような感じの文字列を受け取れる。
multipart/mixed; boundary="-----=_NextPart_46196_50244_54292"
この場合、上記のスクリプトでは、
-----=_NextPart_46196_50244_54292
を取得できる。
身内の勉強会で話す(話した)内容を公開します。
前提知識
- CFG
文脈自由文法(ぶんみゃくじゆうぶんぽう、Context-free Grammar、CFG)とは、言語学や情報工学において全生成規則が以下の形式である形式文法のひとつである。 V → w ここで V は非終端記号であり、w は終端文字と非終端記号から構成される文字列である。「文脈自由」という用語は前後関係に依存せずに非終端記号 V を w に置換できることを意味している。文脈自由文法によって生成される形式言語を文脈自由言語という。(Wikipediaより)
- 記号表
名前とそれに関する型、スコープ、値(名前が定数のとき)などの情報をまとめたものを記号表という。コンパイラの中では、名前は記号表のエントリ(へのポインタ)で代表させるのがふつうである。コンパイラの解析部では記号表に名前の情報を集め、合成部では記号表を利用して目的コードを生成する。(『プログラミング言語処理系』より)
slideshareで使用したソースコードのアーカイブ(.tgz)はこちら。
なお、本スライドで使用しているflexとbisonのバージョンは次の通りである。
$ flex --version flex version 2.5.4 $ bison --version bison (GNU Bison) 2.1 Written by Robert Corbett and Richard Stallman. Copyright (C) 2005 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. [参考訳 -- 法的効力は英文が適用されます] これはフリーソフトウェアです -- 複製についての条件はソースを見ましょう。 一切の保証はありません -- 商業性や目的適合性についての保証すらありません。
このエントリはhttp://developer.yahoo.com/yui/yahoo/を元に、日本語に翻訳したものです。
YAHOO Global Object には唯一のグローバル名前空間があり、そこに全てのYUIライブラリのコードが存在します。 YAHOO Global Object はYUIライブラリを使用する全てのページでインクルードされる必要があり、 またどのYUIコンポーネントよりも先に現れなければなりません。 YAHOO Global Object はライブラリ全体で使用可能な多くのメソッドが存在します。
<!-- YAHOO Global Object ソースファイル --> <script type="text/javascript" src="http://yui.yahooapis.com/2.3.1/build/yahoo/yahoo-min.js"></script> <!-- 追加のソースファイルはここに記述する -->
// "myproduct1"のための名前空間を作成する
YAHOO.namespace ("myproduct1");
YAHOO.myproduct1.Class1 = function(info) {
alert(info);
};
// "mysubproject1"のための名前空間"myproduct2"を作成する
YAHOO.namespace ("myproduct2.mysubproject1");
YAHOO.myproduct2.mysubproject1.Class1 = function(info) {
alert(info);
};
// true、arrayリテラルは配列です
YAHOO.lang.isArray([1, 2]);
// false、objectリテラルは配列ではありません
YAHOO.lang.isArray({"one": "two"});
// しかし、 配列として宣言された場合にはtrue になります
function() {
var a = new Array();
a["one"] = "two";
return YAHOO.lang.isArray(a);
}();
// false、エレメントの集合は配列のようですが、違います
YAHOO.lang.isArray(document.getElementsByTagName("body"));
// true、falseは論理型です
YAHOO.lang.isBoolean(false);
// false、1や"true"という文字列は論理型ではありません
YAHOO.lang.isBoolean(1);
YAHOO.lang.isBoolean("true");
// nullはnullですが、undefinedや""(空文字列)はnullではありません
YAHOO.lang.isNull(null); // true
YAHOO.lang.isNull(undefined); // false
YAHOO.lang.isNull(""); // false
// functionはfunctionですが、オブジェクトはfunctionではありません
YAHOO.lang.isFunction(function(){}); // true
YAHOO.lang.isFunction({foo: "bar"}); // false
// true、整数や少数は数です
YAHOO.lang.isNumber(0);
YAHOO.lang.isNumber(123.123);
// false, strings that can be cast to numbers aren't really numbers
// false、数にキャストできる文字列は、実際には文字列ではありません
YAHOO.lang.isNumber("123.123");
// false、未定義の値や無限大の値は数ではありません
YAHOO.lang.isNumber(1/0);
// true、オブジェクト、関数、配列はオブジェクトです
YAHOO.lang.isObject({});
YAHOO.lang.isObject(function(){});
YAHOO.lang.isObject([1,2]);
// false、プリミティブな値はオブジェクトではありません
YAHOO.lang.isObject(1);
YAHOO.lang.isObject(true);
YAHOO.lang.isObject("{}");
// 文字列
YAHOO.lang.isString("{}"); // true
YAHOO.lang.isString({foo: "bar"}); // false
YAHOO.lang.isString(123); // false
YAHOO.lang.isString(true); // false
// undefinedはundefinedですが、nullとfalseはundefinedではありません
YAHOO.lang.isUndefined(undefined); // true
YAHOO.lang.isUndefined(false); // false
YAHOO.lang.isUndefined(null); // false
// これが保護するものです
Object.prototype.myCustomFunction = function(x) {
alert(x);
}
var o = {};
o["foo"] = "bar";
o["marco"] = "polo";
// これが保護する必要がある部分です
for (var i in o) {
if (YAHOO.lang.hasOwnProperty(o, i)) {
alert("good key: " + i);
} else {
alert("bad key: " + i);
}
}
YAHOO.namespace("test");
YAHOO.test.Class1 = function(info) {
alert("Class1: " + info);
};
YAHOO.test.Class1.prototype.testMethod = function(info) {
alert("Class1: " + info);
};
YAHOO.test.Class2 = function(info) {
// コンストラクタを繋げる
YAHOO.test.Class2.superclass.constructor.call(this, info);
alert("Class2: " + info);
};
// Class2がClass1を継承する。これはClass2のコンストラクタの直後に実行される必要がある
YAHOO.lang.extend(YAHOO.test.Class2, YAHOO.test.Class1);
YAHOO.test.Class2.prototype.testMethod = function(info) {
// 関数を繋げる
YAHOO.test.Class2.superclass.testMethod.call(this, info);
alert("Class2: " + info);
};
var class2Instance = new YAHOO.test.Class2("constructor executed");
class2Instance.testMethod("testMethod invoked");
<!-- debugger output for environments without "console" -->
<!-- コンソールがない環境ではここにデバッガの出力が与えられます -->
<div id="consoleelement"> </div>
<script>
////////////////////////////////////////////////////////////////////////////
// ConsoleProvilderの例はコンソールがある場合はコンソールにログを出力します
// コンソールがない場合は"consoleelement"にログを出力します
////////////////////////////////////////////////////////////////////////////
YAHOO.example.ConsoleProvider = function() { };
YAHOO.example.ConsoleProvider.prototype = {
log: function(msg) {
// use the error console if available (FF+FireBug or Safari)
if (typeof console != "undefined") {
console.log(msg);
// write the msg to a well-known div element
} else {
var el = document.getElementById("consoleelement");
if (el) {
el.innerHTML += "<p>" + msg + "</p>";
}
}
}
};
////////////////////////////////////////////////////////////////////////////
// デバッグメッセージを出力するクラスを定義します
////////////////////////////////////////////////////////////////////////////
YAHOO.example.ClassWithLogging = function() { };
YAHOO.lang.augment(YAHOO.example.ClassWithLogging, YAHOO.example.ConsoleProvider);
////////////////////////////////////////////////////////////////////////////
// テスト
////////////////////////////////////////////////////////////////////////////
var c = new YAHOO.example.ClassWithLogging();
c.log("worked");
</script>
YAHOO.namespace("test");
YAHOO.test.Class1 = function(info) {
YAHOO.log("Class1: " + info, "error", "Class1");
};
<html>
<head>
<script type="text/javascript">
// モジュールがロードされる度にコールバックされます
function mycallback(info) {
alert(info.name + " loaded");
// loggerがページでロードされると動作します
if (info.name == "logger") {
YAHOO.widget.Logger.enableBrowserConsole()
var l = new YAHOO.widget.LogWriter("(");
YAHOO.example.EnvTest = function() {
function log(msg,cat) {
var c=c||"info";
YAHOO.log(msg,c,"LangTest");
}
return {
init: function() {
new YAHOO.widget.LogReader("logoutput");
log("init");
},
showInfo: function(e, module){
// YAHOO.env.getVersion はYAHOO_config.listener が受け取るデータと同じデータを返します
var info = YAHOO.env.getVersion(module);
log("name: " + info.name);
log("version: " + info.version);
log("build: " + info.build);
log("versions: " + info.versions);
log("builds: " + info.builds);
log("mainClass version: " + info.mainClass.VERSION);
}
};
} ();
YAHOO.util.Event.addListener(window, "load", YAHOO.example.EnvTest.init);
YAHOO.util.Event.addListener("showeventbutton", "click", YAHOO.example.EnvTest.showInfo, "event");
}
}
YAHOO_config = {
listener: mycallback
};
</script>
<script src = "../src/js/YAHOO.js" ></script>
<script src = "../../build/event/event.js" ></script>
<script src = "../../build/dom/dom.js" ></script>
<script src = "../../build/dragdrop/dragdrop.js" ></script>
<script src = "../../build/logger/logger.js" ></script>
<link rel="stylesheet" type="text/css" href="../../build/logger/assets/logger.css">
<style> #logoutput {float: right}</style>
</head>
<body>
<input type="button" id="showeventbutton" value="show event version" />
<div id="logoutput"></div>
</body>
</html>
YUI Module NamesとYUI on Mobile: Using YAHOO Global Object with "A-Grade" Mobile Browsersは割愛します。
<?php
// --
// VERSION CHECK
// --
if( version_compare(phpversion(), '5.0', '<') ) {
echo "本スクリプトはPHPのVersion5.0以降でのみ動作します\n";
exit;
}
// --
// LOAD XML
// --
$_lastfmName = 'mahata';
$_targetUrl = "http://ws.audioscrobbler.com/1.0/user/{$_lastfmName}/recenttracks.xml";
$_xmlObj = simplexml_load_file($_targetUrl);
// --
// MAKE ARRAY
// --
$_playList = array();
foreach( $_xmlObj as $_xmlObj_ ) {
$_tArray = array();
$_tArray['artist'] = (string)$_xmlObj_->artist;
$_tArray['name'] = (string)$_xmlObj_->name;
$_tArray['album'] = (string)$_xmlObj_->album;
$_tArray['date'] = date('Y/m/d H:i:s', (int)strtotime($_xmlObj_->date));
$_playList[] = $_tArray;
}
// --
// DUMP ARRAY
// --
print_r( $_playList );
?>
$ php playlist2array.php
Array
(
[0] => Array
(
[artist] => Surface
[name] => クセになりそうだ
[album] => resurface
[date] => 2007/10/03 14:42:00
)
[1] => Array
(
[artist] => Surface
[name] => ハニカムハニ
[album] => resurface
[date] => 2007/10/03 14:37:00
)
[2] => Array
(
[artist] => Surface
[name] => WAIT!
[album] => resurface
[date] => 2007/10/03 14:34:00
)
[3] => Array
(
[artist] => Surface
[name] => Re:START
[album] => Re:START
[date] => 2007/10/03 14:30:00
)
[4] => Array
(
[artist] => Surface
[name] => ちりつもたれつ
[album] => resurface
[date] => 2007/10/03 14:26:00
)
[5] => Array
(
[artist] => Surface
[name] => ワクチン
[album] =>
[date] => 2007/10/03 14:20:00
)
[6] => Array
(
[artist] => Surface
[name] => 夢の続きへ
[album] =>
[date] => 2007/10/03 14:15:00
)
[7] => Array
(
[artist] => LINDBERG
[name] => Brand New Day
[album] => XII
[date] => 2007/10/03 14:09:00
)
[8] => Array
(
[artist] => LINDBERG
[name] => アタシは磨けば光るダイアモンドなのに
[album] => XII
[date] => 2007/10/03 14:05:00
)
[9] => Array
(
[artist] => LINDBERG
[name] => 自転車に初めて乗れた日
[album] => XII
[date] => 2007/10/03 13:59:00
)
)
<head> <title>this is a title.</title> <link href="/js/google_code_prettify/prettify.css" rel="stylesheet" type="text/css"/> <script src="/js/google_code_prettify/prettify.js" type="text/javascript"></script> </head>
<body onload="prettyPrint()">
<pre class="prettyprint">ソースコード</pre>
拡張子がphpのファイル(というか、phpコードが実行される環境)で、
かつphp.iniのshort_open_tagがOnになっていると、XHTMLのxml宣言でエラーが出る。
なぜかというと、インタプリタが<?xml から始まる文字列をphpコードとして解釈するからである。
通常、次のようなerror_logが出力される。
Parse error: parse error, unexpected T_STRING in *.php on line 1
php.ini のshort_open_tag をOffにするのが正しい解決方法だが、
php.ini を勝手に変更してはならないケースでは、次のような回避方法もある。
<?php echo '<?xml version="1.0" encoding="utf-8"?>'."\n" ?>
PHPの仕様に文句をこぼさずに生きていきたいものですね。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html lang="ja"> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <meta name="description" content="mahata.net"> <meta name="keywords" content="mahata, mahata.net, programming, tool"> <meta name="author" content="Yasunori MAHATA"> <title>mahata.net - strip tags</title> <body> <p> ブログにソースコードを貼り付けるツール。 </p> <p> <form action="strip_tags_proc.html" method="post"> <textarea name="code" cols="96" rows="32"></textarea><br /> <input type="submit" /> </form> </p> </body> </html>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="ja">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="description" content="mahata.net">
<meta name="keywords" content="mahata, mahata.net, programming, tool">
<meta name="author" content="Yasunori MAHATA">
<title>mahata.net - strip tags</title>
<body>
<p>
ブログに貼り付けられる形態に変換しました。
</p>
<p>
<textarea cols="96" rows="32"><?php echo str_replace('&', '&', htmlspecialchars($_POST['code'])); ?></textarea><br />
</p>
<hr />
<p>
再度の変換を試したい場合は、次のテキストボックスを利用してください。
</p>
<p>
<form action="strip_tags_proc.html" method="post">
<textarea name="code" cols="96" rows="32"><?php echo $_POST['code']; ?></textarea><br />
<input type="submit" />
</form>
</p>
</body>
</html>