phpDocumentor 2 で使える型名
@var @return @param に使える型の書き方です。
1.4. Definition of a ‘Type’ — phpDocumentor v0.13 documentation のまとめ。
Keyword 基本型など
PHP の型
- null
- string
- integer (int)
- boolean (bool)
- float (double)
- object
- array
- resource
PHP の疑似型
- void
- callback
- mixed
phpDocumentor 2 で初めて見た型
- true false
- self
メソッドチェーンを多用する身としては、 self が非常に嬉しい。
というか PDT も self 対応してた。
まじか……
class-name クラス名
そのスコープにおける namespace も考慮してくれる模様。
Multiple-types 複数の型を指定する
で区切って列挙する。 |
integer|float|null
など。
Arrays 配列
- array
- int[]
- (int|string)[]
- ((bool|null)|string)[]
配列のキーの型を指定する方法はない模様。
[] を使った記法は、もちろん PDT 非対応。
PHP でシェルスクリプトを作成し、制御をシェルに戻したい場合
proc_open() で、 PHP が作る子プロセスの stdin/stdout/stderr と、 PHP プロセス自体のそれらを繋ぐ。
実装例
#!/usr/bin/php -f <?php // usage: mysql_connect.php database_name $dbname = $argv[1]; // get_connection_info() は適当に自作する。 list($host, $user, $password) = get_connection_info($dbname); $proc = proc_open( "mysql -h $host -u $user --password=$password -A --prompt=\"\u:\d>\_\" \"$dbname\"", array( 0 => STDIN, 1 => STDOUT, 2 => STDERR, ), $pipes ); if (!$proc) { fwrite(STDERR, "Failed to connect $dbname ($user@$host)"); exit(1); } foreach ($pipes as $fp) { fclose($fp); } proc_close($proc);
【再訪】重みを持つ要素の配列から、ランダムに1つ選択する。
http://hirobanex.net/article/2011/12/1324792864
http://blog.livedoor.jp/dankogai/archives/51761113.html
似たようなのを 以前 書いたので、改めてまとめただけ。
使い方
/** * weight は 1 以上の整数。 * 合計を 100 にしなければならない……なんてことはない。 * weight が大きいほうが選択されやすい。 */ var weighted = new Weighted([ {value: "hoge", weight: 30}, {value: "moge", weight: 30}, {value: "foo", weight: 20}, {value: "bar", weight: 10}, {value: "jar", weight: 10}, ]); /** * weight の合計が 100 なので、それぞれの出る確率は * hoge = 30 / 100 * moge = 30 / 100 * foo = 20 / 100 * bar = 10 / 100 * jar = 10 / 100 * となる。 */ var result = {}; for (var i = 0; i < 100000; ++i) { // 重みを考慮して、ランダムに選択する。 var value = weighted.select(); if (value in result) { ++result[value]; } else { result[value] = 1; } } var text = []; for (var p in result) { text.push(p + ": " + result[p]); } alert(text.join("\n")); /* hoge: 29823 jar: 9943 foo: 20224 moge: 30099 bar: 9911 確率的には正しそうな結果になった。 */
定義
function Weighted(input) { var i = 0, length = input.length, pair = null, offset = null, sorted = new Array(length * 2), values = new Array(length), parameter = 0; for (; i < length; ++i) { pair = input[i]; values[i] = pair.value; parameter += pair.weight; offset = i * 2; sorted[offset] = i; sorted[offset + 1] = parameter; sorted.push(i, parameter); } sorted.pop(); // 最後の要素は parameter と同じなのでいらない。 this.sorted = sorted; this.parameter = parameter; this.values = values; } Weighted.prototype.select = function Weighted_select() { var sorted = this.sorted, length = sorted.length, left = 1, right = length - 2, middle = null, ceil = Math.ceil, rand = Math.floor(Math.random() * this.parameter); if (length < 2) { return length == 1 ? this.values[0] : null; } do { middle = ceil((left + right) / 2); if (!(middle & 1)) { ++middle; } if (sorted[middle] > rand) { right = middle - 2; if (right < left) { return this.values[sorted[middle - 1]]; } } else { left = middle + 2; if (left > right) { return this.values[sorted[middle + 1]]; } } } while (true); };
わざわざ二分探索を使った理由は、
- 1000 種類のアイテムのうち、 500 種類は同じ確率で、 200 種類はより低い確率で、 100 種類はより低い確率で……
なんていう状況をターゲットにしていた為。
データ量が少なければ、重い順に舐めていったほうがいいだろうけど、量が多い場合はこうしないときつかった。
MySQL によるランキング管理
CREATE TABLE ranking ( user_id INT UNSIGNED NOT NULL COMMENT "ユーザID", score INT NOT NULL COMMENT "順位付けに使う値", rank INT UNSIGNED NOT NULL COMMENT "順位", PRIMARY KEY (user_id), INDEX USING BTREE (score) );
user_id | score | rank |
---|---|---|
1 | 100 | 0 |
2 | 70 | 0 |
3 | 50 | 0 |
4 | 50 | 0 |
5 | 20 | 0 |
というテーブルがあったとき、
SET @rank = 1, @rownum = 0, @prev = null; UPDATE ranking SET rank = (@rank := IF((@rownum := @rownum + 1) AND (@prev <=> score OR (@prev := score) <=> NULL), @rank, @rownum)) ORDER BY score DESC;
することで
user_id | score | rank |
---|---|---|
1 | 100 | 1 |
2 | 70 | 2 |
3 | 50 | 3 |
4 | 50 | 3 |
5 | 20 | 5 |
となります。
ranking テーブル自体をメモリテーブルにして、 1 分ごとに上クエリを発行していれば、中々最新のデータになるのではないでしょうか。
※もちろん score の元データは別のテーブルでちゃんと記録されてるとして。
携帯用ページ上における各種SNS連携方法まとめ
mixi (mixiチェック)
<form method="POST" action="http://m.mixi.jp/share.pl?guid=ON"> <input type="hidden" name="charset" value="shift_jis もしくは utf-8 。デフォルトは shift_jis" /> <input type="hidden" name="check_key" value="識別キー" /> <input type="hidden" name="title" value="リンク先のタイトル (charset の文字エンコーディング)" /> <input type="hidden" name="description" value="リンク先の説明文 (charset の文字エンコーディング)" /> <input type="hidden" name="content_rating" value="19歳未満非対応の場合は 1" /> <input type="hidden" name="image" value="サムネイル画像のURL" /> <input type="hidden" name="primary_url" value="リンク先のURL (*1)" /> <input type="hidden" name="pc_url" value="リンク先のURL(PC用)" /> <input type="hidden" name="smartphone_url" value="リンク先のURL(スマートフォン用)" /> <input type="hidden" name="mobile_url" value="リンク先のURL(携帯用) 携帯電話向け URL のいずれかは必須" /> <input type="hidden" name="mobile_docomo_url" value="リンク先のURL(DoCoMo携帯用)" /> <input type="hidden" name="mobile_au_url" value="リンク先のURL(au携帯用)" /> <input type="hidden" name="mobile_softbank_url" value="リンク先のURL(SoftBank携帯用)" /> <input type="submit" value="mixiチェック" /> </form> <!-- (*1) primary_url は「チェックされた対象」を識別するために使われます。個別のチェックのうち、primary_url が同じものは同じ対象をチェックしたものとして扱われます。-->
mixi パートナーに登録して、mixi チェック用の識別キーを取得する必要がある。
詳細は http://developer.mixi.co.jp/connect/mixi_plugin/mixi_check/spec_mixi_check/
どのパラメータが必須でどのパラメータが省略可能かは↑を参照。
GREE (Social Feedback)
<?php // PHP $url = urlencode('リンク先のURL'); $button_type = 0; // 0-4 の値。 $button_size 16; // 16, 20, 22, 23, 32 のいずれか。 $button_src = 'リンクに使う画像のURL'; // 以上、全て必須パラメータ。 ?> <a href="http://m.gree.jp/?mode=share&act=write&url=<?=$url?>&button_type=<?=$button_type?>&button_size=<?=$button_size?>"><img src="<?=$button_src?>" /></a>
詳細は https://developer.gree.co.jp/connect/plugins/sf
リンクに使う画像は http://i.share.gree.jp/img/share/button/ 以下に用意されているが、全て PNG なので、携帯 (というか古い DoCoMo) で表示するには、 GIF に変換して自分のところにアップロードする必要がある。
さらに (なぜだか知らないが) GREE のページから $url へ遷移してくるとき、 & が & に変換される。
http://example.com/foo?a=1&b=2 という URL を指定した場合は、 http://example.com/foo?a=1&b=2 という URL でアクセスされる。
PHP の arg_separator が & でないと正常に $_GET に格納されないので注意。
私の場合、 arg_separator は & のままで、 $_SERVER['REQUEST_URI'] 中で & があったら & に変換しつつ、自分で $_GET に代入して対処している。
button_type は 0-4 があるが、多分どれでもいい。 GREE 側でどれが一番使われてるか計測してるんだろう、きっと。
twitter (tweet)
<?php // PHP $url = urlencode('リンク先の URL'); $text = urlencode('リンク先の説明文 (UTF-8)'); $via = 'フォローさせたいアカウントその1'; // 省略可。 $related = 'フォローさせたいアカウントその2'; // 省略可。 ?> <a href="http://twtr.jp/share?guid=ON&url=<?=$url?>&text=<?=$text?>&via=<?=$via?>&related=<?=$related?>">つぶやく</a>
詳細は https://dev.twitter.com/docs/tweet-button
via と related は、つぶやいた後におすすめユーザとして表示される。 via を未だフォローしていなければ via が、そうでなければ related が、どちらもフォロー済であれば省略される。
なお、 twtr.jp を使っているユーザが、ポストされた URL へ遷移すると、 Google Wireless Transcoder で中継されてしまう。
これを解消するには、リンク先のページの
<link rel="alternate" media="handheld" href="携帯用URL" />
を入れておく。
参考: http://d.hatena.ne.jp/w6500/20110511
Facebook (Share)
<?php // PHP $url = urlencode('リンク先の URL'); $text = urlencode('リンク先の説明文 (UTF-8)'); ?> <a href="http://m.facebook.com/sharer.php?guid=ON&u=<?=$url?>&t=<?=$text?>">シェア</a>
どうやら古いインターフェースのようで、公式の詳細が見つからなかった。
ウォールに投稿される。
gcc4.4環境でswfmill-0.2.12をコンパイルする
パッチを書いた。
KLabさんとこのエンコーディングパッチ充ててからやってください。
wget http://swfmill.org/releases/swfmill-0.2.12.tar.gz tar xf swfmill-0.2.12.tar.gz cd swfmill-0.2.12 wget http://lab.klab.org/files/flash/encoding.patch patch -p1 < encoding.patch wget http://xif.jp/src/swfmill/0.2.12/gcc44.patch patch -p1 < gcc44.patch ./configure make make install
http://xif.jp/src/swfmill/0.2.12/gcc44.patchの中身
diff -ur swfmill-0.2.12/src/swft/swft_css.cpp swfmill-0.2.12.gcc44/src/swft/swft_css.cpp --- swfmill-0.2.12/src/swft/swft_css.cpp 2006-07-20 22:57:17.000000000 +0900 +++ swfmill-0.2.12.gcc44/src/swft/swft_css.cpp 2011-04-30 18:16:35.640273197 +0900 @@ -6,6 +6,7 @@ #include <libxslt/xsltutils.h> #include <libxml/xpathInternals.h> #include "swft.h" +#include <string.h> using namespace std; #define TMP_STRLEN 0xff @@ -236,4 +237,4 @@ //fprintf(stderr,"looking up style %s: %s\n", needle, r.c_str() ); valuePush( ctx, xmlXPathNewString( (const xmlChar *)r.c_str() ) ); -} \ No newline at end of file +} diff -ur swfmill-0.2.12/src/swft/swft_import.cpp swfmill-0.2.12.gcc44/src/swft/swft_import.cpp --- swfmill-0.2.12/src/swft/swft_import.cpp 2006-07-20 22:57:17.000000000 +0900 +++ swfmill-0.2.12.gcc44/src/swft/swft_import.cpp 2011-04-30 18:16:53.984310124 +0900 @@ -10,7 +10,7 @@ int l; // figure basename (filename without path) - b = strrchr( filename, '/' ); + b = strrchr( const_cast<char*>(filename), '/' ); basename = b ? b+1 : filename; l = strlen(basename); diff -ur swfmill-0.2.12/src/swft/swft_import_mp3.cpp swfmill-0.2.12.gcc44/src/swft/swft_import_mp3.cpp --- swfmill-0.2.12/src/swft/swft_import_mp3.cpp 2006-08-17 19:38:49.000000000 +0900 +++ swfmill-0.2.12.gcc44/src/swft/swft_import_mp3.cpp 2011-04-30 18:17:06.328336831 +0900 @@ -5,6 +5,7 @@ #include "swft.h" #include <sys/types.h> #include <sys/stat.h> +#include <string.h> #define TMP_STRLEN 0xff
重みを持つ要素の配列から、ランダムに1つ選択する。
地道に for だの使って処理してもいいんだけど、配列の要素の数が巨大になったときのパフォーマンスが心配だったので、二分探索にしてみた。
例えば、
『数十種類あるメッセージのうち1つをランダムに表示したいけど、うち数種類はレアにしたい』
みたいな時に使えるはず。
こういう処理は割と頻繁に使うので、汎用化しておくと便利。
サンプルコードは javascript 。
/** * weight が大きいほど選択される可能性が高い。 * weight は 1 以上でなければならない。 */ var messages = [ {message: "Hello1", weight: 100}, // 100/188 で選択される。 {message: "Hello2", weight: 50}, // 50/188 で選択される。 {message: "Hello3", weight: 25}, // 以下略。 {message: "Hello4", weight: 12}, {message: "Hello5", weight: 1}, ]; var input = []; for (var i = 0, length = messages.length; i < length; ++i) { input.push(messages[i].weight); } var map = makeMap(input); // {tree: [0, 100, 1, 150, 2, 175, 3, 187, 4], parameter: 188} // input が大規模でも、 map をキャッシュしておけば速度は稼げる。 var key = getKey(map.tree, randomBetween(0, map.parameter - 1)); alert(messages[key].message); function makeMap(input) { var length = input.length; var parameter = 0; var tree = []; for (var i = 0; i < length; ++i) { parameter += input[i]; tree.push(i, parameter); } tree.pop(); // 最後の parameter は tree には要らないので取り除く。 return {tree: tree, parameter: parameter}; } function getKey(tree, value) { var length = tree.length; if (length < 1) { return null; } if (length === 1) { return tree[0]; } var left = 1; var right = length - 2; var ceil = Math.ceil; do { var middle = ceil((left + right) / 2); if (!(middle & 1)) { ++middle; } if (tree[middle] > value) { right = middle - 2; if (right < left) { return tree[middle - 1]; } } else { left = middle + 2; if (left > right) { return tree[middle + 1]; } } } while (true); } function randomBetween(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }