utf8で3キャリア対応サイトを作成する際に、auの絵文字が小さくならない件の対応 (php emoji utf8

cakePHP + smarty + 絵文字表示 - 雑想空間 これの続き、っちゃー続き。

auの絵文字を表示させる方法は何種類かあるんですが、imgタグで表示させると、fontタグやspanタグのstyleでサイズ指定しても、連動して小さくなってくれません。
au絵文字を大きくする、小さくする - [au [KDDI EZweb]/携帯] ぺんたん info

こちらにも書いてあるように、




このように書けばokですが、

<img localsrc="51" />

imgタグで指定すると、サイズが固定されて、変動しません。

cake php + smarty

cake phpsmarty を利用してサイトを作成した時に、
Smarty で携帯絵文字 - ZeBeVogue別館
こちらの関数にお世話になったのですが、

			case "e";
				if (preg_match("/[^0-9]/", $emoji_array[$data][2])) {
					$put = $emoji_array[$data][2];
				} else {
					$put = "<img localsrc=\"".$emoji_array[$data][2]."\" />";
				}
				break;

基本的には、imgタグで表示するので、文字サイズとともにサイズ変更してくれません。


webコード(SJIS)で書き直そうと思い、規則性やマッピングについて久々に探したら、2007年にすでに解決してました。
KDDI/AUでutf-8のHTMLフォームから送られてくる絵文字コード - Bulknews::Subtech - subtech
EZweb (au) 絵文字の規則性 - 覇王色を求めて
utf-8のサイトでformから送信された場合の対処法をまとめてありますが、普通にwebコード(SJIS)からエンコードしなおす場合にも利用できそうです。

<?php
// eebd89
echo bin2hex(mb_convert_encoding(pack('H*', dechex(hexdec("F649") - 1792)), 'UTF-8', 'unicode'));
?>

ちょっと書き換え

まず絵文字のマッピング表に、新たに、auのwebコード(SJIS)を追加します
表は
絵文字を使いこなして見るためのページ【Docomo】全ページ
こちらから拝借

  1,&#58942;,44,Gj,F660
  2,&#58943;,107,Gi,F665
  3,&#58944;,95,Gk,F664
  4,&#58945;,191,Gh,F65D

末尾に追加してみました。


コードもちょっと変えます

 23     // au の絵文字表示形式
 24     $au_conv = 1; // 0 default 1 size依存
 25 
 26     //携帯UA取得
 27     $agent = $_SERVER["HTTP_USER_AGENT"];
 28     if(preg_match("/[0-9]{1,3}/", $data) && is_numeric($data) && 0 < $data && $data < 253) {
 29         switch (mobile($agent)) {
 30             case "i";
 31                 $put = $emoji_array[$data][1];
 32                 break;
 33             case "e";
 34                 if ($au_conv == 1) {
 35                     if (preg_match("/^[A-F0-9]+$/", $emoji_array[$data][4])) {
 36                         $put = mb_convert_encoding(pack('H*', dechex(hexdec($emoji_array[$data][4]) - 1792)), 'UTF-8', 'unicode');
 37                     }
 38                     else {
 39                         $put = $emoji_array[$data][4];
 40                     }
 41                 }
 42                 elseif (preg_match("/[^0-9]/", $emoji_array[$data][2])) {
 43                     $put = $emoji_array[$data][2];
 44                 } else {
 45                     $put = "<img localsrc=\"".$emoji_array[$data][2]."\" />";
 46                 }
 47                 break;
 48             case "s";
 49                 if (preg_match("/^[A-Z]{1}?/", $emoji_array[$data][3])) {
 50                     $put = "\x1B\$".encode($emoji_array[$data][3])."\x0F";
 51                 } else {
 52                     $put = encode($emoji_array[$data][3]);
 53                 }
 54                 break;
 55             case "p";
 56                 $put = "<img src=\""."/img/mobile/".$emoji_array[$data][0].".gif\" width=\"12\" height=\"12\" border=\"0\" alt=\"\" />";
 57                 break;
 58         }


これで

{emoji num=1}

で、絵文字表示できて、文字サイズに依存する絵文字が出ました

MacPorts メモ

MacPorts

Snow LeopardMacPorts いれる
メモ

Xcode

まずはXcodeがいるので、入れる
Mac本体に付属のOSインストールディスクか
Sign in with your Apple ID - Apple Developer
からinstallする。
Developer Toolsの中になるかな。
AppleIDなければ、作成から。
結構重いので、気長にモンハンでも狩りながら待つ事。

MacPorts

403 Forbidden
ここからも落とせるが、Snow Leopard対応のVersionは、本家から落とした方が良い。
The MacPorts Project -- Home
こっち。

sudo port selfupdate

sudo port sync
PATH

.bash_profile に、

  # MacPorts Installer addition on yyyy-mm-dd_at_hh:mm:ss: adding an appropriate PATH variable for use with MacPorts.
  export PATH=/opt/local/bin:/opt/local/sbin:$PATH
  # Finished adapting your PATH environment variable for use with MacPorts.

って適当に追加されているので、

source ~/.bash_profile
なんか入れてみる
sudo port install git-core
Error: db46 requires the Java for Mac OS X development headers.

db46のinstallに失敗した

Error: db46 requires the Java for Mac OS X development headers.
Error: Download the Java Developer Package from: <https://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?bundleID=20719>
Error: Target org.macports.configure returned: missing Java headers
Error: Failed to install db46

言われた通り、別途入れる
Sign in with your Apple ID - Apple Developer

AppleIDいるよ。
入れたら、やり直せば、成功した。


狩りを止めて、コーディングに戻れば完成。

レンサバの環境構築メモ[sakuraインターネット]

さくらのレンサバを借りたので、環境を作るメモ

bash

さくらはデフォルトシェルがcshらしい。
使い慣れてるbashに変えてみる。

まずは利用可能なシェルを見る

% cat /etc/shells
/bin/sh
/bin/csh
/bin/tcsh
/usr/local/bin/bash
/usr/local/bin/rbash
/usr/local/bin/zsh
/usr/local/bin/rzsh
/usr/bin/passwd

zshもあるらしいけど、とりあえずbash

% chsh -s /usr/local/bin/bash
Password: # パスワードを入力

.bash_profile
.bashrc
を適当に作成。

vim

vimがなく、viなので、vimいれる

wget ftp://ftp.vim.org/pub/vim/unix/vim-7.0.tar.bz2
wget ftp://ftp.vim.org/pub/vim/extra/vim-7.0-extra.tar.gz
wget ftp://ftp.vim.org/pub/vim/extra/vim-7.0-lang.tar.gz

tar jxvf vim-7.0.tar.bz2
tar zxvf vim-7.0-extra.tar.gz
tar zxvf vim-7.0-lang.tar.gz

cd vim70
mkdir patches
cd patches
curl -O 'ftp://ftp.vim.org/pub/vim/patches/7.0/7.0.[001-243]'
cd ..
cat patches/7.0.* | patch -p0
./configure --enable-multibyte --enable-xim --enable-fontset --with-features=big --prefix=/home/ryoff
make && make install

vi .bashrc

alias vi='~/bin/vim'

source ~/.bashrc

screen

wget ftp://ftp.uni-erlangen.de/pub/utilities/screen/screen-4.0.3.tar.gz
tar xzf screen-4.0.3.tar.gz
cd screen-4.0.3
./configure --prefix=/home/user_name
make && make install

yyyymmddhhiiss形式を日付け変換する時の php4 php5 の方法

日付け変換

yyyymmddhhiiss形式の文字列を日付けに変換したかったです。
つまり

echo date("YmdHis", time())."\n";

これ実行して出てくる文字列をふたたびdate関数に渡せるようにしたかった感じ。

strtotime

最も単純に使ってみました。

  1 <?php
  2 
  3 $time = date("YmdHis", time());
  4 echo strtotime($time)."\n";
  5 echo date("Y/m/d H:i:s", strtotime($time))."\n";
  6 
  7 ?>

結果は php5 と php4 で違いました。

php5.2
[ryoff@php5-ryoff ~/mycode/php5]$ php strtotime.php
1287109712
2010/10/15 11:28:32
php4.3
[ryoff@php4-ryoff ~/mycode/php4]$ php strtotime.php
-1
1970/01/01 08:59:59

strtotimeの時点で、-1帰ってくるなぁ。
php4ではうまく動いてくれなかったので、力技....

  1 <?php
  2 
  3 $time = date("YmdHis", time());
  4 echo strtotime($time)."\n";
  5 echo sprintf("%04d/%02d/%02d %02d:%02d:%02d",
  6         substr($time, 0, 4), substr($time, 4, 2),
  7         substr($time, 6, 2),
  8         substr($time, 8, 2), substr($time, 10, 2),
  9         substr($time, 12, 2)
 10        )."\n";
 11 
 12 ?>

雑感

どうでもいいけど、

"Y/m/d H:i:s"

って、

ヤマダ電機! HIS!

って読めるよね。
覚えやすいですね。

local::libのinstallメモ

local::lib を今更ながらinstallしたので、その作業メモ

参考サイト

local::libを使った非rootでのCPAN環境構築 - hide-k.net#blog
第18回 local::lib:ふだんと違う環境でPerlを使う|gihyo.jp … 技術評論社


基本はhide-kさんのサイトの手順どおりに行った。
落としてきて、解凍。

wget http://search.cpan.org/CPAN/authors/id/A/AP/APEIRON/local-lib-1.003001.tar.gz
tar xzvf local-lib-1.003001.tar.gz
cd local-lib-1.003001

BSDPANのエラーを回避する設定

export PKG_DBDIR=$HOME/local/var/db/pkg
export PORT_DBDIR=$HOME/local/var/db/pkg
export INSTALL_AS_USER
export LD_LIBRARY_PATH=$HOME/local/lib
mkdir -p ~/local/var/db/pkg


cpanの初期設定

sudo cpan
....
exit;


makefile

perl MakeFile.PL --bootstrap=$HOME/local

なんかエラー出た

--2010-10-02 20:02:42--  ftp://ftp.dti.ad.jp/pub/lang/CPAN/authors/id/A/AN/ANDK/CHECKSUMS.gz
  (try:20) => `-'
Connecting to ftp.dti.ad.jp|202.216.228.228|:21... connected.
Logging in as anonymous ...
Error in server response, closing control connection.
Giving up.

Issuing "/usr/bin/ftp -n"
Trying 202.216.228.228...
Not connected.
Not connected.

ミラーサイトにつなげない。


CPANに失敗する時のよくある解決策として

export FTP_PASSIVE=1

とあったが、ダメ。


しょうがないので、ミラーサイトを変える。


cpan初期化&再設定

cpan
o conf init
....
....
o conf commit

基本的に全部OK。
地域を日本に設定して、ミラーサイト変える。


再実行

perl MakeFile.PL --bootstrap=$HOME/local

成功

make && make test
All tests successful.
Files=2, Tests=6,  0 wallclock secs ( 0.05 usr  0.01 sys +  0.04 cusr  0.01 csys =  0.11 CPU)
Result: PASS

問題なさそうなので、install

make install

Ethna v2.5.0のソースコードを読み解く Part1 【Ethna_ActionForm.php】

ethnaで書いてて、ふと思った

$hoge = $this->af->get('hoge');

で、hogeが渡されてこなかった場合、$hogeにはnullが入るのか?空が入るのか?


書けばすぐに分かるけど、せっかくなのでEthnaの元コード読んで見る事に

download

404 Not Found - Ethna
download && install 情報は公式に詳しくて書いてあります。
とりあえず、wgetして落として解凍

[ryoff@ryoff ~/ethna]$ tree -L 2
.
|-- Ethna-2.5.0
|   |-- CHANGES
|   |-- Ethna.php
|   |-- LICENSE
|   |-- README
|   |-- bin
|   |-- class
|   |-- misc
|   |-- skel
|   |-- test
|   `-- tpl
`-- package.xml

ControllerやActionClass、ActionFormなどは、class以下にあります。

Ethna_ActionForm.php

$this->af

とあるように、afはActionFormの略です。
なので、

Ethna-2.5.0/class/Ethna_ActionForm.php

を読みます。

get
 132     /**
 133      *  フォーム値のアクセサ(R)
 134      *
 135      *  @access public
 136      *  @param  string  $name   フォーム値の名称
 137      *  @return mixed   フォーム値
 138      */
 139     function get($name)
 140     {
 141         return $this->_getVarsByFormName($this->form_vars, $name);
 142     }

$nameを受け取って、 $this->form_vars と $name を _getVarsByFormName関数に渡していますね。
その引数を返す、と。

form_vars

_getVarsByFormName関数を見るまえに、form_varsという変数を覗きましょう。
form_varsは40行目に初期値が定義されてます。

  39     /** @var    array   フォーム値 */
  40     var $form_vars = array();

この変数に値をsetしているのは、setFormVars関数です。

 299     /**
 300      *  ユーザから送信されたフォーム値をフォーム値定義に従ってインポートする
 301      *
 302      *  @access public
 303      */
 304     function setFormVars()
 305     {
 306         if (isset($_SERVER['REQUEST_METHOD']) == false) {
 307             return;
 308         } else if (strcasecmp($_SERVER['REQUEST_METHOD'], 'post') == 0) {
 309             $http_vars =& $_POST;
 310         } else {
 311             $http_vars =& $_GET;
 312         }
 313 
 314         //
 315         //  ethna_fid というフォーム値は、フォーム定義に関わらず受け入れる
 316         //  これは、submitされたフォームを識別するために使われる
 317         //  null の場合は、以下の場合である
 318         //
 319         //  1. フォームヘルパが使われていない
 320         //  2. 画面の初期表示などで、submitされなかった
 321         //  3. {form name=...} が未設定である
 322         //
 323         $this->form_vars['ethna_fid'] = (isset($http_vars['ethna_fid']) == false
 324                                       || is_null($http_vars['ethna_fid']))
 325                                       ? null
 326                                       : $http_vars['ethna_fid'];
......
......

なのですが、この関数はEthna_ActionForm内からは呼ばれていません。
呼び出し元は、同ディレクトリ内になるEthna_Controllerからです。

./class/Ethna_Controller.php

_trigger_WWW関数内でsetFormVars関数が呼び出されています。

 897     /**
 898      *  フレームワークの処理を実行する(WWW)
 899      *
 900      *  引数$default_action_nameに配列が指定された場合、その配列で指定された
 901      *  アクション以外は受け付けない(指定されていないアクションが指定された
 902      *  場合、配列の先頭で指定されたアクションが実行される)
 903      *
 904      *  @access private
 905      *  @param  mixed   $default_action_name    指定のアクション名
 906      *  @param  mixed   $fallback_action_name   アクション名が決定できなかった場合に実行されるアクション名
 907      *  @return mixed   0:正常終了 Ethna_Error:エラー
 908      */
 909     function _trigger_WWW($default_action_name = "", $fallback_action_name = "")
 910     {
.....
.....
 949         // アクションフォーム初期化
 950         // フォーム定義、フォーム値設定
 951         $form_name = $this->getActionFormName($action_name);
 952         $this->action_form =& new $form_name($this);
 953         $this->action_form->setFormDef_PreHelper();
 954         $this->action_form->setFormVars();
 955         $backend->setActionForm($this->action_form);

_trigger_WWW関数はethnaのメインの処理を行います。

こちらはまた別途読み解いてみます。
今回は、

Controller 内で関数が呼び出されて、htmlのformに入力された値がform_varsにセットされるんだな

ぐらいに思っておきます。


前置きが長くなりましたが、これで、_getVarsByFormName関数に渡す、$this->form_varsという値には、htmlのFormに入力された値がセットされていることが分かりました。

_getVarsByFormName
 206     /**
 207      *  配列の中からキーで指定された要素を取り出す
 208      *
 209      *  @access private
 210      *  @param  array   &$target    対象とする配列
 211      *  @param  string  $nane       キー
 212      *  @return string  指定された要素
 213      */
 214     function _getVarsByFormName(&$target, $name)
 215     {
 216         $keys = $this->_getFormNameArray($name);
 217         return $this->_getVarsByKeys($target, $keys);
 218     }

_getFormNameArray関数に$nameを渡して$keysを受け取り、
_getVarsByKeys関数に$targetと$keysを渡して、戻り値を返しています。
_getVarsByKeys関数で返される値がそのまま

$this->af->get('hoge');

で返される値なので、この二つの関数を読み解けば、戻り値のパターンが分かりそうです。

_getFormNameArray
 185     /**
 186      *  フォーム名に対応するキーの配列を返す
 187      *
 188      *  @access private
 189      *  @param  string  $name   フォーム名
 190      *  @return array   キーの配列
 191      */
 192     function _getFormNameArray($name)
 193     {
 194         // 多次元配列を指定した場合
 195         if (preg_match('/^.*\[[^\]]+\]$/', $name)) {
 196             $buff = preg_replace('/\]\[/', '[', $name); // hoge[foo][bar] => hoge[foo[bar]
 197             $buff = preg_replace('/\]/', "", $buff);    // hoge][foo[bar] => hoge[foo[bar
 198             $ret = explode('[', $buff);                 // hoge[foo[bar   => array('hoge', 'foo', 'var')
 199         } else {
 200             // 多次元配列を指定していない場合
 201             $ret = array($name);
 202         }
 203         return $ret;
 204     }

多次元配列を指定した場合と指定していない場合で、処理が分かれています。
多次元配列を渡すとは、具体的にどんな事をしてくれるのか。
公式サイトに答えが書いてあります。

404 Not Found - Ethna

フォーム定義を以下のように [] 付きのキーで分類して定義するだけです。例を以下に示します。
[phone][home]や、[phone][mobile] のように、複数の階層を使って定義することも可能です。
※テンプレートの例
<form method="post">
  <input type="text" name="User[name]" value="宮崎あおい" />
  <input type="text" name="User[phone][home]" value="01-2345-6789" />
  <input type="text" name="User[phone][mobile]" value="090-1234-5678" />
  <input type="submit">
</form>

サンプルに宮崎あおい、と使用するあたり、非常に好感が持てます。


そして、この方法で、User[name] User[phone]などと階層の途中からでも値を取得できます。

$this->af->get('User[name]')
$this->af->get('User[phone]')

こんな感じ。
196〜198行目で多次元配列をexplodeして、配列にしてますね。

_getVarsByKeys

引き続き、_getVarsByKeys関数です。
_getFormNameArray関数で配列になって戻った$keysを順次処理していきます。

 235     /**
 236      *  配列の中からキーで指定された要素を取り出す
 237      *
 238      *  @access private
 239      *  @param  array   &$target    対象とする配列
 240      *  @param  array   $keys       キーの配列
 241      *  @return string  指定された要素
 242      */
 243     function _getVarsByKeys(&$target, $keys)
 244     {
 245         $count = count($keys);
 246         if ($count == 0) { // 探索完了
 247             return $target;
 248         } elseif ($this->max_form_deps + 1 <= $count) { // 深すぎる配列を制限する
 249             return null;
 250         }
 251 
 252         // まだ探索するキーが残っている
 253         $curval = array_shift($keys);
 254         if (is_array($target) && array_key_exists($curval, $target)) {
 255             return $this->_getVarsByKeys($target[$curval], $keys);
 256         }
 257         return null;
 258     }

max_form_deps はデフォルトでは10階層となっているようです。
$target(=form_vars) 内から、$keysに相当するデータを取得しています。
$keysがなくなるまで、再帰処理してますね。


つまり結果は、

それぞれ帰ります。


関数まねして動かしてみると、

  1 <?php
  2 
  3 $param = array(
  4         'a' => 1,
  5         'b' => array(
  6             'c' => 2,
  7             'd' => array(
  8                 'e' => 3,
  9             ),
 10         ),
 11     );
 12 function getVarsByKeys(&$param, $keys) {
 13     $count = count($keys);
 14     if ($count == 0) {
 15         return $param;
 16     }
 17 
 18     $curval = array_shift($keys);
 19     if (is_array($param) && array_key_exists($curval, $param)) {
 20         return getVarsByKeys($param[$curval], $keys);
 21     }
 22     return null;
 23 }
 24 
 25 var_dump(getVarsByKeys($param, array('a')));
 26 // int(1)
 27 var_dump(getVarsByKeys($param, array('b')));
 28 // array(2) {
 29 //  ["c"]=>
 30 //      int(2)
 31 //      ["d"]=>
 32 //      array(1) {
 33 //          ["e"]=>
 34 //              int(3)
 35 //      }
 36 // }
 37 var_dump(getVarsByKeys($param, array('b','d','e')));
 38 // int(3)
 39 var_dump(getVarsByKeys($param, array('b','d','f')));
 40 // NULL
 41 
 42 ?>

な感じ。

cakePHP とsmartyを使い、html helperで日本語を使うためのメモ

苦労したので、次のためにメモ

初期

cakePHPsmarty使う方法は
第11回 Smartyとフレームワーク(その1:CakePHP編) - Smarty講座
これ見ればいけるので、省略。


んで、このなかで巧く動かない部分があった。

以下のコードを先ほどのindex.tplに書いてみてください。
  {$html->link('yossy先生のSmarty講座', 'http://www.phppro.jp/school/smarty/')}
http://(ドメイン名)/test/index にアクセスしてみて、リンクは表示されたでしょうか?

「いいえ」

<a href="http://www.phppro.jp/school/smarty/"></a> 

こんな感じで、リンク名が入らない


ちなみに、リンク名を英数字だけにすると

<a href="http://www.phppro.jp/school/smarty/">test</a> 

入る。

原因は..

正直よくわからん。
cakeのversionかもしれないし、smartyのversionかもしれないし、レンサバのPHPのversionかもしれん。。
とりあえず日本語使えるようにしようと思って、試行錯誤。


日本語だけ動かないので、きっと日本語入れた場合に、へんなencoding処理とかしてるんだろうなと思い、cakeのhtml helperのソースを読みに行く

html_helper

ファイルは

  • cake/libs/view/helpers/html.php

260行目

/**
 * Creates an HTML link.
 *
.......
    function link($title, $url = null, $options = array(), $confirmMessage = false) {
        $escapeTitle = true;
        if ($url !== null) {
            $url = $this->url($url);
        } else {
            $url = $this->url($title);
            $title = $url;
            $escapeTitle = false;
        }
        if (isset($options['escape'])) {
            $escapeTitle = $options['escape'];
        }

        if ($escapeTitle === true) {
            $title = h($title);
        } elseif (is_string($escapeTitle)) {
            $title = htmlentities($title, ENT_QUOTES, $escapeTitle);
        }

$escapeTitleっての怪しいな。
三つ目の引数の$options内に、"escape"を定義して渡してあげればいいのか。
渡した"escape"は$escapeTitleに入って、htmlentities関数の第三引数に読まれて、$titleに入る。


なんかあやしそうだから、$optionsに"escape"を定義してみる
htmlentitiesの第三引数はcharsetらしいので、"EUC-JP"を定義する

{$html->link('入口', 'http://hoge.com/pages/home', array('escape' => 'EUC-JP'))}
Fatal Error (256): Smarty error: [in /home/hogehogehoge/app/views/tops/index.tpl line 11]: syntax error: unrecognized tag: $html->link('yossy先生のSmarty講座', 'http://www.phppro.jp/school/smarty/', array('escape' => 'EUC-JP',)) (Smarty_Compiler.class.php, line 446) [APP/vendors/smarty/Smarty.class.php, line 1093]


なんかエラーでる。
今度はこのエラー調べる

CakePHP で Smarty を使用するメモ | Sun Limited Mt.
こちらのサイト様で

ただ、.thtml で HTMLヘルパーを使用している場合
{$html->input('Post/name', array('size'=>'30'))}
のままでは、array(…) でエラーになる。

「はい。同じくarray(...)でエラーになります。。」


というわけで、解決方法として紹介されているpluginを導入してみる。
プラグインは↓

function.assign_assoc.php という名前で保存して、

/vendors/samrty/libs/plugins/

以下に保存。


今度はtpl内で

{assign_assoc var='ArrayName' value='escape=>EUC-JP'}
{$html->link('yossy先生のSmarty講座', 'http://www.phppro.jp/school/smarty', $ArrayName)}

と記述すれば完成。
日本語のリンク名が出ましたよ。


追記

今改めてソース読んだら、単純にhtmlspecialchars関数(cakePHP内ではh関数)と、htmlentitiesに渡すcharsetがデフォルトでISO-8859-1で、EUCでコード書いてたからバグってただけか。
今回は全部EUCで作るつもりだし、html.phpを書き換えちゃってもよかったかも。