patternphpMinor
Simple calendar in PHP
Viewed 0 times
phpsimplecalendar
Problem
I made a simple calendar in PHP 7.0, could anyone help with simplification? Any advice appreciated. It works, but I totally believe it can be improved. I am a beginner.
" selected = "selected">
= 2000; $year--) {
$years .= $year . " ";
}
return $years;
}
$years = explode (" ", getYear());
foreach ($years as $number => $chose_year) {
?>
" selected = "selected" selected ='selected'>
"January", "February", "March", "April", "May",
"June", "July", "August", "September", "October", "November",
"December");
foreach($months as $number2 => $chose_month) {
?>
" selected = "selected">
Mon TueWedThuFriSatSun
";
}
else if ($i != 1) echo "\n";
echo "";
if (($chosen_year == date('Y')) and ($chosen_month == date('n')) and date('j') == $array_of_days[$i]) {
echo '' .$array_of_days[$i]. '';
} else {
echo $array_of_days[$i];
}
if (($i % 7 == 0) || ($i == $lastDay)) {
$DayToBrake = $i;
echo "";
}
$i++;
}
echo "\n";
?>
'." Is"." ".$months[$m]." ". date('Y').'';} ?>
Solution
There are any number of things to get into here since, as you say you are a beginner. This code looks a lot like what you may have seen in PHP code examples on the web 10+ years ago, when PHP was used mostly as a web scripting language to add dynamic functionality to otherwise static web pages. So let me first introduce you to what I feel is the best source out there in how to get started working with PHP - PHP: The Right Way. There is a lot of information here, but it is worthwhile to digest it over time, as this is a better source of information than probably 99% of PHP code examples out there.
First, you should strive to separate your PHP logic from your HTML output. Right now you have the classic "spaghetti code" which is very hard to maintain over time. Do your very best to write all the information you need into PHP variables, such that when you get to rendering your HTML (ideally in a different file from your main code), all you are doing is simple variable insertion and perhaps some looping in your HTML template.
I would strongly encourage you to familiarize yourself with PHP's
This can eliminate large portions of your script where you are doing things like manually building date arrays and finding first days of months and such.
This also automatically handles all concerns around daylight savings times, leap years, etc. and you can also incorporate timezone considerations using
Tying to manually work with dates the way you are doing is a bit of a fool's errand.
Get in the habit of using exact comparisons (
Your variable names could certainly be more meaningful. What does
Don't mix function definitions randomly in the middle of procedural sections of code like you are doing with
You are doing some input sanitization (good!), But you really are not validating input in any sense. I could send totally unexpected values for months/years to this script and cause it to break. Never trust user input - validate every single bit of it.
You have way too much vertical white space (i.e. blank lines) in the HTML output part of your script. This makes your code hard to read.
First, you should strive to separate your PHP logic from your HTML output. Right now you have the classic "spaghetti code" which is very hard to maintain over time. Do your very best to write all the information you need into PHP variables, such that when you get to rendering your HTML (ideally in a different file from your main code), all you are doing is simple variable insertion and perhaps some looping in your HTML template.
I would strongly encourage you to familiarize yourself with PHP's
DateTime and related classes. They are much more powerful in working with date manipulation than the old procedural date() functions.This can eliminate large portions of your script where you are doing things like manually building date arrays and finding first days of months and such.
// instantiate DateTime based on current time
$datetime = new DateTime('first day of this month');
// change to first day of next month
$datetime = $datetime->modify('+1 month');
// set up iterator of every month start date for full date range between 2000 and 2030
$startDate = new DateTime('2000-01-01 00:00:00');
$endDate = new DateTime('2030-12-31 23:59:59');
$monthlyInterval = new DateInterval('1m');
$weeklyInterval = new DateInterval('1w');
$dailyInterval = new DateInterval('1d');
$monthlyPeriods = new DatePeriod($startDate, $monthlyInterval, $endDate);
foreach($monthlyPeriods as $firstDayOfMonth) {
// make calendar based on DateTime stored in $firstDayOfMonth
// for example let's say this calendar shows Sunday-Saturday and
// includes days that may be in previous/next month
// Note: DateTime considers Monday as first day of week for relative
// this/week calculations so this jives well with your Mon-Sun calendar
$firstDayOnCalendar = clone($firstDayOfMonth)
->modify('Monday this week');
$lastDayOnCalendar = clone($firstDayOfMonth)
->modify('last day of this month')
->modify('Sunday this week');
// build weeks
$weekPeriods = new DatePeriod($firstDayOnCalendar, $weeklyInterval, $lastDayOnCalendar);
foreach($weekPeriods as $firstSundayOfWeek) {
$days = new DatePeriod($firstSundayOfWeek, $dailyInterval, 7);
foreach ($days as $day) {
// build day cell on calendar using $day
echo $day->format('Y-m-d');
}
}
}This also automatically handles all concerns around daylight savings times, leap years, etc. and you can also incorporate timezone considerations using
DateTimeZoneTying to manually work with dates the way you are doing is a bit of a fool's errand.
Get in the habit of using exact comparisons (
=== and !==) instead of loose comparisons (== and !=). These will go a long way in making your code less fragile to unexpected truthy/falsey behavior that can lead to bugs. You should only use loose comparisons in cases where you truly have a need to match loosely. You might find that these cases are really few and far between.Your variable names could certainly be more meaningful. What does
$english_order convey? What does $do_update_button mean? getYear() does not get a year, it gets a range of years, right?Don't mix function definitions randomly in the middle of procedural sections of code like you are doing with
getYear()You are doing some input sanitization (good!), But you really are not validating input in any sense. I could send totally unexpected values for months/years to this script and cause it to break. Never trust user input - validate every single bit of it.
You have way too much vertical white space (i.e. blank lines) in the HTML output part of your script. This makes your code hard to read.
Code Snippets
// instantiate DateTime based on current time
$datetime = new DateTime('first day of this month');
// change to first day of next month
$datetime = $datetime->modify('+1 month');
// set up iterator of every month start date for full date range between 2000 and 2030
$startDate = new DateTime('2000-01-01 00:00:00');
$endDate = new DateTime('2030-12-31 23:59:59');
$monthlyInterval = new DateInterval('1m');
$weeklyInterval = new DateInterval('1w');
$dailyInterval = new DateInterval('1d');
$monthlyPeriods = new DatePeriod($startDate, $monthlyInterval, $endDate);
foreach($monthlyPeriods as $firstDayOfMonth) {
// make calendar based on DateTime stored in $firstDayOfMonth
// for example let's say this calendar shows Sunday-Saturday and
// includes days that may be in previous/next month
// Note: DateTime considers Monday as first day of week for relative
// this/week calculations so this jives well with your Mon-Sun calendar
$firstDayOnCalendar = clone($firstDayOfMonth)
->modify('Monday this week');
$lastDayOnCalendar = clone($firstDayOfMonth)
->modify('last day of this month')
->modify('Sunday this week');
// build weeks
$weekPeriods = new DatePeriod($firstDayOnCalendar, $weeklyInterval, $lastDayOnCalendar);
foreach($weekPeriods as $firstSundayOfWeek) {
$days = new DatePeriod($firstSundayOfWeek, $dailyInterval, 7);
foreach ($days as $day) {
// build day cell on calendar using $day
echo $day->format('Y-m-d');
}
}
}Context
StackExchange Code Review Q#158981, answer score: 5
Revisions (0)
No revisions yet.