今回は、Webサイトのサイドバー(画面の右側にあるメニュー)を作ったときに出会った、PHPの書き方について紹介します。
最初に見たときは「なにこれ?」と思った書き方でしたが、ひとつずつ分解してみると、とても便利な仕組みでした。
PHPを触り始めたばかりの方にもわかるように、順番に説明していきます
例として、架空のカフェのサイトを使います。
サイドバーに「お店について」と「メニュー」の2つのグループがあり、それぞれにページへのリンクが並んでいる、という場面を想像してください。
(👇例:このサイトで言うと、右のサイト内検索やprofileのところを作成します。)

(👇例:そして右側には、左側に画像、右側に文字というリンクを作ります)

1. まずは完成形のコードを見てみよう
最初に、全体のコードを載せます。
いきなり全部わからなくても大丈夫です。
このあと、上から順番に説明します。
<?php
$current = basename($_SERVER['SCRIPT_NAME']);
$menus = [
'お店について' => ['icon-store', [
'about.php' => ['お店の紹介', 'about.jpg'],
'access.php' => ['アクセス', 'access.jpg'],
]],
'メニュー' => ['icon-cup', [
'coffee.php' => ['コーヒー', 'coffee.jpg'],
'cake.php' => ['ケーキ', 'cake.jpg'],
]],
];
?>
<?php foreach ($menus as $heading => [$headingIcon, $links]): ?>
<nav>
<p><?php echo $heading; ?></p>
<ul>
<?php foreach ($links as $file => [$label, $image]): ?>
<li>
<a<?php if ($file === $current): ?> class="is-current"<?php endif; ?> href="/<?php echo $file; ?>">
<img src="/images/<?php echo $image; ?>" alt="">
<span><?php echo $label; ?></span>
</a>
</li>
<?php endforeach; ?>
</ul>
</nav>
<?php endforeach; ?>
このコードでやっていることは、大きく分けると次の3つです。
- 1つ目は、メニューの中身(見出し、ページ、表示名、画像)を、ひとつの「配列」にまとめて書いていること。
- 2つ目は、その配列を「foreach」でくり返し取り出して、HTMLを自動で作っていること。
- 3つ目は、今見ているページのリンクにだけ、目印のクラスを付けていること。
それぞれ見ていきましょう。
2. 配列ってなに?(メニューの中身をまとめて書く)
配列は、いくつものデータを1つの箱にまとめて入れておける仕組みです。PHPでは [ ] (角かっこ)で書きます。
いちばんシンプルな配列はこんな形です。
$fruits = ['りんご', 'みかん', 'ぶどう'];
これだと、中身に「名前」が付いていません。
そこで、「名前 => 中身」の形で書くと、名前付きの配列になります。これを「連想配列」と呼びます。
$prices = [
'コーヒー' => 400,
'ケーキ' => 500,
];
この場合、「コーヒー」という名前(キー)で、400という値を取り出せます。
今回のコードでは、この連想配列を何段も重ねて(入れ子にして)使っています。ばらして見るとこうなります。
- いちばん外側の名前は、グループの見出しです(「お店について」「メニュー」)。
- その中身は [アイコン名, ページの一覧] という2つセットになっています。
- ページの一覧は、「ファイル名 => [表示名, 画像名]」の連想配列です。
文章にすると少しややこしいですが、
「見出しごとに、アイコンとページの一覧がある。ページごとに、表示名と画像がある」
と考えるとわかりやすいです。
この書き方のいいところは、メニューを1つ増やしたいときに、配列に1行足すだけで済むことです。
たとえば「ランチ」のページを増やすなら、
‘lunch.php’ => [‘ランチ’, ‘lunch.jpg’],
を「メニュー」グループの中に足すだけで、リンクも画像も自動で表示されます。
HTMLを何行もコピーして書き換える必要がありません。
3. foreach でくり返す(しかもHTMLと混ぜやすい書き方)
配列に入れたデータを1つずつ取り出すのが foreach です。基本の形はこうです。
foreach ($prices as $name => $price) {
echo $name . 'は' . $price . '円です';
}
$prices の中身を上から順番に取り出して、名前を $name に、値を $price に入れながら、{ } の中の処理をくり返します。
ここで、今回のコードをもう一度見てみると、{ } ではなく、こんな書き方になっています。
<?php foreach ($menus as $heading => [$headingIcon, $links]): ?>
(ここにHTML)
<?php endforeach; ?>
これは「foreach の別の書き方(オルタナティブ構文)」と呼ばれるものです。
{ の代わりに「:」(コロン)を書き、} の代わりに「endforeach;」で終わらせます。
なぜわざわざこう書くのかというと、HTMLの中に書くときに読みやすいからです。
{ } で書くと、HTMLの途中に } だけがぽつんと残って、「これは何の終わり?」とわかりにくくなります
endforeach と書いてあれば、「ここで foreach が終わるんだな」とひと目でわかります。
同じように、if にも別の書き方があります。
<?php if (条件): ?>
条件に合うときだけ表示するHTML
<?php endif; ?>
今回のコードでは、この if を使って「今見ているページのときだけ class を付ける」ということをしています(5章で説明します)。
4. [$label, $image] ってなに?(取り出しと同時に分ける)
foreach の行に、こんな部分がありました。
foreach ($links as $file => [$label, $image])
ふつうなら「$file => $value」のように、値を1つの変数で受け取ります。
ところが今回の値は [‘お店の紹介’, ‘about.jpg’] のように、2つセットになっています。
そこで、受け取る側も [$label, $image] と書いておくと、セットの1つ目が $label に、2つ目が $image に、自動で分けて入ります。
これを「分割代入」と呼びます(PHP 7.1以降で使える書き方です)。
分割代入を使わないと、こう書くことになります。
foreach ($links as $file => $value) {
$label = $value[0];
$image = $value[1];
}
結果は同じですが、分割代入を使うと1行で済み、何が入っているのかも名前でわかるので読みやすくなります。
5. 今見ているページを見分ける
サイドバーのメニューでは、「今このページを見ています」とわかるように、今のページのリンクだけ色を変えたいことがよくあります。それをしているのが、いちばん上のこの1行です。
$current = basename($_SERVER['SCRIPT_NAME']);
$_SERVER['SCRIPT_NAME'] には、今動いているPHPファイルの場所が入っています。
たとえば「/shop/about.php」のような形です。
basename() は、その中から最後のファイル名の部分だけを取り出す関数です。
「/shop/about.php」なら「about.php」だけが取り出されます。
あとは、メニューのファイル名と比べるだけです。
<a<?php if ($file === $current): ?> class="is-current"<?php endif; ?> href="...">
メニューのファイル名($file)と今のページ($current)が同じときだけ、class=”is-current” が付きます。
CSSで .is-current に色や枠を付けておけば、今いるページだけ目立たせられます。
ちなみに「===」は、「中身も種類もまったく同じか」を比べる書き方です。
「==」でも動くことが多いですが、思わぬ一致を防ぐために「===」を使うのがおすすめです。
さらに、読み上げソフトを使う方にも「今のページ」を伝えたい場合は、class と一緒に aria-current=”page” という属性を付けておくと親切です。
6. いろいろなページで使い回す(include)
このサイドバーは、1つのページだけでなく、サイトのいろいろなページに表示したいものです。そこで、このコードを side.php のような別のファイルに保存しておき、表示したいページで読み込みます。
<?php include 'side.php'; ?>
include は、「指定したファイルの中身を、この場所に入れる」という命令です。こうしておけば、メニューを直したいときは side.php を1か所直すだけで、全部のページに反映されます。
しかも、5章の「今のページを見分ける」仕組みがあるので、同じ side.php を読み込んでいても、ページごとに正しい項目が自動で目立つようになります。
7. 注意したいこと(echo とセキュリティ)
今回のコードでは、<?php echo $label; ?> のように、配列の中身をそのまま画面に出しています。
これは、配列の中身を自分で書いた決まった文字だから大丈夫です。
ただし、フォームに入力された文字やURLの一部など、外から来た文字を表示するときは、そのまま echo してはいけません。
悪意のあるHTMLやスクリプトが混ざっていると、それがそのまま動いてしまうからです。
外から来た文字を表示するときは、
<?php echo htmlspecialchars($text, ENT_QUOTES, 'UTF-8'); ?>
のように htmlspecialchars() を通して、HTMLとして動かない形に変えてから表示しましょう。
まとめ
今回の書き方のポイントをまとめると、次のとおりです。
最初は記号が多くて難しく見えますが、ひとつずつ分けて見ると、どれも「同じことを何度も書かないための工夫」だとわかります。同じようなHTMLを何回もコピーしているな、と感じたら、ぜひ配列と foreach を試してみてください。
最後まで読んでいただき、ありがとうございました。


