.




うぇブログ2
最新エントリ

(1) 2 3 »

2007/10/26
カテゴリ : Ethnaメモ

執筆者: itoh (12:26 pm)
正確には自分だけじゃないんですが、Ethna_Loggerを拡張してIPでログ出力先を限定してしまいます。

やり方は、自前のログクラスを作成します。
<?php
class Tinycms_Logger extends Ethna_Logger
{
    // {{{ begin
    /**
     *  ログ出力を開始する
     *
     *  @access public
     */
    function begin()
    {
		$config = $this->ctl->getConfig();
		$log_display_ip = $config->get('log_display_ip');
		if ($log_display_ip){
			if (in_array($_SERVER['REMOTE_ADDR'], to_array($log_display_ip))){
				parent::begin();
				return null;
			}
		}
		$this->is_begin = false;
	}
    // }}}
}

で、コントローラに追加
<?php
...snip...
include_once('Tinycms_Logger.php');
...snip...
/**
 *  Tinycmsアプリケーションのコントローラ定義
 *
 *  @author     {$author}
 *  @access     public
 *  @package    Tinycms
 */
class Tinycms_Controller extends Ethna_Controller
...snip...
    /**
     *  @var    array   クラス定義
     */
    var $class = array(
...snip...
        'logger'        => 'Tinycms_Logger',

あとは、Configファイルに
<?php
/*
 * tinycms-ini.php
 *
 */
$config = array(
....snip....
	'log_display_ip'        => array(
					 '123.456.789.123').
....snip....

と書く。


なんか、ダサ目の実装なんですが、とりあえずこんな感じ。
123.456.789.123のIPからアクセスしてる人だけにログが出力される。

# 本当ならもっとログConfigを丁寧に処理しないとね。
itohさんのブログを読む | コメント (0) | トラックバック数 (0) | 閲覧数 (6645)
2007/09/15
カテゴリ : Ethnaメモ

執筆者: itoh (5:01 pm)
http://d.hatena.ne.jp/riaf/20070914/1189747693
引用:

AppObjectはJOINのことも微妙に考えられてて、ちょっと弄るだけでJOIN出来る訳だけども、どうもいまいちJOINが有効になる条件がわかりにくい。


Ethna_AppManager::getObjectPropList/getObjectListにて、JOINするかどうかを判定しているコードはここです。Ethna_AppObject::_getSQL_SearchProp
<?php
    function _getSQL_SearchProp($keys, $filter, $order, $offset, $count)
    {
....snip....
        // 検索用追加プロパティ
        if ($this->_isAdditionalField($filter)
            || $this->_isAdditionalField($order)) {
            $search_prop_def = $this->_SQLPlugin_SearchPropDef();

この_isAdditionalFIeldメソッドに渡されているのが、$filter/$orderなので、Ethna_AppManager::getObjectPropListの第2引数の$fieldに_SQLPlugin_SearchPropDef()で定義したものを追加してもJOINしてくれません。私も昔ハマッタ。

Ethna_AppManager::getObjectListが$fieldをとらないので、当然といえば当然なのですが。


だから、JOINさせたいときは、$filterか$orderの配列のキーに_SQLPlugin_SearchPropDef()で定義した「JOINされる側のPropDefのキー」を持っていないとダメです。

バッドノウハウかもしれないですが、私の場合は検索に影響を与えないように
<?php
$filter['join_name'] = new Ethna_AppSearchObject('', OBJECT_CONDITION_NE);

って追加してます。(この場合は、join_nameってのがJOINされるテーブルのフィールドにあるもので、
かつ、_SQLPlugin_SearchPropDefでも定義してるもの。かつ、絶対に空文字列がデータに入ってないもの。


あと、これはAppObjectのダサイところでJOINするときはテーブルに同じフィールド名があると
filter/orderに指定できません。なのでEthnaアプリを作る時は基本的にテーブルのフィールドがかぶらないようにフィールドプレフィクスを付けてます。


AppObejctというよりは、CriteriaであるEthna_AppSearchObjectも実は微妙な振る舞いがあって、それは、ひとつのフィールドに複数の条件をORでつけたい場合。また機会があれば書きますが。

でも、結構何とかなります。

あと、SQL関数を使った条件の場合は、AppSearchObjectは受け付けないのですが。



このブログってTB投げられるけど、受けられないんだよね。
itohさんのブログを読む | コメント (0) | トラックバック数 (0) | 閲覧数 (8376)
2007/09/06
カテゴリ : Ethnaメモ

執筆者: itoh (1:05 pm)
前述のエントリを使って、作りました。

http://pukiwiki.junoe.jp/index.php?Ethna%2FPlugin
itohさんのブログを読む | コメント (0) | トラックバック数 (0) | 閲覧数 (5494)
2007/08/07
カテゴリ : Ethnaメモ

執筆者: itoh (7:36 pm)
よく忘れるのでメモ

<?php
    /**
     *  アプリケーションのエントリポイント
     *
     *  @access public
     *  @param  string  $class_name     アプリケーションコントローラのクラス名
     *  @param  mixed   $action_name    指定のアクション名(省略可)
     *  @param  mixed   $fallback_action_name   アクションが決定できなかった場合に実行されるアクション名(省略可)
     *  @static
     */
    function main($class_name, $action_name = "", $fallback_action_name = "")


$class_nameは、問題ないとして。


$action_nameは、配列で与えるといい。

たとえば、
array('inquery_index', 'inquery_*')
見たいにした場合、actionは、この中のモノに強制される。
つまり、inquery_***********っていう感じ。


で、たとえば、外からのFormでinquery_nodefined_actionとか「まだActionClassに定義していない」アクションが指定で来てしまった場合は、先頭のinquery_indexがアクションとしてFIXされる。



ここまでで十分だと思うんだけど、Fail-Safe的にEthnaは第三の引数をとる。それは、inquery_indexが仮のFallbackにしたけれども、開発中の何かのきっかけで、inquery_indexを消してしまった場合。

そんなときは、第三の引数のActionにFallbackする。ここには、絶対無くならないActionを書いておくと、fail safeなアプリケーションになるということ。
itohさんのブログを読む | コメント (0) | トラックバック数 (0) | 閲覧数 (4286)
2007/07/25
カテゴリ : Ethnaメモ

執筆者: itoh (3:54 pm)
AppObjectをいちいちNewするのってコストかかるじゃないですか。

そこで、Managerで
<?php
	function getObject($forum_id)
	{
		static $__cache ;
		
		if (!isset($__cache)){
			$__cache = array();
		}
		if (!isset($__cache[$forum_id])){
			$__cache[$forum_id] =& new Xanhte_NewbbForum(&$this->backend, 'forum_id', $forum_id);
		}
		
		return $__cache[$forum_id];
	}

ってなキャッシュを作っていたんです。

普通はこれで問題ないでしょう。


ところが、私はXOOPSで使っていたので、
XOOPSのBlock処理(Ethnaで処理)

XOOPSのメインページの処理(もちろんEthnaで処理)
と1回のパースで2回Ethnaを起動させていました。
(Actionチェインとかじゃなくて、CMSのフローとして)


すると、Block処理で作ったAppObjectのキャッシュがメインページでの処理にも生きてくるんですね・・・。



これ、AppObject内のメソッドでActionFormを呼び出さなければOKです。


ところが、AppObject内のメソッドでActionFormと連携するもの・・・たとえば、
・AppObject->exportForm()
・AppObject->importForm()
とかは、NGです。


何がって、Block処理で作られたAppObjectは、その内部参照変数afにBlock処理のActionFormを収めるからです。

そして、キャッシュ機構により、メインページでのActionFormはafに収められず、exportFormは想定外のActionFormへのExportを実行してしまう・・・・。



AppObject自体のキャッシュ機構(Ethnaネイティブの$filterや$orderが同じときにはDBアクセスしないでキャッシュしておく)とは別です。

ちなみに、Ethna2.3ではAppObjectは、newしないでEthna_Backend->getObject()するようになっているけど、こんなキャッシュの仕方はしていない。

あくまで、このちょっと特殊な状況下でのワナ。
itohさんのブログを読む | コメント (0) | トラックバック数 (0) | 閲覧数 (4494)
2007/06/04
カテゴリ : Ethnaメモ

執筆者: itoh (11:52 pm)
Ethnaは、エラー制御演算子@をほとんど使ってないんだけど、一箇所だけセッションで使っている。


Ethna_Session::start();
Ethna_Session::restore();

です。


んで、必要ならLoggerをFileにダンプしてtail -f で見ながら作っていると、何でも補足できた気になって大いにはまった。


しかも、XOOPSの中で使ってる(そんなの日本で2人いるかって話だけど)ので、特定の条件で出てきてしまってさらにな状況に。


XOOPSは、SessionをDBに持っている。Wikiにもずっと前に書いたけど、Ethna+DBセッションって結構気を使う。そして、今回もものすごいレアなケースではまった。
引用:

Fatal error: session_start(): Failed to initialize storage module: user (path:

このエラーメッセージが出てこれば5秒で気が付いたのに・・・。


えっと、確か@は、その抑制レベルを変えられたってきいたんだけど・・。未だに探せてない・・・。
itohさんのブログを読む | コメント (0) | トラックバック数 (0) | 閲覧数 (6974)
2007/05/22
カテゴリ : Ethnaメモ

執筆者: itoh (4:36 pm)
EthnaのCommiterであるhaltさんのエントリに対するEthna使いの反応を見て欲しい。

http://project-p.jp/halt/anubis/blog_show/721


そして、それに対するkoyhogeさんの反応。


このノリって、Mapleにあったのだろうか。



このゆるさ加減はPrimary Commiterから出ているものなのかもしれない。
itohさんのブログを読む | コメント (0) | トラックバック数 (0) | 閲覧数 (5104)
2007/03/11
カテゴリ : Ethnaメモ

執筆者: itoh (1:23 am)
仕事ちょっと空いてる&奥様帰省中のため。

掲示板作成を例に取ったEthnaアプリのサンプルの紹介


ようやくAction。

道のり遠!
itohさんのブログを読む | コメント (0) | トラックバック数 (0) | 閲覧数 (8877)
2007/02/20
カテゴリ : Ethnaメモ

執筆者: itoh (9:10 pm)
Cookieへのアクセス方法って無いような気がしたので、Ethna。

アプリケーションIDはHifi。(例によって、案件コードそのままコピー)

とりあえず、app/Hifi_Cookie.phpとか作って(本当は例によってこれをExtendsしてね)
<?php
class Hifi_Cookie extends Ethna_Cookie
{
}

class Ethna_Cookie
{
    /**#@+
     *  @access     protected
     */

    /** @var    array Cookie値 */
	var $_cookie = array();
	
    /** @var    object Backendオブジェクト */
	var $backend ;
	
    /** @var    array cookieをセットする際のプロパティ */
	var $cookie_prop = array('value', 'expire', 'path', 'domain', 'secure', 'httponly');
	
    /**
     *  Cookieを復帰させる
     *
     *  @access public
     */
	function restore()
	{
		if (isset($_COOKIE)){
			foreach ($_COOKIE as $name=>$value){
				$this->_cookie[$name] = array(
					'update'   => false,  // this flag is true when written this value
					'value'    => $value,
					'expire'   => 0,
					'path'     => null,
					'domain'   => null,
					'secure'   => false,
					'httponly' => false
					);
			}
		}
		
		return null;
	}
	
	
    /**
     *  Cookieの値を得る
     *
     *  @access public
	 *  @param string $name クッキー名
	 *  @param mixed  $value クッキー値の情報
     */
	function get($name=false)
	{
		if ($name === false){
			return $this->_cookie;
		}
		
		if (isset($this->_cookie[$name])){
			return $this->_cookie[$name]['value'];
		}
		
		return null;
	}
	
	
    /**
     *  Cookieをセットする
     *
     *  @access public
	 *  @param string $name クッキー名
	 *  @param mixed  $value クッキー値の情報
     */
	function set($name, $value)
	{
		if (is_array($value)){
			$this->_cookie[$name]['update'] = true ;
			foreach ($this->cookie_prop as $prop_name){
				if (isset($value[$prop_name])){
					$this->_cookie[$name][$prop_name] = $value[$prop_name];
				}
			}
		} else {
			$this->_cookie[$name]['update'] = true ;
			$this->_cookie[$name]['value'] = $value ;
		}
		
		return null;
	}
	
	
    /**
     *  Cookieを削除するHeaderをセットする
     *
     *  @access public
	 *  @param string $name クッキー名
     */
	function remove($name)
	{
		if (isset($this->_cookie[$name])){
			$this->_cookie[$name]['update'] = true ;
			$this->_cookie[$name]['value'] = false ;
		}
	}
	
	
    /**
     *  Cookieを保存する(Backendで呼ばれる)
     *
     *  @access public
     *  @param  object  Ethna_Controller    &$controller    コントローラオブジェクト
     */
	function save()
	{
		foreach ($this->_cookie as $name => $value){
			if ($value['update']){
				$setcookie_args = array($name);
				foreach ($this->cookie_prop as $prop){
					if (substr(phpversion(),0,3) < 5.2 && $prop == "httponly"){
						continue;
					}
					$setcookie_args[] = $value[$prop];
				}
				call_user_func_array('setcookie', $setcookie_args);
			}
		}
	}
}
?>


これだけだと、使えないので、Backendを拡張
<?php
class Hifi_Backend extends Ethna_Backend
{
    /** @var    object      COOKIEオブジェクト */
	var $cookie = null; 
	
    /**
     *  バックエンド処理を実行する(+Cookie処理)
     *
     *  @access public
     *  @param  string  $action_name    実行するアクションの名称
     *  @return mixed   (string):Forward名(nullならforwardしない) Ethna_Error:エラー
     */
    function perform($action_name)
    {
        $forward_name = null;

        $action_class_name = $this->controller->getActionClassName($action_name);
        $this->action_class =& new $action_class_name($this);
        $this->ac =& $this->action_class;
		
		// cookieの復帰
		$this->cookie =& $this->class_factory->getObject('cookie');
		$this->cookie->restore();
		
        // アクションの実行
        $forward_name = $this->ac->authenticate();
        if ($forward_name === false) {
			$return = null;
        } else if ($forward_name !== null) {
			$return = $forward_name;
		} else {
			$forward_name = $this->ac->prepare();
			if ($forward_name === false) {
				$return = null;
			} else if ($forward_name !== null) {
				$return = $forward_name;
			} else {
				$return = $this->ac->perform();
			}
		}
		
		// cookieの保存
		$this->cookie->save();
		
        return $return ;
    }
	
	
	function &getCookie()
	{
		return $this->cookie;
	}
}
?>


んで、Controllerに
<?php
require_once 'Hifi_Cookie.php';
.............
    /**
     *  @var    array   クラス定義
     */
    var $class = array(
...........
        'backend'       => 'Hifi_Backend',
...........
	'cookie'        => 'Hifi_Cookie',
    );
?>

とかして登録。

itohさんのブログを読む | コメント (0) | トラックバック数 (0) | 閲覧数 (9031)
2007/02/08
カテゴリ : Ethnaメモ

執筆者: itoh (8:16 pm)
Ethna使ってて、絵文字使ってみました。

とりあえず、絵文字変換コードは
PHP 携帯絵文字 自動変換スクリプト
さんを使わせてもらいました。多謝。

で、これをEthnaで使う方法です。

1. Ethna_FilterにObフィルタをかませて一括変換

Ethna_Filterにobをかませて一括str_replace変換をかけます。処理が重いかなと思いましたが、意外といいかも。何しろシンプルです。

たとえば、絵文字デリミタを[%%]として[%F997%]はに変わるなど。
このデリミタはさすがにハードコーディングされてしまいます。

この何がどの絵面かというのは、上記のライブラリにバンドルされていて、
http://www.junoe.jp/docs/keitai_emoji/admin.php
みたいにして使えます。すげー便利です。


2. Smarty_Functionを使ってプラグインで記述

最初、1.は重いかなと思い、これでやってましたが・・・

*1. テンプレートをSJISで書かないといけない
*2. Smarty変数をEthna_ViewClass::forward()するまえにSJISに変換しないといけない
*3. テンプレートに半角カナを使って書かないといけない

というのが足かせになります。最初、2.でいきましたが、結局1.で落ち着きそうです。



実際には、下記のEthnaフィルタを使います。
preFilterで、GPC変数をSJISから内部エンコーディングに変更します。
そのあと、ob_startをかけますが、ob_end_flushする際に変換する順番としては、最後に絵文字変換をかけるようにしないといけません。

下記のコードに出てくる
ITT_Util::con_encは実質mb_convert_variablesと同じです。
ITT_Util::isMobileAgentは携帯かどうかをチェックするメソッドです。
<?php
/**
 *	文字コード変換フィルタの実装
 *  output.handlerには頼らない;)
 *	@author		{$author}
 *	@access		public
 *	@package	Tinycms
 */
class Showtie_Filter_MobileExchange extends Ethna_Filter
{
	/**#@+
	 *	@access	private
	 */

	/**
	 *	@var	int		開始時間
	 */

	/**#@-*/

	/// 携帯の場合、GPCのSJIS->UTF-8など
	/**
	 *	実行前フィルタ
	 *   -- POST/GETされるのはSJISと決め付けて、UTF-8に変換する
	 *
	 *	@access	public
	 */
	function preFilter()
	{
		
		if (ITT_Util::isMobileAgent()){
			$to_enc = mb_internal_encoding();
			$_POST = ITT_Util::con_enc($_POST, $to_enc, 'SJIS');
			$_GET = ITT_Util::con_enc($_GET, $to_enc, 'SJIS');
			$_COOKIE = ITT_Util::con_enc($_COOKIE, $to_enc, 'SJIS');
			ob_start(array($this, '_outputMobileConverFilter'));
		}
	}

	/**
	 *	実行後フィルタ
	 * 
	 *	@access	public
	 */
	function postFilter()
	{
		if (ITT_Util::isMobileAgent()){	
			ob_end_flush();
		}
	}
	
	
	/** 
	 * @brief 携帯用obフィルター
	 *
	 * @access protected
	 * @param $buffer string 変換前文字列
	 * @return $buffer string 変換後文字列
	 */
	function _outputMobileConverFilter($buffer)
	{
		// image_extention
//		$mobile_extention = ITT_Util::getMobileImageExtention();
		$mobile_extention = 'jpg';
		$buffer = str_replace('<iext>', $mobile_extention, $buffer);
		
		// カナ変換 => view::forward()
		$buffer = mb_convert_kana($buffer, 'ka', 'UTF-8');
		
		// encodingは最後
		$internal = @mb_internal_encoding();
		$output = 'SJIS';
		// 文字コード変換
		$buffer = mb_convert_encoding($buffer , $output, $internal );
		
		// 絵文字変換はバイナリを扱うので最後
		$buffer = $this->_emojiConverFilter($buffer);
		
		// Contents-Length header
		header('Content-Type: text/html; charset=Shift_JIS');
		header('Content-Length: '.strlen($buffer));
		
		return $buffer ;
	}
	
	
	/** 
	 * @brief 携帯絵文字変換
	 *
	 * @access protected
	 * @param $buffer string 変換前文字列
	 * @return $buffer string 変換後文字列
	 */
	function _emojiConverFilter($buffer)
	{
		require_once 'emoji/MobileClass.php';
		$mc =& new MobileClass();
		
		$before_str = array();
		$after_str  = array();
		foreach ($mc->EMOJI as $code=>$conv_array){
			$before_str[] = '[%'.$code.'%]';
			$after_str[]  = $mc->Convert($code);
		}
		
		return str_replace($before_str, $after_str, $buffer);
	}
}
?>





ちなみに、2の方法でSmartyプラグインを作ると
<?php
/**
 *	smarty function : emoji
 *
 *  携帯絵文字をそれぞれのキャリア別に表示
 *   (Showtie_Filter_MobileExchange::_emojiConverFilter()を使うかの二択)
 *
 *	sample:
 *	<code>
 *	{emoji code="F9A3"}
 *	</code>
 *  
 *  @deprciate

 *	@param	string	Ethna_ActionName
 *	@return	string	HTML
 */
require_once 'emoji/MobileClass.php';
function smarty_function_emoji($params, &$smarty)
{
	static $mc;
	
	extract($params); // ex: get $name = 'namae';
	
	if (!isset($code)) return '' ;
	
	if (!isset($mc)){
		$mc =& new MobileClass();
	}
	
	return $mc->Convert($code);
}
?>

ですが、その後の処理が意外と面倒なのでオススメではないです。
itohさんのブログを読む | コメント (24) | トラックバック数 (0) | 閲覧数 (25550)

(1) 2 3 »



 





メインメニュー

カテゴリ一覧

Google Adsense

うぇブログ カレンダー


XoopsCube Ring
Amethyst Blue - BULLETIN


.