PHP Logo 共通部分を PHP で管理(外部ファイル化・テンプレート化)

サイト内で共通している部分(ヘッダーやフッターなど)を CMS のように別々に管理することで、変更があった場合などその部分のみ変更することで全ページに反映させることができるので便利です。

以下はサイトの共通部分を PHP で管理する方法(外部ファイル化やナビゲーションメニュー、異なる階層のページに対応する方法、現在のページのメニューにクラスを追加する方法、簡易的なテンプレートの作成方法など)の覚書です。

更新日:2022年03月11日

作成日:2021年6月1日

関連ページ:

全ページで共通する部分を外部ファイル化

ヘッダーやナビゲーション、フッターなど全ページで共通する部分を外部ファイル化して各ページで読み込むようにすることで、共通ファイルを変更すれば、全てのページに変更を反映させることができます。

外部ファイル化はパーツ化やコンポーネント化などとも言いますが、共通する部分を別のファイルにして PHP の include や require を使って読み込むようにします。

最初の例は同じ階層にある4つのページ(about.php、contact.php、index.php、news.php)で構成されています。

.
├── about.php
├── contact.php
├── css
│   └── style.css
├── favicon.ico
├── images
│   ├── logo.svg
│   └── sample_01.jpg
├── index.php  //トップページ
├── js
│   └── main.js
└── news.php

以下はヘッダーやフッターなどの全てのページで共通する部分を外部ファイル化する一例です。どの部分を外部ファイル化するかは、ファイル構造やそのサイトの内容により異なります。

また、この例では共通部分として切り出した外部ファイルは inc というフォルダを作成して保存します。

index.php(外部ファイル化する前のファイル)
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>My Site Top</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="説明(トップページ)">
<link rel="stylesheet" href="css/style.css">
<link rel="shortcut icon" href="favicon.ico">
</head>
<body>
<div class="wrapper">
  <header>
    <div class="container"> 
    <a>
      <img class="logo" src="images/logo.svg" width="141" height="36" alt="logo">
      <h1>My Site</h1>
      </a> 
    </div>
    <nav class="global-nav">
      <div class="container">
        <ul>
          <li><a href="index.php">Home</a></li>
          <li><a href="news.php">News</a></li>
          <li><a href="about.php">About</a></li>
          <li><a href="contact.php">Contact</a></li>
        </ul>
      </div>
    </nav>
  </header>
  <div class="container">
    <main>
      <section>
        <h2>Top page</h2>
        <p><img src="images/sample_01.jpg" width="600" height="400" alt="">Lorem ipsum dolor sit amet,...</p>
      </section>
      <section>
        <h3>Heading</h3>
        <p>Repellat nihil unde commodi soluta dignissimos dicta...</p>
      </section>
    </main>
    <aside>
      <section>
        <h4>Side Bar</h4>
        <p>Recusandae nostrum cupiditate.</p>
      </section>
    </aside>
  </div>
</div>
<footer>
  <div class="container">
    <p><small>&copy;Copyright</small></p>
  </div>
</footer>
<script src="js/main.js"></script>
</body>
</html>    
外部ファイル 説明
head.php 1〜9行目。head 要素の閉じタグ </head> はそれぞれのページで追加の要素を記述できるように読み込み元に残しています
header.php 13〜30行目。h1 要素やロゴ画像、グローバルナビゲーション
side.php 42〜47行目。サイドバー部分
footer.php 50〜55行目。フッターと全ページで共通の JavaScript

この例では head.php と header.php に分割していますが、例えば、1〜31行目までをまとめて header.php として外部ファイル化して、その中で特定のページの処理を分岐するなども考えられます。

head.php

head.php では、title 要素と meta 要素(name="description")はそれぞれのページで値が異なるので、PHP の変数を使って出力するようにしています。

必要に応じて meta 要素などを更に別途外部ファイル化することもできます。

head.php
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title><?php echo $title; ?></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="<?php echo $description; ?>">
<link rel="stylesheet" href="css/style.css">
<link rel="shortcut icon" href="favicon.ico">

header.php

header.php ではサイトのロゴとタイトルへのリンクをホーム(index.php)の場合は a 要素の href 属性を指定せず、その他のページではホームへのリンクとするため、$is_home というトップページの判定用の変数が設定(定義)されているかどうかで出力を変えています。

header.php
<header>
  <div class="container"> 
    <a <?php echo isset($is_home) ? '': 'href="index.php"' ?>> 
      <img class="logo" src="images/logo.svg" width="141" height="36" alt="logo">
      <h1>My Site</h1>
    </a> 
  </div>
  <nav class="global-nav">
    <div class="container">
      <ul>
        <li><a href="index.php">Home</a></li>
        <li><a href="news.php">News</a></li>
        <li><a href="about.php">About</a></li>
        <li><a href="contact.php">Contact</a></li>
      </ul>
    </div>
  </nav>
</header>

side.php

side.php
<aside>
  <section>
    <h4>Side Bar</h4>
    <p>Recusandae nostrum cupiditate.</p>
  </section>
</aside>

footer.php

この例では、共通で使用する JavaScript を含めています。追加の JavaScript があればそれぞれのページで読み込みます。

footer.php
<footer>
  <div class="container">
    <p><small>&copy;Copyright</small></p>
  </div>
</footer>
<script src="js/main.js"></script>

以下はこの時点でのフォルダ構成です。

.
├── about.php
├── contact.php
├── css
│   └── style.css
├── favicon.ico
├── images
│   ├── logo.svg
│   ├── sample_01.jpg
│   └── sample_02.jpg
├── inc  //外部ファイルのディレクトリ
│   ├── footer.php
│   ├── head.php
│   ├── header.php
│   └── side.php
├── index.php //トップページ
├── js
│   └── main.js
└── news.php

インクルード

PHP の include や require を使って footer.php や head.php などの外部ファイルを読み込みます。

include と require の違いは、指定したファイルが読み込めなかった場合に処理を継続するかどうかで、ファイルが読み込めなくても処理を継続するには include を使い、エラーで処理を停止する場合は、requireを使います。(関連:外部ファイルの利用

この例では include を使って、何らかの理由で外部ファイルが読み込めない場合でもそれ以降の部分は表示されるようにします。

指定したファイルが読み込めない場合は、include でもエラーは表示され、ファイルのパスなどが表示されてしまうので必要に応じて本番環境ではエラーメッセージを非表示にすると良いかも知れません。(関連:エラーメッセージの制御

以下はトップページの index.php で inc フォルダに保存した外部ファイルを読み込む例です。

include や require に指定するファイルのパスは絶対パスまたはカレントディレクトリからの相対パスで指定できます。この例では相対パスで指定しています。

include や require は厳密には関数ではなく特別な言語構造であるため、引数を囲む括弧 () は不要です。

index.php
<?php
$title = 'My Site Top';
$description = '説明(トップページ)';
$is_home = true; //トップページの判定用の変数
include 'inc/head.php'; // head.php の読み込み
?>
<!-- 特定のページでのみ読み込むスタイルシートなどがあればここに追加 -->
</head>
<body>
<div class="wrapper">
  <?php include 'inc/header.php'; ?> <!-- header.php の読み込み -->
  <div class="container">
    <main>
      <section>
        <h2>Top page</h2>
        <p> <img src="images/sample_01.jpg" width="600" height="400" alt=""> Lorem ipsum dolor sit amet,... </p>
      </section>
      <section>
        <h3>Heading</h3>
        <p>Repellat nihil unde commodi soluta dignissimos dicta...</p>
      </section>
    </main>
    <?php include 'inc/side.php'; ?> <!-- side.php の読み込み -->
  </div>
</div>
<?php include 'inc/footer.php'; ?> <!-- footer.php の読み込み -->
</body>
</html>

上記の2〜3行目は、読み込む head.php で使用する変数を設定しています。

head.php
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<!-- 読み込み元ファイル(index.php)の2行目の $title -->
<title><?php echo $title; ?></title> 
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 読み込み元ファイル(index.php)の3行目の $description -->
<meta name="description" content="<?php echo $description; ?>">
<link rel="stylesheet" href="css/style.css">
<link rel="shortcut icon" href="favicon.ico">

index.php の4行目では、このページはトップページなので読み込む header.php で使用する変数 $is_home に true を設定しています。

header.php
<header>
  <div class="container"> 
    <!-- $is_home が定義されていれば何も出力せず、定義されていなければ href 属性を出力 -->
    <a <?php echo isset($is_home) ? '': 'href="index.php"' ?>> 
      <img class="logo" src="images/logo.svg" width="141" height="36" alt="logo">
      <h1>My Site</h1>
    </a> 
  </div>
  ・・・中略・・
</header>

同様に、about.php や news.php、contact.php でも外部ファイルをインクルードするように変更します。

以下は news.php の例です。

about.php や news.php、contact.php の場合は、$is_home という変数を定義していないので、header.php で読み込んだロゴとタイトルへのリンクの a 要素には href 属性が設定されます。

news.php
<?php
$title = 'News | My Site';
$description = '説明(News ページ)';
include 'inc/head.php'; // head.php の読み込み
?>  
</head>
<body>
<div class="wrapper">
  <?php include 'inc/header.php'; ?> <!-- header.php の読み込み -->
  <div class="container">
    <main>
      <section>
        <h2>News</h2>
        <p><img src="images/sample_02.jpg" width="600" height="400" alt="">Lorem ipsum dolor sit amet,...</p>
      </section>
      <section>
        <h3>Heading</h3>
        <p>Repellat nihil unde commodi soluta dignissimos dicta...</p>
      </section>
    </main>
    <?php include 'inc/side.php'; ?> <!-- side.php の読み込み -->
  </div>
</div>
<?php include 'inc/footer.php'; ?> <!-- footer.php の読み込み -->
</body>
</html>

階層が異なる場合

前述の例の場合、全てのページのファイルが同じ階層(ルートの直下)にありましたが、異なるディレクトリにページがある(ルートディレクトリ以外にある)場合は、インクルードするためのパスやリンクの値(ナビゲーションのリンクや CSS や JavaScript、画像などのパス)を変更する必要があります。

前述の例と同じ4ページの構成ですが、フォルダ構成とファイル名を以下のように変更します。

前述の About ページ(about.php)や Contact ページ(contact.php)、News ページ(news.php)はそれぞれフォルダを作成して配置し、ファイル名を index.php に変更してあります。

.
├── about
│   └── index.php
├── contact
│   └── index.php
├── css
│   └── style.css
├── favicon.ico
├── images
│   ├── logo.svg
│   ├── sample_01.jpg
│   └── sample_02.jpg
├── inc
│   ├── footer.php
│   ├── head.php
│   ├── header.php
│   └── side.php
├── index.php //トップページ
├── js
│   └── main.js
└── news
     └── index.php

インクルードするためのパスやリンクを変更します。

トップページの index.php

トップページの index.php は位置も変わっていないので外部ファイルのインクルードはこのままでも大丈夫ですが、他のファイルに合わせて以下のように変更します(ルートディレクトリのファイルの選択肢)。

この例ではインクルードなどは相対パスを使用しています。フォルダ構成を変更したことでトップページ以外のページでのインクルードのパスが変わるため、 $path という変数を作成して調整します。

トップページの場合は、元のままでもインクルードの部分は大丈夫ですが、リンクの調整などもあるため、$path にカレントディレクトリを表す ./ を設定します。

トップページ index.php
<?php
$title = 'My Site Top';
$description = '説明(トップページ)';
$is_home = true;  //ホーム(トップページ)の判定用の変数を定義(header.php で使用)
$path = './';  //追加(カレントディレクトリを表す ./ )
include $path .'inc/head.php'; //変更($path. を追加)
?>
</head>
<body>
<div class="wrapper">
  <?php include $path .'inc/header.php'; ?><!-- 変更($path. を追加) -->
  <div class="container">
    <main>
      <section>
        <h2>Top page</h2>
        <!-- 変更(src 属性の値に $path. を追加) -->
        <p> <img src="<?php echo $path; ?>images/sample_01.jpg" width="600" height="400" alt=""> Lorem ipsum dolor sit amet,... </p>
      </section>
      <section>
        <h3>Heading</h3>
        <p>Repellat nihil unde commodi soluta dignissimos dicta...</p>
      </section>
    </main>
    <?php include $path .'inc/side.php'; ?><!-- 変更($path. を追加) -->
  </div>
</div>
<?php include $path .'inc/footer.php'; ?><!-- 変更($path. を追加) -->
</body>
</html>

トップページ以外

トップページ以外も同様に以下のように変更します。

トップページ以外の場合は、$path に1つ上のディレクトリを表す ../ を設定します。

news/index.php
<?php
$title = 'News | My Site';
$description = '説明(News ページ)';
$path = '../';  //追加(1つ上のディレクトリを表す ../ )
include $path.'inc/head.php'; //変更($path. を追加)
?>  
</head>
<body>
<div class="wrapper">
  <?php include $path.'inc/header.php'; ?>
  <div class="container">
    <main>
      <section>
        <h2>News</h2>
        <!-- 変更(src 属性の値に $path. を追加) -->
        <p><img src="<?php echo $path; ?>images/sample_02.jpg" width="600" height="400" alt="">Lorem ipsum dolor sit amet,...</p>
      </section>
      <section>
        <h3>Heading</h3>
        <p>Repellat nihil unde commodi soluta dignissimos dicta...</p>
      </section>
    </main>
    <?php include $path.'inc/side.php'; ?><!-- 変更($path. を追加) -->
  </div>
</div>
<?php include $path.'inc/footer.php'; ?><!-- 変更($path. を追加) -->
</body>
</html>

外部ファイルのリンクなども変更します。

head.php

head.php ではスタイルシートなどの読み込みのリンクに $path を出力する記述(echo $path;)を追加します。

これにより、例えば News ページの index.php では href の値は href="../css/style.css" になります。

head.php
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title><?php echo $title; ?></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="<?php echo $description; ?>">
<!-- $pathの出力を href 属性の値に追加 -->
<link rel="stylesheet" href="<?php echo $path; ?>css/style.css">
<!-- $pathの出力を href 属性の値に追加 -->
<link rel="shortcut icon" href="<?php echo $path; ?>favicon.ico">

filemtime() 更新時にキャッシュをクリア

スタイルシートや JavaScript の読み込みではクエリパラメータ(ファイル名の後に ? に続けて指定するパラメータ)で filemtime() を使ってファイルの更新日時を付与すれば、ファイルを更新するたびにクエリパラメータが変更されるので、ブラウザキャッシュが更新されるようになります。

filemtime() の引数は、ファイルへのパスを指定します。

head.php の CSS などの読み込み部分
<!-- クエリパラメータに filemtime() を使ってファイルの更新日時を付与 -->
<link rel="stylesheet" href="<?php echo $path; ?>css/style.css?<?php echo filemtime($path.'css/style.css'); ?>">
<link rel="shortcut icon" href="<?php echo $path; ?>favicon.ico?<?php echo filemtime($path.'favicon.ico'); ?>">

上記のように記述することで、例えば以下のような出力になり、ファイルを更新する度にクエリパラメータ(1622262843の部分)が変更され、キャッシュをクリアできます。

<link rel="stylesheet" href="./css/style.css?1622262843">
<link rel="shortcut icon" href="./favicon.ico?1367707887">

header.php

header.php にもパスやリンクの調整用の値 $path を追加します。

ナビゲーションのリンクではディレクトリを追加しファイル名も news.php などから index.php に変更します。

header.php
<header>
  <div class="container"> 
    <a <?php echo isset($is_home) ? '': 'href="' .$path .'index.php"' ?>> 
      <!-- <?php echo $path; ?> を src 属性の値に追加 -->
      <img class="logo" src="<?php echo $path; ?>images/logo.svg" width="141" height="36" alt="logo">
      <h1>My Site</h1>
    </a> 
  </div>
  <nav class="global-nav">
    <div class="container">
      <ul>
        <!--$pathの出力を追加し、ドップページ以外はディレクトリを追加&ファイル名を変更 -->
        <li><a href="<?php echo $path; ?>index.php">Home</a></li>
        <li><a href="<?php echo $path; ?>news/index.php">News</a></li>
        <li><a href="<?php echo $path; ?>about/index.php">About</a></li>
        <li><a href="<?php echo $path; ?>contact/index.php">Contact</a></li>
      </ul>
    </div>
  </nav>
</header>

footer.php

footer.php も JavaScript の読み込みに $path の出力を追加します。

footer.php
<footer>
  <div class="container">
    <p><small>&copy;Copyright</small></p>
  </div>
</footer>
<!-- $path の出力を src 属性の値に追加 -->
<script src="<?php echo $path; ?>js/main.js"></script>

JavaScript の読み込みではスタイルシート同様、クエリパラメータで filemtime() を使ってファイルの更新日時を付与して、ファイルを更新するたびにブラウザキャッシュが更新されるようにすることが多いです。

<script src="<?php echo $path; ?>js/main.js?<?php echo filemtime($path.'js/main.js'); ?>"></script>

この例の場合、side.php にはリンクや画像などの記述がないので変更はありません。

もう1つ深い階層の例

例えば以下のように news のディレクトリの中に events というディレクトリを作成した場合

.
├── about
│   └── index.php
 ・・・中略・・・
├── inc
│   ├── footer.php
│   ├── head.php
│   ├── header.php
│   └── side.php
├── index.php
├── js
│   └── main.js
└── news
     ├── events //追加
     │   └── index.php //追加
     └── index.php

この例の場合は $path の値を ../../ にします。階層に応じて $path の値の ../ の数を調整し適切にインクルードしたり画像などを読み込めるようにします。

news/events/index.php
<?php
$title = 'Events | My Site';
$description = '説明(Events ページ)';
$path = '../../';  //この値が階層により異なる
include $path.'inc/head.php';
?>  
</head>
<body>
<div class="wrapper">
  <?php include $path.'inc/header.php'; ?>
  <div class="container">
    <main>
      <section>
        <h2>Events</h2>
        <p><img src="<?php echo $path; ?>images/sample_03.jpg" width="600" height="400" alt="">Lorem ipsum dolor sit amet,...</p>
      </section>
      <section>
        <h3>Heading</h3>
        <p>Repellat nihil unde commodi soluta dignissimos dicta...</p>
      </section>
    </main>
    <?php include $path.'inc/side.php'; ?>
  </div>
</div>
<?php include $path.'inc/footer.php'; ?>
</body>
</html>

以下はナビゲーションに Events(news/events/index.php)を追加した例です。この例の場合はその他のメニュー項目と横並びにしていますが、必要に応じてドロップダウン(ul 要素をネスト)などにします。

header.php
<header>
  <div class="container"> 
    <a <?php echo isset($is_home) ? '': 'href="' .$path .'index.php"' ?>> 
      <img class="logo" src="<?php echo $path; ?>images/logo.svg" width="141" height="36" alt="logo">
      <h1>My Site</h1>
    </a> 
  </div>
  <nav class="global-nav">
    <div class="container">
      <ul>
        <li><a href="<?php echo $path; ?>index.php">Home</a></li>
        <li><a href="<?php echo $path; ?>news/index.php">News</a></li>
        <!-- Events を追加 -->
        <li><a href="<?php echo $path; ?>news/events/index.php">Events</a></li>
        <li><a href="<?php echo $path; ?>about/index.php">About</a></li>
        <li><a href="<?php echo $path; ?>contact/index.php">Contact</a></li>
      </ul>
    </div>
  </nav>
</header>

ルートディレクトリのファイルの選択肢

ルートディレクトリ(正確にはサイトの一番上のディレクトリ)にあるファイル、この例ではトップページの index.php では、ファイルの最初の部分に$path = './'または$path = ''を指定するだけで、他のインクルードや画像のパスなどの部分に $path を追加しなくても大丈夫ですが、上記の例では他のファイルと同じ運用にするためにインクルードや画像のパスなどにも $path を出力するようにしています。

また、 ルートディレクトリ(サイトの一番上のディレクトリ)にあるファイルでは $path を設定(記述)せずに、外部ファイル側で $path が定義されていない場合の判定を記述するという方法もあります。

例えば以下のように外部ファイルの echo $path; の記述を変更すれば機能するかと思います($path が定義されていない場合は、./ または空文字を出力)。

//全ての echo $path; の記述を以下に変更

echo isset($path) ? $path: './';
//または
echo isset($path) ? $path: '';

※但し、ファイルの位置($path の値)を必要とする処理は色々とあるので、全てのファイルで共通の書き方にしておく方が管理がしやすいと思います。

絶対パスと相対パス

今までの例ではインクルードする際のパスの指定は相対パスを使用しましたが、$_SERVER 変数を使って(絶対パスで)指定することもできます。

絶対パスと言ってもこの場合、環境に合わせてパスを取得できる $_SERVER['DOCUMENT_ROOT'] などの環境変数を使います(サーバー変数や関数)。

$_SERVER(環境変数)の1つ $_SERVER['DOCUMENT_ROOT'] はサーバーのコンフィグレーションファイル(httpd.conf)で定義されていて、ドキュメントルート(htdocs や public_html)へのパスを返してくれます($_SERVER['DOCUMENT_ROOT'] の値は環境によっては異なる可能性もあります)。

例えば、inc フォルダの head.php をインクルードするには、ファイルの階層に関係なく以下のように記述することができます。

include $_SERVER['DOCUMENT_ROOT'] .'/inc/head.php';

但し、$_SERVER['DOCUMENT_ROOT'] を使ってパスを指定する場合は、サイトがサーバーのルートディレクトリにインストールされている必要があります。

本番環境(ドメインまたはサブドメイン直下)やローカル環境でバーチャルホストを設定してサイトがサーバーのルートディレクトリにインストールされていれば機能しますが、サイトのサブディレクトリやローカル環境の htdocs の中のディレクトリにインストールされている場合はうまく機能しません(もしかしたらよい方法があるかも知れませんが)。

例えばローカル環境(MAMP)で htdocs の中に作成したサイトの場合、$_SERVER['DOCUMENT_ROOT'] で取得される値は「/Applications/MAMP/htdocs」になってしまい、作成したサイトのルートディレクトリではなく、MAMP のルートディレクトリが返ります。

バーチャルホストを設定していれば問題ありません。

// http://localhost/example/ の場合
echo $_SERVER['DOCUMENT_ROOT'];  
// /Applications/MAMP/htdocs (MAMP のドキュメントルート)が出力される

// http://example.localhost/(バーチャルホスト)の場合
echo $_SERVER['DOCUMENT_ROOT'];  
// /Applications/MAMP/htdocs/example(期待通りの値)

制作環境はローカルであったり、本番サーバーのテストディレクトリ(サブディレクトリ)であったりと色々な場合があるので、個人的には相対パスを使用することがほとんどです。

以下は前述の例を絶対パス($_SERVER['DOCUMENT_ROOT'])で書き換える例です。

この例では以下のような全体で利用する変数を定義したファイル(vars.php)を inc フォルダに保存して全てのページで読み込むようにします。

$doc_root は $_SERVER['DOCUMENT_ROOT'] を繰り返し記述するのを避けるため変数に値を代入しています。

$host_url はそのサイトのホームの URL で、ナビゲーションのリンクや画像などへの URL を絶対 URL で記述するのに使用します。$_SERVER['HTTP_HOST'] はアクセスされているページのホスト名を返します。

6〜8行目は SSL化されていても、サーバーが $_SERVER['HTTPS'] の値を返さない場合の対応で、サーバーが $_SERVER['HTTPS'] の値を返す場合は不要です(記述してあっても問題はないと思います)。

vars.php
<?php
//ドキュメントルートのパス
$doc_root = $_SERVER['DOCUMENT_ROOT'] ;

//サーバーが $_SERVER['HTTPS'] の値を返さない場合の対策
if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) and $_SERVER['HTTP_X_FORWARDED_PROTO'] === "https") {
  $_SERVER['HTTPS'] = 'on';
} 

//Home の URL の組み立て(例 http://exmaple.com または https://exmaple.com)
$host_url = (empty($_SERVER['HTTPS']) ? 'http://' : 'https://'). $_SERVER['HTTP_HOST'];

?>

以下はこの例のフォルダ構成です。

.
├── about
│   └── index.php
├── contact
│   └── index.php
├── css
│   └── style.css
├── favicon.ico
├── images
│   ├── logo.svg
│   ├── sample_01.jpg
│   ├── sample_02.jpg
│   └── sample_03.jpg
├── inc
│   ├── footer.php
│   ├── head.php
│   ├── header.php
│   ├── side.php
│   └── vars.php  //上記ファイル
├── index.php
├── js
│   └── main.js
└── news
     ├── events
     │   └── index.php
     └── index.php

以下はトップページの index.php の例です。

相対パスを使用する場合の $path の部分を $_SERVER['DOCUMENT_ROOT'] や $doc_root に置き換えます。

vars.php で定義した変数を使用するので vars.php を先にインクルードします(5行目)。

$_SERVER['DOCUMENT_ROOT'] や $doc_root の最後にはスラッシュ「/」がないのでスラッシュを追加します。

また、$host_url の最後にもスラッシュが付いていないので、スラッシュを追加します($doc_root や $host_url の変数の定義の際に、スラッシュを付けておくという方法もありますが)。

トップページの index.php
<?php
$title = 'My Site Top';
$description = '説明(トップページ)';
$is_home = true;
include $_SERVER['DOCUMENT_ROOT'] . '/inc/vars.php';  //こちらを先に読み込む
include $doc_root .'/inc/head.php';
?>
</head>
<body>
<div class="wrapper">
  <?php include $doc_root .'/inc/header.php'; ?>
  <div class="container">
    <main>
      <section>
        <h2>Top page</h2>
        <p> <img src="<?php echo $host_url; ?>/images/sample_01.jpg" width="600" height="400" alt=""> Lorem ipsum dolor sit amet,... </p>
      </section>
      <section>
        <h3>Heading</h3>
        <p>Repellat nihil unde commodi soluta dignissimos dicta...</p>
      </section>
    </main>
    <?php include $doc_root .'/inc/side.php'; ?>
  </div>
</div>
<?php include $doc_root .'/inc/footer.php'; ?>
</body>
</html>

その他のページも同様に変更します。

以下は Events ページの例です。$_SERVER['DOCUMENT_ROOT'] (絶対パスを出力する変数)を使用ているので、階層が異なっていてもインクルードの記述は同じです。

news/events/index.php
<?php
$title = 'Events | My Site';
$description = '説明(Events ページ)';
include $_SERVER['DOCUMENT_ROOT'] .'/inc/vars.php'; 
include $doc_root .'/inc/head.php';
?>  
</head>
<body>
<div class="wrapper">
  <?php include $doc_root .'/inc/header.php'; ?>
  <div class="container">
    <main>
      <section>
        <h2>Events</h2>
        <p><img src="<?php echo $host_url; ?>/images/sample_03.jpg" width="600" height="400" alt="">Lorem ipsum dolor sit amet,...</p>
      </section>
      <section>
        <h3>Heading</h3>
        <p>Repellat nihil unde commodi soluta dignissimos dicta...</p>
      </section>
    </main>
    <?php include $doc_root .'/inc/side.php'; ?>
  </div>
</div>
<?php include $doc_root .'/inc/footer.php'; ?>
</body>
</html>

外部ファイル

$path を使用しないように変更しているので、外部ファイルの $path を使っている部分を vars.php で定義した $host_url を使った記述に変更します。※ $host_url には最後にスラッシュが付かないので追加します(または $host_url にスラッシュを含めるという方法もあります)。

head.php
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title><?php echo $title; ?></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="<?php echo $description; ?>">
<!-- $host_url に変更し / を追加  -->
<link rel="stylesheet" href="<?php echo $host_url; ?>/css/style.css">
<!-- $host_url に変更し / を追加 -->
<link rel="shortcut icon" href="<?php echo $host_url; ?>/favicon.ico">
header.php
<header>
  <div class="container"> 
   <!-- $host_url に変更し / を追加 -->
   <a <?php echo isset($is_home) ? '': 'href="' .$host_url .'/index.php"' ?>> 
      <!-- $host_url に変更し / を追加 -->
      <img class="logo" src="<?php echo $host_url; ?>/images/logo.svg" width="141" height="36" alt="logo">
      <h1>My Site</h1>
    </a> 
  </div>
  <nav class="global-nav">
    <div class="container">
      <ul>
         <!-- 以下も全て $host_url に変更し / を追加 -->
        <li><a href="<?php echo $host_url; ?>/index.php">Home</a></li>
        <li><a href="<?php echo $host_url; ?>/news/index.php">News</a></li>
        <li><a href="<?php echo $host_url; ?>/news/events/index.php">Events</a></li>
        <li><a href="<?php echo $host_url; ?>/about/index.php">About</a></li>
        <li><a href="<?php echo $host_url; ?>/contact/index.php">Contact</a></li>
      </ul>
    </div>
  </nav>
</header>
footer.php
<footer>
  <div class="container">
    <p><small>&copy;Copyright</small></p>
  </div>
</footer>
<!-- $host_url に変更 -->
<script src="<?php echo $host_url; ?>/js/main.js"></script>

テンプレート化

ob_start()や ob_get_contents() などを使って簡易的なテンプレートを作ることができます。

echo を実行すると記述してある位置(タイミング)で HTML に標準出力します。

ob_start() を使うと、標準出力を出力せずにバッファリング(バッファという領域に一時的に保存)することができます(ob は output buffer)。

バッファリングした値は任意のタイミングで取り出すことができ、ob_get_contents() を使って変数に代入して任意のタイミングで echo を使って出力したり、ob_end_flush() を使って出力することができます。

また、ob_start() でバッファリングを開始したら、ob_end_clean() を使ってバッファリングを終了してバッファをクリアする必要があります。または、バッファの内容を取得し、出力バッファをクリアする関数 ob_get_clean() を使うこともできます。

ob_start()

ob_start() は出力のバッファリングを有効にする関数です(output buffer start:出力バッファの開始)。

以下は ob_start() の基本的な使い方です。

<?php 
// バッファリング開始(以降は出力をせずバッファに保存)
ob_start(); 

echo 'foo'; //出力せずバッファに保存
echo 'bar'; //出力せずバッファに保存
        
// バッファの内容を変数に代入      
$buffer = ob_get_contents();  
        
// バッファをクリアしてバッファリングを終了       
ob_end_clean(); 
        
// 変数に代入したバッファの内容を出力
echo '$buffer は '.$buffer;
//「$buffer は foobar」と出力される      
 ?>

ob_get_contents() はバッファの内容を取得する関数です。バッファの内容はクリアしません。

<?php 
//バッファリング開始(出力をせずバッファに保存)
ob_start(); 

echo 'foo'; //出力せずバッファに保存
echo 'bar'; //出力せずバッファに保存
        
// バッファの内容を変数に代入(バッファの内容はクリアしない)     
$buffer = ob_get_contents();  
        
echo strtoupper($buffer); //出力せずバッファに保存
    
// バッファの内容を変数に代入(バッファの内容はクリアしない)   
$buffer2 = ob_get_contents();  
        
// バッファの内容をクリアしてバッファリングを終了       
ob_end_clean(); 
        
// 変数に代入したバッファの内容を出力
echo '$buffer は '.$buffer .'<br>';
//「$buffer は foobar」と出力される  
        
echo '$buffer2 は '.$buffer2 .'<br>';
//「$buffer2 は foobarFOOBAR」と出力される  
 ?>

ob_get_clean() という関数を使うと、現在のバッファの内容を取得して出力バッファをクリアすることができ、 ob_get_contents() と ob_end_clean() を同時に実行するのと同じことになります。

以下の場合、9行目の ob_get_clean() でバッファをクリアしてバッファリングが終了終了しているので、バッファリングを再開するには 再度 ob_start() を実行します(12行目)。

$buffer2 に取得される内容は、12行目の2回目の ob_start() 以降の内容になります。

<?php 
//バッファリング開始(出力をせずバッファに保存)
ob_start(); 

echo 'foo'; //出力せずバッファに保存
echo 'bar'; //出力せずバッファに保存
        
// バッファの内容を変数に代入し、バッファをクリアしてバッファリングを終了      
$buffer = ob_get_clean();  
  
//バッファリング開始
ob_start();
        
echo strtoupper($buffer); //出力せずバッファに保存
         
// バッファの内容を変数に代入、バッファをクリアしてバッファリングを終了 
$buffer2 = ob_get_clean();  
         
// 変数に代入したバッファの内容を出力
echo '$buffer は '.$buffer .'<br>';
//「$buffer は foobar」と出力される  
        
echo '$buffer2 は '.$buffer2 .'<br>';
//「$buffer2 は FOOBAR」と出力される  
 ?>

include や require

include や require を使った外部ファイルを読み込む際にも出力をバッファリングすることができます。

echo と同様、include や require も記述したところで処理(出力)が実行されます。

include や require も以下のように ob_start() を使ってバッファリングすることで、読み込んだファイルを変数に格納して、任意のタイミングで必要に応じて出力することができます。

<?php
//出力バッファを有効に
ob_start();  

//ファイルをインクルード
include '/path/to/template.php';  

//インクルードした出力を変数に保存
$buffer = ob_get_contents(); 

//出力バッファをクリア
ob_end_clean();  

//インクルードした内容を出力
echo $buffer;  
?>

例えば、以下のようなパラメータの変数を記述すればインクルードするファイル(テンプレート)でその値を使用することができます。インクルードするファイルに渡すパラメータの変数($attrs)の記述は include の前であれば、ob_start() の後でも問題ありません。

<?php
//インクルードするテンプレートファイルに渡すパラメータ
$attrs = ['foo', 'bar', 'baz']; 
ob_start();
//テンプレートファイルをインクルード
include '/path/to/template.php';
$buffer = ob_get_contents();
ob_end_clean();
echo $buffer;
?>

以下はインクルードするテンプレートファイル template.php です。

template.php
<?php 
foreach($attrs as $attr) {
  echo '<p>Hello, '. $attr . ' !</p>'."\n";
}

出力される $buffer の内容は以下になります。

<p>Hello, foo !</p>
<p>Hello, bar !</p>
<p>Hello, baz !</p>

簡易テンプレートの作成

以下のような関数を作成して簡単にテンプレートファイルにパラメータを渡して出力できるようにします。

  • $file:テンプレートファイル
  • $attrs:テンプレートファイルに渡すパラメータ
<?php 
function load_my_template($file, $attrs){
  ob_start();
  include $file; //テンプレートファイルのインクルード
  $html = ob_get_contents();
  ob_end_clean();
  echo $html; //出力
}

上記の関数 load_my_template() の定義を全てのファイルで読み込むファイルに記述しておくか、必要に応じてページごとに関数を記述したファイルを読み込みます。

このページの今までの例では、全てのページで使用する変数や関数を記述してあるファイル vars.php があるのでそこに記述するなど、環境に合わせて load_my_template() の定義を読み込みます。

vars.php 抜粋
<?php 
//このスクリプトを実行しているページのホームディレクトリからのパスを含むファイル名
$script_name = $_SERVER['SCRIPT_NAME'];  
//このスクリプトを実行しているページのファイル名
$script_basename = basename($script_name);  

・・・中略・・・

//テンプレートの関数
function load_my_template($file, $attrs){
  ob_start();
  include $file;
  $html = ob_get_contents();
  ob_end_clean();
  echo $html;
}

テンプレートを出力するファイルでは、テンプレートの関数を読み込み、パラメータを渡して関数を実行(呼び出し)します。以下の例ではパラメータを記述して渡していますが、別ファイルに記述して関数の前でインクルードするなども可能です。

<?php
$path = './';  
include $path.'inc/vars.php'; //テンプレートの関数の読み込み
include $path .'inc/head.php';
?>
</head>
<body>
<div class="wrapper">
  <?php include $path .'inc/header.php'; ?>
  ・・・中略・・・
  <?php
    //パラメータ(関数の引数に直接記述しても同じ)
    $attrs = ['foo', 'bar', 'baz']; 
    //テンプレートの関数を実行
    load_my_template( $path."templates/template.php", $attrs );
  ?>
  ・・・中略・
<?php include $path .'inc/footer.php'; ?>
</body>
</html>
templates/template.php(テンプレートファイル)
<?php 
foreach($attrs as $attr) {
  echo '<p>Hello, '. $attr . ' !</p>'."\n";
}

load_my_template() を記述した位置で以下が出力されます。

<p>Hello, foo !</p>
<p>Hello, bar !</p>
<p>Hello, baz !</p>

テンプレートのサンプル

以下は figure 要素を使って画像とキャプションを出力するテンプレートの例です。

$attrs は load_my_template() の第2パラメータで、この例では配列が渡されます。

for 文で配列の要素の数だけ figure 要素を出力し、画像の src やキャプションなどは渡されたパラメータから取得します。指定しているクラス属性は特に意味はありません。

image-gallery.php(テンプレートファイル)
<?php for($i = 0; $i < count($attrs); $i++){ ?>
<div class="col-lg-3 col-md-6">
  <figure class="image-gallery-figure"> 
    <img src="<?php echo $attrs[$i]['src']; ?>" 
         class="figure-img img-fluid" 
         width="400" height="300" 
         alt="<?php echo $attrs[$i]['alt']; ?>">
    <figcaption>
      <h4 class="figure-caption-h4"><?php echo $attrs[$i]['title']; ?></h4>
      <div class="caption">
        <p><?php echo $attrs[$i]['caption']; ?></p>
      </div>
    </figcaption>
  </figure>
</div>
<?php } ?>

この例ではテンプレートの関数に渡すパラメータは別途以下のようなファイルに記述しておきます。

$path はテンプレートを出力するファイルで定義されている変数(相対パスの値)です。

image-gallery-entries.php
<?php 
$attrs_image_gallery = [
  [
    'title' => 'タイトル1',
    'src' => $path.'images/sample_01.jpg',
    'alt' => 'ビーチの風景の写真',
    'caption' => 'カリブのビーチ'
  ],
  [
    'title' => 'タイトル2',
    'src' => $path.'images/sample_02.jpg',
    'alt' => '街の写真',
    'caption' => 'ニューヨークウェストビレッジ'
  ],
  [
    'title' => 'タイトル3',
    'src' => $path.'images/sample_03.jpg',
    'alt' => '床屋さんの写真',
    'caption' => 'ニューヨークの床屋さん'
  ],
];

テンプレートを出力するページ(ファイル)では、テンプレートの関数 load_my_template() を定義したファイルをインクルードしておきます。

テンプレートを出力するページ(ファイル)
<?php
$path = './';  
include $path.'inc/vars.php'; //テンプレートの関数が定義されているファイル
include $path .'inc/head.php';
?>
</head>
<body>
<div class="wrapper">
  <?php include $path .'inc/header.php'; ?>
  <div class="container">
    ・・・中略・・・
  <div class="row">
  <?php
  //関数に渡すパラメータ($attrs_image_gallery)が記述されているファイルをインクルード
  include $path . 'templates/image-gallery-entries.php';
  //テンプレートファイルとパラメータを指定して実行
  load_my_template( $path."templates/image-gallery.php", $attrs_image_gallery );
  ?>
  </div>
    ・・・中略・・・
</body>
</html>

load_my_template() を記述した位置には以下が出力されます。

<div class="col-lg-3 col-md-6">
  <figure class="image-gallery-figure"> 
  <img src="./images/sample_01.jpg" class="figure-img img-fluid" width="400" height="300" alt="ビーチの風景の写真">
    <figcaption>
      <h4 class="figure-caption-h4">タイトル1</h4>
      <div class="caption">
        <p>カリブのビーチ</p>
      </div>
    </figcaption>
  </figure>
</div>
<div class="col-lg-3 col-md-6">
  <figure class="image-gallery-figure"> 
  <img src="./images/sample_02.jpg" class="figure-img img-fluid" width="400" height="300" alt="街の写真">
    <figcaption>
      <h4 class="figure-caption-h4">タイトル2</h4>
      <div class="caption">
        <p>ニューヨークウェストビレッジ</p>
      </div>
    </figcaption>
  </figure>
</div>
<div class="col-lg-3 col-md-6">
  <figure class="image-gallery-figure"> 
  <img src="./images/sample_03.jpg" class="figure-img img-fluid" width="400" height="300" alt="床屋さんの写真">
    <figcaption>
      <h4 class="figure-caption-h4">タイトル3</h4>
      <div class="caption">
        <p>ニューヨークの床屋さん</p>
      </div>
    </figcaption>
  </figure>
</div>

関連項目:独自テンプレートで出力(パンくずリスト)