Home

CYPHER-WORKS(コピペプログラマから書けるプログラマへ)

「お泊まりごろごろgeek in twitterアプリ」に行ってきた

いまさら報告。
コンドミニアムに泊まってみんなでtwitterアプリを作ろう。
なる趣旨のイベントに行ってきました。

どんなことしかというと、下のがだいたいの流れです
◆9時に東京駅集合

◆車レンタルしてコンドミニアム(千葉県南房総市白浜)に出発

◆車内からでもあいのりという形でネットをする。雑談でSencha TouchやらTwitter関連の話をする。
途中アクアライン入り口でのメロンパンにテンションマックス。

◆コンドミニアム到着。荷物をおいてお昼に出掛ける。そこで今回のイベントの趣旨を聞きつつゴールを決める。
今回の趣旨は「Twitter APIを利用して、みんなでアイデアをだして、Webアプリを作成しよう」おおまかにこんな感じ。
30分くらいブレインストーミングをして作成するアプリを決めた。
別に目的ある人は、これに参加しなくて自分の作成したいものを作るのもあり(これ自分。協調性ない?汗)。

◆2時くらいから7時くらいまでガリガリプログラミング。得意な分野でわかれたような気がする。
デザイン出来る人はコーディング、JS得意な人はフロントまわり、その他、みたいな。
自分は作業BGMききながらプログラミング。
合宿さいこーと思ったひととき。

◆7時くらいになったら夕ごはん。このくらいになるとだいぶ形になってきた。
正直一日で完成できるくらいのゴールにしたのがよかったと思う。
こんだては主催者の方が野菜たっぷりカレーを作ってくれました。
ほんとありがとう。おいしかったです。というか家みたいな感じでほんとよかったです。
合宿さいこーと思ったひととき。

◆夕ごはん食べ終わって引き続き作業。
ごろごろしながら作業してなんとなく完成迎える。
これ。http://measure.nextfun.asia/
11時くらいまで。そっからお風呂入ったり、布団敷いたりでその日は終了。

◆朝。朝ごはんも主催者の方が作ってくれてみんなでおいしくほおばる。
ありがとう。
10時(だったかな?)前にはこのコンドミニアム出なきゃいけなかったので、
出発。帰路。

◆おわり。
ではなく、なんなとなく時間もあるから千葉のドイツ村に行った。
ビール飲みーの、芝そりーの、ゲーセンやりーの遊ぶ。
これでおわり。

所感:
かなり楽しかったです。
開発合宿は今まで一回も行ったことはないけど、こんなアットホームを感じる合宿はめずらしいと思います。
開発合宿に行く機会があるならこれからも行ってみようと思える経験になりました。
合宿に行った皆様ありがとうございました。

なおなお、2010/10/02に開催されるPHP Matsuriに行きます。
これも日にちをまたいで行われるイベントです。興味ある方はぜひ。

ではでは。

第五回CakePHP勉強会に行ってきた

  • 2010-05-30 (日)
  • PHP

第五回CakePHP勉強会に行ってきました。
以下、覚書の印象に残った事柄を抜粋して書きます。

内容

// {{{ 特報目玉イベント2010 安藤さん

海外からスカイプを通してのプレゼンです。
今年のCakeMatsuriの紹介をしてくれました。
発表のスライドショーです。http://www.slideshare.net/yandod/20100529-phpday

オールナイト!!!!!!!
朝9時から翌日5時まで
講演スペースと開発スペースが用意される?各自開発するスペース
未発表作成・機能のコンテストを開催
開発タイム終了後いデモを実施
作業スペースは終夜可
レクレーションやイベントを開催予定
Litiumの思想をセッション
ワークショップ
入門者向け、中級者向け、Litium入門者
プラグインライブラリ賞
wordpressやsymfonyでも
ハッカソン賞
イベント開始後にテーマに沿って開発
ドキュメント賞
ベンチマークなどなど
協賛して頂ける企業を募集
コンテスト各賞のスポンサー
ディナー協賛などなど
運営スタッフ募集

かなりおもしろそーー!!!!!
twitterの方で情報が流れます。@phpmatsuri
// }}}

// {{{ CakePHP 1.3 Stable cakephperさん
発表資料です。http://www.slideshare.net/ichikaway/cakephp-tokyo5

1.3の新機能

Routes
Prefix Routing
Adminルーディングと同じ扱いに
RouteClass
ルーティング処理マッチング時に自作クラス呼び出し
DBの値を見てマッチング処理を行う
→例1:該当のslugがDBにあればroutingする
→例1:urlの一部変更でリダイレクト

config/routes.php

App::import( 'Lib', 'routes/SlugRoute' );
Router::connect(
    '/:slug',
    array( 'controller' => 'posts', 'action' => 'view' ),
    array( 'routeClass' => 'SlugRoute' )
);

app/libs/routes/slug_route.php

class Slug extends CakeRoute {

    function parse( $url ) {

        $params = parent::parse( $url );

        App::import( 'Model', 'Post' );

        $Post = new Post();

        $count = $Post->find( 'count', array( 'conditions' =>
            array( 'Post.slug LIKE ?' => $params['slug'] . '%' ) ) );

        if( $count ) {
            return $params;
        }
        return false;
    }
}

Virtual Fields
DBカラムを仮想定義して、Fieldとして扱える
詳細:http://book.cakephp.org/ja/view/1608/Virtual-fields
  ;http://d.hatena.ne.jp/hiromi2424/20100204/1265274976

Validation
マルチバイト対応
maxLength()などはmb_strlen()を使用
mb_strlen()が定義されていなければマルチバイトクラスのMultibyte::strlen()を利用
国別のルールを適用
Phone, postcode, SSN
例;日本のpostcodeなら222-3333というルール
→例あるからスライド参照しろ

// }}}

// {{{ Ktai Library on CakePHP1.3 MASA-Pさん

ライブラリクラスの分割
絵文字や機種情報なとをすべて読み込むとメモリを消費するので
→サブクラス化して必要なものだけ読み込むようにした

絵文字コンバートルーチンの改良
絵文字総当たりで置換していたためとても重かったためアルゴリズムを改良
異なるエンコーディングへの変換の場合に
絵文字のキャッシュ機能を付加

IPアドレスによるキャリア判定を搭載!!!!!!!!!!!
ユーザーエージェント偽装によるアクセスを対応

// }}}

// {{{ コアライブラリのエレガントなハック hiromi2424さん
http://github.com/hiromi2424/hack_plugin

どうしてハックするの

アプリケーションポリシーの埋め込み
sessionハイジャック対策でlogを吐きたいときなどなど

どうやってハックするの
エイリアスみたいにマップされたオブジェクトを置換する

マップングされたオブジェクトの置換
Component::_loaded[ $key ] // $keyにくるのはコンポーネントの名前
オブジェクトへのリファレンス
フックするタイミング
initialize()

init() ←オブジェクトのマッピング
initialize()←プロパティなどの設定
startup()←staring process処理を開始

// }}}

// {{{ twitterとcloud serverとcakeで新規サービス 谷井さん
スライド:http://docs.google.com/present/view?id=d4b7q8p_21cq5hkjg8

twitterアプリの問題
APIの返事が遅すぎる

1時間に150回制限
oauthを使う
APIの返事が遅すぎる
twitterのデータをDBに保存してキャッシュすればいけんじゃない?

rackspaceはいい
スケールの設定
DNSの逆引きができる

負荷対策
apatchの設定
MaxClient 150 → 1000
MaxKeepAliveRequests 100 → 20000

mysqlの設定
my.cnfをいじる
[mysqld-5.0]
max_connections=450
thread_cache=450
key_buffer_size=10M
// }}}

以下LTです
「WordPressの中でCakePHPを使って連携する」
スライド:http://www.slideshare.net/karadweb/cakephp-and-wordpress
おもしろい。自分でも動かしてみようかな。

「CakePHPでjQueryを使ってみた」
http://github.com/nano-eight
スライド:http://www.slideshare.net/nanoeight/cakephpjquery-4357936
素晴らしすぐる!jQueryのプラグインをヘルパーで利用できるようにする。
使わせてもらいます!

「CakePHP tips for mys next project」
スライド:http://bit.ly/9yFAps
参考になるサイトを紹介してくれました。

「あのCMS eZ publishをCakePHPのModelにしちゃう」
スライド:http://prezi.com/ugsu8hwgypud/datasource-for-ez-publish-in-cakephp/
eZ publishとCakePHPの連携方法です

実”戦”CakePHP Plugin」
スライド:http://www.slideshare.net/k1LoW/cakephp-plugin-4364102
pluginの紹介でした。

所感

自分的にはKtai Libraryがip addressチェックを実装してくれていることがうれしいですね。
jQueryプラグインをヘルパーで吸収して利用出来るようにしたnano_eightさんのサンプルとソースはかなりそそられます。
「勉強会は懇親会が本番です。」こういうふうに言う勉強会は珍しいですよね。
運営者が勉強会を運営するにあたって大切にしていることが、勉強会で出逢う人との「つながり」だそうです。
やはり運営にそういう意識があるからなのか、アットホームな勉強会になりとても楽しいものでした。
この場でお礼申し上げます。運営者の皆様、また発表者の皆様ありがとうございました。

話は変わりますが、勉強会で全員にプレゼントされたマシュマロです。http://twitpic.com/1s1rmx
かわいいですね。

ではでは

Ktai Library勉強会に行った

  • 2010-04-25 (日)
  • PHP

第二回目が開催されたので行ってきましたー。

概要

アジェンダとしては、「PHPで作る携帯サイト-デベロッパーズガイド-」の第五章を読んでローカルで実装するのみといったかなりシンプルなものです。

ただ特典というかかなりうれしいのが本の著者であるMASA-Pさんがサポートとして出席してくれていることです。
分からないことや、質問は直接著者に問うことができます。またまた誤植の報告などもこの場ですることもw・・。

実際勉強会の雰囲気はモクモク開発を行う人、談笑しながら開発を行う人、MASA-Pさんに質問をしながら開発を行う人それぞれです。

所感

本に沿って勉強していく中で、だいたい躓くところが決まっているようでした。今回でいうと、configurationモデルの存在です。cakephpにもともとあるconfigureクラスと勘違いしてモデルの生成をしないで開発を進めていくと躓きます。あらかじめconfig/sqlにあるソースでテーブルを作成しておきましょう。また後々にシェルの説明がある箇所で躓くことが多いようです。こういう場で発見できた躓きポイントは、後から勉強する人たちのためにサポートサイトに残しておきたいですね。

あと、今後の勉強会ではKtai Libraryを使用した携帯サイト開発実体験というライトニングトークがあったらいいですよねー。公式サイトで使用しました、でもこういう苦労がありました。みたいな。。今のところないなら、自分でやりますか、携帯サイト作りますか!!そんなこんなの所感です。

開催してくれたyashioさん、会場提供してくれたE2さん、サポートで来てくれているMASA-Pさん、ありがとうございました。

フレームワークを作る ルーティング処理 調査編 その3

  • 2010-03-16 (火)
  • PHP

Url Routing with PHPを紹介してきました。このシリーズはラストみたいです。ルーティング処理だけでなく、MVCフレームワークのCを重点的にした単純なフレームワークを作成したところで完了みたいですね。元ネタはこちらです。
最後にデモとサンプルソースのダウンロードがありますが、URLからコントローラーとアクションの指定まで考慮している作りみたいです。

このシリーズを読んでき来た人たちへ。今までのコードに多少修正を加えました。多少ディレクトリ構成とAxial_URLInterpreterとAxial_Command objectを、多くはAxial_CommandDispatchクラスです。今回新たにAxial_Controllerというクラスを作成していきます。

フロントコントローラからコントローラーへアクセスするようにしましょう。方法として、名前のつけ方とディレクトリ構成のルールを決めて、各コマンド(アクション)を実行させるようにしましょう。コマンドが実行されるとき、該当のコントローラーが読み込まれ、executeメソッドが読まれるようにします。

<?php
/**
  * コントローラー実行クラス
  */
class Axial_CommandDispatcher
{
    var $Command;

    //  コンストラクタ 実行するコマンド(コントローラー)の格納
    function Axial_CommandDispatcher(&$command)
    {
        $this->Command = $command;
    }

    // 指定したコントローラーが存在するか
    function isController($controllerName)
    {
        if(file_exists('axial/controllers/'.$controllerName.'/controller.'.$controllerName.'.php'))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    // コントローラー実行
    function Dispatch()
    {
        $controllerName = $this->Command->getControllerName();

        //  指定したコントローラーが存在していなかったらエラーコントローラークラスを指定
        if($this->isController($controllerName) == false)
        {
            $controllerName = 'error';
        }

        // 指定したコントローラーを読み込む
        include('axial/controllers/'.$controllerName.'/controller.'.$controllerName.'.php');

        // インスタンス生成・実行
        $controllerClass = $controllerName."Controller";
        $controller = new $controllerClass($this->Command);
        $controller->execute();
    }
}
?>

include(’axial/controllers/’.$controllerName.’/controller.’.$controllerName.’.php’);の箇所を見て分かるように、コントローラー名に使用するディレクトリを作成し、実行するファイル名もcontrollerコントローラー名.phpのように名前付しなければならないような作りにしています。

自分でコントローラーを追加していく中で継承しなければいけないクラスがあります。Axial_Controllerです。Axial_Controllerでは、ディスパッチコントローラーから(Axial_CommandDispatcher)execute()が必ず呼ばれ、指定したアクションを実行する処理をします。指定したアクションがなかったら_error()関数を実行し、そもそも指定アクションがnullだったら_default()関数を実行します。指定アクションの命名規則は_を頭に付けるようにしています。

<?php
class Axial_Controller
{
    var $Command;

    // コンストラクタ
    function Axial_Controller(&$command)
    {
        $this->Command = $command;
    }

    // アクションの指定がなければ、この関数が呼ばれる
    function _default()
    {
    }

    // 指定したアクションがなければ、この関数が呼ばれる
    function _error()
    {
    }

    // ディスパッチャからこの関数が呼ばれる。アクションの振り分け。
    function execute()
    {
    	// アクション取得
        $functionToCall = $this->Command->getFunction();

        // アクションがnull
        if($this->Command->getFunction() == '')
        {
            $functionToCall = 'default';
        }

        //  指定したアクションがない
        if(!is_callable(array(&$this,'_'.$functionToCall)))
        {
            $functionToCall = 'error';
        }

        // 指定したアクションを実行
        call_user_func(array(&$this,'_'.$functionToCall));
    }
}
?>

あとアクションの指定ができるようになったことで、URLのパース処理も多少修正されているはずです。ちょっと見てみましょう。

<?php
class Axial_URLInterpreter
{

    var $Command;

    function Axial_URLInterpreter()
    {
        $requestURI = explode('/', $_SERVER['REQUEST_URI']);
        $scriptName = explode('/',$_SERVER['SCRIPT_NAME']);
        $commandArray = array_diff_assoc($requestURI,$scriptName);
        $commandArray = array_values($commandArray);
        $controllerName = $commandArray[0];

        // ここが追加された箇所
        $controllerFunction = $commandArray[1];

        $parameters = array_slice($commandArray,2);

        // ルートで実行されているか
        // コントローラーの指定がなかったらrootコントローラーを指定
        // _defaultアクションが実行される
        if($controllerName == '')
        {
            $controllerName = 'root';
        }

        $this->Command = new Axial_Command($controllerName,$controllerFunction,$parameters);
    }

    function getCommand()
    {
        return $this->Command;
    }
}
?>

$controllerFunction = $commandArray[1];が追加されていますね。/でexploadされたものに0番目にコントローラー名、1番目にアクション名がくるだろうってだけですね。

他に特筆すべきことは、その2とその3のindex.phpを比べるとルーティング処理とコントローラーの呼び出し部分の処理は変わっていないのですが、HTMLの記述がなくなっています。各コントローラーのアクションで呼び出しされるようになっています。確かにMVCのCに重点的に実装したフレームワークになっているようです。

デモとサンプルコードがこちらに公開されています。

とりあえず、このチュートリアルは以上になります。

このチュートリアルを書いたDoug Hillさん。Thank you!!

ではでは。

フレームワークを作る ルーティング処理 調査編 その2

  • 2010-03-16 (火)
  • PHP

前回の続きです。

今回は第2弾です。元ネタはこちら

前回の例には2点問題があるので、解決していきましょう。
1点目に、URLをパースする方法
2点目に、コマンドの実行がswitch文での振り分け

目的は、もっとフレキシブルなURLの解釈をさせることです。そのために基本的なコマンドオブジェクトを作成していきます。

<?php
class Axial_Command
{
        var $commandName = '';
        var $parameters = array();

        // コマンド名格納、パラメータ格納
        function Axial_Command($commandName,$parameters)
        {
            $this->commandName = $commandName;
            $this->parameters = $parameters;
        }

        // コマンド名取得
        function getCommandName()
        {
            return $this->commandName;
        }

        // パラメータ格納
        function getParameters()
        {
            return $this->parameters;
        }
}
?>

次にURLをパースするクラスが必要です。前回で紹介したパース処理と上の処理を使ってコマンドオブジェクトを作成します。

<?php
class Axial_UrlInterpreter
{
	var $command;
	function Axial_UrlInterpreter()
	{
		$requestURI = explode('/', $_SERVER['REQUEST_URI']);
		$scriptName = explode('/',$_SERVER['SCRIPT_NAME']);
	for($i= 0;$i < sizeof($scriptName);$i++)
	{
		if ($requestURI[$i]	== $scriptName[$i])
		{
			unset($requestURI[$i]);
		}
	}

	$commandArray = array_values($requestURI);
	$commandName = $commandArray[0];
	$parameters = array_slice($commandArray,1);
	$this->command = new Axial_Command($commandArray[0],$parameters);

	}

	function getCommand()
	{
		return $this->command;
	}
}
?>

最後にコマンドオブジェクトを実行箇所に渡します。まだこの時点ではコードをシンプルにさせたいのでswitch文を使用しておきます。

<?php
class Axial_CommandDispatcher
{
    var $command;
    function Axial_CommandDispatcher($command)
    {
        $this->command = $command;
    }

    function Dispatch()
    {
        switch ($this->command->getCommandName())
        {
            case 'commandOne' :
                include('commandone.php');
                break;
            case 'commandTwo' :
                include('commandtwo.php');
                break;
            case '':
                include('root.php');
                break;
            default:
                include('default.php');
                break;
        }
    }
}
?>

今回の挙げたものを統合した例です。前回に作成した.htaccessが必要です。動作するデモとサンプルソースがこちらからダウンロードできます。

// 今回例に挙げたファイル読み込み
include('axial.command.php');
include('axial.urlinterpreter.php');
include('axial.commanddispatcher.php');

// URLパース処理 コマンド名取得 axial.command.phpにコマンド名とパラメータ格納
$urlInterpreter = new Axial_UrlInterpreter();

// 上のパース処理からコマンド名取得
$command = $urlInterpreter->getCommand();

// 実行処理にコマンド名格納
$commandDispatcher = new Axial_CommandDispatcher($command);

// コマンド実行
$commandDispatcher->Dispatch();

次にチュートリアルでswitch処理を直します。

以上です。

ではでは。

フレームワークを作る ルーティング処理 調査編 その1

  • 2010-03-15 (月)
  • PHP

ルーティング処理を調査します。
ルーティングの設定によってURLの構成とロジックファイルの構成が変わってくるので重要な部分です。

以下のサイトをまず見てみましょう。ルーティング処理のチュートリアルみたいですね。
Url Routing with PHP – Part One

ダメな例から挙げていってなぜダメかを説明しながらブラッシュアップするチュートリアルみたいです。

まず、.htaccessから紹介しています。

Options +FollowSymLinks #1
IndexIgnore */* #2
# Turn on the RewriteEngine
RewriteEngine On #3
# Rules
RewriteCond %{REQUEST_FILENAME} !-f #4
RewriteCond %{REQUEST_FILENAME} !-d #5
RewriteRule . index.php #6

#1 FollowSymLinksはシンボリックリンクのリンク先に辿れることを許可します
#2 「IndexIgnore命令はディレクトリをリスト表示しているときに隠すためのファイルのリストに追加します」だそうです。ワイルドカードで/を囲むって意味はすべてのファイルってことなんですかね??
#3 「mod_rewriteを使用するという宣言」だそうです。
#4 存在するファイルがなかったらtrue(下の処理にいく) / あったらfalse
#5 存在するディレクトリがなかったらtrue(下の処理にいく) / あったらfalse
#6 すべてindex.phpを実行しろ

みたいな感じだと思いまぷ(汗

エントリポイントを作成していきましょう
エントリポイントとは起点となる処理を意味します。

単純なルーティングを設定してみましょう。URLをスラッシュで区切る方法です。
例1:www.example.com/command/parameter1/parameter2/

$requestURI = explode('/', $_SERVER['REQUEST_URI']);

Array ( [0] => [1] => command [2] => parameter1 [3] => parameter2 [4] => )

しかしこの例は、commandの前にディレクトリが一つでも噛んでいると正常に動作しません。(フロントコントローラ-ここではエントリポイントのこと-がルートディレクトリでないと正常に動作しません)
例えば、次のURL:www.example.com/myapps/app1/command/parameter1/parameter2/
(commandの前にapp1が入っている。)
この場合$requestURIは次のようになります。

Array ( [0] => [1] => myapps [2] => app1 [3] => command [4] => parameter1 [5] => parameter2 [6] => )

myappsとapp1が邪魔なのでこれを除去するためには$_SERVER['SCRIPT_NAME'] の変数が必要です。

$scriptName = explode('/',$_SERVER['SCRIPT_NAME']);

中身は

Array ( [0] => [1] => myapps[2] => app1 [3] => index.php )

以下が不要なURL(パスと実行スクリプト)を消去する処理です。

$requestURI = explode('/', $_SERVER['REQUEST_URI']);
$scriptName = explode('/',$_SERVER['SCRIPT_NAME']);

for($i= 0;$i < sizeof($scriptName);$i++)
{
    if ($requestURI[$i]     == $scriptName[$i])
    {
                unset($requestURI[$i]);
    }
}
$command = array_values($requestURI);

以下が$commandの中身です。パスと実行スクリプトが失くなっています。

Array ( [0] => command [1] => parameter1 [2] => parameter2 [3] => )

実行コマンドを指定するスクリプトを書いてみましょう。

switch($command[0])
{
      case 'commandOne' :
                echo 'You entered command: '.$command[0];
                break;
      case 'commandTwo' :
                echo 'You entered command: '.$command[0];
                break;
      default:
                echo 'That command does not exist.';
                break;
}

こんなんブサイク過ぎですね。次のチュートリアルで改善されたスクリプトがあるますのでご安心を。

以下に実際に振る舞うサンプルが紹介されています。examples.phpaddiction.com/urlrouter/part_1/

次のチュートリアルはルーティング処理をクラスにまとめて、名前空間を使用することをやります。

フレームワークを作る 初級者からの脱却 調査編

  • 2010-03-04 (木)
  • PHP

フレームワークを自分で作れるようになりたい!

というか、CakePHPやら、Ethnaやら、Symfonyやら、ついでにCodeIgnighterだって、コアファイルと付属品が多すぎて実力がにぇ自分には(汗)把握できにぇ。

ということで自分で作ってみたらそれらの設計思想やらコードの理解が深まるんでないの?さらには自分のスキルも高まるんでないの?

よし!作るか、作りながらパクってもいいから、とにかく一から理解してみよう!

自分で景気付けしたわけですが、そんなこんなでこれから色々調べつつ自作フレームワーク完成を目標に挑戦してみようと思います。

まずは「PHP 自作フレームワーク」でGoogle先生に問い合わせましょう。

うん、けっこう作り方ありますね。

The symfony platform: Create your very own framework
これなんてほぼ作り方網羅してるんじゃないですかね。Symfonyを作成してる方かな。ただSymfonyのsfCoreAutoLoadを使用していて、それを前提に構築していくんですけど、結局コアな部分は大きいライブラリ。。かなり良い資料でもしかしたら解答以上のものかもしんないけど、雰囲気しか分からないです〜泣。
よく出てくる言葉「ジェネリック」。忘れてます。wikipediaだと

ジェネリック(総称あるいは汎用)プログラミング (generic programming)はデータ形式に依存しないコンピュータプログラミング方式。

これはデータ型でコードをインスタンス化するのか、あるいはデータ型をパラメータとして渡すかということに関わらず、同じソースコードを利用できるということである。

なるほど。それでこの例のものを自分の技術レベルで書ければ最高。けど、コア部分が高度。まぁ迷った時にこの設計方針と記述内容を参考にするくらいでいいかな。

 
次行ってみます。
PHPフレームワーク ちいたん

ちいたんとは世界最軽量のPHP用MVCフレームワーク

だそうです。
コードを見てみました。なるほど〜。ただ素のままだとフレームワーク上のエントリポイントがないので設定ファイルの読み込みを各アクションファイルに書かないといけないので煩わしいし、DRYじゃないですね。

はい、エントリポイントを追加してみた方がいらっしゃるみたいです。ソースも公開しているみたいですね。
ちいたん カスタム① フロントコントローラ化その他

ちいたんはかなり勉強になるんじゃないでしょうか。よい感じだと思います。たぶんかなり参考にするか、ほぼ流用していく気がします。

 
PHPで(M)VCフレームワーク自作
ルーティング処理だけの例ですね。

 
[memovie] 自作フレームワークについてとフォームからのPOST
これは勉強になる!素晴らしすぎではないでしょうか。参考にします。

PHP Webフレームワークを自作するサイトです。(@^^@)
んっ?全体さらってみたけど、これで読んだら今回の趣旨完結してしまわないか?
そのくらい俊逸サイトかも。

自作フレームワークをgithubへ公開
(くわ、そろそろ疲れてきた。寝よう。)あとでソース読ませて頂きます。

CakePHP内の処理を理解する
参考になるかな。とりあえず載せておきます。

軽量なMVCフレームワークの自作(改訂版)
セッション管理、永続オブジェクトの保持の概念など勉強になりました。

CakePHPの何か
おまけくらいに。古いバージョンのが今よりも軽量だろうから参考になるかも?

速構Web Framework
あとで読まさせて頂きます。

よし、Google先生に聞くのはこの辺でおしまいと。作りながら、上記のサイトだったり、有名どこのフレームワークを参考にやっていこうと思います。
 
最後にどんなフレームワークを作りたいか、というか最低限どの点を抑えていくか挙げていきたいと思います。
・MVCパターン
・エントリポイントのティスパッチャでコントローラー、モデル、ビューを振り分ける。
・コントローラー、モデル、ビューはフォルダ毎に分かれる構成。
・セッション管理
・データベース接続管理

とりあえず以上のものを目指します。

ではでは。

第50回PHP勉強会に行った

  • 2010-02-28 (日)
  • PHP

第50回PHP勉強会に参加したので、そのエントリ。

発表は三つあって、
1. mixiアプリについて(Weboo!)
2. PHPでWEB開発を行うようにしてオープンソーシャルアプリを作る(KuniTsuji)
3. 運用した気になるモバイルオープンソーシャル (個々一番)

1. mixiアプリについて(Weboo!)

  • ユーザ属性
  • だいたい20代以前半。ちょっと女性

  • アプリ嗜好性
  • mixi:ソーシャルグラフ(リアルなともだちと)
    gree:バーチャルグラフ(見ず知らずな人)

  • ユーザアクティブ数
  • ログインユーザ:1189 →1257
    滞在時:2:40→ 3:42
    pV:150.6→ 270

  • PCでは、OpenSocial Javascript API。モバイルでは、OpenSocial Restful Protocol。
  • 最新阪のOAuthにはバグがある
  • $request->to_header();

    引数に以下みたいなものを指定する
    $reqeust->to_header( apimixipralform);(たぶん間違ってます)

  • どんアプリがヒットするか?
  • ・友人と一緒に遊べないアプリは流行らない
    ・分かりやすさが大事
    ・ほとんど友人からの招待でアプリを始める
    ・友達と一緒にプレイしている空気感
    ・巻き込み感→友人に紹介したくなる
    ・継続性→変化がある→更新情報など
    ・初期はアプリはゲームと勘違いされていたが、結局長くプレイされるのはコニュニケーション性が強いアプリ

  • モバイルの開発もPCからアクセス可能な静的ファイルを配信可能なサーバも準備中
  • 3/16にDeNA技術セミナーがあります

 
所感:アプリを作成する上でコミュニケートするという部分がミソだよということを伝えたかったように思われます。人間の心理を利用したもの、飽きられないものが人気を出す秘訣ではないかなと感じました。

2. PHPでWEB開発を行うようにしてオープンソーシャルアプリを作る(KuniTsuji)

  • webサービスをそのままmixiアプリに移植することができないか?
  • モバイルは可能そうだが、PCはできなそう。

  • CodeIgnighterを利用してやってみた。
  • ・通常のaタグと、formタグは利用できない。それを吸収する処理を利用した。(anchor(),form_openなどの関数を作成)
    ・セッションの処理、oauthの処理を隠蔽

  • その他処理
  • 1.gadgets.util.registerOnLoadHandler(init)でVIEWER,OWNER,アプリ情報を取得
    2. gedgets.io.makeRequest
    3. gadgets.views.requestNavigateTo(supportedViews[view].params)で遷移処理

 
所感:既存にあるサービスをそのままオープンソーシャル対応にする試みは大変ファンキーなものです。ぜひ汎用化できたら是非オープン化してほしいものです(笑)

3. 運用した気になるモバイルオープンソーシャル (個々一番)

  • PCとモバイルの違い
  • 絵文字
    javascriptが使えない
    会社間契約が必要

  • 公式・勝手サイトの違い
  • 携帯識別番号が必要ない
    opensocailの仕組み上で認証する

  • 注意点(opensocialに限らず)
  • 外部のAPIは信用しない
    ・返事が帰ってこない
    ・バグだってあるさ

  • ちょっとしたヒント
  • PHPでAuthorzationヘッダとれないよ
    Basic認証とかはパースして置き換えてるよ
    apache_request_headersを使うとよい

  • ゆるふわコーディング
  • エラーが出ました画面より、mixiと繋がりませんみたいな画面を出したほうがよい

  • インフラ・パフォーマンス
  • 画像生成をキュー処理に書き換え
    画像サーバを外部に→AmazonS3
    サーバ間に間に合わないので一部の機能をEC2へ。現在は使ってない
    memcacged適用範囲を増やす
    L7ロードバランサを増やす
    DBマスター分割

    結局いいサーバかったら(100万しないくらい)問題の部分は解決した

所感:インフラの戦いは人気が出たらマストです。結局性能の高いサーバを購入したら悩んでいたことが解決したっていうのは、お金がある時分はいいのですけど、最初はなかなかアプローチできないことかもしれませんね。やはりウノウさんが試したようにできることのなかで最善のパフォーマンスを出すことが必須なような気が致します。その過程を発表して頂いて勉強になりました。ありがとうございます。

最後に会場を提供して頂いた株式会社コンテンツワン川井様、並びにスピーカー、運営して頂いた方々ありがとうございました。

ではでは。

VirtualBox install CentOS to Mac

いつもミドルウェアのインストールはapt-getやらyumやらportやらで楽していたので、丁度環境構築する機会があったのでソースからのインストールでやってみました。以下その作業ログです。

CentOSのインストール過程は下記のサイトに詳しく記載されています。必要最低限のものしか設定したくない人にはうってつけの参考サイトです。
CentOS5インストール

ちょっとはまりました。ネットワークに繋がらなかったので以下のコマンドを叩きました。

ifdown eth1
ifup eth1

こんな設定ありなのか・・・
 
ホストOSからゲストOSにsshとhttpの接続をする設定。かなりはまりました。以下のサイト通りに行えばできます。ネットワークの設定の箇所とiptablesの設定の箇所です。
VirtualBox 2.2 と CentOS 5.3 でローカル開発環境

 
ちょっと余談。ここまでくる間に使えそうな情報を見つけたので載せておきます。

FireWallとSELinuxの設定画面を再表示するコマンド
$ system-config-securitylevel-tui
コマンドラインからVirtualBox立ち上げ
$ VBoxManage startvm “仮想マシン名” –type headless
コマンドラインからVirtualBox止め
$ VBoxManage controlvm “仮想マシン名” poweroff

 
 
では、やっとお目当ての者たちを取り込みます。
apacheインストール
まず、必要なライブラリインストール

yum -y install gcc
yum -y install openssl-devel

ソースの置き場所はこちら:http://ftp.riken.jp/net/apache/httpd/

# cd /usr/local/src/
# wget http://ftp.riken.jp/net/apache/httpd/httpd-2.2.14.tar.gz
# tar xvfz httpd-2.2.14.tar.gz
# cd httpd-2.2.14
# ./configure –prefix=/usr/local/apache2 –enable-module=so –enable-so –enable-ssl –enable-rewrite

起動スクリプトの作成
参考サイト:CentOS 5.3にApache をインストールの起動スクリプトの作成以降を同じことやりました。
ちょっとつまづいたのは、参考サイトの中にある実行ユーザを作成したあと、httpdを起動しようとしたら以下のエラーが吐かれました。

httpd を停止中: [失敗]
httpd を起動中: (98)Address already in use: make_sock: could not bind to address [::]:80
(98)Address already in use: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
Unable to open logs [失敗]

すでに使用しているポートに働きかけしてるから失敗?
同じサイトなのかな。解決方法が載ってます。apacheの再起動でエラー (98)Address already in use: make_sock: could not bind to address [::]:80とても参考になりました。ありがとうございました。

 
MySQLのインストール
ソースはこちら:http://ftp.iij.ad.jp/pub/db/mysql/Downloads/MySQL-5.1/
参考サイトはこちら:http://wiki.livedoor.jp/callistoiv/d/MySQL%20Memo
http://tatsunet.net/contents/linux/centos4/lamp/
必要なもの取得

# yum -y install ncurses-devel
# wget http://ftp.iij.ad.jp/pub/db/mysql/Downloads/MySQL-5.1/mysql-5.1.41.tar.gz

ユーザ作成

# groupadd mysql
# useradd mysql -g mysql -M -s /sbin/nologin

インストール作業

# tar xvfz mysql-5.1.41.tar.gz
# cd mysql-5.1.41
# ./configure –prefix=/usr/local/mysql –with-charset=utf8 –with-collation=utf8_general_ci –with-extra-charsets=all –with-readline –with-mysqld-user=mysql
# make
# make install

DB初期化

# ./scripts/mysql_install_db
# chown -R mysql:mysql /usr/local/mysql/var/
# /usr/local/mysql/bin/mysqld_safe –user=mysql &
# /usr/local/mysql/bin/mysqladmin -u root password ‘XXXXXXXXXX’
# /usr/local/mysql/bin/mysqladmin -u root -p shutdown

MySQLの自動起動の設定

# cp support-files/mysql.server /etc/rc.d/init.d/mysql
# chmod 755 /etc/rc.d/init.d/mysql
# chkconfig –add mysql
# chkconfig –list mysql
mysql 0:off 1:off 2:on 3:on 4:on 5:on 6:off
# /etc/rc.d/init.d/mysql start

 
では次に、PHPインストール
ソースの置き場所はこちら:http://www.php.net/downloads.php
参考サイト:http://sj6.org/vmware_setup_by_centos/
必要なものインストール

# yum install libxml2*
# yum install libjpeg*
# yum install gd-devel
# yum install openldap-devel
# yum install libxslt-devel
# yum -y install curl-devel
# yum -y install libmcrypt-devel

# wget http://jp.php.net/get/php-5.3.1.tar.gz/from/this/mirror
# tar xvfz php-5.3.1.tar.gz
# cd php-5.3.1
# ./configure –prefix=/usr/local/lib/php5 –with-apxs2=/usr/local/apache2/bin/apxs –with-pear=/usr/local/lib/php5/pear –enable-zend-multibyte –enable-mbstring –enable-mbregex –with-xsl –with-mysql=/usr/local/mysql –with-pdo-mysql=/usr/local/mysql –with-curl –with-curlwrappers –with-zlib-dir –with-zlib –with-gd=shared –with-png-dir –with-jpeg-dir –with-ttf –with-ldap=shared –with-mcrypt
# make
# make install
# cp php.ini-development /usr/local/lib/php.ini

次に、/usr/local/lib/php.iniを編集します。編集内容は参考サイトにある通りにしました。
最後に/usr/local/apache2/conf/httpd.confに.phpがphpだと分からせるようにする設定を記述します。

<IfModule dir_module>
    DirectoryIndex index.html index.php # index.phpを追加
</IfModule>

 ・・・・・
    # If the AddEncoding directives above are commented-out, then you
    # probably should define those extensions to indicate media types:
    #
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz
    AddType application/x-httpd-php .php # この行を追加

/usr/local/apache2/htdocs/phpinfo.phpを作成したりして動作確認。

以上です。ふぅ、かなり時間かかった。

ではでは

ExtJS GridPanelについて groupingとか

前回はGridPanelでインタラクティブに動く方法を調査してみました。
今回はGridPanelにレンダリングする情報を、グループに分けてみようと思います。

グループに分けていない普通のグリッドは以下の記述で実現できます。というか前回の一番最初に紹介したものと同じです。
デモはこちら

    <script>
    Ext.onReady(function(){

        Ext.BLANK_IMAGE_URL = './extjs/resources/images/default/s.gif';
        Ext.QuickTips.init();

        // グリッド設定
        var grid = new Ext.grid.GridPanel({

            // 表示設定
            renderTo: document.body,

            // フレーム設定
            frame: true,

            // サイズ設定
            height: 300,
            width: 500,

            // ボーダー設定
            border: false,

            // カラムモデル設定
            cm: new Ext.grid.ColumnModel({

                columns: [{
                    width: 180,
                    header: '作業年',
                    dataIndex: 'YYYYMM',
                    sortable:true
                },{
                    width: 180,
                    header: '作業年月',
                    dataIndex: 'DD',
                    sortable:false
                },{
                    width: 180,
                    header: '出勤時間',
                    dataIndex: 'ATTEND_FROM'
                },{
                    width: 180,
                    header: '退出時間',
                    dataIndex: 'ATTEND_TO'
                },{
                    width: 180,
                    header: '状況',
                    dataIndex: 'STATUS',
                    sortable:false
                }]

            }),

            // ビュー設定
            viewConfig: {

                 // 強制フィット設定
                 forceFit: true

            },

            // ストア設定
            store: new Ext.data.JsonStore({

                 // 自動読み込み設定
                 autoLoad: true,

                 // 自動破棄設定
                 autoDestroy: true,

                 // リクエストURL設定
                 url: 'sss/attend-list.php',

                 // StoreID設定
                 storeID: 'AttendListStore',

                 // ルートノード設定
                 root: 'items',

                 // ID設定
                 idProperty: 'id',

                 // フィールド設定
                 fields: ['ID', 'YYYYMM', 'DD', 'ATTEND_FROM', 'ATTEND_TO', 'STATUS']

            })

        });

    });
    </script>

では次に、グルーピングをするコードを記載します。グルーピングしたデモはこちら

    <script>
    Ext.onReady(function(){

        Ext.BLANK_IMAGE_URL = './extjs/resources/images/default/s.gif';
        Ext.QuickTips.init();

        var grid = new Ext.grid.GridPanel({

            // 表示設定
            renderTo: document.body,

            // フレーム設定
            frame: true,

            // サイズ設定
            height: 300,
            width: 500,

            // ボーダー設定
            border: false,

            // カラムモデル設定
            cm: new Ext.grid.ColumnModel({

                columns: [{
                    width: 180,
                    header: '作業年',
                    dataIndex: 'YYYYMM',
                    sortable:true
                },{
                    width: 180,
                    header: '作業年月',
                    groupable: false,
                    dataIndex: 'DD',
                    sortable:false
                },{
                    width: 180,
                    header: '出勤時間',
                    groupable: false,
                    dataIndex: 'ATTEND_FROM'
                },{
                    width: 180,
                    header: '退出時間',
                    groupable: false,
                    dataIndex: 'ATTEND_TO'
                },{
                    width: 180,
                    header: '状況',
                    groupable: false,
                    dataIndex: 'STATUS',
                    sortable:false
                }]

            }),

            // ビュー設定
            viewConfig: {

                 // 強制フィット設定
                 forceFit: true

            },

            // ストア設定
            store: new Ext.data.GroupingStore({

                // 自動読み込み設定
                autoLoad: true,

                // 自動破棄設定
                autoDestroy: true,

                // リクエストURL設定
                url: 'sss/attend-lIst.php',

                // ソート設定
                sortInfo: {
                    field: 'YYYYMM',
                    direction: 'DESC'
                },

                // グループ単位でソート設定
                groupOnSort: true,

                // グループ単位でのソート設定
//                groupDir: 'DESC',

                // グループフィールド設定
                groupField: 'YYYYMM',

                // StoreID設定
                storeID: 'AttendListStore',

                // 読み込み設定
                reader: new Ext.data.JsonReader({

                    // ルートノード設定
                    root: 'items',

                    // ID設定
                    id: 'id',

                    // フィールド設定
                }, ['ID', 'YYYYMM', 'DD', 'ATTEND_FROM', 'ATTEND_TO', 'STATUS']
                )

            }),

            view: new Ext.grid.GroupingView({
                forceFit:true,
                hideGroupedColumn: true
//                groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
            })
        });

    });

    </script>

それでは、コードを見ていきましょう。上と下で違うのはデータレコードを保持する設定のstoreと、それに付随するviewの設定です。まずstoreの設定から見ていきましょう。上はExt.data.JsonStoreを設定していますが、下はExt.data.GroupingStoreを設定しています。もう少し詳しく見てみると、GroupingStoreの中でJsonStoreを設定しています。
そもそもstoreは何を設定するかというと、Ext.data.storeを設定することになっています。ではGroupingStoreは大丈夫なのかというとExt.data.GroupingStoreはExt.data.storeを継承しているので使用しても問題ないようです。

では、グルーピングで設定しているコンフィグオプションを見ていきましょう。、

                // ソート設定
                sortInfo: {
                    field: 'YYYYMM',
                    direction: 'DESC'
                },

                // グループ単位でソート設定
                groupOnSort: true,

                // グループ単位でのソート設定
//                groupDir: 'DESC',

                // グループフィールド設定
                groupField: 'YYYYMM',

                // StoreID設定
                storeID: 'AttendListStore',

                // 読み込み設定
                reader: new Ext.data.JsonReader({

                    // ルートノード設定
                    root: 'items',

                    // ID設定
                    id: 'id',

                    // フィールド設定
                }, ['ID', 'YYYYMM', 'DD', 'ATTEND_FROM', 'ATTEND_TO', 'STATUS']
                )

sortInfoはソート順を指定します。ローカルなソートのために、ソート方向(direction)プロパティが大文字小文字の違いを識別することに注意してください。
そして次にgroupOnSort 。これはgroupFieldで指定したフィールドでソート可、不可設定の役割をします。これをfalseにすると、groupFieldで設定したフィールドでソートできません。ここでちょっと話がそれますが、sortに関して今回私は手こずりました。というか、満足した形で終えられてません。あとで詳しく書かせてもらいます。
groupFieldは何でグルーピングするか。
storeIDはStoreMgrに登録するために使用するidです。 メモ: もし、id(非推奨)が指定される場合、割り当てがstoreIdに変わります。
readerはデータレコードを取り扱います。
あと、注意することに、groupFieldで指定したカラムはcm(columnModel)の方でも記述しないと正常に動作しませんので気をつけてください。

最後にviewの設定を付け加えます。hideGroupedColumnでグルーピングしたカラムを隠しています。

            view: new Ext.grid.GroupingView({
                forceFit:true,
                hideGroupedColumn: true
//                groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
            })

さてさて、とりあえず動くようにはなると思います。
 
 
ただ、ソート処理に関して今でもクエスチョンの疑問符が頭の上に浮いてます。

デフォルトではグルーピングのソート処理は、グルーピングされた中のレコードを対象にソート処理ができます。グルーピングでソート処理していないデモです

今回、私はグルーピングした中の情報だけでなく、グルーピングそのもののソートも実現しようとしました。デモを見ていただければ、グルーピングした情報でソートできると思います(デモではグルーピングしたフィールドが隠れているので、一旦「作業年月」のフィールドを表示してからでないと見ることができません)。groupOnSortを指定すると中のレコードでなくグルーピングでソートができるようになりました。
で、問題はgroupField以外でソートしようとすると、そのカラムでグルーピングしようとしてしまいデフォルトで実現されていたグルーピング内でのソートが実現できませんでした。なのでgourpField以外はソートできないようにしています。本当はグルーピングされた中のレコードでソートしたかったのですけど。。

う〜ん、そんなこんなで実現できなかったこともあるので、今回の実装は満足していません。分かる方いましたら是非教えてください。

ではでは

Home

Search
Feeds
Meta

Return to page top