Tatsuya Blog
msgbartop
ニートになりたいプログラマ
msgbarbottom

2010-07-19 13:21 [CodeIgniter]CI_Profilerを拡張してCI_Session内容も出力してみた

  • Share

Controllerなどで下記のように記述するとデバッグ用に変数の内容などを出力してくれます。

$this->output->enable_profiler(true);

デフォルトだと、下記の情報が表示されるようです。

  • URI STRING
  • CLASS/METHOD
  • MEMORY USAGE
  • BENCHMARKS
  • GET DATA
  • POST DATA
  • QUERIES

今回は省略しますが、プロファイラ用のホックなんかを使っておくと便利です。

追記:2010-07-20
大したプログラムではないですけど、残しておきます。

system/application/hooks/XC_ProfilerHook.php

< ?php
/**
 * プロファイラ用ホック
 * @author Tatsuya Fukata
 *
 */
class XC_ProfilerHook {
	/** CIインスタンス */
	private $CI;
 
	/**
	 * コンストラクタ
	 */
	public function __construct(){
		$this->CI =& get_instance();
	}
 
	public function enable_profiler() {
		$this->CI->output->enable_profiler($this->CI->config->item('hook_enable_profiler'));
	}
}
 
?>

で、設定ファイルに下記を追加します。

system/application/config/config.php

//プロファイラホックの有効化
$config['hook_enable_profiler'] = true;

さらに、hooks.phpに下記も追加します。

system/application/config/hooks.php

//プロファイラ用ホック
$hook['post_controller_constructor'][] = array(
	'class' => 'XC_ProfilerHook',
	'function' => 'enable_profiler',
	'filename' => 'XC_ProfilerHook.php',
	'filepath' => 'hooks',
);

これで、ローカルや、開発環境ではプロファイラを有効にしておいて、テスト環境や本番で一括でプロファイラの設定を切り替えることができます。

で、本題のPHP標準のSession機構を使用するCI_Session拡張のコードです。

前回の記事でCI_Sessionの拡張について書いたついでにCI_Sessionの持つ、ユーザデータの内容も出力するようにしてみました。

system/application/libraries/XC_Profiler.php

< ?php
/**
 * デフォルトのプロファイラ結果にCI_Sessionの持つ、ユーザデータを追加する
 * 
 * @author Tatsuya Fukata
 *
 */
class XC_Profiler extends CI_Profiler {
	public function __construct() {
		parent::__construct();
	}
 
	public function run() {
		$output = "<div id='codeigniter_profiler' style='clear:both;background-color:#fff;padding:10px;'>";
 
		// XXX スーパクラスのprivateメソッドのため、個別定義
		$output .= $this->_compile_uri_string();
		$output .= $this->_compile_controller_info();
		$output .= $this->_compile_memory_usage();
		$output .= $this->_compile_benchmarks();
		$output .= $this->_compile_get();
		$output .= $this->_compile_post();
		$output .= $this->_compile_queries();
 
		$output .= $this->_compile_session();
 
		$output .= '';
 
		return $output;
	}
 
	/**
	 * ユーザデータ情報を返す。
	 * @return String
	 */
	protected function _compile_session() {
		if (!isset($this->CI->session) || !is_subclass_of($this->CI->session, 'CI_Session')) {
			return '';
		}
 
		$output  = "\n\n";
		$output .= '<fieldset style="border:1px solid #FF3535;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee">';
		$output .= "\n";
		$output .= '<legend style="color:#FF3535;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_session').'&nbsp;&nbsp;</legend>';
		$output .= "\n";
		$output .= "\n\n<table cellpadding='4' cellspacing='1' border='0' width='100%'>\n";
 
		$session = $this->CI->session->all_userdata();
		if (empty($session) || count($session)==0) {
			$output .= "<tr><td width='100%' style='color:#FF3535;font-weight:normal;background-color:#eee;'>".$this->CI->lang->line('profiler_no_session')."</td></tr>\n";
		} else {
			foreach ($session as $key => $val) {
				$output .= "<tr><td width='100%' style='color:#FF3535;font-weight:normal;background-color:#eee;'>".$key.' => '.print_r($val, true)."</td></tr>\n";
			}
		}
 
		$output .= "</table>\n";
		$output .= "</fieldset>";
 
		return $output;
	}
}
?>

プロファイラ結果として使用する言語ファイルを作成します。
デフォルトのものは、system/language/english/profiler_lang.phpに定義されているので、独自で作成した言語ファイル内で読み込むようにします。

system/application/language/english/profiler_lang.php

< ?php
 
// 既存の言語ファイル
@include_once(BASEPATH.'language/english/profiler_lang.php');
 
// 以下、独自追加分
$lang['profiler_session'] = 'SESSION';
$lang['profiler_no_session'] = 'No SESSION data exists';
 
/* End of file profiler_lang.php */
/* Location: ./system/language/english/profiler_lang.php */
?>

Tags:

2010-07-19 12:00 [CodeIgniter]CI_Sessionを拡張してPHP標準Sessionに対応してみた

  • Share

CodeIgniterが提供しているセッションクラスは、Cookieに全て保存するか、ユーザが追加したデータはデータベースに保存しておいて、キーはCookieに保存するかの2種類を選択することができます。

ただ、PHP標準のセッション機構を使うことには対応しておらず、どうしても利用したい場合は下記のようにベタで記述する必要があります。

session_start();
$_SESSION['hoge'] = 'foo';

上記でも基本的に動くとは思いますが、セッション機構を切り替える際のコストが高いので、コアクラスのCI_Sessionを拡張してPHP標準セッション機構も選択できるようにしてみました。

system/application/libraries/XC_Session.php

< ?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * PHP標準Session機構を利用できる
 * 
 * @author Tatsuya Fukata
 *
 */
class XC_Session extends CI_Session {
	/** PHP標準のSession機能を使用するか */
	private $sess_use_php_session = false;
 
	/** 初期化時に読み込む設定ファイルのキー一覧 */
	private static $INIT_CONFIG_KEYS = array(
		'sess_use_php_session', 
		'sess_encrypt_cookie', 
		'sess_use_database', 
		'sess_table_name', 
		'sess_expiration', 
		'sess_match_ip', 
		'sess_match_useragent', 
		'sess_cookie_name', 
		'cookie_path', 
		'cookie_domain', 
		'sess_time_to_update', 
		'time_reference', 
		'cookie_prefix', 
		'encryption_key',
	);
 
	/**
	 * コンストラクタ
	 * @param Array $params
	 */
	public function __construct($params = array()) {
		$this->CI =& get_instance();
		foreach (self::$INIT_CONFIG_KEYS as $key) {
			$this->$key = (isset($params[$key])) ? $params[$key] : $this->CI->config->item($key);
		}
 
		if ($this->use_not_php_session()) {
			parent::__construct($params);
		} else {
			$this->initialize($params);
		}
	}
 
	/**
	 * PHP標準Session利用時の初期化
	 * @param Array $params
	 * @return void
	 */
	protected function initialize($params) {
		session_start();
 
		// XXX スーパクラスのprivateメソッドのため、個別定義
		// Set the "now" time.  Can either be GMT or server time, based on the
		// config prefs.  We use this to set the "last activity" time
		$this->now = $this->_get_time();
 
		// Set the session length. If the session expiration is
		// set to zero we'll set the expiration two years from now.
		if ($this->sess_expiration == 0) {
			$this->sess_expiration = (60*60*24*365*2);
		}
 
		// セッションが既に存在すれば更新、未作成の場合はデフォルト値を作成する
		if (!$this->sess_read()) {
			$this->sess_create();
		} else {
			$this->sess_update();
		}
 
		/*
		 * XXX スーパクラスのprivateメソッドのため、個別定義
		 * メソッド「_flashdata_sweep」「_flashdata_mark」「_sess_gc」は
		 * CI_Sessionにプライベートメソッドを意識して定義されているため、
		 * 本来であれば、XC_Sessionで再定義したいところ。
		 */
		// Delete 'old' flashdata (from last request)
	   	$this->_flashdata_sweep();
 
		// Mark all new flashdata as old (data will be deleted before next request)
	   	$this->_flashdata_mark();
 
		// Delete expired sessions if necessary
		$this->_sess_gc();
 
 
	}
 
	/**
	 * Override
	 */
	public function sess_read() {
		if ($this->use_not_php_session()) {
			return parent::sess_read();
		}
 
		if (count($_SESSION) == 0) {
			return false;
		}
 
		// Is the session current?
		if (($_SESSION['last_activity'] + $this->sess_expiration) < $this->now) {
			$this->sess_destroy();
			return FALSE;
		}
 
		// Does the IP Match?
		if ($this->sess_match_ip == TRUE AND $_SESSION['ip_address'] != $this->CI->input->ip_address()) {
			$this->sess_destroy();
			return FALSE;
		}
 
		// Does the User Agent Match?
		if ($this->sess_match_useragent == TRUE AND trim($_SESSION['user_agent']) != trim(substr($this->CI->input->user_agent(), 0, 50))) {
			$this->sess_destroy();
			return FALSE;
		}
 
		$this->userdata = $_SESSION;
		return true;
	}
 
	/**
	 * Override
	 */
	public function sess_create() {
		if ($this->use_not_php_session()) {
			parent::sess_create();
			return;
		}
 
		// 初期データを設定
		$_SESSION = array(
			'session_id' => session_id(),
			'ip_address' => $this->CI->input->ip_address(),
			'user_agent' => substr($this->CI->input->user_agent(), 0, 50),
			'last_activity' => $this->now
		);
 
		$this->userdata = $_SESSION;
		$this->sess_write();
	}
 
	/**
	 * Override
	 */
	public function sess_write() {
		if ($this->use_not_php_session()) {
			parent::sess_write();
			return;
		}
 
		$_SESSION = $this->userdata;
	}
 
	/**
	 * Override
	 */
	public function sess_update() {
		if ($this->use_not_php_session()) {
			parent::sess_update();
			return;
		}
 
		// We only update the session every five minutes by default
		if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now) {
			return;
		}
 
		// 新しいセッションIDの生成し、設定
		session_regenerate_id(true);
		$this->userdata['session_id'] = session_id();
		$this->userdata['last_activity'] = $this->now;
 
		$this->sess_write();
	}
 
	/**
	 * Override
	 */
	public function sess_destroy() {
		if ($this->use_not_php_session()) {
			parent::sess_destroy();
			return;
		}
 
		if (isset($_COOKIE[session_name()])) {
			setcookie(session_name(), '', time()-42000, '/');
		}
 
		$this->userdata = array();
		$this->sess_write();
 
		session_destroy();
	}
 
	/**
	 * PHP標準Sessionを利用するかを返す。
	 * @return bool
	 */
	protected function use_php_session() {
		return $this->sess_use_php_session === true;
	}
 
	/**
	 * PHP標準Sessionを利用しないかを返す。
	 * @return bool
	 */
	protected function use_not_php_session() {
		return !$this->use_php_session();
	}
}
?>

また、設定ファイルに下記を追加します。

system/application/config/config.php

// PHP標準セッションを利用するか
$config['sess_use_php_session'] = TRUE;

Tags:

2010-07-06 23:08 [CodeIgniter]CI_Languageをメッセージに外部から値を組み込めるように拡張してみた

  • Share

CodeIgniter標準の言語クラスでは、メッセージに変数などを埋め込める機能が存在しません。もしかすると誰かが既に作ってる可能性は限りなく高いですが、対した実装でもないので勢いで作っちゃいました。

やってることは簡単で、可変長引数を取得し、メッセージ内に組み込まれた置換対象文字である{0}や{1}を置換しているだけです。

CI_Languageと下位互換性は保っているので、置き換えてもそのまま動かすことも可能です。

system/application/helpers/xc_language_helper.php

< ?php  if (!defined('BASEPATH')) exit('No direct script access allowed');
 
if ( ! function_exists('lang'))
{
	/**
	 * 言語ファイルから対象のメッセージを取得する。
	 * 
	 * @param $line メッセージキー
	 * @param $id
	 * @param $args 置換値(可変長引数)
	 */
	function lang($line, $id = '')
	{		
		$CI =& get_instance();
		$line = $CI->lang->line($line);
 
		// 可変長引数以外の変数の個数
		$define_arg_num = 2;
 
		// 置換引数指定の場合は、メッセージ内の置換対象文字列を置換する。
		$num = func_num_args();
		if ($num > $define_arg_num) {
			for ($i=0; $i< ($num-$define_arg_num); $i++) {
				$line = str_replace('{'.$i.'}', func_get_arg($define_arg_num+$i), $line);
			}
		}
 
		if ($id != '')
		{
			$line = '<label for="'.$id.'">'.$line."";
		}
 
		return $line;
	}
}
 
?>

system/application/libraries/XC_Language.php

< ?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
 
class XC_Language extends CI_Language {
	public function __construct() {
		parent::__construct();
	}
 
	/**
	 * 言語ファイルからメッセージを取得し、置換対象文字列が指定されている場合は、置換後の文字列を返す。
	 * 
	 * @param $line メッセージキー
	 * @param $args 置換値(可変長引数)
	 */
	function line($line = '') {
		$line = ($line == '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line];
 
		// 可変長引数以外の変数の個数
		$define_arg_num = 1;
 
		// 置換引数指定の場合は、メッセージ内の置換対象文字列を置換する。
		$num = func_num_args();
		if ($num > $define_arg_num) {
			for ($i=0; $i< ($num-$define_arg_num); $i++) {
				$line = str_replace('{'.$i.'}', func_get_arg($define_arg_num+$i), $line);
			}
		}
 
		return $line;
	}
}
?>

Tags:

2010-07-04 15:58 [CodeIgniter]Twigと連携してみた – その1

  • Share

PHPの軽量フレームワークである日本CodeIgniterユーザ会とDJangoやTilesのように継承機能を用いたPHPテンプレートエンジン「Twig – The flexible, fast, and secure template language for PHP」を簡単に連携させてみましたので設定を書きたいと思います。

ただ、CodeIgniterで作成したHelperなどをTwig内での使用までは検証できなかったのでまた時間がある時にでもやってみたいと思います。

下記で説明しているファイルはこちらのファイルに含まれています。
説明用にソースを一部編集していますので、異なる部分もあるのでご了承ください。

ci-twig-1.0.0.zip

※下記のソースはjamiepittock’s codeigniter-twig at master – GitHubを拝借して一部編集しています。

全体の構成

.
`-- system
    |-- application
    |   |-- config
    |   |   `-- twig.php // Twig用設定ファイル
    |   |-- controllers
    |   |-- errors
    |   |-- helpers
    |   |-- hooks
    |   |-- language
    |   |-- libraries
    |   |   |-- Twig // Twigライブラリ
    |   |   `-- Twig.php // Twigクラス
    |   |-- models
    |   `-- views
    |-- cache
    |   `-- twig // Twig用キャッシュディレクトリ
    |-- codeigniter
    |-- database
    |   `-- drivers
    |-- fonts
    |-- helpers
    |-- language
    |   `-- english
    |-- libraries
    |-- logs
    |-- plugins
    `-- scaffolding
        |-- images
        `-- views

追加ファイル

application/libraries/Twig

http://www.twig-project.org/installationからダウンロードしたTwigのlibディレクトリ以下を格納する。

application/config/twig.php

< ?php if (!defined('BASEPATH')) exit('No direct script access allowed');
$config['template_dir'] = APPPATH.'views';
$config['cache_dir'] = BASEPATH.'cache/twig';
?>

application/libraries/Twig.php

< ?php if (!defined('BASEPATH')) {exit('No direct script access allowed');}
 
class Twig
{
	private $CI;
	private $_twig;
	private $_template_dir;
	private $cache_dir;
 
	/**
	 * Constructor
	 *
	 */
	function __construct()
	{
		$this->CI =& get_instance();
		$this->CI->config->load('twig');
 
		ini_set('include_path',
		ini_get('include_path') . PATH_SEPARATOR . APPPATH . 'libraries/Twig');
 
		require_once (string) "Autoloader" . EXT;
		log_message('debug', "Twig Autoloader Loaded");
 
		Twig_Autoloader::register();
 
		$this->_template_dir = $this->CI->config->item('template_dir');
		$this->_cache_dir = $this->CI->config->item('cache_dir');
 
		$loader = new Twig_Loader_Filesystem($this->_template_dir);
		$this->_twig = new Twig_Environment($loader, array(
			'debug' => true,
			'cache' => $this->_cache_dir,
		));
	}
 
	public function view($template, $data = array()) {
		$template = $this->_twig->loadTemplate($template);
 
		echo $template->render($data);
	}
}
?>

サンプル

application/controllers/

< ?php
 
class Welcome extends Controller {
	function Welcome()
	{
		parent::Controller();
		$this->load-library('twig');
	}
 
	function index()
	{
		$data['name'] = 'Tatsuya';
		$this->twig->view('welcome_message.php', $data);
	}
}
 
/* End of file welcome.php */
/* Location: ./system/application/controllers/welcome.php */
?>

Tags: ,

2010-06-13 21:07 ロリポップが新構成サーバ移設でPHPエラー

  • Share

http://lolipop.jp/newsletter/2010/20100527/

何やらロリポップがサーバ移設を行なったのが原因なのか、以前作成したPHPの注文フォームが動かなくなったので修正して欲しいという依頼がありました。

挙動から言うと入力画面でセッションにあるクラスのインスタンスを格納して各画面で利用しているのですが、初期表示以降、画面を描画しようとするとセッション内に入れたはずのインスタンスがNULLになっていて落ちていました。

php.iniの設定がユーザの管理画面から行なえるようになったらしいので、その辺が怪しいんじゃないかと見てみるとsession.auto_start=1という設定が。

セッション処理関数(session)

クラス定義をロードする前にセッションが開始され、内部に保持していたクラスがうまくデシリアライズされないのが原因らしいです。

Tags:

2010-01-09 19:39 [WordPress]WP-GMaps-Entryのv0.3をリリースしました。

  • Share

今回のリリースで、WordPress 2.5から導入されたショートコードというのを使うように変更しました。なので、当プラグインの動作基準というのがWordPress 2.5以上という制限が加わりました。

また、ショートコードを使用するにあたり、構文が若干変更になりました。以前は各パラメータをカンマで区切っていたのですが、半角スペースで区切るようになりました。

また、当ブログサイトに専用ページを設けましたので、そちらからもダウンロードサイトに行けるようになっています。ダウンロードはプロジェクトサイトの方から行えるようになっていますので、そちらからどうぞ。

Tags:

2009-07-30 00:58 PHPでUTF8のメールの件名が文字化ける件について

  • Share

今日は久々にはまってしまいました。

PHPでUTF8のメール送信処理を作成していたんですが、特定の文字列がある場合のみ、件名が文字化けるんですよ。しかも、ローカルのUbuntuの環境では起きず、テストサーバ上でのみ発生。。。あ、テストサーバのPHPのバージョン確認するの忘れた。

で、原因なんですが、どうやら「mb_encode_mimeheader」が原因だったようです。しょうがないので、以下のようにして自分で作成しました。base64限定ですけどね^^;

1
2
3
function encodeBase64MimeHeader($str,$charset='UTF-8'){
	return '=?'.$charset.'?B?'.base64_encode($str).'?='
}

最近、メールに関する情報を調べていたので理解しながら修正できた感があり、久々にPHPを書いてて嬉しくなりましたw

Tags: , ,

2009-01-16 22:33 namazu for php モジュール

  • Share

メモ

phpからnamazuの機能を利用するためのモジュール。

■ソース

http://sourceforge.jp/projects/php-i18n/

■freebsdへの導入手順

http://park1.wakwak.com/~ima/freebsd_namazuso.html

■windowsへの導入手順

http://win.kororo.jp/archi/tips/namazu_php.php

■サンプルコード

http://park1.wakwak.com/~ima/php_namazu.html

Tags:

2009-01-15 14:41 CodeIgniterか、Symfonyか

  • Share

現在、社内で使用する基盤FWの選定を行っています。

Syfmonyは既に1度だけプロジェクトで使用したことがあり、良い点、悪い点、まだ未確認な点などが上がっています。ただ、CodeIgniterに関しては、フォームレベルで使用したことがあるだけで、本格的に中規模レベル(2、3人月程度)のプロジェクトで使用したことがありません。なので、まだ良い点や悪い点などが上がっていません。

現在、CodeIgniterのユーザガイドを読みながら少しコードを試し書きしています。自分のいつもの開発環境は、IDEとしてEclipseのPDTを使用しているのですが、Symfonyも同様ですが、CodeIgniterの方が、Eclipseの補完機能が機能していないです。(動的にフィールドを定義している為など)

現在の各FWの検証状況は以下のような感じです。

■Syfmony(version 1.1.4)

□良い点

  • Yahooでの採用実績がある
  • 基本的なWebFWとして、ほぼ完成している
  • リリースにrysncを採用しており、作業が制限されるが作業の安定性(人為的ミスなど)、簡略化されている
  • WebFWではあるが、バッチ処理も考慮されている(コマンドラインからの実行)
  • sfPropelPagerが意外と便利
  • 入力チェック情報を外部ファイルとして分離できる
  • 独自のURLマッピングが可能
  • 多言語化の為の機能は容易されている

□悪い点

  • フォームの入力・確認・完了の確認画面が考慮されていない。ただし、ラッピングする形では対応可能。
  • ログローテーションの設定が弱い(設定ファイルで定義できない?)
  • モデル自体の容量が大きい為、対象のオブジェクトを使用する際に、メモリ使用量が大きい。
  • init-adminが融通が効かなすぎて実務には使えない。
  • レスポンスが悪い
  • YAMLが書きにくい

□未確認な点

  • 複数DBの接続切り替え
  • モバイルへの対応
  • 大量データの処理

■CodeIgniter

□良い点

  • 縛りが緩く、また、ソースが比較的小さい為、拡張性は高い
  • 基本的なライブラリ群は用意されている
  • インストールが楽
  • php4でも動作可能
  • 独自のURLマッピングが可能
  • 複数のDBの定義が可能。また、接続切り替えも容易
  • 多言語化の為の機能は容易されている

□悪い点

  • 入力チェックがコントローラと密結合している
  • コマンドラインからの実行が考慮されていない
  • URLと実際のコントローラのファイルが密結合している
  • アクセスメソッド(URL)が一つのコントローラにまとまっていて、分離できない。
  • FWのコードがphp4ベースで書かれている。

□未確認な点

  • モバイルへの対応
  • 実際のパフォーマンス
  • ログのローテーション
  • ログファイルの切り替え

というような感じになっています。まだ、出しきれていないので、間違っている部分などがあるかと思いますが、その際はコメントいただければと思います。

Tags: ,

2008-11-16 12:45 PHPで10進数<->32進数の相互変換

  • Share

プロジェクトで10進数から32進数へ変換する処理が必要だったので作ってみました。

あまり、PHPをメインではやってないのでもしかすると、既にそういう関数が組み込まれてたりするのかも^^;

とりあえず、クラスにまとめてみたのでもしよかったら見てやってください。そして、もしよかったら使ってやってください。

■参考サイト

全ソースコードは以下から…

(more…)

Tags: , , ,