Generate a year dropdown in PHP

Build a labelled year select with integer comparisons, a deliberate timezone and validation of a requested selected year.

Generate the range, keep the choice explicit

A small loop is easier to maintain than a long hand-written list of years. This example lists the current year down to 1950 and optionally selects a valid year query parameter.

The useful fixes over the old example are a real PHP opening tag, integer year values, a label/name for the field, and validation before using an incoming selection.

PHP template

<?php
$earliest = 1950;
$latest = (int) (new DateTimeImmutable('now', new DateTimeZone('Europe/London')))
    ->format('Y');
if ($earliest > $latest) {
    throw new LogicException('The year range is reversed.');
}

$selected = $latest;
if (array_key_exists('year', $_GET)) {
    $selected = is_string($_GET['year'])
        ? filter_var($_GET['year'], FILTER_VALIDATE_INT, ['options' => [
            'min_range' => $earliest, 'max_range' => $latest,
        ]])
        : false;
}
?>
<label for="year">Year</label>
<select id="year" name="year" required>
  <option value=""<?= $selected === false ? ' selected' : '' ?>>Choose a year</option>
  <?php for ($year = $latest; $year >= $earliest; $year--): ?>
    <option value="<?= $year ?>"<?= $year === $selected ? ' selected' : '' ?>><?= $year ?></option>
  <?php endfor; ?>
</select>

Save it in a PHP template and serve it through PHP. A URL ending in ?year=2024 selects 2024 when that year is in range. A missing parameter uses the current year; an invalid or array-shaped value leaves the placeholder selected rather than pretending it was accepted.

Adapt the bounds to the task

Change 1950 to the genuine earliest useful year. A booking form might need future years instead; a year-of-birth field should not silently default to the current year. For a large or uncertain range, a labelled text input with a clear example can be easier than a long select.

The values printed here are generated integers, not raw query strings. If you add user-controlled labels or attributes, escape them for their HTML context. The handler receiving the eventual form submission must repeat the range validation—rendering valid options does not prevent a forged request. A cached page also needs refreshing when its upper year changes.

References: PHP integer validation and DateTimeImmutable.

Original version9 December 2014

Kept here for reference and earlier links. The updated guide above is the recommended starting point; older code may depend on retired services or different software versions.

A very common feature of forms is the dropdown

<select>
</select>

box, commonly using a range of years as the options.
Writing and maintaining these long list of options/years can be very monotonous via plain HTML.

This following codebyte generates a simple select box, with an for each year based on some basic arguments.

Change $currently_selected to be the option you want as the top/default option of the select box.
$earliest_year to be the lowest year you want the range to start at.
$latest_year to be the highest year you want your range to go to.

  <!--?php 
  // Sets the top option to be the current year. (IE. the option that is chosen by default).
  $currently_selected = date('Y'); 
  // Year to start available options at
  $earliest_year = 1950; 
  // Set your latest year you want in the range, in this case we use PHP to just set it to the current year.
  $latest_year = date('Y'); 

  print '&lt;select&gt;';
  // Loops over each int[year] from current year, back to the $earliest_year [1950]
  foreach ( range( $latest_year, $earliest_year ) as $i ) {
    // Prints the option with the next year in range.
    print '&lt;option value="'.$i.'"'.($i === $currently_selected ? ' selected="selected"' : '').'&gt;'.$i.'&lt;/option&gt;';
  }
  print '&lt;/select&gt;';
  ?-->

The above code byte generates:











































































Keep exploring

A couple more useful notes.