[Smarty] セレクトボックス生成関数(html_options)を使いやすくする
Smartyにはデフォルトで「html_options」という関数が存在します。


[PHP側]


$smarty->assign( 'options', array(1,2,3) );

[テンプレート側]

{html_options name=test options=$options}


これで選択肢が「1,2,3」というセレクトボックスを出力できます。
しかし、決まりきった選択肢に対して、いちいちPHP側でコードを書くのは面倒です。


しかもこのhtml_options、id属性を指定できません。


そこで、その辺を改善する関数(zb_html_select_options)を作ってみました。


[テンプレート側]


{zb_html_select_options id=test123 name=test options=1,2,3}

とするだけで、選択肢が「1,2,3」というセレクトボックスを出力できます。 id属性も出力できます。


また年月日など、数値を選択するセレクトボックスで選択肢を範囲指定できるようにしました。


[テンプレート側]


{zb_html_select_options name=date_d id=selD options_from=1 options_to=31}

これで「1〜31」までの日を選択できるリストボックスを生成できます。
options_stepを指定すれば増分も指定できます。


更にevalを使って今日を選択できます。


[テンプレート側]


{zb_html_select_options name=date_d id=selD options_from=1 options_to=31 selected=eval:date('j')}

パラメータの値が「eval:」で始まる文字列であった場合、それ以降の文字列をPHPの式としてevalした結果を設定します。 「selected=eval:date('j')」は今日を選択することになります。


「eval:」の記述は、options、options_from、options_to、options_step、selectedパラメータで指定可能です。


■書式


{zb_html_select_options パラメータ=値}


   パラメータ    値
----------------------------------------------------------
id         selectタグのid属性を指定する。
name       selectタグのname属性を指定する。
class       selectタグのclass属性を指定する。
style       selectタグのstyle属性を指定する。
options      OPTIONタグの配列。配列のkeyがvalueに、配列のvalue が表示名になります。
options_from    数値を選択するセレクトボックスの場合に、下限値を指定する。optionsが指定された場合は無視される。
options_to     数値を選択するセレクトボックスの場合に、上限値を指定する。optionsが指定された場合は無視される。
options_step    数値を選択するセレクトボックスの場合に、選択肢の間隔を指定する。optionsが指定された場合は無視される。
selected      初期状態で選択される値を指定する。


■サンプル
年:現在の年−2〜+2年の範囲 初期状態で現在の年を選択
月:1〜12の範囲 初期状態で現在の月を選択
日:1〜31の範囲 初期状態で現在の日を選択


({zb_html_select_options name=reserve_date_y id=selY options_from=eval:date('Y')-2 options_to=eval:date('Y')+2 selected=eval:date('Y')})年
({zb_html_select_options name=reserve_date_m id=selM options=1,2,3,4,5,6,7,8,9,10,11,12 selected=eval:date('n')})月
({zb_html_select_options name=reserve_date_d id=selD options_from=1 options_to=31 selected=eval:date('j')})日


■ダウンロード
zb_html_select_options.txt


ダウンロード後、Smartyのpluginsフォルダに「function.zb_html_select_options.php」という名前で保存してください。


| comments(1) | trackbacks(0) | by stk2k
配列+プロパティ両方のアクセスができるクラスを作ってみました
オブジェクトをSmartyにバインドしたとき、


{$hoge->bar}


と書くのが面倒に感じたり、Smartyに怒られてから「あれ、これオブジェクトだったっけ、それとも配列だったっけ?」とソースコードを読み返したりしたことはありませんか?
そんなものぐさなPHPerの方のために、配列のようにも、オブジェクトのようにもアクセスできるクラスを作ってみました。


<?php

class DualObject implements Iterator, ArrayAccess
{
  private $__data;
  private $__field_list;

  /*
   *  コンストラクタ
   */
  public function __construct()
  {
    $this->__data   = array();

    // __で始まるフィールドは除外
    $this->__field_list = array();
    $vars = get_object_vars($this);
    foreach( $vars as $key => $value ){
      if ( strpos($key,'__')===FALSE ){
        $this->__field_list[] = $key;
      }
    }
  }

  /*
   *  ArrayAccessインタフェース:offsetGetの実装
   */
  public function offsetGet($offset)
  {
    if ( $offset instanceof Object ){
      $offset = $offset->s();
    }
    if ( property_exists($this, $offset) ) {
      return $this->$offset;
    }
    return isset($this->__data[$offset]) ? $this->__data[$offset] : NULL;
  }

  /*
   *  ArrayAccessインタフェース:offsetSetの実装
   */
  public function offsetSet($offset, $value)
  {
    if ( $offset instanceof Object ){
      $offset = $offset->s();
    }
    if ( property_exists( $this, $offset ) ){
      $this->$offset = $value;
    }
    else {
      $this->__data[$offset] = $value;
      if ( FALSE === array_search( $offset, $this->__field_list ) ){
        $this->__field_list[] = $offset;
      }
    }
  }

  /*
   *  ArrayAccessインタフェース:offsetExistsの実装
   */
  public function offsetExists($offset)
  {
    if ( $offset instanceof String ){
      $offset = $offset->s();
    }
    return FALSE !== array_search( $offset, $this->__field_list );
  }

  /*
   *  ArrayAccessインタフェース:offsetUnsetの実装
   */
  public function offsetUnset($offset)
  {
    if ( $offset instanceof Object ){
      $offset = $offset->s();
    }
    if ( property_exists( $this, $offset ) ){
      unset($this->$offset);
    }
    if ( isset($this->__data[$offset]) ){
      unset($this->__data[$offset]);
    }
    $pos = array_search( $offset, $this->__field_list );
    if ( FALSE !== $pos ){
      unset($this->__field_list[$pos]);
    }
  }

  /*
   *  Iteratorインタフェース:rewidの実装
   */
  public function rewind()
  {
    reset($this->__field_list);
  }

  /*
   *  Iteratorインタフェース:currentの実装
   */
  public function current()
  {
    $offset = current($this->__field_list);
    return $this->offsetGet($offset);
  }

  /*
   *  Iteratorインタフェース:keyの実装
   */
  public function key()
  {
    return current($this->__field_list);
  }

  /*
   *  Iteratorインタフェース:nextの実装
   */
  public function next()
  {
    return next($this->__field_list);
  }

  /*
   *  Iteratorインタフェース:validの実装
   */
  public function valid()
  {
    $offset = current($this->__field_list);
    return $this->offsetExists($offset);
  }

  /*
   *  プロパティアクセス(取得)
   */
  public function __get( $name )
  {
    return $this->offsetGet($name);
  }

  /*
   *  プロパティアクセス(設定)
   */
  public function __set( $name, $value )
  {
    $this->offsetSet($name, $value);
  }


  /*
   *  dump
   */
  public function dump()
  {
    print "--------------[ dump start ]--------------" . nl2br(PHP_EOL);
    foreach( $this as $key => $value ){
      print "[$key]$value" . nl2br(PHP_EOL);
    }
    print "--------------[ dump end ]--------------" . nl2br(PHP_EOL);
  }
}


使い方はこんな感じ。


<?php

require_once 'dual_object.php';

class MyObject extends DualObject
{
  public $foo;
}

$o = new MyObject();

$o->dump();

// 通常のpublicフィールドアクセス
$o->foo = 'Boo!';

// 配列でもアクセスできる
$o['foo'] = 'Bazz!';

// 定義していなくてもアクセスできる
$o['bar'] = 'Woot!';
$o->dump();
$o->bar = 'Mzzzz';
$o->dump();

// 読み取りもできる
echo 'o:foo=' . $o['foo'] . nl2br(PHP_EOL);
echo 'o:foo=' . $o->foo . nl2br(PHP_EOL);
echo 'o:bar=' . $o['bar'] . nl2br(PHP_EOL);
echo 'o:bar=' . $o->bar . nl2br(PHP_EOL);

$o->dump();


出力結果。

--------------[ dump start ]--------------
[foo]
--------------[ dump end ]--------------
--------------[ dump start ]--------------
[foo]Bazz!
[bar]Woot!
--------------[ dump end ]--------------
--------------[ dump start ]--------------
[foo]Bazz!
[bar]Mzzzz
--------------[ dump end ]--------------
o:foo=Bazz!
o:foo=Bazz!
o:bar=Mzzzz
o:bar=Mzzzz
--------------[ dump start ]--------------
[foo]Bazz!
[bar]Mzzzz
--------------[ dump end ]--------------


DualObjectを継承して使うとPHP側でプロパティアクセスし、Smarty側では配列としてアクセスするといった使い方ができます。
ちなみにIteratorも実装しているので、Dual#dump()を見ても分かるように、


  $obj = new MyObject();
  $obj->foo = 'bar';
  ...
  foreach( $obj as $key => $value ){
    ...
  }


のように記述してオブジェクトのプロパティと値を列挙することができます。

| comments(0) | trackbacks(0) | by stk2k
さくらでWAFをONにするとPHPSESSIDがENC_PHPSESSIDになってセッション復元できない件
タイトル長い(笑)

OpenPNEである日突然招待メール送信時の画像認証(キャプチャ画像による認証)ができなくなってしまい、ハマってしまったので、メモ。

エラー内容は、「確認キーワードが誤っています」。

登録時のエラー箇所で_SESSIONの中を見てみると、確かに空。
っていうか、何も入ってない。

なんだこりゃ、ってことで、キャプチャ画像を生成している部分を確認。
public_html/cap.phpからkcapctha.phpを呼び出している。こいつか?
と思うが、特にセッションがらみのロジックは無い。
そこで、Kcaptchaのロジックを外し、session_start()したあとのヘッダ情報を確認してみるが、PHPSESSIDが正しく吐かれている。
がしかし、ブラウザが受け取るクッキーは

Cookie: ENC_PHPSESSID=%2becFX2FNcQbB61OuymsUNxFW6NVBmuTaOW/sfxrJuahEguweCV3eRLQoQhDiD2L5

みたいになっている。
なんでやーと1日悩みました・・・が、サーバのコントロールパネルを見ていてハタと気づきました。

-------------------------------------------------------
本機能を有効にすることにより、ソフトウェアの動作やインストールに影響が出る場合があります。 ご利用の前に必ずこちらをご一読ください。
-------------------------------------------------------

これか!(笑

てことで、サーバのWAF設定を外してみると、ビンゴ、見事にセッションを復元できました。

多分、

ブラウザ ←→ WAF ←→ Webサーバ

って構成になってて、WAFでENC_PHPSESSIDとPHPSESSIDの変換をしているんだろうなと。
んー、しかしこんな簡単な簡単なロジックで復元できないとなると、WAFってあんまり意味ないようなw
(パソコンで言うと、ウィルスも駆除する代わりにExcelが起動できなくなるウィルス対策ソフトみたいなw)

| comments(0) | trackbacks(0) | by stk2k
CharcoalPHP 1.0 Coming soon
久しぶりの投稿になります。 ここ最近PHP案件ばかりやっていたのですが、 ここ最近よく感じるのは、PHPってなんだかんだでやっぱり生産性いいよなあとか、ふざけんなPHPつかえねーとかいいんだか悪いんだかなPHPがやっぱり好きだなってことですね。PHPのすごさはCやJavaにありがちなFatalError系のヨクワカランエラーがほとんど出ないことと、いろんなライブラリを用意しなくても最初からある程度のことができるのにあまり重さを感じないことだと思います。

そんなPHPのライトな思想にJavaのインタフェース設計の思想を持ち込もうとして作ったのがCharcoalPHPなわけですが、最近になってインタフェースはいいけれども、設定ファイルが増えるのはPHPらしくないなと。 かと言ってすべての設定をコードで書くのも何かが違う・・・なにがいいんだろうとしばらく考えていました。 その結果がもう少しで出てきそうなので、この際メジャーバージョンでもリリースしてしまうかと思っています。ちょっと危険かなー?(苦笑

今考えているのは、

・ディレクトリ構成をプログラム動作に反映させる(配置するだけで使えるようになるとか)
・開発モードを実装し、クラスが見つからない場合、配置先を自動でパースしてautoloadコードを自動生成する(リロードするとエラーが出なくなるイメージです)
・プログラムの分割単位として、ディレクトリ単位といった小さなファイルセットで動作するモジュールのようなものの導入
・自前でクラス定義をするケースを極力なくすための、フレームワーク提供イベントクラス、タスククラスを充実させる

といったことで、地味ですが、設定ファイルを無くしたり小さな単位で再利用可能とすることでかなり生産性を向上できるのではないかと思っています。

| comments(0) | trackbacks(0) | by stk2k
CharcoalPHP 0.11.0リリース
本日CharcoalPHP 0.11.0をリリースしました。


・複数のリクエストに渡ってデータを保持できるタスクであるステートフルタスクを実装しました。


ステートフルタスクの実装は簡単です。通常のタスクの実装に加えて、IStatefulインタフェースを実装するだけです。ステートフルタスクの保持するデータはフレームワークにより自動的に保存/復元されます。保存データはセッションハンドラ経由でファイル等に書き出されます。


CharcoalPHP 0.10.0は下記のページからダウンロードできます。


http://sourceforge.jp/projects/charcoalphp/files/


| comments(0) | trackbacks(0) | by stk2k
| 1/8PAGES | >>