Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHPによる"非"構造化プログラミング入門 -本当に熱いスパゲティコードを求めて- #phpe...

PHPによる"非"構造化プログラミング入門 -本当に熱いスパゲティコードを求めて- #phperkaigi

hideki kinjyo

March 21, 2025
Tweet

More Decks by hideki kinjyo

Other Decks in Programming

Transcript

  1. 登壇者挨拶 <?= ͜Μʹͪ͸ʙʙʂ ?>   Fatal error: Uncaught Error:

    Undefined constant "͜Μʹͪ͸ʔʂ" in php-wasm run script:1
  2. イントロ   <?php for ($i = 10; $i >=

    0; $i--) { echo $i . PHP_EOL; } echo '🚀 ϩέοτൃࣹʂ'; こういうのが、
  3. イントロ   <?php $i = 10; COUNTDOWN: if ($i

    <= 0) goto FINISH; echo --$i . PHP_EOL; goto COUNTDOWN; FINISH: echo '🚀 ϩέοτൃࣹʂ'; こう!
  4. イントロ   <?php declare(strict_types=1); use ImafuApp\App; require dirname(__FILE__, 2)

    
 . '/bootstrap.php'; (new App())->run(); index.phpだって、
  5. イントロ   <?php declare(strict_types=1); $dbo = mysqli_connect( getenv('DB_HOST'), getenv('DB_USER'),

    getenv('DB_PASS'), getenv('DB_NAME'), ); if (!$dbo) goto db_error; /* ϦΫΤετͷॲཧ */ $action = filter_input(INPUT_GET, 'action'); if ($action === 'detail') goto detail_message; if ($action === 'new') goto new_message; if ($action === 'register') goto register_message; goto list_msgs; /* ϝοηʔδҰཡ */ list_msgs: $page = filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT); goto check_page; set_default_page: $page = 1; goto end_page_check; check_page: if ($page >= 1) goto end_page_check; goto set_default_page; end_page_check: $totalCount = 0; $msgs = []; $result = mysqli_query( 
 $dbo, 
 'SELECT COUNT(*) FROM messages', 
 ); if ($result !== false) goto list_msgs_page; $totalCount = (int)mysqli_fetch_column($result); $limit = 10; $offset = ($page - 1) * $limit; $stmt = mysqli_prepare( 
 $dbo, 
 'SELECT * FROM messages ORDER BY id DESC LIMIT ? OFFSET ?' 
 ); mysqli_stmt_bind_param($stmt, 'ii', $limit, $offset); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); $msgs = mysqli_fetch_all($result, MYSQLI_ASSOC); $msgCnt = count($msgs); $totalPage = ceil($totalCount / 10); list_msgs_page: ?> <!DOCTYPE html> <html> <head> <title>఻ݴҰཡ</title> <link rel="stylesheet" href="assets/geo-bootstrap/swatch/ bootstrap.min.css"> </head> <body> <div class="container" style="padding-bottom: 5rem;"> <div class="page-header"> <h1>఻ݴҰཡ</h1> <a class="btn btn-info" href="/?action=new">৽ن౤ߘ</a> </div> <div class="row"> <div class="span12"> <ul class="list-group mb-4"> <?php $i = 0; msg_loop: if ($i >= $msgCnt) goto msg_loop_end; $message = $msgs[$i]; ?> <li class="list- group-item"> <a href="/? action=detail&id=<?= $message['id'] ? >"> <?= htmlspecialchars($message['title']) ?> </a> by <?= htmlspecialchars($message['author']) ?> </li> <?php $i++; goto msg_loop; msg_loop_end: ?> </ul> </div> <div class="span12"> <div class="pager"> <ul> <li class="page-item"> <a class="page-link" href="/">࠷ॳͷϖʔδ</ a> </li> <?php goto check_prev_page; prev_page: ?> <li class="page-item"> <a class="page-link" href="/?page=<?= $page - 1 ?>">લͷϖʔδ</a> </li> <?php goto check_next_page; check_prev_page: if ($page <= 1) goto check_next_page; goto prev_page; check_next_page: if ($page >= $totalPage) goto end_pagination; next_page: ?> <li class="page-item"> <a class="page-link" href="/?page=<?= $page + 1 ?>">࣍ͷϖʔδ</a> </li> <?php goto end_pagination; end_pagination: ?> <li class="page-item"> <a class="page-link" href="?page=<?= intdiv($i, 10) + 1 ?>">࠷ޙͷϖʔδ</a> </li> </ul> </div> </div> </div> </div> </body> </html> <?php goto end; /* ϝοηʔδৄࡉ */ detail_message: $id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT); $stmt = mysqli_prepare($dbo, 'SELECT * FROM messages WHERE id = ?'); mysqli_stmt_bind_param($stmt, 'i', $id); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); if (mysqli_num_rows($result) === 0) goto redirect_home; $message = mysqli_fetch_assoc($result); ?> <!DOCTYPE html> <html> <head> <title><?= htmlspecialchars($message['title']) ? ></title> <link rel="stylesheet" href="assets/geo-bootstrap/swatch/ bootstrap.min.css"> </head> <body> <div class="container" style="padding-bottom: 5rem;"> <div class="page-header"> <h1><?= htmlspecialchars($message['title']) ? ></h1> <a class="btn btn-info" href="index.php">Ұཡʹ໭Δ</a> </div> <div class="row"> <div class="span12"> <article> <header> <p> <span class="label label-inverse">౤ߘऀ</ span> <?= htmlspecialchars($message['author']) ?> </p> <p> <span class="label label-inverse">౤ߘ೔</ span> <?= $message['created_at'] ?> </p> </header> <p class="well"> <?= nl2br(htmlspecialchars($message['body'] )) ?> </p> </article> </div> </div> </div> </body> </html> <?php goto end; /* ৽نϝοηʔδ */ new_message: ?> <!DOCTYPE html> <html> <head> <title>৽ن఻ݴ࡞੒</title> <link rel="stylesheet" href="assets/geo-bootstrap/swatch/ bootstrap.min.css"> </head> <body> <div class="container" style="padding-bottom: 5rem;"> <div class="page-header"> <h1>৽ن఻ݴ࡞੒</h1> <a class="btn btn-info" href="index.php">Ұཡʹ໭Δ</a> </div> <div class="row"> <div class="span12"> <?php if (empty($errors)) goto new_message_form; ?> <div class="alert alert-error"> <ul> <?php $i = 0; $errorCount = count($errors); error_list_item: if ($i >= $errorCount) goto error_list_end; $errorField = array_keys($errors)[$i]; ?> <li><?= $errors[$errorField] ?></li> <?php $i++; goto error_list_item; error_list_end: ?> </ul> </div> <?php new_message_form: ?> <form method="post" action="/?action=register" class="form- horizontal well"> <div class="control-group"> <label class="control-label">λΠτϧ</label> <div class="controls"> <input type="text" name="title" maxlength="50" required> </div> </div> <div class="control-group"> <label class="control-label">ຊจ</label> <div class="controls"> <textarea name="body" maxlength="500" rows="10" required></textarea> </div> </div> <div class="control-group"> <label class="control-label">౤ߘऀ໊</label> <div class="controls"> <input type="text" name="author" maxlength="50" required> </div> </div> <label> : </label><br> <button type="submit" class="btn btn-primary"> ొ࿥</button> <button type="submit"></button> </form> </div> </div> </div> </body> </html> <?php goto end; /* ϝοηʔδొ࿥ */ register_message: if ($_SERVER['REQUEST_METHOD'] !== 'POST') goto redirect_new; $postMessage = [ 'title' => filter_input(INPUT_POST, 'title'), 'author' => filter_input(INPUT_POST, 'author'), 'body' => filter_input(INPUT_POST, 'body'), ]; $errors = []; validate_title: if ($postMessage['title'] && mb_strlen($postMessage['title']) <= 50) goto validate_author; $errors['title'] = 'λΠτϧ͸50จࣈҎ಺Ͱ ೖྗ͍ͯͩ͘͠͞'; validate_author: if ($postMessage['author'] && mb_strlen($postMessage['author']) <= 50) goto validate_body; $errors['author'] = '౤ߘऀ໊͸50จࣈҎ಺ Ͱೖྗ͍ͯͩ͘͠͞'; validate_body: if ($postMessage['body'] && mb_strlen($postMessage['body']) <= 500) goto validate_end; $errors['body'] = 'ຊจ͸500จࣈҎ಺Ͱೖྗ ͍ͯͩ͘͠͞'; validate_end: if ($errors) goto new_message; $postMessage['created_at'] = date('Y-m- d H:i:s'); $stmt = mysqli_prepare($dbo, 'INSERT INTO `msgs` (`title`, `author`, `body`, `created_at`) VALUES (?, ?, ?, ?)'); mysqli_stmt_bind_param($stmt, 'ssss', $postMessage['title'], $postMessage['author'], $postMessage['body'], $postMessage['created_at']); mysqli_stmt_execute($stmt); if (mysqli_stmt_error($stmt)) goto end; $id = mysqli_stmt_insert_id($stmt); header("Location: /? action=detail&id={$id}"); goto end; /* ΤϥʔϋϯυϦϯά */ db_error: echo "σʔλϕʔε઀ଓΤϥʔ", PHP_EOL; goto end; redirect_home: header("Location: /"); goto end; redirect_new: header("Location: /?action=new"); goto end; end: goto program_exit; /* ϓϩάϥϜऴྃ */ program_exit: このくらい 
 じゃないと!
  6. 自己紹介 • 金城秀樹 / きんじょうひでき • GitHub: @o0h / 𝕏

    : @o0h_ • 好きなFWはCakePHP • アイコンは美味しい鮭親子丼の写真です • 最近はPodcastをやっています • ハッシュタグ: #readlinefm • 🆕 パンフ記事も書いているので見てください!  
  7. 伝言掲示板の機能①: 伝言一覧   Styled with: Geo for Bootstrap, a

    Timeless Theme by Divshot https://code.divshot.com/geo-bootstrap/
  8. 伝言掲示板の機能②: 伝言の表示   Styled with: Geo for Bootstrap, a

    Timeless Theme by Divshot https://code.divshot.com/geo-bootstrap/
  9. 伝言掲示板の機能③: 伝言の登録   Styled with: Geo for Bootstrap, a

    Timeless Theme by Divshot https://code.divshot.com/geo-bootstrap/
  10. スタイル②を味わうためのルール ① いつもの ② 構造化プログラミング(手続き型) ③ "非"構造化プログラミング   クラスなし

    
 手続きを関数にまとめる 関数はreturnで値を返さない 
 (グローバル変数を操作する)
  11. スタイル③を味わうためのルール ① いつもの ② 構造化プログラミング(手続き型) ③ "非"構造化プログラミング   クラスなし

    関数なし 「ブロック」なし 分岐は条件付きジャンプのみ可 高階関数・式のネストなし ファイルの分割なし
  12. ③ルール1: クラスなし   readonly class User { public function

    __construct( private string $name, private int $age, ) { } } ✕
  13. ③ルール3: 「ブロック」なし   foreach ($array as $key => $value)

    printf('%s => %s', $key, $value); ✕ if ($a) { echo 'a'; } elseif ($b) { echo 'b'; } else { echo 'c'; } ✕
  14. ③ルール4: 条件付きジャンプのみ可   if ($me !== 'taro') goto honshori;

    echo 'Taro is here!'; honshori: ◯ if ($me === 'taro') { echo 'Taro is here!'; } ✕
  15. ③ルール5: 高階関数・式のネストなし   array_map( 
 fn ($x) => $x

    + 1, $values, 
 ); mb_substr( $title 0, mb_strlen($displayLimit), ); ✕ ✕
  16. いつもの姿 • 素朴なMVC2にして、 
 こんな感じの構成に • 小さい部品が多数ある • ノリが分かれば、 


    目当ての処理を 
 見つけやすそう   . ├── Action │ ├── BaseAction.php │ └── ViewMessage.php ├── App.php ├── Driver │ └── DBO.php ├── Entity │ └── Message.php ├── Exception ├── Repository ├── View ├── public │ ├── assets │ └── index.php └── templates └── view_message.php ※ファイル数が多いので 
 一部省略
  17. 構造化(手続き型)の姿 • 似たようなロジックを 
 1つのファイルに集約 • ファイル数はぐっと減る   .

    ├── assets ├── index.php ├── lib │ ├── actions.php │ ├── db.php │ └── helper.php └── templates ├── list_msgs.php ├── new_message.php └── view_message.php
  18. 俺たちはここまで来たぞ   . ├── Action │ ├── BaseAction.php │

    └── ViewMessage.php ├── App.php ├── Driver │ └── DBO.php ├── Entity │ └── Message.php ├── Exception ├── Repository ├── View ├── public │ ├── assets │ └── index.php └── templates └── view_message.php . ├── assets └── index.php
  19. いつもの姿 • 実務は持たない • 起動設定を行い、 
 FWに処理を委ねる   <?php

    declare(strict_types=1); use ImafuApp\App; require dirname(__FILE__, 2) 
 . '/bootstrap.php'; (new App())->run();
  20. 構造化(手続き型)の姿 • 指定された内容から 
 起動する処理を判断して • 対応する手続きを呼び出す • もちろん、より多くを関数 の中に隠すこともできる

    <?php declare(strict_types=1); bootstrap(); // $_GET['action']ͷ୅ΘΓ $action = filter_input( 
 INPUT_GET, 
 'action' 
 ); match ($action) { 'detail' => detailMessage(), 'new' => newMessage(), 'register' => registerMessage(), default => listMessages(), }; exit();  
  21. 非構造化 • 長い • 読めますか? • ここに全部ある <?php declare(strict_types=1); $dbo

    = mysqli_connect( getenv('DB_HOST'), getenv('DB_USER'), getenv('DB_PASS'), getenv('DB_NAME'), ); if (!$dbo) goto db_error; /* ϦΫΤετͷॲཧ */ $action = filter_input(INPUT_GET, 'action'); if ($action === 'detail') goto detail_message; if ($action === 'new') goto new_message; if ($action === 'register') goto register_message; goto list_msgs; /* ϝοηʔδҰཡ */ list_msgs: $page = filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT); goto check_page; set_default_page: $page = 1; goto end_page_check; check_page: if ($page >= 1) goto end_page_check; goto set_default_page; end_page_check: $totalCount = 0; $msgs = []; $result = mysqli_query( 
 $dbo, 
 'SELECT COUNT(*) FROM messages', 
 ); if ($result !== false) goto list_msgs_page; $totalCount = (int)mysqli_fetch_column($result); $limit = 10; $offset = ($page - 1) * $limit; $stmt = mysqli_prepare( 
 $dbo, 
 'SELECT * FROM messages ORDER BY id DESC LIMIT ? OFFSET ?' 
 ); mysqli_stmt_bind_param($stmt, 'ii', $limit, $offset); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); $msgs = mysqli_fetch_all($result, MYSQLI_ASSOC); $msgCnt = count($msgs); $totalPage = ceil($totalCount / 10); list_msgs_page: ?> <!DOCTYPE html> <html> <head> <title>఻ݴҰཡ</title> <link rel="stylesheet" href="assets/ geo-bootstrap/swatch/bootstrap.min.css"> </head> <body> <div class="container" style="padding- bottom: 5rem;"> <div class="page-header"> <h1>఻ݴҰཡ</h1> <a class="btn btn-info" href="/? action=new">৽ن౤ߘ</a> </div> <div class="row"> <div class="span12"> <ul class="list-group mb-4"> <?php $i = 0; msg_loop: if ($i >= $msgCnt) goto msg_loop_end; $message = $msgs[$i]; ?> <li class="list-group- item"> <a href="/? action=detail&id=<?= $message['id'] ?>"> <?= htmlspecialchars($message['title']) ?> </a> by <?= htmlspecialchars($message['author']) ?> </li> <?php $i++; goto msg_loop; msg_loop_end: ?> </ul> </div> <div class="span12"> <div class="pager"> <ul> <li class="page- item"> <a class="page- link" href="/">࠷ॳͷϖʔδ</a> </li> <?php goto check_prev_page; prev_page: ?> <li class="page- item"> <a class="page- link" href="/?page=<?= $page - 1 ?>">લͷϖʔ δ</a> </li> <?php goto check_next_page; check_prev_page: if ($page <= 1) goto check_next_page; goto prev_page; check_next_page: if ($page >= $totalPage) goto end_pagination; next_page: ?> <li class="page- item"> <a class="page- link" href="/?page=<?= $page + 1 ?>">࣍ͷϖʔ δ</a> </li> <?php goto end_pagination; end_pagination: ?> <li class="page- item"> <a class="page- link" href="?page=<?= intdiv($i, 10) + 1 ?>"> ࠷ޙͷϖʔδ</a> </li> </ul> </div> </div> </div> </div> </body> </html> <?php goto end; /* ϝοηʔδৄࡉ */ detail_message: $id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT); $stmt = mysqli_prepare($dbo, 'SELECT * FROM messages WHERE id = ?'); mysqli_stmt_bind_param($stmt, 'i', $id); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); if (mysqli_num_rows($result) === 0) goto redirect_home; $message = mysqli_fetch_assoc($result); ?> <!DOCTYPE html> <html> <head> <title><?= htmlspecialchars($message['title']) ?></ title> <link rel="stylesheet" href="assets/ geo-bootstrap/swatch/bootstrap.min.css"> </head> <body> <div class="container" style="padding- bottom: 5rem;"> <div class="page-header"> <h1><?= htmlspecialchars($message['title']) ?></h1> <a class="btn btn-info" href="index.php">Ұཡʹ໭Δ</a> </div> <div class="row"> <div class="span12"> <article> <header> <p> <span class="label label-inverse">౤ߘऀ</span> <?= htmlspecialchars($message['author']) ?> </p> <p> <span class="label label-inverse">౤ߘ೔</span> <?= $message['created_at'] ?> </p> </header> <p class="well"> <?= nl2br(htmlspecialchars($message['body'])) ?> </p> </article> </div> </div> </div> </body> </html> <?php goto end; /* ৽نϝοηʔδ */ new_message: ?> <!DOCTYPE html> <html> <head> <title>৽ن఻ݴ࡞੒</title> <link rel="stylesheet" href="assets/ geo-bootstrap/swatch/bootstrap.min.css"> </head> <body> <div class="container" style="padding- bottom: 5rem;"> <div class="page-header"> <h1>৽ن఻ݴ࡞੒</h1> <a class="btn btn-info" href="index.php">Ұཡʹ໭Δ</a> </div> <div class="row"> <div class="span12"> <?php if (empty($errors)) goto new_message_form; ?> <div class="alert alert- error"> <ul> <?php $i = 0; $errorCount = count($errors); error_list_item: if ($i >= $errorCount) goto error_list_end; $errorField = array_keys($errors)[$i]; ?> <li><?= $errors[$errorField] ?></li> <?php $i++; goto error_list_item; error_list_end: ?> </ul> </div> <?php new_message_form: ?> <form method="post" action="/?action=register" class="form- horizontal well"> <div class="control- group"> <label class="control-label">λΠτϧ</label> <div class="controls"> <input type="text" name="title" maxlength="50" required> </div> </div> <div class="control- group"> <label class="control-label">ຊจ</label> <div class="controls"> <textarea name="body" maxlength="500" rows="10" required></textarea> </div> </div> <div class="control- group"> <label class="control-label">౤ߘऀ໊</label> <div class="controls"> <input type="text" name="author" maxlength="50" required> </div> </div> <label> : </label><br> <button type="submit" class="btn btn-primary">ొ࿥</button> <button type="submit"></ button> </form> </div> </div> </div> </body> </html> <?php goto end; /* ϝοηʔδొ࿥ */ register_message: if ($_SERVER['REQUEST_METHOD'] !== 'POST') goto redirect_new; $postMessage = [ 'title' => filter_input(INPUT_POST, 'title'), 'author' => filter_input(INPUT_POST, 'author'), 'body' => filter_input(INPUT_POST, 'body'), ]; $errors = []; validate_title: if ($postMessage['title'] && mb_strlen($postMessage['title']) <= 50) goto validate_author; $errors['title'] = 'λΠτϧ͸50จࣈҎ಺Ͱೖྗ͠ ͍ͯͩ͘͞'; validate_author: if ($postMessage['author'] && mb_strlen($postMessage['author']) <= 50) goto validate_body; $errors['author'] = '౤ߘऀ໊͸50จࣈҎ಺Ͱೖྗ͠ ͍ͯͩ͘͞'; validate_body: if ($postMessage['body'] && mb_strlen($postMessage['body']) <= 500) goto validate_end; $errors['body'] = 'ຊจ͸500จࣈҎ಺Ͱೖྗͯ͘͠ ͍ͩ͞'; validate_end: if ($errors) goto new_message; $postMessage['created_at'] = date('Y-m-d H:i:s'); $stmt = mysqli_prepare($dbo, 'INSERT INTO `msgs` (`title`, `author`, `body`, `created_at`) VALUES (?, ?, ?, ?)'); mysqli_stmt_bind_param($stmt, 'ssss', $postMessage['title'], $postMessage['author'], $postMessage['body'], $postMessage['created_at']); mysqli_stmt_execute($stmt); if (mysqli_stmt_error($stmt)) goto end; $id = mysqli_stmt_insert_id($stmt); header("Location: /?action=detail&id={$id}"); goto end; /* ΤϥʔϋϯυϦϯά */ db_error: echo "σʔλϕʔε઀ଓΤϥʔ", PHP_EOL; goto end; redirect_home: header("Location: /"); goto end; redirect_new: header("Location: /?action=new"); goto end; end: goto program_exit; /* ϓϩάϥϜऴྃ */ program_exit:  
  22. 構造化プログラミング • リクエストから`?page=`を INTで矯正して取り出し、 
 不正な範囲だったら`1`で 上書きする • 今っぽいスタイルでも手続 き型のスタイルでも同じ

    • FWを使ってればリクエスト内 容に生で触ることは無さそう かな?くらいの違い $page = filter_input( INPUT_GET, 'page', FILTER_VALIDATE_INT, [ 'options' => [ 'default' => 1, ] ] ); if ($page < 1) { $page = 1; }  
  23. code review $page = filter_input( INPUT_GET, 'page', FILTER_VALIDATE_INT, [ 'options'

    => [ 'default' => 1, ] ] ); if ($page < 1) { $page = 1; } if文を使っている => 条件付きジャンプに変える 必要がある if ($page < 1) { $page = 1; }  
  24. 構造化プログラミング • あるいは、`max()` で値を 丸めてしまうことも $page = max(1, filter_input( INPUT_GET,

    'page', FILTER_VALIDATE_INT, [ 
 'options' => [ 
 'default' => 1, 
 ], 
 ] )); $limit = 10; $offset = ($page - 1) * $limit;  
  25. code review $page = max(1, filter_input( INPUT_GET, 'page', FILTER_VALIDATE_INT, [

    
 'options' => [ 
 'default' => 1, 
 ], 
 ] )); $limit = 10; $offset = ($page - 1) * $limit; $page = max(1, filter_input( 式のネストをしている 
 (`max()` の中で `filter_input()` を使っている)  
  26. • ここからが • 俺達の本気だ!! $page = max(1, filter_input( INPUT_GET, 'page',

    FILTER_VALIDATE_INT, [ 
 'options' => [ 
 'default' => 1, 
 ], 
 ] )); $limit = 10; $offset = ($page - 1) * $limit; code review これを非構造化したら、どうなる?  
  27. 非構造化すると… $page = filter_input( 
 INPUT_GET, 
 'page', 
 FILTER_VALIDATE_INT

    
 ); goto check_page; set_default_page: $page = 1; goto end_page_check; check_page: if ($page >= 1) goto end_page_check; goto set_default_page; end_page_check: チェックポイント ✓条件分岐はジャンプで ✓式の中で別の式を呼んでい ない ※ 味わい深さを優先して、 やや汚いコードにしています  
  28. 非構造化すると… $page = filter_input( 
 INPUT_GET, 
 'page', 
 FILTER_VALIDATE_INT

    
 ); goto check_page; set_default_page: $page = 1; goto end_page_check; check_page: if ($page >= 1) goto end_page_check; goto set_default_page; end_page_check: `$_GET`のparamを取得して $page = filter_input( 
 INPUT_GET, 
 'page', 
 FILTER_VALIDATE_INT 
 );  
  29. 非構造化すると… $page = filter_input( 
 INPUT_GET, 
 'page', 
 FILTER_VALIDATE_INT

    
 ); goto check_page; set_default_page: $page = 1; goto end_page_check; check_page: if ($page >= 1) goto end_page_check; goto set_default_page; end_page_check: 値の範囲をチェックする 
 処理にジャンプ goto check_page; check_page:  
  30. 非構造化すると… $page = filter_input( 
 INPUT_GET, 
 'page', 
 FILTER_VALIDATE_INT

    
 ); goto check_page; set_default_page: $page = 1; goto end_page_check; check_page: if ($page >= 1) goto end_page_check; goto set_default_page; end_page_check: 指定されたのが1以上なら 
 何もしないで次の処理へ if ($page >= 1) goto end_page_check; end_page_check:  
  31. 非構造化すると… $page = filter_input( 
 INPUT_GET, 
 'page', 
 FILTER_VALIDATE_INT

    
 ); goto check_page; set_default_page: $page = 1; goto end_page_check; check_page: if ($page >= 1) goto end_page_check; goto set_default_page; end_page_check: 正常範囲に含まれなければ 
 デフォルト値のセット処理へ goto set_default_page; set_default_page:  
  32. 非構造化すると… $page = filter_input( 
 INPUT_GET, 
 'page', 
 FILTER_VALIDATE_INT

    
 ); goto check_page; set_default_page: $page = 1; goto end_page_check; check_page: if ($page >= 1) goto end_page_check; goto set_default_page; end_page_check: `$page` に 
 デフォルト値をセット $page = 1;  
  33. 非構造化すると… $page = filter_input( 
 INPUT_GET, 
 'page', 
 FILTER_VALIDATE_INT

    
 ); goto check_page; set_default_page: $page = 1; goto end_page_check; check_page: if ($page >= 1) goto end_page_check; goto set_default_page; end_page_check: `$page`関連の処理は 
 コレで終わり。次の処理へ goto end_page_check; end_page_check:  
  34. いつもの 取得部分(コントローラー) • 「取っ て」「仕込ん で」「表 示して」みたいな感じです • (特に説明したいことナシ) $msgs

    = $this 
 ->repository 
 ->getMulti($limit, $ofset); $payload = new Payload( msgs: $msgs, ); $this->render($payload);  
  35. いつもの 表示部分(プレゼンテーション) • メッセージのコレクション を、foreachで回す • その中で素朴にechoしている のみ <ul class="list-group

    mb-4"> <?php foreach ($msgs as $m): ?> <li class="list-group-item"> <a href="/?action=detail&id=<?= $m->id ?>" > <?= h($m->title) ?> </a> by <?= h($m->author) ?> </li> <?php endforeach; ?> </ul>  
  36. 構造化プログラミング 取得部分(手続き) • 処理が単位ごとに切り出され ているので、OOPをベースに 書いたコードと骨組みは変わ らない印象 • クラス-メソッドの仕組みが ないので、関数同士の関連性

    が見抜きにくい気もする • それも、namespaceや命名の工夫 で支援できそう $dbo = getDbConnection(); $msgs = getMessages( $dbo, $page ); render( compact('msgs'), 'list_msgs.php', );  
  37. 構造化プログラミング 表示部分(プレゼンテーション) • オブジェクトが連想配列に なったくらいで、ほとんど何 も変わっていない • そもそもテンプレートに複雑 なロジックを持っていなけれ ば、出てくるのはループや簡

    単な分岐くらい <ul class="list-group mb-4"> <?php foreach ($msgs as $m): ?> <li class="list-group-item"> <a href="/?action=detail&id=<?= $m['id'] ?>" > <?= h($m['title']) ?> </a> by <?= h($m['author']) ?> </li> <?php endforeach; ?> </ul>  
  38. 構造化プログラミング 表示部分(プレゼンテーション) • オブジェクトが連想配列に なったくらいで、ほとんど何 も変わっていない • そもそもテンプレートに複雑 なロジックを持っていなけれ ば、出てくるのはループや簡

    単な分岐くらい <ul class="list-group mb-4"> <?php foreach ($msgs as $m): ?> <li class="list-group-item"> <a href="/?action=detail&id=<?= $m['id'] ?>" > <?= h($m['title']) ?> </a> by <?= h($m['author']) ?> </li> <?php endforeach; ?> </ul> これを非構造化したら、どうなる?  
  39. 非構造化すると… $msgs = []; $msgCnt = 0; $stmt = mysqli_prepare(

    $dbo, 'SELECT * FROM messages ORDER BY id DESC LIMIT ? OFFSET ?' ); mysqli_stmt_bind_param( $stmt, 'ii', $limit, $offset ); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); if (!$result) goto db_error; $msgs = mysqli_fetch_all( 
 $result, MYSQLI_ASSOC 
 ); $msgCnt = count($msgs); まずは伝言の取得部分  
  40. 非構造化すると… $msgs = []; $msgCnt = 0; $stmt = mysqli_prepare(

    $dbo, 'SELECT * FROM messages ORDER BY id DESC LIMIT ? OFFSET ?' ); mysqli_stmt_bind_param( $stmt, 'ii', $limit, $offset ); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); if (!$result) goto db_error; $msgs = mysqli_fetch_all( 
 $result, MYSQLI_ASSOC 
 ); $msgCnt = count($msgs);   初期化はやっておくと便利 
 やっておきましょう $msgs = []; $msgCnt = 0;
  41. 非構造化すると… $msgs = []; $msgCnt = 0; $stmt = mysqli_prepare(

    $dbo, 'SELECT * FROM messages ORDER BY id DESC LIMIT ? OFFSET ?' ); mysqli_stmt_bind_param( $stmt, 'ii', $limit, $offset ); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); if (!$result) goto db_error; $msgs = mysqli_fetch_all( 
 $result, MYSQLI_ASSOC 
 ); $msgCnt = count($msgs);   PDOや`$stmt->fetch()`などの OOPスタイルを使えないので、 すべて「手続き型インター フェイス」を利用。 
 それ以外は普通のコード $stmt = mysqli_prepare( $dbo, 'SELECT * FROM messages ORDER BY id DESC LIMIT ? OFFSET ?' ); mysqli_stmt_bind_param( $stmt, 'ii', $limit, $offset ); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); 参考: PHP: 手続き型とオブジェクト指向イン ターフェイス - Manual https://php.net/manual/ja/mysqli.quickstart.dual- interface.php
  42. 非構造化すると… $msgs = []; $msgCnt = 0; $stmt = mysqli_prepare(

    $dbo, 'SELECT * FROM messages ORDER BY id DESC LIMIT ? OFFSET ?' ); mysqli_stmt_bind_param( $stmt, 'ii', $limit, $offset ); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); if (!$result) goto db_error; $msgs = mysqli_fetch_all( 
 $result, MYSQLI_ASSOC 
 ); $msgCnt = count($msgs);   失敗時はジャンプで脱出 if (!$result) goto db_error;
  43. 非構造化すると… $msgs = []; $msgCnt = 0; $stmt = mysqli_prepare(

    $dbo, 'SELECT * FROM messages ORDER BY id DESC LIMIT ? OFFSET ?' ); mysqli_stmt_bind_param( $stmt, 'ii', $limit, $offset ); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); if (!$result) goto db_error; $msgs = mysqli_fetch_all( 
 $result, MYSQLI_ASSOC 
 ); $msgCnt = count($msgs);   ここは 
 「普通」な処理と変わらない $msgs = mysqli_fetch_all( 
 $result, MYSQLI_ASSOC 
 ); $msgCnt = count($msgs);
  44. 非構造化すると… if (!$result) goto db_error; $msgs = mysqli_fetch_all( 
 $result,

    MYSQLI_ASSOC ); $msgCnt = count($msgs); ?> <!DOCTYPE html> <html> <head> <title>఻ݴҰཡ</title> <link rel="stylesheet" href="…"> </head> <body> <div class="container"> 取得部分→表示部分  
  45. 非構造化すると… if (!$result) goto db_error; $msgs = mysqli_fetch_all( 
 $result,

    MYSQLI_ASSOC ); $msgCnt = count($msgs); ?> <!DOCTYPE html> <html> <head> <title>఻ݴҰཡ</title> <link rel="stylesheet" href="…"> </head> <body> <div class="container"> 取得部分→表示部分 • 「ファイルの分割ができな い」を真っ当にやるため、 
 ロジックの中にテンプレー ト部分も埋め込む形にした   <!DOCTYPE html> <html> <head> <title>఻ݴҰཡ</title> <link rel="stylesheet" href="…"> </head> <body> <div class="container">
  46. 非構造化すると… <ul class="list-group mb-4"> <?php $i = 0; msg_loop: if

    ($i >= $msgCnt) goto msg_loop_end; $m = $msgs[$i]; ?> <li class="list-group-item"> <a href="/?action=detail&id=<?= $m['id'] ?>"> <?= htmlspecialchars($m['title']) ?> </a> by <?= htmlspecialchars($m['author']) ?> </li> <?php $i++; goto msg_loop; msg_loop_end: ?> </ul> 表示部分(プレゼンテーション) • パッと見で、 
 印象はどうでしょうか? • やっていることは、さっきま でのコードと同じではある…  
  47. 比べてみる 「制御構文」を用いて 
 構造化した場合と比較 <ul> <?php 
 foreach ($msgs as

    $m): 
 ?> <li> </li> <?php endforeach; ?> </ul>   <ul class="list-group mb-4"> <?php $i = 0; msg_loop: if ($i >= $msgCnt) goto msg_loop_end; $m = $msgs[$i]; ?> <li class="list-group-item"> <a href="/?action=detail&id=<?= $m['id'] ?>"> <?= htmlspecialchars($m['title']) ?> </a> by <?= htmlspecialchars($m['author']) ?> </li> <?php $i++; goto msg_loop; msg_loop_end: ?> </ul> <ul class="list-group mb-4"> <?php $i = 0; msg_loop: if ($i >= $msgCnt) goto msg_loop_end; $m = $msgs[$i]; ?> </li> <?php $i++; goto msg_loop; msg_loop_end: ?> </ul>
  48. 非構造化すると… <ul class="list-group mb-4"> <?php $i = 0; msg_loop: if

    ($i >= $msgCnt) goto msg_loop_end; $m = $msgs[$i]; ?> <li class="list-group-item"> <a href="/?action=detail&id=<?= $m['id'] ?>"> <?= htmlspecialchars($m['title']) ?> </a> by <?= htmlspecialchars($m['author']) ?> </li> <?php $i++; goto msg_loop; msg_loop_end: ?> </ul>   $i = 0; ループカウンターを 
 初期化して
  49. 非構造化すると… <ul class="list-group mb-4"> <?php $i = 0; msg_loop: if

    ($i >= $msgCnt) goto msg_loop_end; $m = $msgs[$i]; ?> <li class="list-group-item"> <a href="/?action=detail&id=<?= $m['id'] ?>"> <?= htmlspecialchars($m['title']) ?> </a> by <?= htmlspecialchars($m['author']) ?> </li> <?php $i++; goto msg_loop; msg_loop_end: ?> </ul>   反復処理の開始部分 msg_loop:
  50. 非構造化すると… <ul class="list-group mb-4"> <?php $i = 0; msg_loop: if

    ($i >= $msgCnt) goto msg_loop_end; $m = $msgs[$i]; ?> <li class="list-group-item"> <a href="/?action=detail&id=<?= $m['id'] ?>"> <?= htmlspecialchars($m['title']) ?> </a> by <?= htmlspecialchars($m['author']) ?> </li> <?php $i++; goto msg_loop; msg_loop_end: ?> </ul>   反復回数と最大回数を比較 if ($i >= $msgCnt) goto msg_loop_end; msg_loop_end: 最後の伝言の処理が 
 終わったのが確認されたら、 
 反復処理を抜ける
  51. 非構造化すると… <ul class="list-group mb-4"> <?php $i = 0; msg_loop: if

    ($i >= $msgCnt) goto msg_loop_end; $m = $msgs[$i]; ?> <li class="list-group-item"> <a href="/?action=detail&id=<?= $m['id'] ?>"> <?= htmlspecialchars($m['title']) ?> </a> by <?= htmlspecialchars($m['author']) ?> </li> <?php $i++; goto msg_loop; msg_loop_end: ?> </ul>   カウンターを使って イテレーション $m = $msgs[$i];
  52. 非構造化すると… <ul class="list-group mb-4"> <?php $i = 0; msg_loop: if

    ($i >= $msgCnt) goto msg_loop_end; $m = $msgs[$i]; ?> <li class="list-group-item"> <a href="/?action=detail&id=<?= $m['id'] ?>"> <?= htmlspecialchars($m['title']) ?> </a> by <?= htmlspecialchars($m['author']) ?> </li> <?php $i++; goto msg_loop; msg_loop_end: ?> </ul>   カウンターを更新して、 
 反復処理の開始地点に戻る $i++; goto msg_loop; msg_loop:
  53. 伝言のバリデーション • フィールドごとに 
 ifで検証 • 何の変哲もないですよね? • エラーがあったら、 `$errors`

    にエラーメッ セージを格納する $errors = []; if ( $input['title'] === null || mb_strlen($input['title']) > 50 ) { $errors['title'] = '50ࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞'; } if ( 
 $input['author'] === null || mb_strlen($input['author']) > 50 ) { $errors['author'] = '50จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞'; } // தུ return $errors;  
  54. 非構造化 • もう読めますね?? • 今回のルールである「ifの 中に入れられるのはジャン プだけ」を守った形   $errors

    = []; validate_title: if ($input['title'] && mb_strlen($input['title']) <= 50) goto validate_author; $errors['title'] = 'λΠτϧ͸50จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞'; validate_author: if ($input['author'] && mb_strlen($input['author']) <= 50) goto validate_body; $errors['author'] = '౤ߘऀ໊͸50จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞'; // தུ validate_end: if ($errors) goto new_message;
  55. 非構造化   validate_title: if ( $input['title'] && mb_strlen($input['title']) <=

    50 ) goto validate_author; $errors['title'] = 'λΠτϧ͸50จࣈҎ಺Ͱೖྗͯͩ͘͠͞ ͍'; validate_author: if ($input['author'] && … if ( $input['title'] && mb_strlen($input['title']) <= 50 ) データの形式をチェックして
  56. 非構造化   validate_title: if ( $input['title'] && mb_strlen($input['title']) <=

    50 ) goto validate_author; $errors['title'] = 'λΠτϧ͸50จࣈҎ ಺Ͱೖྗ͍ͯͩ͘͠͞'; validate_author: if ($input['author'] && … goto validate_author; OKだったら次のチェックへ validate_author:
  57. 非構造化   validate_title: if ( $input['title'] && mb_strlen($input['title']) <=

    50 ) goto validate_author; $errors['title'] = 'λΠτϧ͸50จࣈҎ ಺Ͱೖྗ͍ͯͩ͘͠͞'; validate_author: if ($input['author'] && … $errors['title'] = 'λΠτϧ͸50จࣈҎ ಺Ͱೖྗ͍ͯͩ͘͠͞'; ジャンプされなかったら 
 エラー時の処理に入る
  58. "非"構造化プログラミングでは、どうしていた? 昔から、別の手段で実現していた話ではある   順接 or … 分岐 or …

    反復 or … 「ソースコードを上から読んで」実行する 条件付きジャンプで「状態に応じて」実行する 分岐の応用で「必要な回数繰り返し」実行する
  59. "非"構造化プログラミングと比べてみると・・ 昔から、別の手段で実現していた話ではある   順接 or … 分岐 or …

    反復 or … 「ソースコードを上から読んで」実行する 条件付きジャンプで「状態に応じて」実行する 分岐の応用で「必要な回数繰り返し」実行する 一体、何が変わったのか?
  60. コードで考える 大体こんなコード   $messages = mysqli_fetch_all($result); $messageCnt = count($messages);

    $i = 0; loop_start: if ($i >= $messageCnt) goto loop_end; $message = $messages[$i]; echo $message['title']; echo $message['body']; echo $message['created']; $i++; goto loop_start; loop_end:
  61. コードで考える   $messages = mysqli_fetch_all($result); $messageCnt = count($messages); $i

    = 0; loop_start: if ($i >= $messageCnt) goto loop_end; $message = $messages[$i]; echo $message['title']; echo $message['body']; echo $message['created']; $i++; goto loop_start; loop_end: 伝言を取得 1件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了
  62. コードで考える   $messages = mysqli_fetch_all($result); $messageCnt = count($messages); $i

    = 0; loop_start: if ($i >= $messageCnt) goto loop_end; $message = $messages[$i]; echo $message['title']; echo $message['body']; echo $message['created']; $i++; goto loop_start; loop_end: 伝言を取得 1件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了
  63. コードで考える   $messages = mysqli_fetch_all($result); $messageCnt = count($messages); $i

    = 0; loop_start: if ($i >= $messageCnt) goto loop_end; $message = $messages[$i]; echo $message['title']; echo $message['body']; echo $message['created']; $i++; goto loop_start; loop_end: 伝言を取得 1件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了
  64. コードで考える   $messages = mysqli_fetch_all($result); $messageCnt = count($messages); $i

    = 0; loop_start: if ($i >= $messageCnt) goto loop_end; $message = $messages[$i]; echo $message['title']; echo $message['body']; echo $message['created']; $i++; goto loop_start; loop_end: 伝言を取得 1件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了
  65. コードで考える   $messages = mysqli_fetch_all($result); $messageCnt = count($messages); $i

    = 0; loop_start: if ($i >= $messageCnt) goto loop_end; $message = $messages[$i]; echo $message['title']; echo $message['body']; echo $message['created']; $i++; goto loop_start; loop_end: 伝言を取得 1件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了
  66. コードで考える   $messages = mysqli_fetch_all($result); $messageCnt = count($messages); $i

    = 0; loop_start: if ($i >= $messageCnt) goto loop_end; $message = $messages[$i]; echo $message['title']; echo $message['body']; echo $message['created']; $i++; goto loop_start; loop_end: 伝言を取得 1件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了
  67. コードで考える   $messages = mysqli_fetch_all($result); $messageCnt = count($messages); $i

    = 0; loop_start: if ($i >= $messageCnt) goto loop_end; $message = $messages[$i]; echo $message['title']; echo $message['body']; echo $message['created']; $i++; goto loop_start; loop_end: 伝言を取得 1件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了
  68. ( ) 次の伝言へ コードで考える   $messages = mysqli_fetch_all($result); $messageCnt

    = count($messages); $i = 0; loop_start: if ($i >= $messageCnt) goto loop_end; $message = $messages[$i]; echo $message['title']; echo $message['body']; echo $message['created']; $i++; goto loop_start; loop_end: 伝言を取得 1件取り出す 件名を表示 本文を表示 投稿日時を表示 無かったら終了 無かったら終了
  69. 構造化すると・・・ 大体こんなコード   $messages = getMessages($dbo, $page); foreach ($messages

    as $message) { echo $message['title']; echo $message['body']; echo $message['author']; }
  70. 構造化すると・・・   $messages = getMessages($dbo, $page); foreach ($messages as

    $message) { echo $message['title']; echo $message['body']; echo $message['author']; } 伝言を取得 1件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了
  71. 構造化すると・・・   $messages = getMessages($dbo, $page); foreach ($messages as

    $message) { echo $message['title']; echo $message['body']; echo $message['author']; } 伝言を取得 1件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了
  72. 構造化すると・・・   $messages = getMessages($dbo, $page); foreach ($messages as

    $message) { echo $message['title']; echo $message['body']; echo $message['author']; } 伝言を取得 1件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了
  73. 構造化すると・・・   $messages = getMessages($dbo, $page); foreach ($messages as

    $message) { echo $message['title']; echo $message['body']; echo $message['author']; } 伝言を取得 1件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了
  74. 構造化すると・・・   $messages = getMessages($dbo, $page); foreach ($messages as

    $message) { echo $message['title']; echo $message['body']; echo $message['author']; } 伝言を取得 1件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了
  75. 構造化すると・・・   $messages = getMessages($dbo, $page); foreach ($messages as

    $message) { echo $message['title']; echo $message['body']; echo $message['author']; } 伝言を取得 1件取り出す 次の伝言へ 無かったら終了 件名を表示 本文を表示 投稿日時を表示
  76. 自由に飛び回るコードとは? gotoプログラミングでも、 
 「意味的なまとまり」は 
 もちろん存在する   $i =

    0; loop_start: if ($i >= $cnt) goto loop_end; sub_loop: $item = $items[$i]; // தུ sub_loop_end: $i++; goto loop_start; loop_end:
  77. 自由に飛び回るコードとは?   $i = 0; loop_start: if ($i >=

    $cnt) goto loop_end; sub_loop: $item = $items[$i]; // தུ sub_loop_end: $i++; goto loop_start; loop_end: loop_start: ここが「ループ処理」の 
 まとまり loop_end:
  78. 自由に飛び回るコードとは? が!! 
 読み手が見出すしかない 
 (強制力がない)   $i =

    0; loop_start: if ($i >= $cnt) goto loop_end; sub_loop: $item = $items[$i]; // தུ sub_loop_end: $i++; goto loop_start; loop_end:
  79. 自由に飛び回るコードとは?   $i = 0; loop_start: if ($i >=

    $cnt) goto loop_end; sub_loop: $item = $items[$i]; // தུ sub_loop_end: $i++; goto loop_start; loop_end: goto sub_loop; sub_loop: 「まとまり」の外から 
 直接的に内に進入できる
  80. 順接実行の世界   foreach ( 
 $messages as $message )

    { /// தུ } 「ブロック」を用いる 
 「順接実行」の世界なら 
 こうしたワープが防がれる
  81. 順接実行の世界   foreach ( $messages as $message ) {

    /// தུ continue; } 入口に戻るか 
 (ブロックの内側の移動) continue; foreach (
  82. 順接実行の世界   foreach ( $messages as $message ) {

    break; /// தུ } 出口に向かうか 
 (ブロック脱出の唯一の方法) break; }
  83. ∴「自由に飛び回れない」とは?   foreach ( $messages as $message ) {

    break; /// தུ } 「特定の場所にジャンプする」 
 というより 
 「ループを進める・終了する」 
 という操作の概念になる
  84. ∴「自由に飛び回れない」とは?   foreach ( $messages as $message ) {

    break; /// தུ } これによって 「意味的なまとまり」は 
 「ハードな制約」になっている
  85. 順接実行の場合 前にあるステップから入り、 
 次にあるステップに抜けるのが 
 順接実行   伝言を取得 無かったら終了

    件名を表示 本文を表示 投稿日時を表示 次の伝言へ 1件取り出す 1件ごとの 
 逐次処理
  86. 両者を比較してみると   件名を表示 本文を表示 投稿日時を表示 1件取り出す 1件ごとの 
 逐次処理

    件名を表示 本文を表示 投稿日時を表示 1件取り出す どこからでも・どこへでも ブロックを境に出入りを制限
  87. 制御フローの抽象化   伝言を取得 1件ずつ処理 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 1件取り出す

    foreach ( 
 $messages as $message ) { /// தུ } 「まとまり」が 
 明確に 
 構文として現れる
  88. コールスタック? • サブルーチンや関数の「今呼ばれたもの」「呼び出し元(戻り先)」を 
 積み上げて管理する • スタックを減らす = 「入口〜出口」の 処理が終わった時に、呼び出し元に戻す

    • スタックオーバーフロー = 呼び出しが 深くなりすぎてスタックが・・・状態 • gotoだと「終わった時に戻る場所」 を特定できないので、 
 コールスタックも積まれない  
  89. 「構造化(あるいは非構造化)」の威力 • 構造化 = ブラックボックス化の力 • そのために「順序どおりに」の秩序が欠かせない • そにれよって、複数のレイヤーの「流れ」「関心」を切り離した •

    構造化のためのサブルーチン • 「入口」は一箇所だけで「出口」は呼び出し元に固定 • 「呼び出された側」が「外の世界の、具体的な位置を気にする」という 
 相互的な依存状態も解消される。疎結合化を促進する。  
  90. 抽象 • 組み合わせ: 
 具体を積み上げて 
 抽象を支える • 分解: 


    各モジュールの外からは 
 その中身は干渉できない • 単純化: 
 問題と解法の「正しさ」は 
 レイヤーごとに保証する 抽象 具体 アルゴリズムや 
 I/O制御 ユースケースや 
 モデル
  91. 参考書籍 • E.W.ダイクストラ, C.A.R.ホーア, O.-J.ダール 共著ほか. 構造化プ ログラミング, サイエンス社, 1975,

    (サイエンスライブラリ. 情報電 算機 ; 32) • ドナルド・E.クヌース 著ほか. 文芸的プログラミング, アスキー, 1994.3. • 葉田善章 著. コンピュータの動作と管理. 改訂版, 放送大学教育振興 会, 2017.3, (放送大学教材).  
  92. PHPのgoto 〜新しくてきれいなgoto〜 • マニュアル: https://php.net/goto • (それでも乱用するものでもないが)従来的な問題に対して、大きく制約を加え て安全な利用を促進している • 公式ドキュメントでも「limited

    goto」と称した記述がある • https://www.php.net/releases/5_3_0.php • 別ファイルや、ブロック外→内部へのジャンプが禁止された(limited)もの • 導入は5.3から。名前空間、遅延的束縛、無名関数などが入ったバージョン • GOTO in PHP 5.3: is it Really That Evil? | PHP Architect • https://www.phparch.com/2009/06/goto-in-php-5-3-is-it-really-that-evil/ • 追加の経緯や考察が述べられている記事。よくまとまっていて面白い  
  93. WEB+DB PRESS Vol.33 『構造化プログラミング入門』 https://gihyo.jp/magazine/wdpress/archive/2006/vol33 • かなり簡潔で、要点を突いた読み物になっているので 
 「構造化プログラミングって何?」という人におすすめしやすい •

    「前史」「近代構造化技法」「モデル化」といった話の流れがあり、 背景〜実用までを扱っている • PDF版はGihyo Digital Publishingで販売しているので、問題なく手に 入れることが可能  
  94. Composerでの例 • ユーザーのキー入力を待つ 対話的な部分 • 「その操作は実行できない よ」というケースでデフォ ルトの処理(=help表示)に 飛ばす・・という処理 •

    switchの中で、別のブランチ からdefaultに飛ばしている   <?php while (true) { switch ($this->io->ask(...)) { case 'y': $this->discardChanges($path); break 2; case 's': if (!$update) { goto help; } $this->stashChanges($path); break 2; // লུ case '?': default: help : $this->io->writeError(...); if ($update) { $this->io->writeError(' s - stash changes and try to reapply them after the update'); } $this->io->writeError(' ? - print help'); break; } } src: 
 https://github.com/composer/composer/ blob/2.8.6/src/Composer/Downloader/ GitDownloader.php#L375
  95. 可視化に使ったツール • 事前にXdebugの設定(※ゴミファイルが溜まらないように注意) • xdebug.trace_format=3 • xdebug.mode=trace • xdebug.start_with_request=yes •

    `frmegraph.pl`を `/opt` 以下に設置後、次のような処理を挟んでおくと便利   register_shutdown_function(function () { $traceFileName = xdebug_get_tracefile_name(); xdebug_stop_trace(); exec("/opt/framegraph/flamegraph.pl {$traceFileName} --width 720 > /tmp/trace.svg"); echo '<div class="container-fluid"><div style="text-align: center">' . file_get_contents('/tmp/trace.svg') . '</div></div>'; });
  96. なぜファイルの分割を封印したのか? • PHPは基本的にファイル単位でのスコープを持たない(変数などコンテキストが 共有される)ので、分割したところで大きな問題はないか?とも考えつつ・・・ • が、その方が「より、らしくなる」と考えた理由がいくつか 1. PHPのgotoの制約 • 同じファイル上の同じコンテキストのラベルにしかジャンプできない

    2. (構造化とは関係ないが)昔の「スパゲティ」の起因の考慮 • 「ファイルの分割と動的なロード」をサポートしていた言語があったか?が不透明 (調査しきれていない) • 一方、静的な取り込みについては、COBOLのCOPY命令などが存在しており、、どの程 度「見通し」「秩序」に貢献していたのか?は気になる • => ということで、主に「1」の理由を重視し、封印しようと決定  
  97. グローバルスコープオンリー • 結果的に成立しそうなので明文化しなかったものの、「グローバルスコープしか 使ってはいけない」は重要視している観点の1つ • たとえば「サブルーチンはあるが、コンテキストが共有される」ものは、構造化 の恩恵(階層的なプログラミング)を受けられなくなる • 「引数やローカル変数を持たないサブルーチン」を持つ言語も存在し、これは「構造化」よ りも「再利用のための改良GOTO」な感覚が近そう

    • 「構造化プログラミング」の縛りでも「関数はreturnで値を返さない 
 (グローバル変数を操作する)」としたが、これは「関数の中でローカル変数を利用するのを 禁止する」ものではない • 一方で、今回の発表は主に「制御の流れ」に焦点を絞ってデータの持ち回しにつ いては踏み込んでいないので、割愛したもの