PHP: Validate & Check Dates

More times than I can count I’ve had to validate dates in PHP, not only check if it is in the required format but also to make sure the date actually exists. Until recently there has not been a good, native way of doing this, yes checkdate() has been around for a while but it’s particularly fussy with it’s input, what we needed was a mixture of date() where we can specify the input format and checkdate() to validate it. Enter the DateTime class. Technically available from PHP >= 5.2.0 it isn’t really worth using until >=5.3.0 due to bugs and missing features, (DateTime::createFromFormat or DateTime::getLastErrors in particular).

The traditional approach ( PHP < 5.3.0 )

If you are still running an older version of PHP or are developing for WordPress, (WordPress’s minimum requirements are still 5.2.4 at the time of writing this), below is a very handy PHP date validator function I wrote. Simply pass the date you wish to check as the first value and the format it should be in as the second.

	/**
	 * Validates a string as a date in the inputted format.
	 * Version 1.0
	 *
	 * @param type $date The date to check.
	 * @param type $format The format the date should be in. Defaults to YYYY-MM-DD
	 * @return boolean
	 */
	function validate_date( $date = null, $format = 'YYYY-MM-DD' ){

		// Return FALSE if $date empty
		if( ! $date )
			return FALSE;

		// $date is trimed
		$date = trim( $date );

		// If $format empty return to default, else UPPERCASE and trim.
		$format = empty( $format ) ? 'YYYY-MM-DD' : strtoupper( $format );

		// $format is 10 char in length and contains all the required chars
		if( strlen( $format ) != 10 ||
		    strpos( $format, 'YYYY' ) === FALSE ||
		    strpos( $format, 'MM' ) === FALSE ||
		    strpos( $format, 'DD' ) === FALSE
		)
			return FALSE;

		// Get $format serparator
		$date_seperator = str_replace( array( 'Y', 'M', 'D' ) , '', $format );
		$date_seperator = $date_seperator[0];

		// Explode $format into parts
		$format_parts = explode( $date_seperator, $format );

		// Explode our date into parts
		$date_parts = explode( $date_seperator, $date );

		// Count $date_parts; Quicker than doing it inline
		$date_parts_count = count( $date_parts );

		// Cycle through $date_parts, compare the length to the equivilent $format_parts length, set to var
		for( $i = 0; $i &lt; $date_parts_count; $i++ ){

			// Check each datepart is a valid in
			if( ! filter_var( ( int ) $date_parts[ $i ], FILTER_VALIDATE_INT ) )
				return FALSE;

			// Compare $date_part length to equivilent $format_part length
			if( strlen( $date_parts[ $i ] ) != strlen( $format_parts[ $i ] )  )
				return FALSE;

			// Set our variables so we check it's a valid date later
			if( $format_parts[ $i ][0] == 'Y' ){

				$year = $date_parts[ $i ];

			} elseif( $format_parts[ $i ][0] == 'M' ){

				$month = $date_parts[ $i ];

			} elseif( $format_parts[ $i ][0] == 'D' ){

				$day = $date_parts[ $i ];

			}

		}

		// Finally, check it's a valid date
		if( checkdate( $month, $day, $year ) ){

			return TRUE;

		} else {

			return FALSE;

		}

	}

The modern approach ( PHP >= 5.3.0)

If you know you’re going to be using a more modern version of PHP you can take advantage of the very cool and now native DateTime class. Among it’s many cool new features the best is DateTime::createFromFormat() which allows you to re-format an inputted date. Yes it’s very similar to using date() & strtotime() together BUT this has all sorts of useful additions, the most important of which is error checking.

Christos Pontikis has done a great post on the best way to use the class and how to work around it’s current limitations. A copy of his final recommended solution is below. It basically involves explicitly checking the DateTime::getLastErrors() function to see if the date was valid or not.


/**
 * Check if a string is a valid date(time)
 *
 * DateTime::createFromFormat requires PHP >= 5.3
 *
 * @param string $str_dt
 * @param string $str_dateformat
 * @param string $str_timezone (If timezone is invalid, php will throw an exception)
 * @return bool
 */
function check_date( $str_dt, $str_dateformat, $str_timezone ) {

$date = DateTime::createFromFormat( $str_dateformat, $str_dt, new DateTimeZone( $str_timezone ) );
return $date && DateTime::getLastErrors()['warning_count'] == 0 && DateTime::getLastErrors()['error_count'] == 0;

}

N.B. There are still some errors with the DateTime class. Check out the User comments on the DateTime::createFromFormat for a more in depth discussion.

WordPress: Auto adds slashes to $_POST, $_GET, $_REQUEST, $_COOKIE

When I first hard coded a form into a WordPress page and print_r() the results, all the quotes were escaped. Now, being a reasonably savvy developer at times, my first thought was obviously, “Oh the server must have magic quotes turned on”. So I did my due diligence and checked, but oddly it didn’t. After a lot of googling it turns out that the WordPress core automatically adds slashes to all input arrays. “Why, oh god why” you rightly ask. Well the answer is simple, WordPress has been around a long time now and has to run on a variety of different servers all with different settings. To maintain consistency and provide the best possible security they decided to force adding slashes to all input, even if the server had magic_quotes turned off, ensuring that consistency is maintained across the core code.

Why would you ever want magic quotes and what does it do?

Magic Quotes were added to PHP very early on as a one size fits all approach to help beginners write better and more secure code. What it does is automatically add slashes () to single and double quotes in any possible user facing data array, ($_POST, $_GET, $_REQUEST, $_COOKIE). Adding slashes to any data going near a database helps prevent SQL injection and makes database interaction much safer and is generally a good thing. However, PHP automatically adding slashes to data arrays has caused no end of headaches for developers over the years, namely because you often need to manipulate the data before it goes anywhere near a database, if it ever does. Most servers these days have magic quotes disabled, indeed, it’s been depreciated in PHP since 5.3.0 and will be actively removed, 5.4.0. You can read more about magic quotes on the official PHP site here.

Ok, how do I to fix it in WordPress

WordPress realises you might not want slashes automatically added and so provides a nice function called stripslashes_deep that enables you to remove them should you wish. WARNING: As mentioned above, the WordPress core relies on the all code being escaped, (in particular $_REQUEST), so don’t remove it early on in the page execution, only later after all the initial core stuff has loaded and you want to process the data.

Either way, you can use array_map with the native WordPress stripslashes_deep function to remove all the added slashes. Use the code below to do just that.


$_POST = array_map( 'stripslashes_deep', $_POST );
$_GET = array_map( 'stripslashes_deep', $_GET );
$_COOKIE = array_map( 'stripslashes_deep', $_COOKIE );
$_REQUEST = array_map( 'stripslashes_deep', $_REQUEST );