symfonyのproject:deployがやたらと遅い

最近symfony1.4.9を使っていてproject:deployコマンドがやたら遅くて、rsyncってこんな遅かったかな?と思ったらsfFileSystem::execute()が、出力バッファを読み込む度に0.1秒sleepしている。

lib/task/sfFilesystem.class.php
<?php

  public function execute($cmd, $stdoutCallback = null, $stderrCallback = null)
  {
    $this->logSection('exec ', $cmd);

    $descriptorspec = array(
      1 => array('pipe', 'w'), // stdout
      2 => array('pipe', 'w'), // stderr
    );

    $process = proc_open($cmd, $descriptorspec, $pipes);
    if (!is_resource($process))
    {
      throw new RuntimeException('Unable to execute the command.');
    }

    stream_set_blocking($pipes[1], false);
    stream_set_blocking($pipes[2], false);

    $output = '';
    $err = '';
    while (!feof($pipes[1]) || !feof($pipes[2]))
    {
      foreach ($pipes as $key => $pipe)
      {
        if (!$line = fread($pipe, 128))
        {
          continue;
        }

        if (1 == $key)
        {
          // stdout
          $output .= $line;
          if ($stdoutCallback)
          {
            call_user_func($stdoutCallback, $line);
          }
        }
        else
        {
          // stderr
          $err .= $line;
          if ($stderrCallback)
          {
            call_user_func($stderrCallback, $line);
          }
        }
      }

      usleep(100000);
    }

    fclose($pipes[1]);
    fclose($pipes[2]);

    if (($return = proc_close($process)) > 0)
    {
      throw new RuntimeException('Problem executing command.', $return);
    }

    return array($output, $err);
  }


元々は、sleep(0.1)となっていたが、実際sleepは効いていなかったバグがあり、
ミリ秒ならusleepでしょ、という修正が入ったときに遅くなった模様。


deploy遅すぎるよ!とreopenしてpatchが添付されているが、特に対応されていない。

取り敢えず添付のpatchを適用すると速度改善されました。直前のfreadで読込めた場合はusleepしなくなります。deployのような出力が多いものだと結構変わりますね。

array_chunkって便利ですよね

http://jp.php.net/array_chunk
例: 要素数Nの配列を、テーブルで1行2列ずつ表示したいようなときとか。

コード

<?php
$fruits = array(
    'apple', 'orange', 'strawberry', 'banana', 'cherry',
);
?>
<table border="1">
<?php foreach (array_chunk($fruits, 2) as $chunk): ?>
  <tr>
<?php   foreach (array_pad($chunk, 2, null) as $fruit): // 2より少ない場合はnullで埋める ?>
    <td><?php if ($fruit === null): ?>&nbsp;<?php else: ?><?php echo $fruit ?><?php endif ?></td>
<?php   endforeach ?>
  </tr>
<?php endforeach ?>
</table>

出力

<table border="1">
  <tr>
    <td>apple</td>
    <td>orange</td>
  </tr>
  <tr>
    <td>strawberry</td>
    <td>banana</td>
  </tr>
  <tr>
    <td>cherry</td>
    <td>&nbsp;</td>
  </tr>
</table>

と、いった感じでテンプレート等でよく使ってます。(HTMLのエスケープは省略してます)

Apache VirtualHostの効率的な管理方法

ってどうするのが良いのでしょうか。今回の条件は下記として...

  • 開発環境。
  • 複数サービスのVirtualHostを立てたい。
  • 開発環境なのでSSLはとりあえずオレオレ証明書を使いまわす。

とりあえず最近は、開発環境向けにこんな感じで設定している。
設定が変わるたびにいろいろな箇所を修正するのが大変なので、可能な限り共通部分は別ファイルに切りだしてIncludeするように。

conf/
  httpd.conf
conf.d/
  vhost/
    example.com.conf
    _example.com.conf
    _ssl.conf
  • サーバ全体に対する設定はhttpd.confとconf.d/*.confに記述。
  • httpd.confの末尾で、VirtualHostの設定ファイルをInclude。
  • conf.d/vhost 以下に example.com.conf のようなドメイン別ファイルを用意。
  • 80番,443番で共通の設定は _example.com.confのようにドメイン内の共通ファイルを作成し、ドメイン別ファイルからIncludeする。
  • SSLでの共通設定はドメインを超えて _ssl.conf に記述し、各ドメイン別ファイルからIncludeする。

いろいろ端折ってるけどファイルの中身はこんな感じ。

conf/httpd.conf

# ...上部でサーバ全体向けの設定
NameVirtualHost *:80
NameVirtualHost *:443
Include conf.d/vhost/example.com

conf.d/vhost/example.com.conf

<VirtualHost *:80>
    Include conf.d/vhost/_example.com.conf
</VirtualHost>
<VirtualHost *:443>
    Include conf.d/vhost/_example.com.conf
    Include conf.d/vhost/_ssl.conf
</VirtualHost>

conf.d/vhost/_example.com.conf

ServerName example.com
ServerAlias www.example.com

DirectoryIndex index.php
DocumentRoot "/path/to/example.com/www"
<Directory "/path/to/example.com/www">
    AllowOverride All
    Allow from All
</Directory>

CustomLog logs/example.com/access_log combined
ErrorLog logs/example.com/error_log

conf.d/vhost/_ssl.conf

SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
SSLCertificateFile /etc/pki/tls/certs/localhost.crt
SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
SetEnvIf User-Agent ".*MSIE.*" \
         nokeepalive ssl-unclean-shutdown \
         downgrade-1.0 force-response-1.0    

で、サービスのVirtualHostを追加するごとに、example.com.conf, _example.com.confが増えていく感じ。

mod_rewriteでリダイレクトあれこれ

ドメインの正規化

wwwあり・なしをどちらか片方に寄せたい場合。
今回はwww.example.comなど、example.com以外でアクセスされたらexample.comにリダイレクトする設定。

<VirtualHost *:80>
    # (1)
    ServerName  example.com
    ServerAlias www.example.com

    RewriteEngine On
    # (2)
    RewriteCond %{HTTP_HOST} !^example\.com
    # (3)
    RewriteRule .* http://example.com$0 [QSA,NE,R=301,L]
</VirtualHost>

(1)で、どちらのホスト名でもVirtualHostが動くようにする。

(2)は正規表現の末尾に$を付けないようにする。Hostリクエストヘッダは

Host = "Host" ":" host [ ":" port ] ; Section 3.2.2

http://www.studyinghttp.net/header#Host

と定義されているように、host名の後に「:ポート番号」が付与される形式がある。

例えば http://www.example.com:80/ にアクセスした場合に

Host: www.example.com:80

と送ってくるクライアントに対応できないので、前方一致で比較する。

(3)で

にアクセスした場合

にリダイレクトする。


リダイレクト時の各オプションは、

QSA

QueryString append。QSAを付与しないとQueryStringを上書いてします。詳しくは後述。

NE

No escape。URLエンコードを抑制する。NEを付与しないと、

にアクセスした場合に「%」が2重にエンコードされ

にリダイレクトされてしまう。

R=301

Moved Permanently

L

ここでルールの適用を終了する。


QSAはこの場合不要だけど、定義変更時に忘れがちなので付けておく。

HTTPSを強制

HTTPでアクセスされたらHTTPSにリダイレクトする。

RewriteEngine On
# (1)
RewriteCond %{HTTPS} !=on
RewriteRule .* https://example.com$0 [QSA,NE,R=301,L]

(1) SSL接続時にmod_ssl環境変数HTTPSにonをセットするのでそれで判断する。

DoCoMo機種の場合、強制的にguid=ONを付与

強制するのはどうなの?って感じですが。

RewriteEngine On
# (1)
RewriteCond %{HTTP_USER_AGENT} ^DoCoMo [NC]
# (2)
RewriteCond %{QUERY_STRING} !^(.*&)?guid=ON(&.*)?$ [NC]
# (3)
RewriteCond %{HTTPS} !=on
# (4)
RewriteRule .* http://example.com$0?guid=ON [QSA,NE,R=302,L]

(1) UserAgentがDoCoMoから始まるもの かつ
(2) QueryStringにguid=ONが含まれていない場合 かつ
(3) HTTPS接続で無い場合
(4) guid=ONを付与してリダイレクトする

ただし、アクセスする度にguid=ON付きURLにリダイレクトしたり、POSTのリクエストがリダイレクトされても困るので、
別途、アプリケーション側で内部リンクに自動的にguid=ONを付与するような仕組みを設けること。

(4)でR=302な理由は、DoCoMo端末のみのリダイレクト(!= Moved Permanently)であるのと、
301リダイレクトだと「サイトが移動しました(301)」のメッセージがでるので302を返すように。

(4)で「QSA」がついていないと、

にアクセスした場合に、Query Stringが書き換えられ

になってしまう。「QSA」が付与されていると

となる。

参考にさせていただいたリンク

iTermかTerminal.appか

先日MacBookPro 13インチ 2.53GHzを購入してMacデビューしました。ちなみにまだLeopardです。

いろいろインストールしたり、dotファイルを整理したりと開発環境を整えているのですが、一番躓いたのがターミナルの選定でした。
Terminal.appとiTermを行ったり来たりしていて、自分の周りにはTerminal.app派が多かったのですが、最終的にiTermに落ち着きました。以下が理由です。

  • 256色表示できる。
  • キーマップ設定でCtrl-,やCtrl-.に独自コードを割り当てれる。

2つ目の理由の方が大きくて、元々、puttyでscreenの切り替えを下記のような感じでCtrl-カーソルキーで操作していたのですが、
.screenrc

# for putty
bindkey "^[OA" stuff "\015exit\012"  # ctrl-up
bindkey "^[OC" next                  # ctrl-right
bindkey "^[OD" prev                  # ctrl-left

途中からiceiv+puttyを知って移行し、プライベートキー定義でCtrl-,で^[OD、Ctrl-.で^[OCを送信するように設定して以来、Ctrl-, Ctrl-.でのscreen切り替えに慣れ親しんでいたので、使えないとかな〜り不便でした。
で、Terminal.appの設定ではできなさそうで、iTermでは設定できたのでiTermに落ち着きました。

iTermの設定は、「プロファイルの管理」->「キーボード設定」から

  • Ctrl-,
    • キー: hex code 0x2c
    • 修飾キー: Control
    • 動作: send escape sequence
    • 送信コード: OD
  • Ctrl-.
    • キー: hex code 0x2e
    • 修飾キー: Control
    • 動作: send escape sequence
    • 送信コード: OC

としてます。Ctrl-カーソルキーもputtyの送信するキーコードにあわせて設定。Ctrl-; Ctrl-:とかもEmacs用に独自のコードを設定してたけど、EmacsGUIに移行したからまあ良いかなと。

とりあえずiTermでこのまま進めてみよう。でも時間があったらrxvtも一度試してみようかな。。

追記 2009-09-11

なんか、iTermの通常のカーソルキー入力も奪っていて困ったり、ほかの設定も追加したりしたのでメモ。
.screenrc

bindkey "^[[5A" stuff "\015exit\012"  # up
bindkey "^[[5B" screen                # down
bindkey "^[[5C" next                  # right
bindkey "^[[5D" prev                  # left
  • Ctrl-,
    • キー: hex code 0x2c
    • 修飾キー: Control
    • 動作: send escape sequence
    • 送信コード: [5D
  • Ctrl-.
    • キー: hex code 0x2e
    • 修飾キー: Control
    • 動作: send escape sequence
    • 送信コード: [5C
  • Ctrl-↑
    • キー: cursor up
    • 修飾キー: Control
    • 動作: send escape sequence
    • 送信コード: [5A
  • Ctrl-↓
    • キー: cursor down
    • 修飾キー: Control
    • 動作: send escape sequence
    • 送信コード: [5B
    • 割り込み優先度高
  • Ctrl-→
    • キー: cursor right
    • 修飾キー: Control
    • 動作: send escape sequence
    • 送信コード: [5C
    • 割り込み優先度高
  • Ctrl-←
    • キー: cursor left
    • 修飾キー: Control
    • 動作: send escape sequence
    • 送信コード: [5D
    • 割り込み優先度高

ターミナル上のEmacsにペーストしたい

普段、PuTTY上でEmacsを使っているのですが、そのEmacsにコード等をペーストしたいときに、
S-Insert等でそのままペーストしても、キー入力として送られるので、自分の環境だといろいろ問題が起きてました。

  • global-mapのC-mに'newline-and-indentを設定してるので、コードを貼り付けたときに階段状に余計なインデントがされてしまう。
  • key-chordで設定したキーの文字列が含まれるとkey-chordが起動してしまう。("jk"とか)
  • undo一発で元の状態に戻せるようにしたい。

いろいろ調べてみたのですが、良い解決方法が見つけられなかったので
今までは我慢するか、vimを起動して:set pasteをしてたのですが、
やっぱりEmacsにペーストしたい!!ってことで、ちまちま調べながらelispを書いてみました。


undoの部分はundo-groupを使うようにしました。load-path上に置いておきます。
http://www.mahalito.net/~harley/elisp/undo-group.el

term-paste-mode.el

;; http://www.mahalito.net/~harley/elisp/undo-group.el
(require 'undo-group)

(defvar term-paste-mode-map
  (let ((map (make-keymap))
        (i ? ))
    (while (< i ?~) ;; 取りあえずスペース 〜 ~までself-insert-commandをセットして上書く
      (define-key map (char-to-string i) 'self-insert-command)
      (setq i (1+ i)))
    (define-key map "\C-m" 'newline)
    map))

(defcustom term-paste-mode-on-hook nil
  "Hook to run when term-paste-mode is activated."
  :group 'term-paste
  :type 'hook)

(defcustom term-paste-mode-off-hook nil
  "Hook to run when term-paste-mode is deactivated."
  :group 'term-paste
  :type 'hook)

(define-minor-mode term-paste-mode
  "Minor mode for pasting from any terminal applications."
  :lighter " Paste"
  :group 'term-paste
  (cond (term-paste-mode
         ;; minor-mode-mapの優先順位を上げる
         (setq minor-mode-map-alist
               (cons (cons 'term-paste-mode term-paste-mode-map)
                     (delete (assq 'term-paste-mode minor-mode-map-alist) minor-mode-map-alist)))
         (undo-group-boundary)
         (run-hooks 'term-paste-mode-on-hook))
        (t
         (run-hooks 'term-paste-mode-off-hook)
         )))

(provide 'term-paste-mode)

.emacs

(require 'term-paste-mode)

(add-hook 'term-paste-mode-on-hook
          (lambda ()
            (key-chord-mode 0)
            ))
(add-hook 'term-paste-mode-off-hook
          (lambda ()
            (key-chord-mode 1)
            ))
(defalias 'p 'term-paste-mode)

M-x pでterm-paste-modeをtoggleさせます。

  1. M-x p でterm-paste-modeをonに
  2. S-Insertで貼り付け
  3. M-x p でterm-paste-modeをoffに

元に戻す場合は
M-x undo-group

ちゃんと動かなかったらすみません。もっと良い方法あるかなあ。

追記 2009-10-01

githubにあげました。http://github.com/tetsujin/term-paste-mode