2012年9月21日金曜日

PHPのarrayの入れ子を作るのに苦労して=&とunsetを使って何とかした話

PHPで、

こんな感じのDBから読んできたデータから、


こんな構造を作りたいなと思ったのです。

こんな感じかな。
<?php
//ここから
function row($type,$typeName,$value1,$value2){
    return array("type" => $type,
                 "typeName" => $typeName,
                 "value1" => $value1,
                 "value2" => $value2);
}
$rows = array(row(1,"あ","ABC","DEF"),
            row(1,"あ","AAA","BBB"),
            row(1,"あ","XXX","YYY"),
            row(2,"い","BEEF","PORK"),
            row(2,"い","COFFEE","TEA"),
            row(2,"い","MILK","COCOA"));
//ここまではサンプルデータを作っているところなので本題とは関係なし。
//データはソートはされているものとする。

$tree = array();    // $treeが最終的なルート配列オブジェクト
$prevType = null;   // Typeが変わったらこの処理をする…というためのもの
foreach($rows as $r){  //1行ずつ読む
    if($prevType !== $r["type"]){ //もしTypeが変わったら
        // 変更チェック用の変数を更新し
     $prevType = $r["type"];
        // 親のノードを新規作成し、$typenodeを差し替え
        $typenode = array("type" => $r["type"],
                          "typeName" => $r["typeName"],
                          "values" => array()); // 親のノードは子を格納する配列を持つ
        // 作った親のノードをルート配列に追加
        $tree[] = $typenode;
    }
    // 子のノードを作成
    $valuenode = array("value1" => $r["value1"],
                        "value2" => $r["value2"]);
    // 今の時点で $typenode で親ノードが参照出来るはずで、
    // それに対して"values"で引ける子ノード格納用配列に、
    // 子のノードを追加
    $typenode["values"][] = $valuenode;
}
実行してみた結果。
( ಠ益ಠ)
ステップ実行してみると、
        :
        // 作った親のノードをルート配列に追加
        $tree[] = $typenode;
    }
        :
    // 今の時点で $typenode で親ノードが参照出来るはずで、
    // それに対して"values"で引ける子ノード格納用配列に、
    // 子のノードを追加
    $typenode["values"][] = $valuenode;
}
この下の方の$valuenodeを追加しているところで、
「$typenode」には追加されるものの、
「$treeからたどれる『$typenodeと同じものであるはずのもの』」
には追加されているように見えない。
        $tree[] = $typenode;
で、$typenodeそのものがコピーされてしまっているように見える。
本当は違っててその後変更が発生しそうになった時にコピーされるんだろうけど。

なのでこうしてみた。=&で参照渡し的な何かになるはずと聞いて。
        $tree[] =& $typenode;
実行してみた結果。
(╬ಠ益ಠ)

今度は、二週目の
        // 親のノードを新規作成し、$typenodeを差し替え
        $typenode = array("type" => $r["type"],
                          "typeName" => $r["typeName"],
                          "values" => array()); // 親のノードは子を格納する配列を持つ
        // 作った親のノードをルート配列に追加
        $tree[] =& $typenode;
の上の方のところで「新しいノードを作って$typenodeを差し替えた」だけのつもりが、
既に一周目でセットした$tree[0]の要素までも差し替わってしまった。

参照渡し…ってそこまで参照なの!?
これ、そもそもマインドモデルが上の図と違ってて、模式図で表せない。

最終的にこうしたら何とかなった。
$tree = array();
$prevType = null;
foreach($rows as $r){
    if($prevType !== $r["type"]){
        $prevType = $r["type"];
        unset($typenode);
        $typenode = array("type" => $r["type"],
                          "typeName" => $r["typeName"],
                          "values" => array());
        $tree[] =& $typenode;
    }
    $valuenode = array("value1" => $r["value1"],
                        "value2" => $r["value2"]);
    $typenode["values"][] = $valuenode;
}
差し替える前にunsetをする事で、
$typenodeというシンボルと実体との紐付けが解放されるので、
あらためて作ったarrayをセットしても既存のデータ構造に影響を与えない。

なんだこの仕様。

array関数で新たに配列作って変数に代入する、って
「mallocしてそのポインタを返す」的な感覚かと思ってたら、
「返す先のポインタがあったら指している領域に上書きする」みたいな
日本語すら怪しくなるような動きもするとは。
理解に苦しむ。

2012年9月18日火曜日

mroongaのデータを壊してテーブルも消せなくなったのを何とかした風

先週末ハマった件のメモ書き。
これで合っているかどうか保証も何も全く無いので
参考にする場合は自己責任で。

mroongaをストレージモードで使った開発中、
安直な設計でテーブル作って膨大なデータのinsertクエリを実行してしまった。

全然戻ってこないのでクエリのキャンセルをしたのだがそれでも戻ってこず。
mysqldのrestartも実行したが戻ってこず。
仕方なくkill -9で抹殺。

その後再起動し、この設計じゃダメだなと思いdrop tableしようとしたら
drop tableも戻ってこない。

プロセスリストを見てみると
select count(*) into @discard from `information_schema`.`PARTITIONS`
というSQLをdebian-sys-maintが実行していて全然戻ってきていない。
こいつがロックしている線が濃厚。


当該のSQLやdebian-sys-maintでググってみると
「debian系のおせっかいだからスクリプトいじって黙らせちゃえよ」
みたいな記事が見つかるが、
それもなんだかイカン気がする。

やけくそでmysql止めて
/var/lib/mysql/【スキーマ名】.mrn.*
のファイルを全消しして
再起動してdrop tableしてみる。
瞬時にdisconnected from server的なメッセージが出て、
mysqldのプロセスIDが変わってる。
悪化/(^o^)\

repair tableとかあがいてみる。
「このエンジンはリペアとかないよ」的なメッセージが出る。

そこでふと、以前mysqlのバージョンだけ上がってmroongaプラグインが読み込めなくなって
mysqlの起動が出来なくなってヒイコラした時、
その後drop tableは普通にできたよなーという記憶が。

ということでUNINSTALL PLUGINして、dropしてみる。
…成功!


あらためてINSTALL PLUGINして、テーブル作ってまともな量insert。
無事動作している模様。


多分こうなった場合、
  1. UNINSTALL PLUGIN
  2. drop table
  3. mrnファイル削除
  4. INSTALL PLUGIN
  5. create table
とすればいいのかなという気がしているけど
実はまずいのかもしれない。不明。


やってみる場合自己責任で。

2012年8月6日月曜日

ubuntuでusermod -Gでやらかしてsudo出来なくなった場合

開発PCの/var/lib/mysqlの中のファイルをいちいちsudoせずに参照したくなった。
ubuntuのユーザ名はadmin。
mysqlグループでの権限追加して、自分もmysqlグループに入ればいいだろ。
$ sudo chmod g+rx /var/lib/mysql
$ sudo usermod -G mysql admin
ログアウトしてログイン。
$ cd /var/lib/mysql
よし入れた。
おっと、見たいのはこの中のデータベースごとのディレクトリ内。
$ cd db1
bash: cd: db1: Permission denied
おおっと、データベースごとのディレクトリにも権限を足さないといけない。
$ sudo chmod g+rx db1
[sudo] password for admin:admin is not in the sudoers file.  This incident will be reported.
?…!! やっちまった!

usermod -G は所属グループを上書きするので,
元々所属していたsudoグループから引っ剥がされてしまった!
もとい、引っ剥がしてしまった orz


まあまあ、ありがちありがち。
一度深呼吸して、PCをブチ切り再起動。

grubで[recovery mode]を選択して起動し、rootシェルを選択。
# mount -o rw,remount /
# usermod -G adm,cdrom,sudo,dip,plugdev,mysql admin
# reboot
再起動で復活。

後の方のusermod -G以降は、ほぼ同じ設定で立てていたVMから拾ってきたもの。
そういうのが無い方でやらかしてすぐだった方は
/etc/group- に直前の設定が残っているので
rootシェルで入ったあとすぐ参照して控えましょう。

複数回usermodやっちゃった方は南無。
でもとりあえずadmとsudoぐらい入っておけば何とかなるでしょう。

2012年6月18日月曜日

Wineで3Dなソフトがうまく動かないとき

普段使っているのはIntelのしょぼいグラフィックチップのPCなのだが
Windows用の3DなソフトをWineで動かしてみようとしたら
Unhandled page fault on read access
なんていうのが出た。

色々調べてみたところ、テクスチャの圧縮展開ライブラリ関係に問題があるらしい。
まあそりゃそもそもmesaドライバだしな。

libtxc-dxtn-s2tc というのを入れると改善するかも、とのこと。
sudo apt-get install libtxc-dxtn-s2tc-bin libtxc-dxtn-s2tc0
おお。魔法のようだ。

2012年6月17日日曜日

Win7でユーザのログインIDなどを変更する


Windows 7 は基本的にユーザの表示名が使われるようになっているので
表示名だけ変えれば普段は気にならないが、
リモートデスクトップなどで繋ぐ時に混乱の元になる。

リモートデスクトップで繋ぐ際に指定するユーザ名称を変更するには、
  管理ツール→コンピューターの管理
で


  コンピューターの管理→システムツール→ローカル ユーザーとグループ→ユーザー
を開き


  「名前」をクリックして変更。
基本的にはこれでOK。

ただしこれだとユーザフォルダの名前が古いままで若干気持ちが悪い。
ユーザフォルダの名称を揃えるには、別な管理者アカウントでログインし、
C:\Users\の下のフォルダ名称を変更した上で、
レジストリエディタで
  HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\ProfileList

の下の
  ProfileImagePath
の値をそれに揃えてやる事で、
次に該当ユーザでログインした際に辻褄が合う。 
この過程で、フォルダ名を変えようとして管理者アカウントでログインする時に
「ユーザの切り替え」をしてしまうと、元のユーザフォルダは使用中になっていて変更できないので
PCごと再起動する方がよい。

また、PCを再起動して別な管理者アカウントでログインしただけなのに
既に使用中になっていることもある。
自分の場合、Windows Media Playerのサービスがアクセスしていた。
 管理ツール→サービス
を開き、
 Windows Media Player Network Sharing Service
を「停止」させたところ、フォルダ名の変更ができた。

フォルダ操作をしようとしたときに「使用中です」となってしまう場合、
Unlocker を使うと何のプログラムがロックしているのか分かって便利。

2012年6月14日木曜日

ubuntuにapache-google-apps-ssoをインストール

https://github.com/epotocko/apache-google-apps-sso
ApacheのレイヤーでGoogle Apps認証かけちゃうというもの。
mod_auth_memcookieというモジュールとphpを使ってる。

まずapt関係で必要なものをゲット。
apt-get install memcached php5-memcache
apt-get install apache2-prefork-dev libmemcache-dev
次に、mod_auth_memcookieをビルド。
http://sourceforge.net/projects/authmemcookie/files/
からソースをDLし展開。
tar zf mod_authmemcookie_v1.0.2.tar.gz
cd mod_authmemcookie_v1.0.2
ubuntuの場合apxs2のインストールパスが違うので、Makefileを修正。
MY_APXS=/usr/sbin/apxs2
        ↓
MY_APXS=/usr/bin/apxs2
ビルド&インストール。
make
sudo make install
apacheのロードスクリプトを書く。
/etc/apache2/mods-available/auth_memcookie.load
#一行で書く
LoadModule mod_auth_memcookie_module /usr/lib/apache2/modules/mod_auth_memcookie.so
有効化。
sudo a2enmod auth_memcookie

引き続き、phpなどの設定。

/var/www/auth ディレクトリを認証モジュール用に使い、
その他サイト全体を認証の対象とすることにする。

https://github.com/epotocko/apache-google-apps-sso
の中のsrc/public直下のphpファイルと、src/GAppsディレクトリ自体を
/var/www/auth/下にコピー。
git clone https://github.com/epotocko/apache-google-apps-sso.git
cd apache-google-apps-sso/src
sudo mkdir /var/www/auth
sudo cp -rf GApps /var/www/auth/
sudo cp -rf public/* /var/www/auth/.
次にphp-openid。
12.04ならばaptで入れたものでもOKだった。
apt-get install php-openid
10.04だとバージョンが古くてうまく動かなかったので、公式から落としてきて
その中のAuthディレクトリを/var/www/auth/以下にコピーする。
git clone https://github.com/openid/php-openid.git
cp -rf php-openid/Auth /var/www/auth/
ここから下は https://github.com/epotocko/apache-google-apps-sso の
公式の情報と基本的に同じ。

/var/www/auth/settings.phpファイルを更新。
コメントアウトされていない二行を編集。
GApps_Session::$BASE_URL = 'http://【ホスト名】/auth/'; //動かすホスト名
GApps_Session::$DOMAIN_NAME = '【Google Appsドメイン名】';
apacheの設定を書き足す。
default編集するなり、別ファイル作ってVirtualHostに入れるなり。
<IfModule mod_auth_memcookie.c>
    <Location />
        Auth_memCookie_CookieName GOOG_SESSIONID
        Auth_memCookie_Memcached_AddrPort 127.0.0.1:11211
        ErrorDocument 401 "/auth/login.php"
        Auth_memCookie_Authoritative on
        AuthType Cookie
        AuthName "GApps Login"
    </Location>
</IfModule>

<LocationMatch "^/(?!auth)">
    Require valid-user
</LocationMatch>
apache再起動。
sudo /etc/init.d/apache restart

2012年6月10日日曜日

gitで名前やメールアドレスを置換する

毎回調べてしまうので自分用メモ。
git filter-branch --commit-filter '
if [ "$GIT_COMMITTER_NAME" = "【修正前の名前】" ]; then
    GIT_AUTHOR_NAME="【修正後のAUTHORの名前】"
    GIT_AUTHOR_EMAIL="【修正後のAUTHORのメールアドレス】"
    GIT_COMMITTER_NAME="【修正後のCOMMITTERの名前】"
    GIT_COMMITTER_EMAIL="【修正後のCOMMITTERのメールアドレス】"
fi
git commit-tree "$@"
' HEAD