フラットなPHPから
 フレームワークへ
       株式会社 adingo
      前田 雅央 @brtriver
@brtriver
Steps


         フラットなPHP

             リファクタリング




          フレームワーク


        * フラットなPHP = 1ファイルで書かれたPHP
Steps
            フラットなPHP
             View部分の分離

        アプリケーション(ドメイン)部分の分離

          Viewにレイアウト機能を追加

             ページを追加する

          フロントコントローラー導入

            ライブラリを利用する


            フレームワーク
どのフレームワークを使う?


なぜフレームワークを使う?
Step 0




     DBからデータ取得し
          取得したデータを
         HTMLで表示する
<?php
$pdo = new PDO(
   'mysql:host=localhost;dbname=blog_db;charset=utf8',
                                                                           list.php
   'myuser',
   'mypassword',
   array(PDO::ATTR_EMULATE_PREPARES => false)
 );
$stmt = $pdo->query('SELECT id, title FROM post');
?>


<html>
  <head>
    <title>投稿の一覧</title>
  </head>
  <body>
    <h1>投稿の一覧</h1>
    <ul>
       <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?>
       <li>
          <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>">
             <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?>
          </a>
       </li>
       <?php endwhile; ?>
    </ul>
  </body>
</html>
<?php
$pdo = new PDO(
   'mysql:host=localhost;dbname=blog_db;charset=utf8',
                                                                           list.php
   'myuser',
   'mypassword',
   array(PDO::ATTR_EMULATE_PREPARES => false)
 );
$stmt = $pdo->query('SELECT id, title FROM post');
?>


<html>
  <head>
    <title>投稿の一覧</title>
  </head>
  <body>
    <h1>投稿の一覧</h1>
    <ul>
       <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?>
       <li>
          <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>">
             <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?>
          </a>
       </li>
       <?php endwhile; ?>
    </ul>
  </body>
</html>
<?php
$pdo = new PDO(
   'mysql:host=localhost;dbname=blog_db;charset=utf8',
                                                                           list.php
   'myuser',
   'mypassword',
   array(PDO::ATTR_EMULATE_PREPARES => false)
 );
$stmt = $pdo->query('SELECT id, title FROM post');
?>


<html>
  <head>
    <title>投稿の一覧</title>
  </head>
  <body>
    <h1>投稿の一覧</h1>
    <ul>
       <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?>
       <li>
          <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>">
             <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?>
          </a>
       </li>
       <?php endwhile; ?>
    </ul>
  </body>
</html>
<?php
$pdo = new PDO(
   'mysql:host=localhost;dbname=blog_db;charset=utf8',
                                                                           list.php
   'myuser',


DBからデータ取得
 );
   'mypassword',
   array(PDO::ATTR_EMULATE_PREPARES => false)


$stmt = $pdo->query('SELECT id, title FROM post');
?>


<html>
  <head>
    <title>投稿の一覧</title>
  </head>
  <body>
    <h1>投稿の一覧</h1>
    <ul>
       <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?>
       <li>
          <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>">
             <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?>
          </a>
       </li>
       <?php endwhile; ?>
    </ul>
  </body>
</html>
<?php
$pdo = new PDO(
   'mysql:host=localhost;dbname=blog_db;charset=utf8',
                                                                           list.php
   'myuser',


DBからデータ取得
 );
   'mypassword',
   array(PDO::ATTR_EMULATE_PREPARES => false)


$stmt = $pdo->query('SELECT id, title FROM post');
?>


<html>
  <head>
    <title>投稿の一覧</title>
  </head>
  <body>
    <h1>投稿の一覧</h1>
    <ul>
       <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?>



             取得したデータを表示
       <li>
          <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>">
             <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?>
          </a>
       </li>
       <?php endwhile; ?>
    </ul>
  </body>
</html>
クライアント             サーバー




          リクエスト
                  PHP +
Browser
                  HTML
          レスポンス
                  list.php
Good

       シンプル
       1ファイルたったの26行
Bad
アプリケーションの拡張性が無い


      体系化されていない
       再利用性がない
体系化されていない




フォームの送信を行うコードの追加したい...

            どこに書く?



   同じファイル?別ファイルで書く?
コードの再利用性が低い
 新しい別ファイルでlist.phpのコードを利用
 できない。




     新しいページを コピペで作成
クライアント                     サーバー


             エ スト       PHP + HTML
           リク

                 ポ ンス
            レス            list.php
 Browser




                                     Bad
クライアント                     サーバー


             エ スト       PHP + HTML
           リク

                 ポ ンス
            レス            list.php
 Browser                             COPY




                                     Bad
クライアント                      サーバー


             エ スト        PHP + HTML
           リク

                  ポ ンス
             レス            list.php
 Browser                              COPY
           リクエ
                スト

                         PHP + HTML
           レスポ
               ンス

                          list2.php



                                      Bad
Step 1




         View部分の分離
show.php
<?php
$pdo = new PDO(
   'mysql:host=localhost;dbname=blog_db;charset=utf8',
   'myuser',
   'mypassword',
   array(PDO::ATTR_EMULATE_PREPARES => false)
 );
$stmt = $pdo->query('SELECT id, title FROM post');
?>


<html>
  <head>
    <title>投稿の一覧</title>
  </head>
  <body>
    <h1>投稿の一覧</h1>
    <ul>
       <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?>
       <li>
          <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>">
             <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?>
          </a>
       </li>
       <?php endwhile; ?>
    </ul>
  </body>
</html>
show.php
<?php
$pdo = new PDO(
   'mysql:host=localhost;dbname=blog_db;charset=utf8',
   'myuser',



 );
   'mypassword',
                            ロジック部分
   array(PDO::ATTR_EMULATE_PREPARES => false)


$stmt = $pdo->query('SELECT id, title FROM post');
?>


<html>
  <head>
    <title>投稿の一覧</title>
  </head>
  <body>
    <h1>投稿の一覧</h1>
    <ul>
       <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?>
       <li>
          <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>">
             <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?>
          </a>
       </li>
       <?php endwhile; ?>
    </ul>
  </body>
</html>
show.php
<?php
$pdo = new PDO(
   'mysql:host=localhost;dbname=blog_db;charset=utf8',
   'myuser',



 );
   'mypassword',
                            ロジック部分
   array(PDO::ATTR_EMULATE_PREPARES => false)


$stmt = $pdo->query('SELECT id, title FROM post');
?>


<html>
  <head>
    <title>投稿の一覧</title>
  </head>
  <body>
    <h1>投稿の一覧</h1>
    <ul>
                 View(HTML)部分
       <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?>
       <li>
          <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>">
             <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?>
          </a>
       </li>
       <?php endwhile; ?>
    </ul>
  </body>
</html>
show.php
<?php
$pdo = new PDO(
   'mysql:host=localhost;dbname=blog_db:charset=utf8',
   'myuser',
   'mypassword',
   array(PDO::ATTR_EMULATE_PREPARES => false)
 );
$stmt = $pdo->query('SELECT id, title FROM post');


// HTML部分のコードを読み込む
require 'templates/list.php';

                        別ファイルに分離
show.php
<?php
$pdo = new PDO(
   'mysql:host=localhost;dbname=blog_db:charset=utf8',
   'myuser',
   'mypassword',  ロジック部分
   array(PDO::ATTR_EMULATE_PREPARES => false)
 );
$stmt = $pdo->query('SELECT id, title FROM post');


// HTML部分のコードを読み込む
require 'templates/list.php';

                        別ファイルに分離
クライアント                サーバー




          リクエスト   controller
Browser              PHP
                      list.php
クライアント                サーバー




          リクエスト   controller
Browser              PHP
                      list.php
クライアント                 サーバー




           リクエスト   controller
Browser               PHP
                       list.php




          レスポンス
                   view
                     HTML
                   template/list.php
クライアント                   サーバー




           リクエスト    controller
Browser                  PHP
                         list.php




          レスポンス
                    view
                         json
                   template/list-json.php
Good
       View部分の分離



   テンプレートの変更に強い
Step 2




  アプリケーション(ドメイン)
      部分の分離
index.php
<?php
$pdo = new PDO(
   'mysql:host=localhost;dbname=blog_db:charset=utf8',
   'myuser',
   'mypassword',
   array(PDO::ATTR_EMULATE_PREPARES => false)
 );
$stmt = $pdo->query('SELECT id, title FROM post');
index.php
<?php
$pdo = new PDO(
   'mysql:host=localhost;dbname=blog_db:charset=utf8',

         データベースへ接続する
   'myuser',
   'mypassword',
   array(PDO::ATTR_EMULATE_PREPARES => false)
 );
$stmt = $pdo->query('SELECT id, title FROM post');
index.php
<?php
$pdo = new PDO(
   'mysql:host=localhost;dbname=blog_db:charset=utf8',

         データベースへ接続する
   'myuser',
   'mypassword',
   array(PDO::ATTR_EMULATE_PREPARES => false)
 );

         全てのポストを取得する
$stmt = $pdo->query('SELECT id, title FROM post');
ドメイン
  データベースへの接続する
  全てのポストを取得する



   model.php に分離
<?php


// model.php


function get_database_connection()
{
  $pdo = new PDO(
   'mysql:host=localhost;dbname=blog_db;charset=utf8',
   'myuser',
   'mypassword',
   array(PDO::ATTR_EMULATE_PREPARES => false)
  );
  return $pdo;                                   get_database_connection
}


function get_all_posts()
{
  $pdo = get_database_connection();


    $stmt = $pdo->query('SELECT id, title FROM post');
    $posts = array();
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
      $posts[] = $row;
    }                                                    get_all_posts
    return $posts;
}
                                                         model.php
リファクタリング後のlist.php

<?php
require 'model.php';


$posts = get_all_posts();


require 'templates/list.php';


                       コントローラーは制御するだけ
リファクタリング後のlist.php

<?php
require 'model.php';            ロジック読込

$posts = get_all_posts();


require 'templates/list.php';


                       コントローラーは制御するだけ
リファクタリング後のlist.php

<?php
require 'model.php';            ロジック読込

$posts = get_all_posts();   全ポストを取得

require 'templates/list.php';


                       コントローラーは制御するだけ
リファクタリング後のlist.php

<?php
require 'model.php';            ロジック読込

$posts = get_all_posts();   全ポストを取得

require 'templates/list.php';    ビュー読込


                       コントローラーは制御するだけ
Good
アプリケーション(ドメイン)部分の分離


 コントローラが本来のコントローラーに


       シンプルなMVC
クライアント        サーバー




              リクエスト
                      controller
   Browser                   PHP
                             list.php



             レスポンス
                      view
                             HTML
                      template/list.php
クライアント        サーバー
                                        model
                                           Logic
                get_all_posts             model.php


              リクエスト
                      controller
   Browser                   PHP
                             list.php
                                                $POSTS


             レスポンス
                      view
                             HTML
                      template/list.php
Step 3




 Viewにレイアウト機能を追加
レイアウト機能

   Viewの共通部分を再利用できる仕組み
   <html>
     <head>
       <title>トップページ</title>
     </head>
     <body>
       <h1>こんにちは!こんにちは!</h1>
     </body>
   </html>

            template/layout.php
レイアウト機能

   Viewの共通部分を再利用できる仕組み
   <html>
     <head>
       <title>トップページ</title>
     </head>
     <body>
       <h1>こんにちは!こんにちは!</h1>
     </body>
                     動的部分を決定
   </html>

            template/layout.php
レイアウト機能

    PHPで書き換え
   <html>
     <head>
       <title><?php echo $title ?></title>
     </head>
     <body>
       <?php echo $content ?>
     </body>
   </html>

                 template/layout.php
ob_start() ... ob_get_clean()
<?php $title = '投稿のリスト' ?>                                 list.php

<?php ob_start() ?>
   <h1>投稿のリスト</h1>
   <ul>
     <?php foreach ($posts as $post): ?>
     <li>
        <a href="show.php?id=<?php echo
htmlspecialchars($post['id'], ENT_QUOTES, 'utf-8') ?>">
           <?php echo htmlspecialchars($post['title'], ENT_QUOTES,
'utf-8') ?>
        </a>
     </li>
     <?php endforeach; ?>
   </ul>
<?php $content = ob_get_clean() ?>


<?php include 'layout.php' ?>
ob_start() ... ob_get_clean()
<?php $title = '投稿のリスト' ?>                                 list.php

<?php ob_start() ?>
   <h1>投稿のリスト</h1>
   <ul>
     <?php foreach ($posts as $post): ?>
     <li>
        <a href="show.php?id=<?php echo
htmlspecialchars($post['id'], ENT_QUOTES, 'utf-8') ?>">
           <?php echo htmlspecialchars($post['title'], ENT_QUOTES,
'utf-8') ?>
        </a>
     </li>
     <?php endforeach; ?>
   </ul>
<?php $content = ob_get_clean() ?>


<?php include 'layout.php' ?>
ob_start() ... ob_get_clean()
<?php $title = '投稿のリスト' ?>                                 list.php

<?php ob_start() ?>
   <h1>投稿のリスト</h1>
   <ul>
     <?php foreach ($posts as $post): ?>
     <li>
        <a href="show.php?id=<?php echo
htmlspecialchars($post['id'], ENT_QUOTES, 'utf-8') ?>">
           <?php echo htmlspecialchars($post['title'], ENT_QUOTES,
'utf-8') ?>
        </a>
     </li>
     <?php endforeach; ?>
   </ul>
<?php $content = ob_get_clean() ?>


<?php include 'layout.php' ?>
ob_start() ... ob_get_clean()
<?php $title = '投稿のリスト' ?>                                 list.php

<?php ob_start() ?>
   <h1>投稿のリスト</h1>
   <ul>
     <?php foreach ($posts as $post): ?>
     <li>
        <a href="show.php?id=<?php echo
htmlspecialchars($post['id'], ENT_QUOTES, 'utf-8') ?>">
           <?php echo htmlspecialchars($post['title'], ENT_QUOTES,
'utf-8') ?>
        </a>
     </li>
     <?php endforeach; ?>
   </ul>
<?php $content = ob_get_clean() ?>
                                       出力せずにバッファリング
<?php include 'layout.php' ?>
ob_start() ... ob_get_clean()
<?php $title = '投稿のリスト' ?>                                 list.php

<?php ob_start() ?>
   <h1>投稿のリスト</h1>
   <ul>
     <?php foreach ($posts as $post): ?>
     <li>
        <a href="show.php?id=<?php echo
htmlspecialchars($post['id'], ENT_QUOTES, 'utf-8') ?>">
           <?php echo htmlspecialchars($post['title'], ENT_QUOTES,
'utf-8') ?>
        </a>
     </li>
     <?php endforeach; ?>
   </ul>
<?php $content = ob_get_clean() ?>
                                       出力せずにバッファリング
<?php include 'layout.php' ?>
クライアント        サーバー
                                        model
                get_all_posts              Logic
                                           model.php


              リクエスト
                      controller
   Browser                   PHP
                             list.php

                                                $POSTS

             レスポンス
                      view
                          HTML
                      template/list.php
クライアント        サーバー
                                        model
                get_all_posts               Logic
                                            model.php


              リクエスト
                      controller
   Browser                   PHP
                             list.php

                                                 $POSTS

             レスポンス
                      view
                          HTML
                      template/list.php

                                        layout
                                           HTML
                                           layout.php
Good
  テンプレートの可用性がUP


Bad
 テンプレートエンジンの機能より劣る
     Smarty, Twig...
テンプレートエンジンのメリット

   Pure PHP
<title><?php echo htmlspecialchars($title, ENT_QUOTES, 'utf-8') ?></title>




   Twig (escape)
                     <title>{{ title }}</title>


   Twig (un escape)
                  <title>{{ title¦raw }}</title>
Step 4




       ページを追加する
   blogの詳細ページ(show)
list.phpと同じ仕組を作る
<?php


require_once 'model.php';


$post = get_post_by_id($_GET['id']);


require 'templates/show.php';     特定の記事を取得




                            show.php
クライアント        サーバー
                                      model
               get_post_by_id              Logic
                                            model.php


              リクエスト
                       controller
   Browser                    PHP
                           show.php

                                               $POST


             レスポンス
                       view
                           HTML
                      template/show.php

                                      layout
                                          HTML
                                          layout.php
クライアント         controller
                                             サーバー
                      PHP
                                            get_all_posts
                      list.php

            view
                 HTML              $POSTS

            template/list.php


             layout                   model
  Browser           HTML                    Logic
                      layout.php            model.php



             view
                                    $POST
                   HTML
            tempalte/show.php

                 controller
                                       get_post_by_id
                        PHP
                      show.php
クライアント         controller
                                             サーバー
                      PHP
                                            get_all_posts
                      list.php

            view
                      一覧表示処理
                 HTML              $POSTS

            template/list.php


             layout                   model
  Browser           HTML                    Logic
                      layout.php            model.php



             view
                                    $POST
                   HTML
            tempalte/show.php

                 controller
                                       get_post_by_id
                        PHP
                      show.php
クライアント         controller
                                             サーバー
                      PHP
                                            get_all_posts
                      list.php

            view
                      一覧表示処理
                 HTML              $POSTS

            template/list.php


             layout                   model
  Browser           HTML                    Logic
                      layout.php            model.php



             view
                                    $POST
                   HTML
                      詳細表示処理
            tempalte/show.php

                 controller
                                       get_post_by_id
                        PHP
                      show.php
クライアント         controller
                                             サーバー
                      PHP
                                            get_all_posts
                      list.php

            view
                      一覧表示処理
                 HTML              $POSTS

            template/list.php


             layout                   model
  Browser           HTML                    Logic
                      layout.php            model.php



             view
                                    $POST
                   HTML
                      詳細表示処理
            tempalte/show.php

                 controller
                                       get_post_by_id
                        PHP
                      show.php
Good
 Model, Layoutが共通化できている




Bad
   リクエストを受けるPHPは
    model.phpを忘れずに
  requireしなければならない
Step 5




    フロントコントローラー
フロントコントローラー?




      全てのリクエストを
       最初に必ず受ける
      1つのPHPファイル
Before
クライアント                                   サーバー
         http://example.com/list.php
                         controller
                              PHP
                              list.php


   Browser
Before
クライアント                                   サーバー
         http://example.com/list.php
                         controller
                              PHP
                              list.php


   Browser
                         controller
                              PHP
                             show.php

    http://example.com/show.php?id=5
After
クライアント                             サーバー



             front controller   controllers
   Browser         PHP              PHP
                 index.php      controllers.php
After
クライアント                                              サーバー

http://example.com/index.php/list         list_action()

                       front controller          controllers
    Browser                  PHP                     PHP
                           index.php             controllers.php
After
クライアント                                               サーバー

http://example.com/index.php/list          list_action()

                       front controller           controllers
    Browser                  PHP                      PHP
                           index.php              controllers.php



                                          show_action(5)

http://example.com/index.php/show?id=5
フロントコントローラー (routing)
require_once 'model.php';
require_once 'controllers.php';


// リクエストを内部的にルーティング
$uri = $_SERVER['REQUEST_URI'];
if ($uri === ('/index.php/list')) {
   list_action();
} elseif ( preg_match("#^/index.php/show#", $uri) &&
isset($_GET['id'])) {
   show_action($_GET['id']);
} else {
   header('Status: 404 Not Found');
   echo '<html><body><h1>ページが見つかりません</h1></
body></html>';
}
                                            index.php
フロントコントローラー (routing)
require_once 'model.php';
require_once 'controllers.php';


// リクエストを内部的にルーティング
$uri = $_SERVER['REQUEST_URI'];
if ($uri === ('/index.php/list')) {
   list_action();
} elseif ( preg_match("#^/index.php/show#", $uri) &&
isset($_GET['id'])) {
   show_action($_GET['id']);
} else {
   header('Status: 404 Not Found');
   echo '<html><body><h1>ページが見つかりません</h1></
body></html>';
}
                                            index.php
フロントコントローラー (routing)
require_once 'model.php';
require_once 'controllers.php';


// リクエストを内部的にルーティング
$uri = $_SERVER['REQUEST_URI'];
if ($uri === ('/index.php/list')) {
   list_action();
} elseif ( preg_match("#^/index.php/show#", $uri) &&
isset($_GET['id'])) {
   show_action($_GET['id']);
} else {
   header('Status: 404 Not Found');
   echo '<html><body><h1>ページが見つかりません</h1></
body></html>';
}
                                            index.php
フロントコントローラー (routing)
require_once 'model.php';
require_once 'controllers.php';


// リクエストを内部的にルーティング
$uri = $_SERVER['REQUEST_URI'];
if ($uri === ('/index.php/list')) {
   list_action();
} elseif ( preg_match("#^/index.php/show#", $uri) &&
isset($_GET['id'])) {
   show_action($_GET['id']);
} else {
   header('Status: 404 Not Found');
   echo '<html><body><h1>ページが見つかりません</h1></
body></html>';
}
                                            index.php
フロントコントローラー (routing)
require_once 'model.php';
require_once 'controllers.php';


// リクエストを内部的にルーティング
$uri = $_SERVER['REQUEST_URI'];
if ($uri === ('/index.php/list')) {
   list_action();                   一覧表示へのリクエスト
} elseif ( preg_match("#^/index.php/show#", $uri) &&
isset($_GET['id'])) {
   show_action($_GET['id']);
} else {
   header('Status: 404 Not Found');
   echo '<html><body><h1>ページが見つかりません</h1></
body></html>';
}
                                        index.php
フロントコントローラー (routing)
require_once 'model.php';
require_once 'controllers.php';


// リクエストを内部的にルーティング
$uri = $_SERVER['REQUEST_URI'];
if ($uri === ('/index.php/list')) {
   list_action();                   一覧表示へのリクエスト
} elseif ( preg_match("#^/index.php/show#", $uri) &&
isset($_GET['id'])) {
   show_action($_GET['id']);        記事詳細へのリクエスト
} else {
   header('Status: 404 Not Found');
   echo '<html><body><h1>ページが見つかりません</h1></
body></html>';
}
                                        index.php
フロントコントローラー (routing)
require_once 'model.php';
require_once 'controllers.php';


// リクエストを内部的にルーティング
$uri = $_SERVER['REQUEST_URI'];
if ($uri === ('/index.php/list')) {
   list_action();                   一覧表示へのリクエスト
} elseif ( preg_match("#^/index.php/show#", $uri) &&
isset($_GET['id'])) {
   show_action($_GET['id']);        記事詳細へのリクエスト
} else {
   header('Status: 404 Not Found');
   echo '<html><body><h1>ページが見つかりません</h1></
body></html>';                        想定外のリクエスト
}
                                        index.php
フロントコントローラー(アクション)
<?php                              controllers.php
// controllers.php
function list_action()
{
   $posts = get_all_posts();
   require 'templates/list.php';
}


function show_action($id)
{
  $post = get_post_by_id($id);
  require 'templates/show.php';
}
フロントコントローラー(アクション)
<?php                            controllers.php
// controllers.php
function list_action()
{
   $posts = get_all_posts();
   require 'templates/list.php';
}                              一覧表示へのリクエスト

function show_action($id)
{
  $post = get_post_by_id($id);
  require 'templates/show.php';
}
フロントコントローラー(アクション)
<?php                            controllers.php
// controllers.php
function list_action()
{
   $posts = get_all_posts();
   require 'templates/list.php';
}                              一覧表示へのリクエスト

function show_action($id)
{
  $post = get_post_by_id($id);
  require 'templates/show.php';
}                              記事詳細へのリクエスト
Good
   設定の読込などの集中管理
       ルーティング機能
Point

   クラスはまだ使ってません


     クラスで書かなくても
   ある程度の体系化は可能です
Step6




        ライブラリを利用する
ライブラリを利用する




 HttpFoundation Component
HttpFoundation Component を使うことで....




  Webアプリケーションで必須な
$_GET, $_POST, レスポンスデータ
    をオブジェクトで扱える
HttpFoundation Component を使うことで....


Pure PHP

   $id = (isset($_GET[ id ]))? $_GET[ id ]: 0;




 Object
          $id = $request->query( id , 0);
HttpFoundation Component を
    採用しているプロジェクト


       Symfony2
          Silex
        Drupal8
        phpBB4
          ppi2
        Laravel4
         Zikula
           ... more
HttpFoundation Component は
   今後のデファクトスタンダート

                   Laravel4




                              ??



                      FuelPHP2
Pure PHP

// リクエストを内部的にルーティング
$uri = $_SERVER['REQUEST_URI'];
if ($uri === ('/index.php/list')) {
   list_action();
} elseif ( preg_match("#^/index.php/show#", $uri) &&
isset($_GET['id'])) {
   show_action($_GET['id']);
} else {
   header('Status: 404 Not Found');
   echo '<html><body><h1>ページが見つかりません</h1></body></html>';
}

                                           index.php
Symfony component

$request = Request::createFromGlobals();


$uri = $request->getPathInfo();
if ($uri === '/') {
   $response = list_action();
} elseif ($uri === '/show' && $request->query->has('id')) {
   $response = show_action($request->query->get('id'));
} else {
   $html = '<html><body><h1>Page Not Found</h1></body></
html>';
   $response = new Response($html, 404);
}


$response->send();                         index.php
Symfony component

$request = Request::createFromGlobals();


$uri = $request->getPathInfo();
if ($uri === '/') {
   $response = list_action();
} elseif ($uri === '/show' && $request->query->has('id')) {
   $response = show_action($request->query->get('id'));
} else {
   $html = '<html><body><h1>Page Not Found</h1></body></
html>';
   $response = new Response($html, 404);
}


$response->send();                         index.php
<?php                              controllers.php
// controllers.php
function list_action()
{
   $posts = get_all_posts();
   require 'templates/list.php';
}
<?php                              controllers.php
// controllers.php
function list_action()
{
   $posts = get_all_posts();
   require 'templates/list.php';
}

                     テンプレートをレンダリング (echo)
Controllers
function list_action()
{
  $posts = get_all_posts();
  $html = render_template('templates/list.php', array('posts' =>
$posts));
  return new Response($html);
}


function render_template($path, $params)
{
  extract($params, EXTR_SKIP);
  ob_start();
  require $path;
  $html = ob_get_clean();


    return $html;
}
Controllers
function list_action()
{
  $posts = get_all_posts();
  $html = render_template('templates/list.php', array('posts' =>
$posts));
  return new Response($html);
}
                              Responseオブジェクトを返す
function render_template($path, $params)
{
  extract($params, EXTR_SKIP);
  ob_start();
  require $path;
  $html = ob_get_clean();


    return $html;
}
Controllers
function list_action()
{
  $posts = get_all_posts();
  $html = render_template('templates/list.php', array('posts' =>
$posts));
  return new Response($html);
}
                              Responseオブジェクトを返す
function render_template($path, $params)
{
  extract($params, EXTR_SKIP);
  ob_start();
  require $path;
  $html = ob_get_clean();


    return $html;
}
Controllers
function list_action()
{
  $posts = get_all_posts();
  $html = render_template('templates/list.php', array('posts' =>
$posts));
  return new Response($html);
}
                              Responseオブジェクトを返す
function render_template($path, $params)
{
  extract($params, EXTR_SKIP);
  ob_start();
  require $path;
  $html = ob_get_clean();


    return $html;
}
                             典型的なPHPテンプレートパターン
CakePHP




DietCake
CakePHP




DietCake
CakePHP




DietCake
Good
  簡易的なフレームワークが完成
       車輪の再開発を防ぐ
      必要な機能だけ利用できる
        再利用性が高い

Bad
必要以上の機能が含まれるかもしれない
クライアント             サーバー
                                                model
                                                     Logic
                                                     model.php
                            list_action()

            リクエスト front controller          controllers
  Browser               PHP                    PHP
                    index.php           controllers.php


                              show_action()



              レスポンス
                             view
                                     HTML
                            template/show.php

                                                layout
                                                      HTML
                                                   layout.php
クライアント              サーバー
                                        Model
                    MVC

            リクエスト
  Browser
                           controller




             レスポンス


                              view
Final




        フレームワークを使う
            とは?
フレームワークを使う




    フレームワークを使っても
    やりたいことは変わらない
フレームワークを使う




  作りたいのはアプリケーション
フレームワークを使う




     ルーティング
     規約
     コントローラー
     バリデーター
     ......
Silexで書き換えると...
$app = new SilexApplication();
.....
$app->get('/show', function(Application $app, Request $request)
{
    $get_post_by_id = $app['model.post_by_id'];
    $post = $get_post_by_id($request->query->get('id'));
    return $app->render('templates/show.twig',
                         array('post' => $post));
});
フラットなPHPからSymfony2へ

 フラットなPHPからSliexへ

  フラットなPHPからSlimへ

           詳しくはぐぐってみてください
フレームワークを使う




  自分でゼロから作らなくて良い


   万能なフレームワークは無い
   実際に使って思想を理解する
まとめ


  フラットなPHPのデメリット
フレームワークの本来の仕事は何か?


使われるのではなく使うこと
thank you.

フラットなPHPからフレームワークへ

  • 1.
    フラットなPHPから フレームワークへ 株式会社 adingo 前田 雅央 @brtriver
  • 2.
  • 3.
    Steps フラットなPHP リファクタリング フレームワーク * フラットなPHP = 1ファイルで書かれたPHP
  • 4.
    Steps フラットなPHP View部分の分離 アプリケーション(ドメイン)部分の分離 Viewにレイアウト機能を追加 ページを追加する フロントコントローラー導入 ライブラリを利用する フレームワーク
  • 5.
  • 6.
    Step 0 DBからデータ取得し 取得したデータを HTMLで表示する
  • 7.
    <?php $pdo = newPDO( 'mysql:host=localhost;dbname=blog_db;charset=utf8', list.php 'myuser', 'mypassword', array(PDO::ATTR_EMULATE_PREPARES => false) ); $stmt = $pdo->query('SELECT id, title FROM post'); ?> <html> <head> <title>投稿の一覧</title> </head> <body> <h1>投稿の一覧</h1> <ul> <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?> <li> <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>"> <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?> </a> </li> <?php endwhile; ?> </ul> </body> </html>
  • 8.
    <?php $pdo = newPDO( 'mysql:host=localhost;dbname=blog_db;charset=utf8', list.php 'myuser', 'mypassword', array(PDO::ATTR_EMULATE_PREPARES => false) ); $stmt = $pdo->query('SELECT id, title FROM post'); ?> <html> <head> <title>投稿の一覧</title> </head> <body> <h1>投稿の一覧</h1> <ul> <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?> <li> <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>"> <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?> </a> </li> <?php endwhile; ?> </ul> </body> </html>
  • 9.
    <?php $pdo = newPDO( 'mysql:host=localhost;dbname=blog_db;charset=utf8', list.php 'myuser', 'mypassword', array(PDO::ATTR_EMULATE_PREPARES => false) ); $stmt = $pdo->query('SELECT id, title FROM post'); ?> <html> <head> <title>投稿の一覧</title> </head> <body> <h1>投稿の一覧</h1> <ul> <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?> <li> <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>"> <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?> </a> </li> <?php endwhile; ?> </ul> </body> </html>
  • 10.
    <?php $pdo = newPDO( 'mysql:host=localhost;dbname=blog_db;charset=utf8', list.php 'myuser', DBからデータ取得 ); 'mypassword', array(PDO::ATTR_EMULATE_PREPARES => false) $stmt = $pdo->query('SELECT id, title FROM post'); ?> <html> <head> <title>投稿の一覧</title> </head> <body> <h1>投稿の一覧</h1> <ul> <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?> <li> <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>"> <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?> </a> </li> <?php endwhile; ?> </ul> </body> </html>
  • 11.
    <?php $pdo = newPDO( 'mysql:host=localhost;dbname=blog_db;charset=utf8', list.php 'myuser', DBからデータ取得 ); 'mypassword', array(PDO::ATTR_EMULATE_PREPARES => false) $stmt = $pdo->query('SELECT id, title FROM post'); ?> <html> <head> <title>投稿の一覧</title> </head> <body> <h1>投稿の一覧</h1> <ul> <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?> 取得したデータを表示 <li> <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>"> <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?> </a> </li> <?php endwhile; ?> </ul> </body> </html>
  • 12.
    クライアント サーバー リクエスト PHP + Browser HTML レスポンス list.php
  • 13.
    Good シンプル 1ファイルたったの26行
  • 14.
    Bad アプリケーションの拡張性が無い 体系化されていない 再利用性がない
  • 15.
    体系化されていない フォームの送信を行うコードの追加したい... どこに書く? 同じファイル?別ファイルで書く?
  • 16.
  • 17.
    クライアント サーバー エ スト PHP + HTML リク ポ ンス レス list.php Browser Bad
  • 18.
    クライアント サーバー エ スト PHP + HTML リク ポ ンス レス list.php Browser COPY Bad
  • 19.
    クライアント サーバー エ スト PHP + HTML リク ポ ンス レス list.php Browser COPY リクエ スト PHP + HTML レスポ ンス list2.php Bad
  • 20.
    Step 1 View部分の分離
  • 21.
    show.php <?php $pdo = newPDO( 'mysql:host=localhost;dbname=blog_db;charset=utf8', 'myuser', 'mypassword', array(PDO::ATTR_EMULATE_PREPARES => false) ); $stmt = $pdo->query('SELECT id, title FROM post'); ?> <html> <head> <title>投稿の一覧</title> </head> <body> <h1>投稿の一覧</h1> <ul> <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?> <li> <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>"> <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?> </a> </li> <?php endwhile; ?> </ul> </body> </html>
  • 22.
    show.php <?php $pdo = newPDO( 'mysql:host=localhost;dbname=blog_db;charset=utf8', 'myuser', ); 'mypassword', ロジック部分 array(PDO::ATTR_EMULATE_PREPARES => false) $stmt = $pdo->query('SELECT id, title FROM post'); ?> <html> <head> <title>投稿の一覧</title> </head> <body> <h1>投稿の一覧</h1> <ul> <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?> <li> <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>"> <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?> </a> </li> <?php endwhile; ?> </ul> </body> </html>
  • 23.
    show.php <?php $pdo = newPDO( 'mysql:host=localhost;dbname=blog_db;charset=utf8', 'myuser', ); 'mypassword', ロジック部分 array(PDO::ATTR_EMULATE_PREPARES => false) $stmt = $pdo->query('SELECT id, title FROM post'); ?> <html> <head> <title>投稿の一覧</title> </head> <body> <h1>投稿の一覧</h1> <ul> View(HTML)部分 <?php while ($row = $stmt->fetch(PDO::FETCH_ASSOC)): ?> <li> <a href="show.php?id=<?php echo htmlspecialchars($row['id'], ENT_QUOTES, 'utf-8') ?>"> <?php echo htmlspecialchars($row['title'], ENT_QUOTES, 'utf-8') ?> </a> </li> <?php endwhile; ?> </ul> </body> </html>
  • 24.
    show.php <?php $pdo = newPDO( 'mysql:host=localhost;dbname=blog_db:charset=utf8', 'myuser', 'mypassword', array(PDO::ATTR_EMULATE_PREPARES => false) ); $stmt = $pdo->query('SELECT id, title FROM post'); // HTML部分のコードを読み込む require 'templates/list.php'; 別ファイルに分離
  • 25.
    show.php <?php $pdo = newPDO( 'mysql:host=localhost;dbname=blog_db:charset=utf8', 'myuser', 'mypassword', ロジック部分 array(PDO::ATTR_EMULATE_PREPARES => false) ); $stmt = $pdo->query('SELECT id, title FROM post'); // HTML部分のコードを読み込む require 'templates/list.php'; 別ファイルに分離
  • 26.
    クライアント サーバー リクエスト controller Browser PHP list.php
  • 27.
    クライアント サーバー リクエスト controller Browser PHP list.php
  • 28.
    クライアント サーバー リクエスト controller Browser PHP list.php レスポンス view HTML template/list.php
  • 29.
    クライアント サーバー リクエスト controller Browser PHP list.php レスポンス view json template/list-json.php
  • 30.
    Good View部分の分離 テンプレートの変更に強い
  • 31.
    Step 2 アプリケーション(ドメイン) 部分の分離
  • 32.
    index.php <?php $pdo = newPDO( 'mysql:host=localhost;dbname=blog_db:charset=utf8', 'myuser', 'mypassword', array(PDO::ATTR_EMULATE_PREPARES => false) ); $stmt = $pdo->query('SELECT id, title FROM post');
  • 33.
    index.php <?php $pdo = newPDO( 'mysql:host=localhost;dbname=blog_db:charset=utf8', データベースへ接続する 'myuser', 'mypassword', array(PDO::ATTR_EMULATE_PREPARES => false) ); $stmt = $pdo->query('SELECT id, title FROM post');
  • 34.
    index.php <?php $pdo = newPDO( 'mysql:host=localhost;dbname=blog_db:charset=utf8', データベースへ接続する 'myuser', 'mypassword', array(PDO::ATTR_EMULATE_PREPARES => false) ); 全てのポストを取得する $stmt = $pdo->query('SELECT id, title FROM post');
  • 35.
    ドメイン データベースへの接続する 全てのポストを取得する model.php に分離
  • 36.
    <?php // model.php function get_database_connection() { $pdo = new PDO( 'mysql:host=localhost;dbname=blog_db;charset=utf8', 'myuser', 'mypassword', array(PDO::ATTR_EMULATE_PREPARES => false) ); return $pdo; get_database_connection } function get_all_posts() { $pdo = get_database_connection(); $stmt = $pdo->query('SELECT id, title FROM post'); $posts = array(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $posts[] = $row; } get_all_posts return $posts; } model.php
  • 37.
    リファクタリング後のlist.php <?php require 'model.php'; $posts =get_all_posts(); require 'templates/list.php'; コントローラーは制御するだけ
  • 38.
    リファクタリング後のlist.php <?php require 'model.php'; ロジック読込 $posts = get_all_posts(); require 'templates/list.php'; コントローラーは制御するだけ
  • 39.
    リファクタリング後のlist.php <?php require 'model.php'; ロジック読込 $posts = get_all_posts(); 全ポストを取得 require 'templates/list.php'; コントローラーは制御するだけ
  • 40.
    リファクタリング後のlist.php <?php require 'model.php'; ロジック読込 $posts = get_all_posts(); 全ポストを取得 require 'templates/list.php'; ビュー読込 コントローラーは制御するだけ
  • 41.
  • 42.
    クライアント サーバー リクエスト controller Browser PHP list.php レスポンス view HTML template/list.php
  • 43.
    クライアント サーバー model Logic get_all_posts model.php リクエスト controller Browser PHP list.php $POSTS レスポンス view HTML template/list.php
  • 44.
  • 45.
    レイアウト機能 Viewの共通部分を再利用できる仕組み <html> <head> <title>トップページ</title> </head> <body> <h1>こんにちは!こんにちは!</h1> </body> </html> template/layout.php
  • 46.
    レイアウト機能 Viewの共通部分を再利用できる仕組み <html> <head> <title>トップページ</title> </head> <body> <h1>こんにちは!こんにちは!</h1> </body> 動的部分を決定 </html> template/layout.php
  • 47.
    レイアウト機能 PHPで書き換え <html> <head> <title><?php echo $title ?></title> </head> <body> <?php echo $content ?> </body> </html> template/layout.php
  • 48.
    ob_start() ... ob_get_clean() <?php$title = '投稿のリスト' ?> list.php <?php ob_start() ?> <h1>投稿のリスト</h1> <ul> <?php foreach ($posts as $post): ?> <li> <a href="show.php?id=<?php echo htmlspecialchars($post['id'], ENT_QUOTES, 'utf-8') ?>"> <?php echo htmlspecialchars($post['title'], ENT_QUOTES, 'utf-8') ?> </a> </li> <?php endforeach; ?> </ul> <?php $content = ob_get_clean() ?> <?php include 'layout.php' ?>
  • 49.
    ob_start() ... ob_get_clean() <?php$title = '投稿のリスト' ?> list.php <?php ob_start() ?> <h1>投稿のリスト</h1> <ul> <?php foreach ($posts as $post): ?> <li> <a href="show.php?id=<?php echo htmlspecialchars($post['id'], ENT_QUOTES, 'utf-8') ?>"> <?php echo htmlspecialchars($post['title'], ENT_QUOTES, 'utf-8') ?> </a> </li> <?php endforeach; ?> </ul> <?php $content = ob_get_clean() ?> <?php include 'layout.php' ?>
  • 50.
    ob_start() ... ob_get_clean() <?php$title = '投稿のリスト' ?> list.php <?php ob_start() ?> <h1>投稿のリスト</h1> <ul> <?php foreach ($posts as $post): ?> <li> <a href="show.php?id=<?php echo htmlspecialchars($post['id'], ENT_QUOTES, 'utf-8') ?>"> <?php echo htmlspecialchars($post['title'], ENT_QUOTES, 'utf-8') ?> </a> </li> <?php endforeach; ?> </ul> <?php $content = ob_get_clean() ?> <?php include 'layout.php' ?>
  • 51.
    ob_start() ... ob_get_clean() <?php$title = '投稿のリスト' ?> list.php <?php ob_start() ?> <h1>投稿のリスト</h1> <ul> <?php foreach ($posts as $post): ?> <li> <a href="show.php?id=<?php echo htmlspecialchars($post['id'], ENT_QUOTES, 'utf-8') ?>"> <?php echo htmlspecialchars($post['title'], ENT_QUOTES, 'utf-8') ?> </a> </li> <?php endforeach; ?> </ul> <?php $content = ob_get_clean() ?> 出力せずにバッファリング <?php include 'layout.php' ?>
  • 52.
    ob_start() ... ob_get_clean() <?php$title = '投稿のリスト' ?> list.php <?php ob_start() ?> <h1>投稿のリスト</h1> <ul> <?php foreach ($posts as $post): ?> <li> <a href="show.php?id=<?php echo htmlspecialchars($post['id'], ENT_QUOTES, 'utf-8') ?>"> <?php echo htmlspecialchars($post['title'], ENT_QUOTES, 'utf-8') ?> </a> </li> <?php endforeach; ?> </ul> <?php $content = ob_get_clean() ?> 出力せずにバッファリング <?php include 'layout.php' ?>
  • 53.
    クライアント サーバー model get_all_posts Logic model.php リクエスト controller Browser PHP list.php $POSTS レスポンス view HTML template/list.php
  • 54.
    クライアント サーバー model get_all_posts Logic model.php リクエスト controller Browser PHP list.php $POSTS レスポンス view HTML template/list.php layout HTML layout.php
  • 55.
    Good テンプレートの可用性がUP Bad テンプレートエンジンの機能より劣る Smarty, Twig...
  • 56.
    テンプレートエンジンのメリット Pure PHP <title><?php echo htmlspecialchars($title, ENT_QUOTES, 'utf-8') ?></title> Twig (escape) <title>{{ title }}</title> Twig (un escape) <title>{{ title¦raw }}</title>
  • 57.
    Step 4 ページを追加する blogの詳細ページ(show)
  • 58.
    list.phpと同じ仕組を作る <?php require_once 'model.php'; $post =get_post_by_id($_GET['id']); require 'templates/show.php'; 特定の記事を取得 show.php
  • 59.
    クライアント サーバー model get_post_by_id Logic model.php リクエスト controller Browser PHP show.php $POST レスポンス view HTML template/show.php layout HTML layout.php
  • 60.
    クライアント controller サーバー PHP get_all_posts list.php view HTML $POSTS template/list.php layout model Browser HTML Logic layout.php model.php view $POST HTML tempalte/show.php controller get_post_by_id PHP show.php
  • 61.
    クライアント controller サーバー PHP get_all_posts list.php view 一覧表示処理 HTML $POSTS template/list.php layout model Browser HTML Logic layout.php model.php view $POST HTML tempalte/show.php controller get_post_by_id PHP show.php
  • 62.
    クライアント controller サーバー PHP get_all_posts list.php view 一覧表示処理 HTML $POSTS template/list.php layout model Browser HTML Logic layout.php model.php view $POST HTML 詳細表示処理 tempalte/show.php controller get_post_by_id PHP show.php
  • 63.
    クライアント controller サーバー PHP get_all_posts list.php view 一覧表示処理 HTML $POSTS template/list.php layout model Browser HTML Logic layout.php model.php view $POST HTML 詳細表示処理 tempalte/show.php controller get_post_by_id PHP show.php
  • 64.
    Good Model, Layoutが共通化できている Bad リクエストを受けるPHPは model.phpを忘れずに requireしなければならない
  • 65.
    Step 5 フロントコントローラー
  • 66.
    フロントコントローラー? 全てのリクエストを 最初に必ず受ける 1つのPHPファイル
  • 67.
    Before クライアント サーバー http://example.com/list.php controller PHP list.php Browser
  • 68.
    Before クライアント サーバー http://example.com/list.php controller PHP list.php Browser controller PHP show.php http://example.com/show.php?id=5
  • 69.
    After クライアント サーバー front controller controllers Browser PHP PHP index.php controllers.php
  • 70.
    After クライアント サーバー http://example.com/index.php/list list_action() front controller controllers Browser PHP PHP index.php controllers.php
  • 71.
    After クライアント サーバー http://example.com/index.php/list list_action() front controller controllers Browser PHP PHP index.php controllers.php show_action(5) http://example.com/index.php/show?id=5
  • 72.
    フロントコントローラー (routing) require_once 'model.php'; require_once'controllers.php'; // リクエストを内部的にルーティング $uri = $_SERVER['REQUEST_URI']; if ($uri === ('/index.php/list')) { list_action(); } elseif ( preg_match("#^/index.php/show#", $uri) && isset($_GET['id'])) { show_action($_GET['id']); } else { header('Status: 404 Not Found'); echo '<html><body><h1>ページが見つかりません</h1></ body></html>'; } index.php
  • 73.
    フロントコントローラー (routing) require_once 'model.php'; require_once'controllers.php'; // リクエストを内部的にルーティング $uri = $_SERVER['REQUEST_URI']; if ($uri === ('/index.php/list')) { list_action(); } elseif ( preg_match("#^/index.php/show#", $uri) && isset($_GET['id'])) { show_action($_GET['id']); } else { header('Status: 404 Not Found'); echo '<html><body><h1>ページが見つかりません</h1></ body></html>'; } index.php
  • 74.
    フロントコントローラー (routing) require_once 'model.php'; require_once'controllers.php'; // リクエストを内部的にルーティング $uri = $_SERVER['REQUEST_URI']; if ($uri === ('/index.php/list')) { list_action(); } elseif ( preg_match("#^/index.php/show#", $uri) && isset($_GET['id'])) { show_action($_GET['id']); } else { header('Status: 404 Not Found'); echo '<html><body><h1>ページが見つかりません</h1></ body></html>'; } index.php
  • 75.
    フロントコントローラー (routing) require_once 'model.php'; require_once'controllers.php'; // リクエストを内部的にルーティング $uri = $_SERVER['REQUEST_URI']; if ($uri === ('/index.php/list')) { list_action(); } elseif ( preg_match("#^/index.php/show#", $uri) && isset($_GET['id'])) { show_action($_GET['id']); } else { header('Status: 404 Not Found'); echo '<html><body><h1>ページが見つかりません</h1></ body></html>'; } index.php
  • 76.
    フロントコントローラー (routing) require_once 'model.php'; require_once'controllers.php'; // リクエストを内部的にルーティング $uri = $_SERVER['REQUEST_URI']; if ($uri === ('/index.php/list')) { list_action(); 一覧表示へのリクエスト } elseif ( preg_match("#^/index.php/show#", $uri) && isset($_GET['id'])) { show_action($_GET['id']); } else { header('Status: 404 Not Found'); echo '<html><body><h1>ページが見つかりません</h1></ body></html>'; } index.php
  • 77.
    フロントコントローラー (routing) require_once 'model.php'; require_once'controllers.php'; // リクエストを内部的にルーティング $uri = $_SERVER['REQUEST_URI']; if ($uri === ('/index.php/list')) { list_action(); 一覧表示へのリクエスト } elseif ( preg_match("#^/index.php/show#", $uri) && isset($_GET['id'])) { show_action($_GET['id']); 記事詳細へのリクエスト } else { header('Status: 404 Not Found'); echo '<html><body><h1>ページが見つかりません</h1></ body></html>'; } index.php
  • 78.
    フロントコントローラー (routing) require_once 'model.php'; require_once'controllers.php'; // リクエストを内部的にルーティング $uri = $_SERVER['REQUEST_URI']; if ($uri === ('/index.php/list')) { list_action(); 一覧表示へのリクエスト } elseif ( preg_match("#^/index.php/show#", $uri) && isset($_GET['id'])) { show_action($_GET['id']); 記事詳細へのリクエスト } else { header('Status: 404 Not Found'); echo '<html><body><h1>ページが見つかりません</h1></ body></html>'; 想定外のリクエスト } index.php
  • 79.
    フロントコントローラー(アクション) <?php controllers.php // controllers.php function list_action() { $posts = get_all_posts(); require 'templates/list.php'; } function show_action($id) { $post = get_post_by_id($id); require 'templates/show.php'; }
  • 80.
    フロントコントローラー(アクション) <?php controllers.php // controllers.php function list_action() { $posts = get_all_posts(); require 'templates/list.php'; } 一覧表示へのリクエスト function show_action($id) { $post = get_post_by_id($id); require 'templates/show.php'; }
  • 81.
    フロントコントローラー(アクション) <?php controllers.php // controllers.php function list_action() { $posts = get_all_posts(); require 'templates/list.php'; } 一覧表示へのリクエスト function show_action($id) { $post = get_post_by_id($id); require 'templates/show.php'; } 記事詳細へのリクエスト
  • 82.
    Good 設定の読込などの集中管理 ルーティング機能
  • 83.
    Point クラスはまだ使ってません クラスで書かなくても ある程度の体系化は可能です
  • 84.
    Step6 ライブラリを利用する
  • 85.
  • 86.
    HttpFoundation Component を使うことで.... Webアプリケーションで必須な $_GET, $_POST, レスポンスデータ をオブジェクトで扱える
  • 87.
    HttpFoundation Component を使うことで.... PurePHP $id = (isset($_GET[ id ]))? $_GET[ id ]: 0; Object $id = $request->query( id , 0);
  • 88.
    HttpFoundation Component を 採用しているプロジェクト Symfony2 Silex Drupal8 phpBB4 ppi2 Laravel4 Zikula ... more
  • 89.
    HttpFoundation Component は 今後のデファクトスタンダート Laravel4 ?? FuelPHP2
  • 90.
    Pure PHP // リクエストを内部的にルーティング $uri= $_SERVER['REQUEST_URI']; if ($uri === ('/index.php/list')) { list_action(); } elseif ( preg_match("#^/index.php/show#", $uri) && isset($_GET['id'])) { show_action($_GET['id']); } else { header('Status: 404 Not Found'); echo '<html><body><h1>ページが見つかりません</h1></body></html>'; } index.php
  • 91.
    Symfony component $request =Request::createFromGlobals(); $uri = $request->getPathInfo(); if ($uri === '/') { $response = list_action(); } elseif ($uri === '/show' && $request->query->has('id')) { $response = show_action($request->query->get('id')); } else { $html = '<html><body><h1>Page Not Found</h1></body></ html>'; $response = new Response($html, 404); } $response->send(); index.php
  • 92.
    Symfony component $request =Request::createFromGlobals(); $uri = $request->getPathInfo(); if ($uri === '/') { $response = list_action(); } elseif ($uri === '/show' && $request->query->has('id')) { $response = show_action($request->query->get('id')); } else { $html = '<html><body><h1>Page Not Found</h1></body></ html>'; $response = new Response($html, 404); } $response->send(); index.php
  • 93.
    <?php controllers.php // controllers.php function list_action() { $posts = get_all_posts(); require 'templates/list.php'; }
  • 94.
    <?php controllers.php // controllers.php function list_action() { $posts = get_all_posts(); require 'templates/list.php'; } テンプレートをレンダリング (echo)
  • 95.
    Controllers function list_action() { $posts = get_all_posts(); $html = render_template('templates/list.php', array('posts' => $posts)); return new Response($html); } function render_template($path, $params) { extract($params, EXTR_SKIP); ob_start(); require $path; $html = ob_get_clean(); return $html; }
  • 96.
    Controllers function list_action() { $posts = get_all_posts(); $html = render_template('templates/list.php', array('posts' => $posts)); return new Response($html); } Responseオブジェクトを返す function render_template($path, $params) { extract($params, EXTR_SKIP); ob_start(); require $path; $html = ob_get_clean(); return $html; }
  • 97.
    Controllers function list_action() { $posts = get_all_posts(); $html = render_template('templates/list.php', array('posts' => $posts)); return new Response($html); } Responseオブジェクトを返す function render_template($path, $params) { extract($params, EXTR_SKIP); ob_start(); require $path; $html = ob_get_clean(); return $html; }
  • 98.
    Controllers function list_action() { $posts = get_all_posts(); $html = render_template('templates/list.php', array('posts' => $posts)); return new Response($html); } Responseオブジェクトを返す function render_template($path, $params) { extract($params, EXTR_SKIP); ob_start(); require $path; $html = ob_get_clean(); return $html; } 典型的なPHPテンプレートパターン
  • 99.
  • 100.
  • 101.
  • 102.
    Good 簡易的なフレームワークが完成 車輪の再開発を防ぐ 必要な機能だけ利用できる 再利用性が高い Bad 必要以上の機能が含まれるかもしれない
  • 103.
    クライアント サーバー model Logic model.php list_action() リクエスト front controller controllers Browser PHP PHP index.php controllers.php show_action() レスポンス view HTML template/show.php layout HTML layout.php
  • 104.
    クライアント サーバー Model MVC リクエスト Browser controller レスポンス view
  • 105.
    Final フレームワークを使う とは?
  • 106.
    フレームワークを使う フレームワークを使っても やりたいことは変わらない
  • 107.
  • 108.
    フレームワークを使う ルーティング 規約 コントローラー バリデーター ......
  • 109.
    Silexで書き換えると... $app = newSilexApplication(); ..... $app->get('/show', function(Application $app, Request $request) { $get_post_by_id = $app['model.post_by_id']; $post = $get_post_by_id($request->query->get('id')); return $app->render('templates/show.twig', array('post' => $post)); });
  • 110.
    フラットなPHPからSymfony2へ フラットなPHPからSliexへ フラットなPHPからSlimへ 詳しくはぐぐってみてください
  • 111.
    フレームワークを使う 自分でゼロから作らなくて良い 万能なフレームワークは無い 実際に使って思想を理解する
  • 112.
  • 113.