Web技術勉強会 20100925
-
Upload
ryuichi-tanaka -
Category
Documents
-
view
859 -
download
3
description
Transcript of Web技術勉強会 20100925
WEB技術勉強会技術勉強会技術勉強会技術勉強会RYUICHI TANAKA
再びのPHPでオブジェクト指向プログラミング
2年前を振り返ってみる
PHPでオブジェクト指向
� 2年ほど前に挑戦している
� http://www.slideshare.net/mapserver2007/web5sli
de-share
� http://www.slideshare.net/mapserver2007/web11-� http://www.slideshare.net/mapserver2007/web11-
presentation
� 今見るとさすがにいろいろ足らないところがある…。
OOPを導入した自作ブログ
� 2008年10月くらいから開発開始
� 現在は開発終了して稼働中
� http://summer-lights.jp/diarysys4
� 主要機能はすべてクラス化� 主要機能はすべてクラス化
� 自作のテンプレート機構
� MVCを意識
ディレクトリ構造(一部抜粋)
� /
� css/
� php/
� func/ ... 関数群� func/ ... 関数群
� class/ … クラスファイル群
� load.php … 通常の受信処理を一括処理
� ajax.php … Ajaxの受信処理を一括処理
� tmpl/
� index.php
1. index.php(View)はController(load.php)に表示処理を依頼する2. load.phpはリクエストパラメータから判断して処理を開始する3. func/xxx.phpはModel(php/class)に処理を依頼する4. Modelは処理結果をControllerに返す5. Controllerは処理結果を加工してViewに返す6. Viewは処理結果を表示する
問題点(MVC)
� Contorollerが肥大化
� すべての処理をload.php, ajax.phpで一手に引き受けている=保守性低下
� Modelも肥大化� Modelも肥大化
� 個人的にはもう一層挟んだほうがスリム化すると思う
問題点(構造)
� ディレクトリ構造
� MVCではなく言語単位になっているので美しくない
� Controllerがクラス化されていない� Controllerがクラス化されていない
� 汎用クラスとdiarysys4専用Modelが混在
� 分けたほうが再利用性アップ
� ルールが弱い
� 必ず実装すべきメソッド定義がない
反省点を踏まえて反省点を踏まえて
再挑戦
改善(MVC)
� Modelよりコアな層を追加� Library層を追加。ModelはLibraryに対して
(ControllerがModelへ送るように)メッセージを送信する
� Libraryは原則的に汎用性を高くする� システム専用になる場合もあり
� Libraryの用途は3つ� DB処理
� 外部サービス処理
� その他汎用的処理
改善(MVC)
� Libraryはシステムのコアを操作するのでテストをきっちり行う
� Libraryにバグがなければそれを利用するだけのModelのバグもなくなる(はず)のModelのバグもなくなる(はず)
� Modelを利用するだけのContorollerのバグもなくなる(はず)
改善(MVC)
� 役割分担を明確に� Controller
� ロジックはかかない(分岐処理はOK)
� Modelを呼ぶかViewを描画するのどちらか
� Model� Model� システム専用のロジック(SQLなど)を書く� Libraryを呼ぶかControllerに値を返すのどちらか
� View� Controllerからの描画命令に従って描画するだけ
� Library� DB接続などコアな処理または外部リソースにアクセスする。汎用性を高めている。
改善(構造)
� ディレクトリ構造を見直し
� Railsを参考にContoroller、Model、View、Libraryディレクトリを作成
� 静的ファイルはすべてView扱い� 静的ファイルはすべてView扱い
� 処理はひとつのファイル(load.php)で一手に引き受けない
� 必要な処理ごとにContorollerをコールする
ディレクトリ構造
� /� controllers/
� ApplicationController.php … (抽象クラス)
� (各種処理ごとのController)
� models/( Model)� (各種処理ごとのModel)
� views/� images/
� js/
� lib/� DB.php
� DAO.php
� etc.
基本的な処理の流れ
� init.phpでContorollerをインスタンス化
� 各画面個別のContorollerをコール
� ContorollerはModelをコール
� Modelは必要ならLibraryをコール
� Libraryはメインの処理を実行し結果を返す
� Contorollerはlib/Template.phpをコールして結果を指定したテンプレートを表示する
デザインパターンデザインパターン
を適用
Singletonパターン
� Singletonパターンとは
� インスタンスが必ず1つしか生成されないことを保証する。
� 複数のインスタンスを作る必要がない場合は� 複数のインスタンスを作る必要がない場合はメモリの節約のために適用することがある
� 今回適用したクラス
� DB.php データベース接続クラス
DB.php
private $manager = null;
private function __construct() {}
protected function getManager($dbname) {
// ここでDB接続処理(PDO)
$manager = new PDO();$manager = new PDO();
return $manager;
}
ポイント:・コンストラクタはprivateで宣言・DB.phpは継承して使うのでインスタンスを返却するメソッド
(getManager)はprotectedで宣言
DAO.php
class DAO extends DB {
private static $dbaccessor = null;
private function __construct() {}
private function init() {} // 初期処理
public function getInstance() {
if (!is_object(DAO::$dbaccessor)) {
DAO::$dbaccessor = new DAO();
}
DAO::$dbaccessor->init();
return DAO::$dbaccessor;
}
// CRUDを処理するメソッドを以下に定義
}
DAO.php
� 使い方の例
$db = DAO::getInstance(); // (1)
$sql = “INSERT INTO …”;
$bind = array(“name” => “hirasawa_yui”);$bind = array(“name” => “hirasawa_yui”);
$db->insert($sql, $bind);
$db2 = DAO::getInstance(); // (1)と同じObj
// $db3 = new DAO(); // エラー
// $db4 = new DB(); // エラー
TemplateMethodパターン
� TemplateMethodパターンとは
� スーパークラスで処理の大枠を定義しておき、サブクラスで具体的実装を行う。
� 似たような処理だけど、微妙に違う複数� 似たような処理だけど、微妙に違う複数のクラスをまとめて処理したい場合に有効
� 今回の場合、外部サービス(Twitter、Blog、はてブなど)を一気に処理したかった
ServiceAbstract.php
� 外部サービスを処理するためのメソッドを定義。
� クラスはAbstract(抽象)クラスで定義
� 必須メソッドはabstractメソッドで定義� 必須メソッドはabstractメソッドで定義
� 具体実装をしてしまう場合(サブクラスで共通な処理)はprotectedで定義
� インスタンスから呼び出すメソッドはpublicで定義
ServiceAbstract.php
abstract class ServiceAbstract {
function __construct($params = array()) {
$this->prepare($params)
->fetch()
->process();
}}
final public function get() {
return $this->_data;
}
abstract protected function prepare($params);
abstract protected function fetch();
abstract protected function process();
ServiceBookmark.phpclass ServiceBookmark extends ServiceAbstract {
function __construct($params = array()) {
$this->_params = $params;
parent::__construct();
}
public function prepare($params) {
// ブクマデータ取得前処理
return $this;
}
public function fetch() {
// ブクマデータ取得処理
return $this;
}
public function process() {
// ブクマデータ加工
$this->_data = ...
}
使い方の例
$hatebu = new ServiceBookmark();
$data = $hatebu->get();
$twitter = new $twitter = new
ServiceTwitter($param);
$data2 = $twitter->get();
ポイント:
・異なる外部サービスの処理を同じにできる。
まとめて処理する
� リフレクションを使う
� リフレクションはクラスの内部情報が取得できたり、内部構造を書き換えたりすることができる。できる。
� 通常のクラスへのアクセス方法では不可能なことが可能(インスタンスからprivateメソッドを操作したり、定数を書き換えたり)
具体例
$services = array("Twitter", "Blog");foreach ($services as $service_name) {
$refClass = new ReflectionClass($service_name);
$service = $refClass->newInstance();
register($service->get());
}
ポイント:・文字列からインスタンスが生成可能・手続き方法が同じならループで一気に処理可能
今回こだわった点今回こだわった点
こだわったところ
� Contorollerは必ずApplicationControllerを継承する
� ApplicationController#render、ApplicationController#error はabstractで定義ApplicationController#error はabstractで定義し、サブクラスで必ず実装させる
� renderは画面描画、errorはエラー画面描画。これらはどんな画面でも必要なのでabstractにした
� 命名規則はRailsと同じ
こだわったところ
� リクエスト、セッションはModelで扱う
� $_GET, $_SESSIONのようにグローバルを直接
触れてしまうが、規則として直接触らないようにした。うにした。
� ApplicationControllerでリクエスト、セッショ
ンオブジェクトを生成し、サブクラスControllerで利用する。Modelに渡したい場合はController経由でインスタンスを渡す。
� 直接触ると値のValidationができないため
� 原則グローバル変数は使用禁止
こだわったところ
� DB処理
� DB.phpで接続処理,DAO.phpでCRUDの実行
� どちらもSingletonで実装
� DB.phpはインスタンスすら取得できない� DB.phpはインスタンスすら取得できない
� インスタンスが欲しければ必ずDAO.phpにアクセスしなければならない=DB接続を外部からさせない
� DAO.phpを定義することでPDOを抽象化
こだわったところ
� require処理
� phpのrequire/includeは実行中のファイルからの相対パス。厄介なのはInclude元のファイルからの相対パスになる点。からの相対パスになる点。
� 例:/index.phpで/class/Ajax.phpをIncludeしていて、Ajax.phpでrequrieするとき、開始位置は「/」になる(本当は/classになってほしい)
こだわったところ
� require処理
� どのファイルからrequire/includeをコールして
もかならずプロジェクトルートからの相対パスになるようにした=現在のディレクトリパスになるようにした=現在のディレクトリパスを考慮する必要がない
� 例:/ でも /class からでも
Common::import(“class/Ajax”)
でrequireされる。
こだわったところ
� Libraryのテストコードを作成
� SimpleTest(http://www.simpletest.org/)を使用
� symfonyなども利用しているテスティングフレームワークレームワーク
� Eclipse拡張あり(非常に良い)
� インストールが楽(requireするだけ。Eclipseで使う場合はそれすら不要)
� PHPUnitは非常に面倒+PHP4、PHP5で互換性なし
こだわったところ
� CSRF対策
� ワンタイムトークン方式で自前実装
� アクセスするたびにトークンが変化
� 間違ったトークンを渡すと更新できない� 間違ったトークンを渡すと更新できない
� 管理画面での内容更新で適用
こだわったところ
� 異なる外部サービスを同じ手続きで処理する設計
$services = array("Twitter", "Blog");
foreach ($services as $service_name) {
� 外から見ると同じ手続き(メソッド名)だが内部処理がすべて異なる処理を等価的に処理
� 条件分岐(if, switch)未使用
foreach ($services as $service_name) {
$refClass = new ReflectionClass($service_name);
$service = $refClass->newInstance();
register($service->get());
}
LifeStream
� 目的
� 自分の活動内容を一挙に集約すること
� 機能
作成したシステムへのゲートウェイ・リンク� 作成したシステムへのゲートウェイ・リンク
� 自分が行ったPOSTを管理
� Blog、Twitter、ブックマーク、その他サービス
� システム監視(外部非公開)
� ログ一括監視
� 障害監視、障害通知
トップページ(Portal)
プロジェクトページ(Project) :・過去に作ったシステムの説明とリンク
ログインページ(Login) :・はてな認証API+独自認証・デザインはあえてシンプルに
管理ページ(Management) :・プロジェクトページで表示する内容を制御・編集
開発してて思ったこと
� 他の言語の文化を学ぶことで得られるものがある� PHPにない流儀がJavaやRubyにある� PHPだけやってても気付けないことがある� PHPはある意味気軽にできる(環境構築が楽)� PHPはある意味気軽にできる(環境構築が楽)
� 使用できる言語をむやみに増やす必要はない� かえって混乱する、深さが足らなくなる� 適材適所
� テストがやりやすい言語を選ぶ� 結果的にPHPはやりにくい� プログラムを書き続けるとここに行くつく