この記事ではPHPでTodoリストを制作していきます。セッション管理、フォーム処理、配列操作など、Web開発の基礎が学べる実践的なプロジェクトになっています。

Todoリストの概要
今回実装していくTodoリストはシンプルな機能を盛り込んでいます。機能の一覧は以下です。
必要な前提知識
- HTML/CSSの基本
- PHPの基本文法
- フォームの仕組み
- 配列の扱い方
phpの環境構築がまだの方は、次の記事から導入してください。
ソースコードテンプレート
下記ソースコードに処理を追加していきます。
<?php
// ※バックエンドの処理を記述していきます
?>
<!DOCTYPE html>
<html>
<head>
<title>My Todo List</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>My Todo List</h1>
<!-- ※フロントエンドの処理を記述していきます -->
</div>
</body>
</html>
スタイリングが必要であれば以下のcssを利用してください。(任意)
css(クリックで開く)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Helvetica Neue",
Arial,
"Hiragino Kaku Gothic ProN",
"Hiragino Sans",
Meiryo,
sans-serif;
}
body {
background-color: #f5f5f5;
color: rgba(0, 0, 0, 0.87);
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h1 {
color: #1976d2;
font-weight: 60 0;
margin: 2rem 0;
text-align: center;
}
.add-form {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
display: flex;
gap: 10px;
}
.add-form input[type="text"] {
flex: 1;
padding: 12px 16px;
border: 1px solid #e0e0e0;
border-radius: 4px;
font-size: 16px;
transition: border-color 0.2s;
}
.add-form input[type="text"]:focus {
outline: none;
border-color: #1976d2;
}
button {
background: #1976d2;
color: white;
border: none;
padding: 12px 24px;
border-radius: 4px;
font-size: 14px;
font-weight: 500;
text-transform: uppercase;
cursor: pointer;
transition: background 0.2s;
}
button:hover {
background: #1565c0;
}
.todo-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.todo-item {
background: white;
padding: 16px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: space-between;
transition: transform 0.2s;
}
.todo-item:hover {
transform: translateY(-2px);
}
.todo-content {
flex: 1;
}
.todo-text {
font-size: 16px;
}
.delete-btn {
background: transparent;
color: #f44336;
padding: 8px;
min-width: auto;
}
.delete-btn:hover {
background: rgba(244, 67, 54, 0.1);
}
@media (max-width: 600px) {
.container {
padding: 10px;
}
.add-form {
flex-direction: column;
}
}
メモ入力機能 ※フロントエンド側
メモを追加するには、ユーザーが入力したテキストをサーバ側で受け取り、保持しておく必要があります。まずはメモ入力フォームを作成していきます。
<form method="POST" class="add-form">
<input type="text" name="todo" placeholder="新しいタスクを入力" required>
<button type="submit" name="add">追加</button>
</form>
これはユーザーが入力したテキストを、同ページにPOSTする処理です。追加ボタンが押されると、フォーム内のデータがまとめてWebサーバに送られます。
メモ保存機能 ※バックエンド側
送られたデータは、セッションによって保持しています。セッションはサーバ内にデータを保存し、ブラウザを閉じるまでデータを保持することができます。
注意点として、セッションを使うにはsession_start()を行う必要があります。session_start()することで、セッション本体にあたるグローバル変数$_SESSIONにアクセスできるようになります。
セッションの初期化
session_start();
if (!isset($_SESSION['todos'])) {
$_SESSION['todos'] = [];
}
まずセッションをスタートして、セッションにメモが格納されていない場合、空の配列で初期化します。これにより、エラーを防ぎ、安全にデータを追加できます。
isset関数は、連想配列に指定のキーが存在する場合にtrueを返します。今回は関数の前に!がついているため、条件が反転されます。グローバル変数$_SESSIONに’todos’がなければ、空の配列で初期化する処理を行います。
データの保存構造
送信されたデータは以下のような構造で保存されます。
$todo = [
'id' => uniqid(), // ユニークなID
'text' => $_POST['todo'] // メモの内容
];
各項目の役割を見ていきましょう。
- uniqid(): 各メモを識別するための被らないIDを生成
- $_POST[‘todo’]: フォームから送信されたメモの内容
if (isset($_POST['add']) && !empty($_POST['todo'])) {
$todo = [
'id' => uniqid(), // ユニークなID
'text' => $_POST['todo'] // メモの内容
];
array_push($_SESSION['todos'], $todo);
}
この部分では、2つの条件を両方とも満たしているか確認しています。
- isset($_POST[‘add’]): 追加ボタンが押されたかの確認
- !empty($_POST[‘todo’]): メモの内容が空でないことの確認
- 条件A && 条件B: 左と右の条件が両方とも満たされる場合にtrueを返す
empty関数は、連想配列に指定のキーが存在しない場合にtrueを返します。くわえて、空文字やnullの場合もtrueを返します。先述したコードでは、empty関数に!をつけることで条件を反転しています。
条件を満たした場合、array_push()を使って新しいメモを配列に追加します。
実装のポイント
- フォームのバリデーション
- 必須入力項目の設定(required属性)
- サーバーサイドでの入力チェック
- データの永続化
- セッションを使用したデータの一時保存
- 配列構造での管理
- 一意性の確保
- uniqid()によるユニークなID付与
- 後の削除処理で使用
このように、メモの追加機能は単純な処理に見えて、様々な要素が組み合わさっています。次は、保存したメモの一覧表示について解説していきましょう。
メモの一覧表示機能 ※フロントエンド側
保存したメモを表示する部分を追加していきましょう。
<div class="todo-list">
<?php foreach ($_SESSION['todos'] as $todo): ?>
<div class="todo-item">
<div class="todo-content">
<div class="todo-text"><?php echo htmlspecialchars($todo['text']); ?></div>
</div>
</div>
<?php endforeach; ?>
</div>
データの取り出し
セッションに保存されたメモは、foreachループを使って1件ずつ取り出します。 $_SESSION[‘todos’]には先ほど保存した配列が入っており、これを$todo変数に順番に代入しながら処理していきます。
表示の安全性
<?php echo htmlspecialchars($todo['text']); ?>
メモの内容を表示する際は、必ずhtmlspecialchars()関数でエスケープ処理をします。 これはXSS(クロスサイトスクリプティング)と呼ばれる攻撃を防ぐための対策です。ユーザーの入力を表示する場合は必須です。
実装のポイント
- データの取り出し
- foreachループによる配列処理
- 配列のキーを意識した実装
- セキュリティ対策
- htmlspecialchars()によるエスケープ処理
- 安全な出力
- レイアウト設計
- 階層構造を持つHTML
- CSSによるスタイリング
メモの削除機能
保存したメモを削除する機能を見ていきましょう。フロントエンド側の処理から解説していきます。
削除ボタンの実装 ※フロントエンド側
<form method="POST" class="form-inline">
<input type="hidden" name="id" value="<?php echo $todo['id']; ?>">
<button type="submit" name="delete" class="delete-btn">delete</button>
</form>
各メモに削除用のフォームを用意します。
- hidden項目で対象メモのIDを保持
- POSTメソッドで送信
削除処理の実装 ※バックエンド側
if (isset($_POST['delete']) && !empty($_POST['id'])) {
$_SESSION['todos'] = array_filter($_SESSION['todos'], function ($todo) {
return $todo['id'] != $_POST['id'];
});
}
重要なポイント
- 削除の判定
- 削除ボタンが押されたか確認: isset($_POST[‘delete’])
- 削除対象のIDが存在するか確認: !empty($_POST[‘id’])
- 両方の条件を満たしているか確認: isset($_POST[‘delete’]) && !empty($_POST[‘id’])
- array_filter関数の使用
- 配列から特定の要素を除外
- コールバック関数で条件指定
- 新しい配列を生成
- セキュリティ
- POSTメソッドによるセキュリティリスクの低減
- IDの存在確認
データの永続性
削除されたデータはセッションから完全に削除されます。ブラウザを閉じるまでこの状態が維持されます。
これで基本的なTodoリストの機能が完成しました!ソースはこちらです。
<?php
session_start();
// 初期化
if (!isset($_SESSION['todos'])) {
$_SESSION['todos'] = [];
}
// Todoの追加
if (isset($_POST['add']) && !empty($_POST['todo'])) {
$todo = [
'id' => uniqid(),
'text' => $_POST['todo'],
];
array_push($_SESSION['todos'], $todo);
}
// Todoの削除
if (isset($_POST['delete']) && !empty($_POST['id'])) {
$_SESSION['todos'] = array_filter($_SESSION['todos'], function ($todo) {
return $todo['id'] != $_POST['id'];
});
}
?>
<!DOCTYPE html>
<html>
<head>
<title>My Todo List</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>My Todo List</h1>
<form method="POST" class="add-form">
<input type="text" name="todo" placeholder="新しいタスクを入力" required>
<button type="submit" name="add">追加</button>
</form>
<div class="todo-list">
<?php foreach ($_SESSION['todos'] as $todo): ?>
<div class="todo-item">
<div class="todo-content">
<div class="todo-text"><?php echo htmlspecialchars($todo['text']); ?></div>
</div>
<form method="POST" class="form-inline">
<input type="hidden" name="id" value="<?php echo $todo['id']; ?>">
<button type="submit" name="delete" class="delete-btn">delete</button>
</form>
</div>
<?php endforeach; ?>
</div>
</div>
</body>
</html>
次回は、ブラウザが終了してもデータを保持できるように、データベースと連携していきます。