Slide 1

Slide 1 text

PHPによる 
 "非"構造化プログラミング入門 -本当に熱いスパゲティコードを求めて- PHPerKaigi 2025 Hideki Kinjyo GitHub: o0h / X: @o0h_ [公開用] v1.1.3

Slide 2

Slide 2 text

登壇者挨拶 = ͜Μʹͪ͸ʙʙʂ ?>  Fatal error: Uncaught Error: Undefined constant "͜Μʹͪ͸ʔʂ" in php-wasm run script:1

Slide 3

Slide 3 text

登壇者挨拶 今、このスライドを見ているということは 
 皆さんは、既に─── 

Slide 4

Slide 4 text

登壇者挨拶 スパゲティーコードが 書きたくて、知りたくて、堪らないんですね? 

Slide 5

Slide 5 text

イントロ もっと言えば 

Slide 6

Slide 6 text

イントロ  = 0; $i--) { echo $i . PHP_EOL; } echo '🚀 ϩέοτൃࣹʂ'; こういうのが、

Slide 7

Slide 7 text

イントロ 

Slide 8

Slide 8 text

イントロ  run(); index.phpだって、

Slide 9

Slide 9 text

イントロ  = 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: ?> ఻ݴҰཡ
= htmlspecialchars($message['title']) ? >

౤ߘऀ span> = htmlspecialchars($message['author']) ?>

౤ߘ೔ span> = $message['created_at'] ?>

= nl2br(htmlspecialchars($message['body'] )) ?>

৽ن఻ݴ࡞੒
    = $errorCount) goto error_list_end; $errorField = array_keys($errors)[$i]; ?>
  • = $errors[$errorField] ?>
λΠτϧ
ຊจ
౤ߘऀ໊
:
ొ࿥
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: このくらい 
 じゃないと!

Slide 10

Slide 10 text

イントロ ここに集まってくれた 
 友達のみんな 全員で一緒に 

Slide 11

Slide 11 text

イントロ スパゲティコード、 
 やっていこうな!! 

Slide 12

Slide 12 text

自己紹介 • 金城秀樹 / きんじょうひでき • GitHub: @o0h / 𝕏 : @o0h_ • 好きなFWはCakePHP • アイコンは美味しい鮭親子丼の写真です • 最近はPodcastをやっています • ハッシュタグ: #readlinefm • 🆕 パンフ記事も書いているので見てください! 

Slide 13

Slide 13 text

おしながき 今日は3つのゾーンがあります 1. 感じるゾーン 〜「構造化」「非構造化」を感じる〜 2. 知るゾーン 〜「構造化(あるいは非構造化)」の威力を知る〜 3. 考えるゾーン 〜「構造化」がもたらすものを考える〜 4. おしまい 

Slide 14

Slide 14 text

1. 感じるゾーン 
 〜「構造化」「非構造化」を感じる〜 2. 知るゾーン 3. 考えるゾーン 4. おしまい

Slide 15

Slide 15 text

そもそも構造化ってなんですの? 
 〜まずは凄く簡単に、ちょっとだけ〜

Slide 16

Slide 16 text

構造化プログラミング - Wikipedia https://ja.wikipedia.org/wiki/構造化プログラミング 

Slide 17

Slide 17 text

順接、分岐、反復による制御 

Slide 18

Slide 18 text

分割と階層化 

Slide 19

Slide 19 text

構造化プログラミングによく出てくる要素 プログラミングにおけるフロー制御を3つに整理した、と知られている  順接 分岐 反復 書かれた順番に実行される 条件に応じて、対応する処理を実行する 条件を満たしている間、対応する処理を実行する

Slide 20

Slide 20 text

構造化プログラミングによく出てくる要素 プログラミングにおけるフロー制御を3つに整理した、と知られている  順接 分岐 反復 書かれた順番に実行される 条件に応じて、対応する処理を実行する 条件を満たしている間、対応する処理を実行する 上から処理する

Slide 21

Slide 21 text

構造化プログラミングによく出てくる要素 プログラミングにおけるフロー制御を3つに整理した、と知られている  順接 分岐 反復 書かれた順番に実行される 条件に応じて、対応する処理を実行する 条件を満たしている間、対応する処理を実行する ifとかswitchとか

Slide 22

Slide 22 text

構造化プログラミングによく出てくる要素 プログラミングにおけるフロー制御を3つに整理した、と知られている  順接 分岐 反復 書かれた順番に実行される 条件に応じて、対応する処理を実行する 条件を満たしている間、対応する処理を実行する ループ文

Slide 23

Slide 23 text

構造化プログラミング(言語)とは? 言語や指向のレベルで、 「秩序を与えやすくする」ように 
 サポートするもの 

Slide 24

Slide 24 text

構造化プログラミング(言語)とは? "非”構造化の世界では・・  低いレベルでの「構造化」のサポートが不十分 
 使い手が自分で秩序を与える努力が求められる 

Slide 25

Slide 25 text

"非"構造化プログラミング入門 
 〜コードを書いて・並べて・比べてみよう〜

Slide 26

Slide 26 text

お題: 伝言掲示板 実際にPHPアプリケーションを書いて、そのコードを比較していきます ① いつもの ② 構造化プログラミング(手続き型) ③ "非"構造化プログラミング 

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

スタイルを味わうためのルール • 各実装は、それぞれのスタイルを味わうためのルールを設けています • なお、本来のパラダイムやスタイルの「定義通り」ではなくても 
 「典型的なコード」のために独自の縛りを含みます • そもそもPHPで使える機能・使えない機能もあるため 

Slide 31

Slide 31 text

スタイル①を味わうためのルール ① いつもの ② 構造化プログラミング(手続き型) ③ "非"構造化プログラミング  オブジェクト指向、クラス 
 フレームワーク(制御の逆転)

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

スタイル③を味わうためのルール ① いつもの ② 構造化プログラミング(手続き型) ③ "非"構造化プログラミング  クラスなし 関数なし 「ブロック」なし 分岐は条件付きジャンプのみ可 高階関数・式のネストなし ファイルの分割なし

Slide 34

Slide 34 text

③ルール1: クラスなし  readonly class User { public function __construct( private string $name, private int $age, ) { } } ✕

Slide 35

Slide 35 text

③ルール2: 関数なし  function getNanika(): string { return 'ͳʹ͔'; } ✕

Slide 36

Slide 36 text

③ルール3: 「ブロック」なし  foreach ($array as $key => $value) printf('%s => %s', $key, $value); ✕ if ($a) { echo 'a'; } elseif ($b) { echo 'b'; } else { echo 'c'; } ✕

Slide 37

Slide 37 text

③ルール4: 条件付きジャンプのみ可  if ($me !== 'taro') goto honshori; echo 'Taro is here!'; honshori: ◯ if ($me === 'taro') { echo 'Taro is here!'; } ✕

Slide 38

Slide 38 text

③ルール5: 高階関数・式のネストなし  array_map( 
 fn ($x) => $x + 1, $values, 
 ); mb_substr( $title 0, mb_strlen($displayLimit), ); ✕ ✕

Slide 39

Slide 39 text

③ルール6: ファイルの分割なし  require_once __DIR__ . '/model.php'; require __DIR__ . '/template.php'; ✕

Slide 40

Slide 40 text

\整いました!/ これで伝言掲示板を作っていくぞ! 

Slide 41

Slide 41 text

全体のファイル構成を比べてみる 

Slide 42

Slide 42 text

いつもの姿 • 素朴な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 ※ファイル数が多いので 
 一部省略

Slide 43

Slide 43 text

構造化(手続き型)の姿 • 似たようなロジックを 
 1つのファイルに集約 • ファイル数はぐっと減る  . ├── assets ├── index.php ├── lib │ ├── actions.php │ ├── db.php │ └── helper.php └── templates ├── list_msgs.php ├── new_message.php └── view_message.php

Slide 44

Slide 44 text

非構造化 • おわかりいただけますか? • 「ファイルの分割」も封印 したので、このような形に  . ├── assets └── index.php

Slide 45

Slide 45 text

俺たちはここまで来たぞ  . ├── 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

Slide 46

Slide 46 text

index.phpを比べてみる 

Slide 47

Slide 47 text

いつもの姿 • 実務は持たない • 起動設定を行い、 
 FWに処理を委ねる  run();

Slide 48

Slide 48 text

構造化(手続き型)の姿 • 指定された内容から 
 起動する処理を判断して • 対応する手続きを呼び出す • もちろん、より多くを関数 の中に隠すこともできる detailMessage(), 'new' => newMessage(), 'register' => registerMessage(), default => listMessages(), }; exit(); 

Slide 49

Slide 49 text

非構造化 • 長い • 読めますか? • ここに全部ある = 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: ?> ఻ݴҰཡ
= htmlspecialchars($message['title']) ?> title>

౤ߘऀ = htmlspecialchars($message['author']) ?>

౤ߘ೔ = $message['created_at'] ?>

= nl2br(htmlspecialchars($message['body'])) ?>

৽ن఻ݴ࡞੒
    = $errorCount) goto error_list_end; $errorField = array_keys($errors)[$i]; ?>
  • = $errors[$errorField] ?>
λΠτϧ
ຊจ
౤ߘऀ໊
:
ొ࿥ button>
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: 

Slide 50

Slide 50 text

実装内容について: 
 伝言一覧の中身を比べてみる 

Slide 51

Slide 51 text

伝言一覧の表示 どんな仕事? • query paramからページ番号を取得 • 有効なページ番号でなかったら、 
 デフォルト(=1)にリセットする • ページに応じて、DBから伝言(複数)を取得 • 伝言を1件ずつ表示 など 

Slide 52

Slide 52 text

実装内容について: 
 伝言一覧の中身を比べてみる > 
 A. pageの取得と判定 

Slide 53

Slide 53 text

構造化プログラミング • リクエストから`?page=`を INTで矯正して取り出し、 
 不正な範囲だったら`1`で 上書きする • 今っぽいスタイルでも手続 き型のスタイルでも同じ • FWを使ってればリクエスト内 容に生で触ることは無さそう かな?くらいの違い $page = filter_input( INPUT_GET, 'page', FILTER_VALIDATE_INT, [ 'options' => [ 'default' => 1, ] ] ); if ($page < 1) { $page = 1; } 

Slide 54

Slide 54 text

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; } 

Slide 55

Slide 55 text

構造化プログラミング • あるいは、`max()` で値を 丸めてしまうことも $page = max(1, filter_input( INPUT_GET, 'page', FILTER_VALIDATE_INT, [ 
 'options' => [ 
 'default' => 1, 
 ], 
 ] )); $limit = 10; $offset = ($page - 1) * $limit; 

Slide 56

Slide 56 text

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()` を使っている) 

Slide 57

Slide 57 text

• ここからが • 俺達の本気だ!! $page = max(1, filter_input( INPUT_GET, 'page', FILTER_VALIDATE_INT, [ 
 'options' => [ 
 'default' => 1, 
 ], 
 ] )); $limit = 10; $offset = ($page - 1) * $limit; code review これを非構造化したら、どうなる? 

Slide 58

Slide 58 text

非構造化すると… $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: チェックポイント ✓条件分岐はジャンプで ✓式の中で別の式を呼んでい ない ※ 味わい深さを優先して、 やや汚いコードにしています 

Slide 59

Slide 59 text

非構造化すると… $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 
 ); 

Slide 60

Slide 60 text

非構造化すると… $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: 

Slide 61

Slide 61 text

非構造化すると… $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: 

Slide 62

Slide 62 text

非構造化すると… $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: 

Slide 63

Slide 63 text

非構造化すると… $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; 

Slide 64

Slide 64 text

非構造化すると… $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: 

Slide 65

Slide 65 text

実装内容について: 伝言一覧の中身を比べてみる > 
 B. 伝言を1件ずつ表示 

Slide 66

Slide 66 text

伝言を表示する流れ 1. DBから伝言のデータ(複数)を取得して 2. 表示内容を1件ずつ取り出して 3. ページのテンプレートデータを読み込んで 4. 伝言の内容を流し込んで描画する 

Slide 67

Slide 67 text

いつもの 取得部分(コントローラー) • 「取っ て」「仕込ん で」「表 示して」みたいな感じです • (特に説明したいことナシ) $msgs = $this 
 ->repository 
 ->getMulti($limit, $ofset); $payload = new Payload( msgs: $msgs, ); $this->render($payload); 

Slide 68

Slide 68 text

いつもの 表示部分(プレゼンテーション) • メッセージのコレクション を、foreachで回す • その中で素朴にechoしている のみ

Slide 69

Slide 69 text

構造化プログラミング 取得部分(手続き) • 処理が単位ごとに切り出され ているので、OOPをベースに 書いたコードと骨組みは変わ らない印象 • クラス-メソッドの仕組みが ないので、関数同士の関連性 が見抜きにくい気もする • それも、namespaceや命名の工夫 で支援できそう $dbo = getDbConnection(); $msgs = getMessages( $dbo, $page ); render( compact('msgs'), 'list_msgs.php', ); 

Slide 70

Slide 70 text

構造化プログラミング 表示部分(プレゼンテーション) • オブジェクトが連想配列に なったくらいで、ほとんど何 も変わっていない • そもそもテンプレートに複雑 なロジックを持っていなけれ ば、出てくるのはループや簡 単な分岐くらい

Slide 71

Slide 71 text

構造化プログラミング 表示部分(プレゼンテーション) • オブジェクトが連想配列に なったくらいで、ほとんど何 も変わっていない • そもそもテンプレートに複雑 なロジックを持っていなけれ ば、出てくるのはループや簡 単な分岐くらい これを非構造化したら、どうなる? 

Slide 72

Slide 72 text

非構造化すると… $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); まずは伝言の取得部分 

Slide 73

Slide 73 text

非構造化すると… $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;

Slide 74

Slide 74 text

非構造化すると… $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

Slide 75

Slide 75 text

非構造化すると… $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;

Slide 76

Slide 76 text

非構造化すると… $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);

Slide 77

Slide 77 text

非構造化すると… if (!$result) goto db_error; $msgs = mysqli_fetch_all( 
 $result, MYSQLI_ASSOC ); $msgCnt = count($msgs); ?> ఻ݴҰཡ
取得部分→表示部分 

Slide 78

Slide 78 text

非構造化すると… if (!$result) goto db_error; $msgs = mysqli_fetch_all( 
 $result, MYSQLI_ASSOC ); $msgCnt = count($msgs); ?> ఻ݴҰཡ
取得部分→表示部分 • 「ファイルの分割ができな い」を真っ当にやるため、 
 ロジックの中にテンプレー ト部分も埋め込む形にした  ఻ݴҰཡ

Slide 79

Slide 79 text

非構造化すると… 表示部分(プレゼンテーション) • パッと見で、 
 印象はどうでしょうか? • やっていることは、さっきま でのコードと同じではある… 

Slide 80

Slide 80 text

比べてみる 「制御構文」を用いて 
 構造化した場合と比較
    = $msgCnt) goto msg_loop_end; $m = $msgs[$i]; ?>

Slide 81

Slide 81 text

非構造化すると…  $i = 0; ループカウンターを 
 初期化して

Slide 82

Slide 82 text

非構造化すると…  反復処理の開始部分 msg_loop:

Slide 83

Slide 83 text

非構造化すると…  反復回数と最大回数を比較 if ($i >= $msgCnt) goto msg_loop_end; msg_loop_end: 最後の伝言の処理が 
 終わったのが確認されたら、 
 反復処理を抜ける

Slide 84

Slide 84 text

非構造化すると…  カウンターを使って イテレーション $m = $msgs[$i];

Slide 85

Slide 85 text

非構造化すると…  カウンターを更新して、 
 反復処理の開始地点に戻る $i++; goto msg_loop; msg_loop:

Slide 86

Slide 86 text

実装内容について: 
 伝言の登録の中身を比べてみる 

Slide 87

Slide 87 text

実装内容について: 伝言の登録の中身を比べてみる > 
 A. バリデーション 

Slide 88

Slide 88 text

伝言のバリデーション • フィールドごとに 
 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; 

Slide 89

Slide 89 text

非構造化 • もう読めますね?? • 今回のルールである「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;

Slide 90

Slide 90 text

非構造化  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 ) データの形式をチェックして

Slide 91

Slide 91 text

非構造化  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:

Slide 92

Slide 92 text

非構造化  validate_title: if ( $input['title'] && mb_strlen($input['title']) <= 50 ) goto validate_author; $errors['title'] = 'λΠτϧ͸50จࣈҎ ಺Ͱೖྗ͍ͯͩ͘͠͞'; validate_author: if ($input['author'] && … $errors['title'] = 'λΠτϧ͸50จࣈҎ ಺Ͱೖྗ͍ͯͩ͘͠͞'; ジャンプされなかったら 
 エラー時の処理に入る

Slide 93

Slide 93 text

感じるゾーン: まとめ

Slide 94

Slide 94 text

「構造化」と「非構造化」 • 「必要なロジック」は、どちらも十分に実現ができる • ただし、煩雑さや記述量・可読性の面では 
 かなり違った結果をもたらしそうだな・・という印象を受ける • この後、より踏み込んで「何が問題なのか」を見ていきます! 

Slide 95

Slide 95 text

1. 感じるゾーン 2. 知るゾーン 
 〜「構造化(あるいは非構造化)」の威力を知る〜 3. 考えるゾーン 4. おしまい

Slide 96

Slide 96 text

構造化プログラミングの特徴を改めて 
 〜少しだけ詳しく、概念的に〜

Slide 97

Slide 97 text

(再)構造化プログラミングによく出てくる要素 プログラミングにおけるフロー制御を3つに整理した、と知られている  順接 分岐 反復 書かれた順番に実行される 条件に応じて、対応する処理を実行する 条件を満たしている間、対応する処理を実行する

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

"非"構造化では(あるいは構造化では) 
 処理の流れに何が起こっているのか 

Slide 101

Slide 101 text

一体、何が変わったのか? 流れを図にして考えてみる  伝言を取得 無かったら終了 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 1件取り出す

Slide 102

Slide 102 text

一体、何が変わったのか?  伝言を取得 無かったら終了 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 1件取り出す 「上から順」に 
 1ステップずつ処理される

Slide 103

Slide 103 text

一体、何が変わったのか?  伝言を取得 無かったら終了 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 1件取り出す こいつが

Slide 104

Slide 104 text

一体、何が変わったのか?  伝言を取得 無かったら終了 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 1件取り出す 逆行してますね

Slide 105

Slide 105 text

コードで考える 大体こんなコード  $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:

Slide 106

Slide 106 text

コードで考える  $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件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了

Slide 107

Slide 107 text

コードで考える  $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件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了

Slide 108

Slide 108 text

コードで考える  $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件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了

Slide 109

Slide 109 text

コードで考える  $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件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了

Slide 110

Slide 110 text

コードで考える  $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件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了

Slide 111

Slide 111 text

コードで考える  $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件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了

Slide 112

Slide 112 text

コードで考える  $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件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了

Slide 113

Slide 113 text

( ) 次の伝言へ コードで考える  $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件取り出す 件名を表示 本文を表示 投稿日時を表示 無かったら終了 無かったら終了

Slide 114

Slide 114 text

コードで考える  伝言を取得 1件取り出す 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 無かったら終了 3件分の表示で実行される「行」

Slide 115

Slide 115 text

構造化すると・・・ 大体こんなコード  $messages = getMessages($dbo, $page); foreach ($messages as $message) { echo $message['title']; echo $message['body']; echo $message['author']; }

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

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

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

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

Slide 122

Slide 122 text

実行イメージ  伝言を取得 1件取り出す 次の伝言へ 無かったら終了 件名を表示 本文を表示 投稿日時を表示 3件分の表示で実行される「行」

Slide 123

Slide 123 text

比較してみると  非構造化 / gotoベース ループ文

Slide 124

Slide 124 text

比較してみると  非構造化 / gotoベース ループ文 「進んだり戻った り」が発生している ような感じに 遷移が比較的穏やか で、「段差」が少な い感じに

Slide 125

Slide 125 text

処理の流れに何が起こっているのか 機能の概念レベルでは同じことを求めても、 
 処理レベルで見ると違うことが起きている様子がある 

Slide 126

Slide 126 text

順接実行と自由に飛び回れるコード 

Slide 127

Slide 127 text

構造化が秩序なら、非構造化は自由だねっていう 

Slide 128

Slide 128 text

自由に飛び回るコードと順接実行 順接実行「ではない」コードとは、 自由に飛び回れるコード 

Slide 129

Slide 129 text

自由に飛び回るコードとは? 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:

Slide 130

Slide 130 text

自由に飛び回るコードとは?  $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:

Slide 131

Slide 131 text

自由に飛び回るコードとは? が!! 
 読み手が見出すしかない 
 (強制力がない)  $i = 0; loop_start: if ($i >= $cnt) goto loop_end; sub_loop: $item = $items[$i]; // தུ sub_loop_end: $i++; goto loop_start; loop_end:

Slide 132

Slide 132 text

自由に飛び回るコードとは?  $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: 「まとまり」の外から 
 直接的に内に進入できる

Slide 133

Slide 133 text

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

Slide 134

Slide 134 text

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

Slide 135

Slide 135 text

順接実行の世界  foreach ( $messages as $message ) { break; /// தུ } 出口に向かうか 
 (ブロック脱出の唯一の方法) break; }

Slide 136

Slide 136 text

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

Slide 137

Slide 137 text

∴「自由に飛び回れない」とは?  foreach ( $messages as $message ) { break; /// தུ } これによって 「意味的なまとまり」は 
 「ハードな制約」になっている

Slide 138

Slide 138 text

概念化して比較してみる 

Slide 139

Slide 139 text

自由に飛び回るコードと順接実行 こんな処理があった時に  伝言を取得 無かったら終了 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 1件取り出す

Slide 140

Slide 140 text

自由に飛び回るコードの場合 意味的なまとまりに着目すると  伝言を取得 無かったら終了 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 1件取り出す

Slide 141

Slide 141 text

自由に飛び回るコードの場合 次にどの処理に行くかが 
 (かつ、どの処理を始点に) 
 制限されていないのが 
 自由に飛び回れるコード  伝言を取得 無かったら終了 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 1件取り出す

Slide 142

Slide 142 text

順接実行の場合 一方で 
 この意味的なかたまりについて  伝言を取得 無かったら終了 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 1件取り出す

Slide 143

Slide 143 text

順接実行の場合 前にあるステップから入り、 
 次にあるステップに抜けるのが 
 順接実行  伝言を取得 無かったら終了 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 1件取り出す 1件ごとの 
 逐次処理

Slide 144

Slide 144 text

両者を比較してみると  件名を表示 本文を表示 投稿日時を表示 1件取り出す 1件ごとの 
 逐次処理 件名を表示 本文を表示 投稿日時を表示 1件取り出す どこからでも・どこへでも ブロックを境に出入りを制限

Slide 145

Slide 145 text

構造化によって手に入れたもの 
 〜出来るようになった、それで何が嬉しいの?〜

Slide 146

Slide 146 text

制御フローの抽象化  伝言を取得 1件ずつ処理 件名を表示 本文を表示 投稿日時を表示 次の伝言へ 1件取り出す foreach ( 
 $messages as $message ) { /// தུ } 「まとまり」が 
 明確に 
 構文として現れる

Slide 147

Slide 147 text

制御フローの抽象化  「ブロック」を導入することで、 「反復」や「分岐」も 「順接実行」として扱える  制御フローの抽象化 伝言を取得 1件ずつ処理 終了処理

Slide 148

Slide 148 text

構造化によって手に入れたもの  制御フローの抽象化 段階的詳細化 「アウトラインレベル」と 
 「詳細」の分離 問題の局所化

Slide 149

Slide 149 text

構造化によって手に入れたもの  制御フローの抽象化 段階的詳細化 「アウトラインレベル」と 
 「詳細」の分離 問題の局所化 意味のまとまりを「物理」化する モジュール化 関心の分離

Slide 150

Slide 150 text

構造化によって手に入れたもの  制御フローの抽象化 段階的詳細化 問題の局所化 「アウトラインレベル」と 
 「詳細」の分離 概念レベル(フローチャート等)と 
 コードの構造の一致 モデル駆動開発 ダイクストラ 
 「異なるプログラムを 
 比較可能にする」

Slide 151

Slide 151 text

構造化によって手に入れたもの  制御フローの抽象化 段階的詳細化 「アウトラインレベル」と 
 「詳細」の分離 問題の局所化 設計を易しく テスト駆動開発 Port & Adaptor

Slide 152

Slide 152 text

構造化によって手に入れたもの  制御フローの抽象化 段階的詳細化 「アウトラインレベル」と 
 「詳細」の分離 読み書きを易しく 問題の局所化 デバッグ容易性 開発のスケール性

Slide 153

Slide 153 text

構造化によって手に入れたもの  制御フローの抽象化 段階的詳細化 「アウトラインレベル」と 
 「詳細」の分離 問題の局所化 更に踏み込んで 
 「構造化されたサブルーチン」の導入に

Slide 154

Slide 154 text

サブルーチン(のようなものたち) • 手続きを1つに固め、呼び出し可能にするもの • 外部からは「1行目」にしか入れない • 外部へは「呼び出し元」にしか戻れない • 処理の完了後に値を戻す「関数」も同様 

Slide 155

Slide 155 text

サブルーチン(のようなものたち) • 手続きを1つに固め、呼び出し可能にするもの • 外部からは「1行目」にしか入れない • 外部へは「呼び出し元」にしか戻れない • 処理の完了後に値を戻す「関数」も同様  単なる 
 「名前のついた処理のまとまり」 
 以上の意味を持つ

Slide 156

Slide 156 text

サブルーチン(のようなものたち) • 手続きを1つに固め、呼び出し可能にするもの • 外部からは「1行目」にしか入れない • 外部へは「呼び出し元」にしか戻れない • 処理の完了後に値を戻す「関数」も同様  単なる 
 「名前のついた処理のまとまり」 
 以上の意味を持つ これを支えるのが 
 「コールスタック」の仕組み

Slide 157

Slide 157 text

コールスタック? • サブルーチンや関数の「今呼ばれたもの」「呼び出し元(戻り先)」を 
 積み上げて管理する • スタックを減らす = 「入口〜出口」の 処理が終わった時に、呼び出し元に戻す • スタックオーバーフロー = 呼び出しが 深くなりすぎてスタックが・・・状態 • gotoだと「終わった時に戻る場所」 を特定できないので、 
 コールスタックも積まれない 

Slide 158

Slide 158 text

いつもの(OOP)フレームグラフ  とても深い 
 スタック

Slide 159

Slide 159 text

ちょい昔の(構造化・手続き型)フレームグラフ  ちょっと 
 スタック

Slide 160

Slide 160 text

ちょい昔の(構造化・手続き型)フレームグラフ  浅い 
 スタック

Slide 161

Slide 161 text

知るゾーン: まとめ

Slide 162

Slide 162 text

「構造化(あるいは非構造化)」の威力 • 構造化 = ブラックボックス化の力 • そのために「順序どおりに」の秩序が欠かせない • そにれよって、複数のレイヤーの「流れ」「関心」を切り離した • 構造化のためのサブルーチン • 「入口」は一箇所だけで「出口」は呼び出し元に固定 • 「呼び出された側」が「外の世界の、具体的な位置を気にする」という 
 相互的な依存状態も解消される。疎結合化を促進する。 

Slide 163

Slide 163 text

1. 感じるゾーン 2. 知るゾーン 3. 考えるゾーン 
 〜構造化がもたらすものを考える〜 4. おしまい

Slide 164

Slide 164 text

構造化は何をもたらすものか 構造化プログラミングの根っこには 「人間の能力不足」がある 

Slide 165

Slide 165 text

人間の能力不足 数え上げの推論は,それがうまくいく範囲なら問題はないのです が,私達は,あまり回転が速くないので,数え上げの推論が,大変強 力であるというわけにいきません. 
 (中略) 
 抽象は,数え上げの推論に課せられている要求を緩和する,人間の 主たる知性の技法として評価するべきです.  “ [出典] 
 ─ E.W.ダイクストラ, C.A.R.ホーア, O.-J.ダール 共著ほか 
 『構造化プログラミング(サイエンスライブラリ. 情報電算機 ; 32) 』 サイエンス社, P13

Slide 166

Slide 166 text

人類がやりたいこと 問題が複雑化しても簡単に解けるようにするには?  「考えなくて良いことを増やす」 
 「集中をもたらす」 

Slide 167

Slide 167 text

抽象 • 組み合わせ: 
 具体を積み上げて 
 抽象を支える • 分解: 
 各モジュールの外からは 
 その中身は干渉できない • 単純化: 
 問題と解法の「正しさ」は 
 レイヤーごとに保証する 抽象 具体 アルゴリズムや 
 I/O制御 ユースケースや 
 モデル

Slide 168

Slide 168 text

構造化がくれたもの 問題を極所化し、ブラックボックス化して扱う  問題の大きさが知的能力の限界に収りやすくする 

Slide 169

Slide 169 text

何が構造化をもたらしたのか 問題の複雑化・巨大化(機械への依存の拡大)  「人間の知性」を拡張する装置への需要  構造化による解決へ 

Slide 170

Slide 170 text

何が構造化をもたらしたのか 問題の複雑化・巨大化(機械への依存の拡大)  「人間の知性」を拡張する装置への需要  構造化による解決へ  例えば 
 「ソフトウェア危機」や 
 「リーマンの法則」

Slide 171

Slide 171 text

何が構造化をもたらしたのか 構造化による解決へ  扱える問題の範囲が拡大  CPU・メモリの高機能化 

Slide 172

Slide 172 text

何が構造化をもたらしたのか 構造化による解決へ  扱える問題の範囲が拡大  CPU・メモリの高機能化  例えば「コールスタック」も 
 ハードウェアレベルでの 
 サポートで進化した一例

Slide 173

Slide 173 text

私的な見方で色々を言うコーナー

Slide 174

Slide 174 text

① プログラミングの今までとこれから 

Slide 175

Slide 175 text

プログラミング3大リソース CPU・メモリ・人間の脳みそ 

Slide 176

Slide 176 text

プログラミング3大リソース CPU・メモリ・人間の脳みそ  こいつが優れていれば 
 機械語を書けばいいだけの話

Slide 177

Slide 177 text

プログラミング3大リソース CPU・メモリ・人間の脳みそ  ハードウェアの進化 
 → 抽象の応用、数え上げの応用

Slide 178

Slide 178 text

ブラックボックスが来る 高級言語は何を与えたか? 「小クラス主義」「宣言的スタイル」は何を与えたか? AIはどうか? 

Slide 179

Slide 179 text

ブラックボックスが来る 「人間が考えなくて良い範囲」は変化していく 

Slide 180

Slide 180 text

ブラックボックスが来る だって! 
 10年ちょっと前は 
 みんな生JS書いてたんですよ  ~ var that = this; ~

Slide 181

Slide 181 text

② Re: gotoプログラミングについて 

Slide 182

Slide 182 text

gotoプログラミングで何が悪い • クヌース『実際高水準言語でgoto文を廃止すべきだと言いたい。この ことで、プログラミングスタイルにはよい影響を生じる』  [出典] 
 ─ ドナルド・E.クヌース 著ほか 
 『文芸的プログラミング』アスキー , P110 “

Slide 183

Slide 183 text

gotoプログラミングで何が悪い ただし「goto文と聞いただけで全否定」は危うさも感じる • 「理解容易性を妨げるもの」は変わり続けている(機械の進化) • IDEの進化でinheritDocが無用になったように • クラス数やファイル数が増えても十分に追えるようになったように • それなのに「1960年と同じ議論」で留まるのは正しい? • なぜGo言語にgotoがあるのか? • なぜPHPは2010年(ほぼ)になってからgotoを導入したのか? 

Slide 184

Slide 184 text

gotoプログラミングで何が悪い 「誰かが言ってたのをそのまま鵜呑み」は危うい • どうしたら「gotoが便利」になるかを考えてみるのも一興(かも) • 「何にでもデザインパターン」「とりあえずスクラム」と似た雰囲気を感じる • 「例外はgotoの再来か」議論 • 「`break 3;` は直感的に理解しやすいのか」 • 一定の制限のもとに、IDEの機能で「読みやすく」支援できる道は 

Slide 185

Slide 185 text

gotoプログラミングで何が悪い 今日の話で「何が悪いか」は説明できたはず 

Slide 186

Slide 186 text

gotoプログラミングで何が悪い 今日の話で「何が悪いか」は説明できたはず 理解することでもたらされるもの 「本当に今日でも60年前と同じ考えでいいか?」 
 を自分の頭で考える 

Slide 187

Slide 187 text

お便りのコーナー: 私から皆さんへ・・・ 変わっていくものと変わらないものがある • 「問題が複雑になり、解決の陳腐化が早くなる」はこれからも加速し そう • だから「易しくしたい」も強くなっていく • ただし「生身の人間がどこまで介入するか」は変わっていく 

Slide 188

Slide 188 text

お便りのコーナー: 私から皆さんへ・・・ 「GOTOが悪: 読みにくいし混乱の元だから」は変わらないのか? • 60年前に「GOTOは不要、3つのルールでプログラミングしよう」と言わ れたのは、「最低限そのルールだけあれば十分だから」と数学的な証 明があったから • 今の「当たり前」も、当時は「斬新で強力な理論」に力を得た • その後、それと同じくらいのパワーと持つ発見はないのか? • 「GOTOが読みにくい」は本当に変わらない? 

Slide 189

Slide 189 text

お便りのコーナー: 私から皆さんへ・・・ 読み相手が「人間でなくAI」になったらどうか? • 「どこでキャッチされるかわからないthrow Exception」と「明示的に 行き先が示されているgoto」という見方もできるのでは • (静的なラベリングが前提ではあるが)より明示的に依存が示される効果 

Slide 190

Slide 190 text

お便りのコーナー: 私から皆さんへ・・・ 「抽象」よりも「数え上げ」の表現が優先される世界が来る可能性は • 「AI駆動」の開発においては、「情報量を減らす(抽象化・分離と集 中)」旨味が減って、「愚直だが全部明示的」の方が精度が上がる・・ なんて妄想も楽しいですね • 「上から全部読まないと解釈が難しい」「構造化されていれば、目次 を使って必要なところだけ詳細に読める」が真でも、「素直に上から 読んでいけば全部追える」が優勢になったら大局は変わる事もあり得 るのか? 

Slide 191

Slide 191 text

1. 感じるゾーン 2. 知るゾーン 3. 考えるゾーン 4. おしまい

Slide 192

Slide 192 text

本当に熱いスパゲティコードを求めて 本当に熱いスパゲティコードは、 
 決して文法の力で生まれるのではありません。 

Slide 193

Slide 193 text

本当に熱いスパゲティコードを求めて あなたの心から 
 溢れ出すのでしょう。 

Slide 194

Slide 194 text

話はおしまいです ぜひ、明日から自信を持って 
 プログラミングやっていきましょう! 

Slide 195

Slide 195 text

話はおしまいです ぜひ、明日から自信を持って 
 プログラミングやっていきましょう!  goto "非"構造化

Slide 196

Slide 196 text

おしまい! お付き合いいただき ありがとうございました!!

Slide 197

Slide 197 text

参考書籍 • E.W.ダイクストラ, C.A.R.ホーア, O.-J.ダール 共著ほか. 構造化プ ログラミング, サイエンス社, 1975, (サイエンスライブラリ. 情報電 算機 ; 32) • ドナルド・E.クヌース 著ほか. 文芸的プログラミング, アスキー, 1994.3. • 葉田善章 著. コンピュータの動作と管理. 改訂版, 放送大学教育振興 会, 2017.3, (放送大学教材). 

Slide 198

Slide 198 text

Appendix

Slide 199

Slide 199 text

おすすめ書籍・文献 

Slide 200

Slide 200 text

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/ • 追加の経緯や考察が述べられている記事。よくまとまっていて面白い 

Slide 201

Slide 201 text

WEB+DB PRESS Vol.33 『構造化プログラミング入門』 https://gihyo.jp/magazine/wdpress/archive/2006/vol33 • かなり簡潔で、要点を突いた読み物になっているので 
 「構造化プログラミングって何?」という人におすすめしやすい • 「前史」「近代構造化技法」「モデル化」といった話の流れがあり、 背景〜実用までを扱っている • PDF版はGihyo Digital Publishingで販売しているので、問題なく手に 入れることが可能 

Slide 202

Slide 202 text

プログラミング文体練習 https://www.oreilly.co.jp//books/9784814400225/ 副題: Pythonで学ぶ40のプログラミングスタイル • Pythonの本にはなるものの、「色々なスタイルで書いてみて、その背 景にあった歴史や思想についても触れてみよう」という1冊。後半から 複雑になるものの、大変面白いのでオススメ • 写経してやる方が面白いのと、必ずしも全部やらなくても十分に楽しい • 今回のトークテーマに、インスピレーションを与えた存在の1つ 

Slide 203

Slide 203 text

ちょうぜつソフトウェア設計入門 https://gihyo.jp/book/2022/978-4-297-13234-7 副題: PHPで理解するオブジェクト指向の活用 • 全体としても非常に好きな1冊。今回のテーマに特に関連するのが3章5 節の「構造化プログラミングと何が違うのか」で、すっきりとまとめ つつ「オブジェクト指向へのつながり」にも踏み込んだ内容になって いる 

Slide 204

Slide 204 text

身近なgoto 

Slide 205

Slide 205 text

Composerでの例 • ユーザーのキー入力を待つ 対話的な部分 • 「その操作は実行できない よ」というケースでデフォ ルトの処理(=help表示)に 飛ばす・・という処理 • switchの中で、別のブランチ からdefaultに飛ばしている  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

Slide 206

Slide 206 text

PHPでフレームグラフを可視化 

Slide 207

Slide 207 text

可視化に使ったツール • 今回利用したのは、Xdebug + Brendan Greggさんの「FlameGraph」 • Xdebugの中の人のYoutubeでも紹介されている • https://www.youtube.com/watch?v=4EocpeKxI0k 

Slide 208

Slide 208 text

可視化に使ったツール • 事前に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 '
' . file_get_contents('/tmp/trace.svg') . '
'; });

Slide 209

Slide 209 text

縛りの補足 

Slide 210

Slide 210 text

なぜファイルの分割を封印したのか? • PHPは基本的にファイル単位でのスコープを持たない(変数などコンテキストが 共有される)ので、分割したところで大きな問題はないか?とも考えつつ・・・ • が、その方が「より、らしくなる」と考えた理由がいくつか 1. PHPのgotoの制約 • 同じファイル上の同じコンテキストのラベルにしかジャンプできない 2. (構造化とは関係ないが)昔の「スパゲティ」の起因の考慮 • 「ファイルの分割と動的なロード」をサポートしていた言語があったか?が不透明 (調査しきれていない) • 一方、静的な取り込みについては、COBOLのCOPY命令などが存在しており、、どの程 度「見通し」「秩序」に貢献していたのか?は気になる • => ということで、主に「1」の理由を重視し、封印しようと決定 

Slide 211

Slide 211 text

グローバルスコープオンリー • 結果的に成立しそうなので明文化しなかったものの、「グローバルスコープしか 使ってはいけない」は重要視している観点の1つ • たとえば「サブルーチンはあるが、コンテキストが共有される」ものは、構造化 の恩恵(階層的なプログラミング)を受けられなくなる • 「引数やローカル変数を持たないサブルーチン」を持つ言語も存在し、これは「構造化」よ りも「再利用のための改良GOTO」な感覚が近そう • 「構造化プログラミング」の縛りでも「関数はreturnで値を返さない 
 (グローバル変数を操作する)」としたが、これは「関数の中でローカル変数を利用するのを 禁止する」ものではない • 一方で、今回の発表は主に「制御の流れ」に焦点を絞ってデータの持ち回しにつ いては踏み込んでいないので、割愛したもの