最近、ちまちまとPHP を書くことがあって、
ちょっとしたWebアプリ等作るときに
CakePHP などのある程度規模のあるフレームワークを使うまでもないかなって思うことが多々あり、
そういった用途に 小規模向けなフレームワーク (マイクロフレームワーク)
PunyApp というのを作りました。
以前から自分だけで使ってて少しずつ更新して温めていたもので、
ある程度形になったので公開しました。
軽量で中小規模向けなもので、
おおまかな設計は CakePHP を参考にしてて、使い方も似てます。
PunyApp は MVC モデルで他のPHP拡張等は必要ありません。
ダウンロード
要件
- PHP 5.2.0 +
- mod_rewrite が有効 (Apache Server)
ライセンス
機能/特徴
- MySQL, PostgreSQL, SQLite, Posql に対応
- Controller, Model, Viewアクション
- フォームのValidation
- Viewテンプレート変数はデフォルトでHTMLエスケープされる
- Session (データベース)
- Cookie (デフォルトで暗号化)
- データベースエラー時などのEvent
- サンプルのログインフォームが入ってる
レイアウト
/application → アプリケーションディレクトリ
/controllers → コントローラ
/models → モデル
/views → ビュー
/libraries → 共通ライブラリやヘルパー等
/storage → SQLiteなどのファイルベースデータベースやログを保管
/logs → /storage配下を書き込み可にしておく
/databases → 同様
/settings → 設定
app-settings.php → アプリケーション設定ファイル
app-scheme.php → データベーススキーマ
/public → 公開ディレクトリ
/css
/js
index.php
/lib → PunyApp内部ライブラリ
/vendors → 外部ライブラリ等を入れるディレクトリ
index.php
Controllers
コントローラのアクションは、GET や POST などのリクエストメソッドで切り分けができます。
class SampleController extends PunyApp_Controller {
public $models = array('sample');
/**
* GET /login
*/
public function getLogin($params) {
$this->view->render('sample/login');
}
/**
* POST /login
*/
public function postLogin($params) {
$has = $this->sample->hasUser($params['id'], $params['pass']);
if ($has) {
$this->session->userid = $params['id'];
$this->redirect('home');
}
// ...
$this->view->render('sample/login');
}
/**
* Any /login (あらゆるリクエストメソッドに対応)
*/
public function anyLogin($params) {
// ...
}
/**
* Before /login (前処理)
*/
public function beforeLogin($params) {
if (!empty($this->session->userId)) {
$this->redirect('home');
}
}
/**
* After /login (後処理)
*/
public function afterLogin($params) {
// ...
}
/**
* GET /home
*/
public function getHome($params) {
// ...
}
}
before や after で前処理などが設定できます。
any は、すべてのリクエストメソッドに対応します。
メソッド名を any だけ
(function any() {})
にすると、404 にならずに any が実行されます。
引数 $params はリクエストパラメータが渡されます。
コントローラのメソッド名の命名規則は、
リクエストメソッド名
+
アクション名
になります。
GET に対応する hoge だったら、
getHoge
になります。
URL は、
http://www.example.com/コントローラ名/アクション名
となります。
アクション名は、リクエストメソッド名を外した名前です。
SampleController で getHoge の場合は、
http://www.example.com/sample/hoge
という感じになります。
ファイル名は sample.php です
AnyController (any.php) という名前にすると、すべてのリクエストに対応します。
Models
モデルは、PDO を使っているのもあり
基本的にプリペアドステートメントを使って実行します (プレースホルダ)。
class SampleModel extends PunyApp_Model {
public function addUser($userid, $email, $pass) {
// Insert
$sample = $this->newInstance();
$sample->userid = $userid;
$sample->email = $email;
$sample->pass = sha1($pass);
return $sample->save();
}
public function deleteUser($userid) {
return $this->delete(
array('userid' => '?'),
array($userid)
);
}
public function getUser($userid) {
return $this->findOne(
array(
'fields' => array('id', 'userid', 'email'),
'where' => array('userid' => '?')
),
array($userid)
);
}
public function hasUser($userid, $pass) {
return $this->has(
array(
'where' => array(
'userid' => ':userid',
'pass' => ':pass'
)
),
array(
':userid' => $userid,
':pass' => sha1($pass)
)
);
}
}
- array find ( array $query = array(), array $params = array())
find() の引数 $query は、 'distinct', 'fields', 'from', 'as', 'joins', 'where',
'group', 'having', 'order', 'limit', 'offset' が使えます。
public function getUserByName($name) {
return $this->find(
array(
'distinct' => false,
'fields' => array(
'U.id AS id', 'U.name AS name',
'U.category AS cat', 'P.url AS url'
),
'as' => 'U',
'joins' => array(
'type' => 'LEFT',
'table' => 'profile',
'as' => 'P',
'on' => array('U.id' => 'P.id')
),
'where' => array(
'name' => ':name'
),
'group' => 'cat',
'order' => 'id DESC',
'limit' => 10,
'offset' => 5
),
array(
':name' => $name
)
);
}
ある程度のクエリは扱えます。
モデル内では
$this->getDatabase()
が PDO として使えるので、
public function getName($id) {
$sql = 'SELECT name FROM foo WHERE id = ?';
$stmt = $this->getDatabase()->prepare($sql);
$stmt->execute(array($id));
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
上のように直接クエリを書いてもいいですが、
直接クエリを書かずにモデルの find() や delete() 等を使うメリットとしては、
'created' と
'modified' というフィールドが自動てセットされることが大きいです。
テーブル定義にあらかじめ created もしくは modified を定義しておくと、
insert (save), update のタイミングでそれぞれ現在時刻を設定します。
integer や int(11) で定義すると 現在の time() が設定されます。
datetime は Y-m-d H:i:s になります。
varchar(255) で定義すると現在時刻をミリ秒で設定します。
- created : 作成された日時
- modified : 更新された日時
これらはアプリケーション側で created, modified をパラメータに扱う場合は無視されます。
コントローラでのモデル定義は $models に使うモデル名(テーブル名)を記述します。
class SampleController extends PunyApp_Controller {
public $models = array('sample');
public function getLogin($id) {
$user = $this->sample->getUser( ... );
}
}
Views
$this->view->text = 'Hello!';
$this->view->render('index');
ビューでは PHP本来が持ってるテンプレートを使います。
<html>
<body>
<h1>Sample</h1>
<?php echo $text; ?>
</body>
</html>
テンプレート変数はデフォルトでHTMLエスケープされます。
$this->view->text = '<script>alert(1)</script>';
<p><?php echo $text ?></p> // <script>alert(1)</script>
Validation
リクエストパラメータのバリデーションはコントローラ内で行います。
class SampleController extends PunyApp_Controller {
public $validationRules = array(
'id' => array(
'required' => true,
'rule' => array('regex', '/^[a-z0-9]{1,10}$/i'),
'message' => 'Only letters and integers, max 10 characters'
),
'email' => array(
'required' => true,
'rule' => array('email'),
'message' => 'Invalid email address'
),
'pass' => array(
'required' => true,
'rule' => array(
array('minLength', 4),
array('maxLength', 20)
),
'message' => 'Min 4 characters, max 20 characters'
)
);
// ...
}
バリデーションのルールは
email, url, ip, between, minLength, maxLength, regex
等が定義されています。または自分で定義します。
その他、コールバックが使えます。
$this->validationRules['nickname'] = array(
'required' => true,
'rule' => array('callback', function ($value) {
return preg_match('/^[ぁ-ん]/u', $value) && preg_match('/[!]$/u', $value);
}),
'message' => 'ひらがなではじまって「!」で終わらないとダメです'
);
$this->validate() で実行します。
任意のルールを引数に渡すこともできます。
引数を省略すると $validationRules に対して実行します。
class SampleController extends PunyApp_Controller {
// ...
public function postLogin($params) {
if ($this->validate()) {
// ...
}
}
}
以下のViewメソッドからメッセージを取得できます。
- 名前を指定して取得 : string getValidationError( string $name );
- 最後のメッセージを取得 : string getLastValidationError( );
- 全部取得 : array getValidationErrors( );
- HTML<li>で全部取得 : string getValidationErrorMessages( array $attributes = array('style' => 'color:red') );
コントローラ内では
$this->view->getValidationError('hoge')
のように取得できます。
ビュー内では以下のようにして表示できます。
<div class="error">
<?php echo $this->getValidationError('param_name') ?>
</div>
インストールと設定
1. 展開したファイルをサーバーの任意のディレクトリに置きます
2. application/settings/app-settings.php を開いて設定します
$settings = array(
/**
* System settings
*/
'system' => array(
/**
* Debug mode
*
* true = show errors
* false = hide errors
*/
'debug' => true, // デバッグモードだとエラーが起きたとき表示される
/**
* Timezone
*
* e.g., 'America/Chicago', 'Asia/Tokyo' etc.
*/
'timezone' => '', // 日本の場合はここを 'Asia/Tokyo' に設定しておく
...
),
/**
* Database settings
*/
'database' => array(
/**
* Database engine
*
* Available engines: "mysql", "sqlite" and "posql".
*/
'engine' => '', // 使用するデータベース
...
),
/**
* Session settings
*/
'session' => array(
/**
* Session engine
*
* Available engines: "php", "file" and "database".
*/
'engine' => '', // セッションエンジンを設定
...
)
);
3. データベーススキーマを作成します、または application/settings/app-schema.php に記述します
4. application/storage 配下のファイル、ディレクトリのパーミッションを「書き込み可」に設定します
5. 1 で展開したディレクトリにブラウザでアクセスしてみて、PunyApp ~と表示されたら成功です
6. 実際に作ってみましょう
サンプル
/sample/
にサンプルのログインフォームが入っています。
/sample/
にアクセスして確認できます。
なにかあれば以下のレポジトリのIssues, または
@polygon_planet へお願いします。
レポジトリ