本文實作一個可複用的 PHP 日曆類別 Calendar,支援透過 GET 參數切換年月、高亮顯示今日、下拉選單快速跳轉,適合嵌入各類專案。
<?php
class Calendar {
private int $year;
private int $month;
private int $startWeekday; // 0=週日
private int $daysInMonth;
public function __construct() {
$this->year = isset($_GET['year']) ? (int)$_GET['year'] : (int)date('Y');
$this->month = isset($_GET['month']) ? (int)$_GET['month'] : (int)date('m');
$this->startWeekday = (int)date('w', mktime(0, 0, 0, $this->month, 1, $this->year));
$this->daysInMonth = (int)date('t', mktime(0, 0, 0, $this->month, 1, $this->year));
}
public function render(): void {
echo '<table class="cal-table">';
$this->renderHeader();
$this->renderWeekHeader();
$this->renderDays();
echo '</table>';
}
private function renderHeader(): void {
echo '<tr>';
echo '<th><a href="?' . $this->navLink(-1, 0) . '"><<</a></th>'; // 上一年
echo '<th><a href="?' . $this->navLink(0, -1) . '"><</a></th>'; // 上一月
echo '<th colspan="3">';
echo '<form method="get">';
echo '<select name="year" onchange="this.form.submit()">';
for ($y = 1970; $y <= 2100; $y++) {
$sel = ($y === $this->year) ? ' selected' : '';
echo "<option value=\"$y\"$sel>$y</option>";
}
echo '</select>';
echo '<select name="month" onchange="this.form.submit()">';
for ($m = 1; $m <= 12; $m++) {
$sel = ($m === $this->month) ? ' selected' : '';
echo "<option value=\"$m\"$sel>$m</option>";
}
echo '</select>';
echo '</form>';
echo '</th>';
echo '<th><a href="?' . $this->navLink(0, +1) . '">></a></th>'; // 下一月
echo '<th><a href="?' . $this->navLink(+1, 0) . '">>></a></th>'; // 下一年
echo '</tr>';
}
private function renderWeekHeader(): void {
$weeks = ['日','一','二','三','四','五','六'];
echo '<tr>';
foreach ($weeks as $w) echo "<th>$w</th>";
echo '</tr>';
}
private function renderDays(): void {
echo '<tr>';
// 前導空格
for ($i = 0; $i < $this->startWeekday; $i++) echo '<td></td>';
$cell = $this->startWeekday;
for ($d = 1; $d <= $this->daysInMonth; $d++, $cell++) {
$cls = ($d === (int)date('j') && $this->month === (int)date('m') && $this->year === (int)date('Y')) ? ' class="today"' : '';
echo "<td$cls>$d</td>";
if ($cell % 7 === 6) echo '</tr><tr>';
}
// 尾部空格
while ($cell % 7 !== 0) {
echo '<td></td>';
$cell++;
}
echo '</tr>';
}
private function navLink(int $yDelta, int $mDelta): string {
$y = $this->year + $yDelta;
$m = $this->month + $mDelta;
// 月份跨年修正
if ($m < 1) { $m = 12; $y--; }
if ($m > 12) { $m = 1; $y++; }
// 限制年份範圍
$y = max(1970, min(2100, $y));
return "year=$y&month=$m";
}
}
?>
<style>
.cal-table { border-collapse: collapse; width: 100%; max-width: 400px; margin: 1rem auto; font-family: sans-serif; }
.cal-table th, .cal-table td { border: 1px solid #ddd; height: 36px; text-align: center; }
.cal-table th { background: #f5f5f5; }
.cal-table .today { background: #1e90ff; color: #fff; font-weight: bold; }
.cal-table select { padding: 4px; font-size: 14px; }
</style>
<?php
require_once 'Calendar.php';
$cal = new Calendar();
$cal->render();
?>