Controllerなどで下記のように記述するとデバッグ用に変数の内容などを出力してくれます。
$this->output->enable_profiler(true);
デフォルトだと、下記の情報が表示されるようです。
今回は省略しますが、プロファイラ用のホックなんかを使っておくと便利です。
追記:2010-07-20
大したプログラムではないですけど、残しておきます。
< ?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')); } } ?>
で、設定ファイルに下記を追加します。
//プロファイラホックの有効化 $config['hook_enable_profiler'] = true;
さらに、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の持つ、ユーザデータの内容も出力するようにしてみました。
< ?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;"> '.$this->CI->lang->line('profiler_session').' </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に定義されているので、独自で作成した言語ファイル内で読み込むようにします。
< ?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: CodeIgniter
CodeIgniterが提供しているセッションクラスは、Cookieに全て保存するか、ユーザが追加したデータはデータベースに保存しておいて、キーはCookieに保存するかの2種類を選択することができます。
ただ、PHP標準のセッション機構を使うことには対応しておらず、どうしても利用したい場合は下記のようにベタで記述する必要があります。
session_start(); $_SESSION['hoge'] = 'foo';
上記でも基本的に動くとは思いますが、セッション機構を切り替える際のコストが高いので、コアクラスのCI_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(); } } ?>
また、設定ファイルに下記を追加します。
// PHP標準セッションを利用するか $config['sess_use_php_session'] = TRUE;
Tags: CodeIgniter
CodeIgniter標準の言語クラスでは、メッセージに変数などを埋め込める機能が存在しません。もしかすると誰かが既に作ってる可能性は限りなく高いですが、対した実装でもないので勢いで作っちゃいました。
やってることは簡単で、可変長引数を取得し、メッセージ内に組み込まれた置換対象文字である{0}や{1}を置換しているだけです。
CI_Languageと下位互換性は保っているので、置き換えてもそのまま動かすことも可能です。
< ?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; } } ?>
< ?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: CodeIgniter
PHPの軽量フレームワークである日本CodeIgniterユーザ会とDJangoやTilesのように継承機能を用いたPHPテンプレートエンジン「Twig – The flexible, fast, and secure template language for PHP」を簡単に連携させてみましたので設定を書きたいと思います。
ただ、CodeIgniterで作成したHelperなどをTwig内での使用までは検証できなかったのでまた時間がある時にでもやってみたいと思います。
下記で説明しているファイルはこちらのファイルに含まれています。
説明用にソースを一部編集していますので、異なる部分もあるのでご了承ください。
※下記のソースは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
http://www.twig-project.org/installationからダウンロードしたTwigのlibディレクトリ以下を格納する。
< ?php if (!defined('BASEPATH')) exit('No direct script access allowed'); $config['template_dir'] = APPPATH.'views'; $config['cache_dir'] = BASEPATH.'cache/twig'; ?>
< ?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); } } ?>
< ?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: CodeIgniter, Twig
http://lolipop.jp/newsletter/2010/20100527/
何やらロリポップがサーバ移設を行なったのが原因なのか、以前作成したPHPの注文フォームが動かなくなったので修正して欲しいという依頼がありました。
挙動から言うと入力画面でセッションにあるクラスのインスタンスを格納して各画面で利用しているのですが、初期表示以降、画面を描画しようとするとセッション内に入れたはずのインスタンスがNULLになっていて落ちていました。
php.iniの設定がユーザの管理画面から行なえるようになったらしいので、その辺が怪しいんじゃないかと見てみるとsession.auto_start=1という設定が。
クラス定義をロードする前にセッションが開始され、内部に保持していたクラスがうまくデシリアライズされないのが原因らしいです。
Tags: ロリポップ
Javaはもちろんのこと、最近ではPHPやPython、Rubyなどでも利用することが多くなった統合開発環境のEclipseですが、普通に使用していたのでは、ただのテキストエディタとあまり変わりません。
タッチタイピングは前提として、キーボードショートカットを多用すれば良いのです。
上記は個人的に頻繁に使用しているWindows版Eclipseのキーボードショートカットですが、これだけでもマスターして使えばかなり効率化されます。Javaコードに対する固有のショートカットなども含まれていると思います。MacOX版については実行環境がないので分かりませんが、Windows版とLinux版でデフォルトのショートカットが若干違うようです。(ファイル保存やコピー&ペーストなどの基本的なショートカットは省略しています。)
Eclipseを使う場合にはプログラムを左側から順に入力するのではなく、これらのキーボードショートカットを活用し極力タイピング量を減らすことでさらに効率良くなります。
また、マウスを利用するよりもキーボードメインにした方が手を常にキーボードに置くことができるので無駄な動作が減ります。どうしてもマウスを使わないといけない場合でも、Thinkpadなどのトラックポインタ付きキーボードを使えば極力キーボードから手を離さなくて良いのでコーディングに向いていると言えるでしょう。個人的にかもしれませんが、日本語配列よりも英語配列の方がコーディング時に使用頻度の高い記号(波括弧や丸括弧など)が使い易い配置になっているので、個人的には英語配列を好んで使用しています。
USBタイプのキーボードであればこちらがオススメです。
次回はこれらのキーボードショートカットを実際に使用して、効率良くJavaコーディングする方法を書きたいと思います。
Tags: Eclipse, キーボードショートカット, ショートカット
WebSocket関連のサービスを公開するサイトを作成しました。こちらになります。アクセスする場合は、Google ChromeなどWebSocketが実装されているブラウザでのみアクセスしてください。まだ、細かいエラー制御などは行っていません。
昨日、ServersMan@VPSにpywebsocketをインストールしたのですが、今回、websocket-sampleに含まれているlitechatというサンプルを実際に動かしてみました。
litechatサンプルはこちらになります。
元々のlitechatサンプルのサーバ側のカスタマイズ内容は下記の通りとなっています。フロント部分は見ていないのですいません。分かりません。フロント部分の実装には@smegdheadさんに助けてもらいました。ありがとうございました。
これだけのリアルタイム性のあるWEBアプリがHTML含めて100行未満(JavaScriptだけだと50行ほど)で作成できるなんてWebSocket素晴らしいです!サンプルページにサーバ側のソースもそのうちアップしたいと思います。XSS攻撃対策のために、タグはすべて使用不可にしていますが、今後装飾系のタグのみ使用できるようにカスタマイズしていきたいと思います。
Tags: pywebsocket, websocket
HTML5に加わるwebsoketを使用すると今まで以上にリアルタイム性の高いアプリケーションを作成することができる為個人的に非常に注目しています。
メインで使用しているブラウザのChromeには既にWebsocketが実装されており、使用することが可能だということで、簡易的なチャットでも作ってみようかと思い、色々調べていました。
Jetty7でWebSocket開発:マピオンラボ(Java)
サーバ側の実装についてまとめられています。今回私が選択したのは、pywebsocketというものです。これは、apacheのモジュールとして提供されwsプロトコルの場合はwebsocketハンドラに処理を流すというものです。スタンドアロンで使用することもできるらしいのですが、今回はapacheのモジュールとして使用します。pywebsocketという名前からwebsocketハンドラはプログラム言語Pythonで実装する必要があります。
インストールした環境は、ServersMan@VPSでOSはCentOs 5.4です。それでは、早速手順の方を書いていきたいと思います。
手順は以下から
(more…)
Tags: HTML5, Manual, pywebsocket, ServersMan@VPS, websocket
ちょっとあるものを作ろうとGPSだったりGeocode周りのAPIを調べていてサンプルを作ったので、載せたいたと思います。今回は、Geocodeです。GPSのサンプルもあるので、機会があれば載せたいと思います。
サンプルについては、Geocoding (Tatsuya Wiki)を参照下さい。また、サンプルプロジェクトもzip形式でアップしています。
今回のGeocodeでつまずいたのは、Landmark#getNameなどの戻り値を以下のように文字連結しようとするとエラーで落ちていたことです。最初はここで落ちているとは思わず、かなり遠回りして発見しました。ちなみに、今も原因がわかっていません。十分にjavadocを読んでいないのが原因かもしれませんが^^;明日にでも読んでみようと思います。
StringBuffer buffer = new StringBuffer(); buffer.append(landmark.getName() + "\n");
Tags: BlackBerry, geocode
昨日リリースしたばかりですが、本日バグが見つかった為、再度アップしなおしました。
ダウンロードについては、公式サイトから行って下さい。