noldor's diary

調べたことのメモとよしなしごと

wordpressでの本番環境・ステージ環境の取り扱いを考えてみる

wordpressを新規に立てましたが、ステージ環境で新規構築→本番環境に移行、をしようとしてあまりうまくいかなかったので記録です。

立てていた手順は「ステージで作ったものをDBダンプして本番環境でリストア」だったんですが、wordpresscssであるとかの各種パスをURLで記述するため(記事も含め!)、ステージで記事投稿するとステージのURLが記事に残ってつまらないことに。
記事数が大したことがなかったので手作業で1つ1つ訂正することにしましたが、管理画面ログインフォームすらURLで記述されるためログインすらできず。

結局DBダンプファイルを書き換えるという強引過ぎる方法で移行しました。

というわけで次からやるならこの手順かなーというのが以下です。

◇新規構築時
・DB保存対象(記事):BASIC認証かけるなどの状態で本番環境で書いてDBダンプ→URL置換してステージにリストア
ソースコード(テーマ):ステージで構築して本番デプロイ

◇運用時
・DB保存対象(記事):下書き機能を使って書く。ステージで必要な場合は本番DBダンプ→URL置換してステージにリストア
ソースコード(テーマ):ステージで構築して本番デプロイ

運用時にステージ→本番のDB移動なんてしないので新規構築時もそうだろう、と書き出してみて初めて気づきましたよorz

コマンドラインでタブ入力

タブ区切りファイル(tsv)をsortコマンドにかけようとすると、タブってbashだと入力補完でショボーン。
そんなときはCTRL+Vを押すと入力補完されなくなり、普通にタブを入力できるんですね。

sort -t "	" -k 5 access_log

タブ区切りのアクセスログであるとかを見るときはいつもスクリプトを書いていたんですが、これでちょっと楽できそうです。

FTPでアカウント別にIP制限でつまずく

FTPでユーザごとにIP制限をしたいというお話。sftp使いなよと言いたいところですが、なんぞ、FFFTPから離れたくない様子なので設定しました。PAM使ってるしできるだろうと思っていたら、またまたつまずきました。

  • ユーザ別IP制限はpam_access.soで行う
  • ユーザ管理はバーチャルユーザ(pam_userdb.soとvsftpdのguest_enable=yes)で、OSのユーザとは切り離して運用

の組み合わせで撃沈。pam_access.soはOSのユーザで判定するため、pam_access.soで全ユーザ全アクセス許可の設定にしてもログイン失敗します。

しょうがないのでログインできないOSユーザを作成して回避しました。

# useradd -s /sbin/nologin ユーザID

きっともっといい方法があるはず。

CodeIgniter2: form_validationクラスを強引に継承したらバリデーションエラーが表示されなかった

form_validationを拡張するためにはMY_Form_validationを作るのが常套手段と思いますが、アプリケーションをまたいで使いたかったので別クラスにしてつくってみました。

class MY_Form_validation extends CI_Form_validation
{
}

class Oreore_form_validation extends MY_Form_validation
{
}

//コントローラで
$this->load->library('oreore_form_validation');

すると、validation_errors()とform_error()でバリデーションエラーが表示されなくなりました。

原因は、それぞれの関数が「form_validation」ライブラリの存在に依存していたため。チェックをしていたのは「oreore_form_validation」だったので、「form_validation」としてはエラーなしの扱いでした。

form_validationとして読み込ませることで解決。

$this->load->library('oreore_form_validation', '', 'form_validation');

そもそもCodeIgniterの想定しているであろうように継承の順序を変えてやれば何事もないのだけれど、これだと目的を果たせないので上記のような継承順のままとしました。下記であれば問題なく動きます。

class Oreore_form_validation extends CI_Form_validation
{
}

class MY_Form_validation extends Oreore_form_validation
{
}

//コントローラで
$this->load->library('form_validation');

viewのエスケープ方法を調べてみた(OutputEscaperとSmarty3)

追いかけてなかったので知らなかったけど、symfony2ではOutputEscaperは廃止していた。twigでやるということらしい。

Summary
-------
The output escaping component for PHP templates does not work very well
and I think it cannot be "fixed". So, I want to remove its support in
Symfony2 entirely. This means that we won't have automatic output
escaping if you use the PHP templating engine in Symfony2.
I think that makes sense because we have decided to use Twig as the
default templating system (and Twig supports a much more robust
implementation of automatic output escaping -- still not finished yet
though.)

http://groups.google.com/group/symfony-devs/browse_thread/thread/925d40f1cff7fe11?pli=1

捨てるには惜しかったのか、切り出して使えるようにしたものがgithubに残っている。

https://github.com/fabpot/output-escaper


Smartyは3からホワイトリスト式のエスケープが可能になっていた。variableフィルタというのがあった。

type にはフィルタの型を定義します。使える値は "pre"、"post"、"output" および "variable" です。

http://www.smarty.net/docs/ja/api.register.filter.tpl

ところがマニュアルにはプリフィルタ、ポストフィルタ、アウトプットフィルタのことしか書いてなくてvariableフィルタのことが見つからなかった。フォーラムを読んで何とかわかる程度。

http://www.smarty.net/forums/viewtopic.php?p=62862

普通に使うとvariableフィルタによって変換され、変換したくない場合はnofilterを使えばよい。

{$record.CONTACT_NAME nofilter}

現段階の最新版である3.1.7試してみたところ、フィルタの登録には$thisが使えなかった。不具合のようで、Smartyクラスを継承したmy_smartyクラスのメソッドescape_filterをコンストラクタで登録すると

$this->registerFilter("variable", array($this, 'escape_filter')); 

なぜかmy_smarty_escape_filter()を呼び出そうとしてfatalになった。

staticメソッドとして登録することで意図通りに動いた。

$this->registerFilter("variable", array(__CLASS__, 'escape_filter')); 

FAT32のつもりでファイル数上限エラー

ちょっとはまったのでメモ。

linuxからwindowsへ大量のデータを転送する必要があったので、HDDをFAT32でフォーマットして、全部コピー。

sudo mount -t vfat /dev/sdf5 /mnt/usbdisk
sudo mkdir /mnt/usbdisk/fuga
sudo cp -r /hoge/* /mnt/usbdisk/fuga

……のつもりだったけど、途中で容量エラーに。ファイル数が多すぎてディレクトリに入りきらなかったためでした。FAT32自体にはディレクトリあたりのファイル数上限はないけれど、vfatとしてマウントすると上限ができてしまうようです(2万ファイルちょっと)。

vfatでなくFAT32としてマウントする方法がわからなかったので、あきらめてntfsでフォーマットしました。マウントのためにntfs-3gをrpmforgeからyumでインストールします。

sudo yum --enablerepo=rpmforge install ntfs-3g
sudo mount -t ntfs /dev/sdf1 /mnt/usbdisk
sudo mkdir /mnt/usbdisk/fuga
sudo cp -r /hoge/* /mnt/usbdisk/fuga

第8回 ハッシュテーブル

ハッシュテーブルは O(1) を実現する、夢のような方法です。 O(1) の数式の中には n がありません。つまり、どんなにデータ量が増えたとしても速度がほとんど変わらないということです。それを実現するために、力まかせ探索や二分探索のように「違ったら次を探す」ではなく、データ構造に工夫をもたせます。

以下の話は最後にどんでん返しがあるので、途中で根本がひっくり返るような疑問が出てきてもとりあえず最後まで見てみてください。

まずハッシュテーブルとは、アルゴリズムの名前ではなくデータ構造の名前です。利用方法も「この名前に対応する電話番号がほしい」というように、キーに対する値が欲しいという場合に使います(この場合は名前がキー、電話番号が値)。最初からキーに対応する値の取り出しを想定しています。

キーにはどんなものが来るかわからないので、このままでは取り扱えません。そこで、キーを0〜99までに変換する関数を使いましょう。

function hash_func($key)
{
  return crc32($key) % 100;
}

crc32()は文字列から数字を作り出すハッシュ関数です。「ハッシュ」とは英語で「細切れにする」という意味ですが、文字列を細切れにして数字などに作り替えてしまうものです。md5()なら馴染みがあると思いますが、ここではint型で値を返してくれるcrc32()を使いました。
これでキーを0〜99に変換できるので、

$array[hash_func($名前)] = $電話番号;

で配列に格納することができます。しかしこのままだと、名前が違ってもhash_func($名前)の値が同じになってしまうことがあります。0〜99しかないので、101種類の名前を用意すれば必ずどれかがダブります。そこで、名前がダブっても問題ないように工夫します。

<?php
class HashTable
{
  private $array = array();

  private static function _hash($key)
  {
    return crc32($key) % 100;
  }

  private function &_find($key)
  {
    if (isset($this->array[self::_hash($key)])) {
      foreach ($this->array[self::_hash($key)] as &$item) {
        if ($item[0] === $key) {
          return $item;
        }
      }
    }
    return null;
  }

  function set($key, $value)
  {
    $set = $this->_find($key);
    if ($set === null) {
      $this->array[self::_hash($key)][] = array($key, $value);
    } else {
      $set[1] = $value;
    }
  }

  function get($key)
  {
    $set = $this->_find($key);
    if ($set === null) {
      return null;
    } else {
      return $set[1];
    }
  }
}

$hash_table = new HashTable;
$hash_table->set('時報', '117');
$hash_table->set('天気予報', '177');
echo '時報:' . $hash_table->get('時報') . "\n";
echo '天気予報:' . $hash_table->get('天気予報') . "\n";

実行結果は

時報:117
天気予報:177

hash_func($名前)の値がダブっても、配列として全部持ってしまうことで回避します。ダブり続けると配列が長くなり、効率が悪くなりますので0〜200に幅を増やしたり、hash_func()を工夫してダブりにくくしたりという工夫が入ります。


さて、気づかれたかもしれませんが、そもそもPHPの配列はキーに文字列を持つことができます。なのでhash_func()を挟まなくても

$array[$名前] = $電話番号;

でハッシュテーブルと同じことができます。というより、ハッシュテーブルそのものです。

PHP の配列は、実際には順番付けられたマップです。マップは型の一種で、 値をキーに関連付けます。 この型は、いくつかの手法で最適化されます。このため、 実際の配列またはリスト (ベクトル)、(あるマップの実装である) ハッシュテーブル、ディレクトリ、コレクション、スタック、 キュー等として使用することが可能です。

「配列のアルゴリズムはPHPと相性がいい」というのは、この性質のおかげで配列の取り回しが非常に楽だからです。工夫のしがいがあります。


次回は「8クイーン問題」を解きながら高速化の手法である「バックトラック法」について書きます。