8000 Adding validation · WP-API/WP-API@7888ca5 · GitHub
[go: up one dir, main page]

Skip to content
This repository was archived by the owner on Sep 24, 2018. It is now read-only.

Commit 7888ca5

Browse files
committed
Adding validation
Coolest part was I found out that this year is a leap year from this code and testing! Do /wp/v2/posts/?date_query[0][year]=2015&date_query[0][month]=2&date_query[0][day]=29. If it doesn't fail it's a leap year! Hooray. Added validation. This is still not error proof as I need to handle weird cases with how the top level params work, like when monthnum is specified without year. Maybe chopping top level parameter support would be nice, but would leave this incomplete so I will continue to add error handling for those strange cases.
1 parent 73e8583 commit 7888ca5

File tree

1 file changed

+221
-16
lines changed

1 file changed

+221
-16
lines changed

lib/endpoints/class-wp-rest-posts-controller.php

Lines changed: 221 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,55 +1692,60 @@ public function get_collection_params() {
16921692
// Date params starts here.
16931693
$params['year'] = array(
16941694
'description' => __( '4 digit year (e.g. 2011).' ),
1695-
'type' => 'int',
1695+
'type' => 'integer',
16961696
'validate_callback' => 'rest_validate_request_arg',
1697-
'sanitize_callback' => 'absint',
16981697
);
16991698
$params['monthnum'] = array(
17001699
'description' => __( 'Month number (from 1 to 12).' ),
1701-
'type' => 'int',
1700+
'type' => 'integer',
1701+
'minimum' => 1,
1702+
'maximum' => 12,
17021703
'validate_callback' => 'rest_validate_request_arg',
1703-
'sanitize_callback' => 'absint',
17041704
);
17051705
$params['w'] = array(
17061706
'description' => __( 'Week of the year (from 0 to 53). Uses MySQL WEEK command. The mode is dependent on the "start_of_week" option.' ),
1707-
'type' => 'int',
1707+
'type' => 'integer',
1708+
'minimum' => 0,
1709+
'maximum' => 53,
17081710
'validate_callback' => 'rest_validate_request_arg',
1709-
'sanitize_callback' => 'absint',
17101711
);
17111712
$params['day'] = array(
17121713
'description' => __( 'Day of the month (from 1 to 31).' ),
1713-
'type' => 'int',
1714+
'type' => 'integer',
1715+
'minimum' => 1,
1716+
'maximum' => 31,
17141717
'validate_callback' => 'rest_validate_request_arg',
1715-
'sanitize_callback' => 'absint',
17161718
);
17171719
$params['hour'] = array(
17181720
'description' => __( 'Hour (from 0 to 23).' ),
1719-
'type' => 'int',
1721+
'type' => 'integer',
1722+
'minimum' => 0,
1723+
'maximum' => 23,
17201724
'validate_callback' => 'rest_validate_request_arg',
1721-
'sanitize_callback' => 'absint',
17221725
);
17231726
$params['minute'] = array(
17241727
'description' => __( 'Minute (from 0 to 60)' ),
1725-
'type' => 'int',
1728+
'type' => 'integer',
1729+
'minimum' => 0,
1730+
'maximum' => 60,
17261731
'validate_callback' => 'rest_validate_request_arg',
1727-
'sanitize_callback' => 'absint',
17281732
);
17291733
$params['second'] = array(
17301734
'description' => __( 'Second (0 to 60).' ),
1731-
'type' => 'int',
1735+
'type' => 'integer',
1736+
'minimum' => 0,
1737+
'maximum' => 60,
17321738
'validate_callback' => 'rest_validate_request_arg',
1733-
'sanitize_callback' => 'absint',
17341739
);
17351740
$params['m'] = array(
17361741
'description' => __( 'YearMonth (For e.g.: 201307).' ),
1737-
'type' => 'int',
1742+
'type' => 'integer',
17381743
'validate_callback' => 'rest_validate_request_arg',
1739-
'sanitize_callback' => 'absint',
17401744
);
17411745
$params['date_query'] = array(
17421746
'description' => __( 'Date parameters. Make sure parameters are encapsulated in an array. See wp-includes/date.php for more info or check WP_Date_Query code reference.' ),
17431747
'type' => 'array',
1748+
'validate_callback' => array( $this, 'rest_validate_date_query' ),
17441749
);
17451750
return $params;
17461751
}
@@ -1763,4 +1768,204 @@ public function validate_user_can_query_private_statuses( $value, $request, $par
17631768
}
17641769
return new WP_Error( 'rest_forbidden_status', __( 'Status is forbidden' ), array( 'status' => rest_authorization_required_code() ) );
17651770
}
1771+
1772+
/**
1773+
* Validate a request date query.
1774+
*
1775+
* @param mixed $value Value of the argument.
1776+
* @param WP_REST_Request $request Request body.
1777+
* @param string $param Argument name should be date_query
1778+
* @return WP_Error|boolean
1779+
*/
1780+
public function rest_validate_date_query( $value, $request, $param ) {
1781+
if ( 'date_query' !== $param ) {
1782+
return new WP_Error( 'date-query-not-validated', __( 'You are validating a date query on a non date query parameter.' ), array( 'status' => 400 ) );
1783+
}
1784+
1785+
if ( empty( $value ) ) {
1786+
return new WP_Error( 'empty-date-query', __( 'Date query parameter is empty. Please revise your request.' ), array( 'status' => 400 ) );
1787+
}
1788+
1789+
if ( ! is_array( $value ) ) {
1790+
return new WP_Error( 'date-query-improperly-formatted', __( 'Date query must be formatted as an array of array(s).' ), array( 'status' => 400 ) );
1791+
}
1792+
1793+
$valid = true;
1794+
foreach ( $value as $value ) {
1795+
/*
1796+
* Validate 'before' and 'after' up front, then let the
1797+
* validation routine continue to be sure that all invalid
1798+
* values generate errors too.
1799+
*/
1800+
if ( array_key_exists( 'before', $value ) && is_array( $value['before'] ) ) {
1801+
$valid = $this->rest_validate_date_query( $value['before'], $request, $param );
1802+
}
1803+
1804+
if ( array_key_exists( 'after', $value ) && is_array( $value['after'] ) ) {
1805+
$valid = $this->rest_validate_date_query( $value['after'], $request, $param );
1806+
}
1807+
1808+
// Array containing all min-max checks.
1809+
$min_max_checks = array();
1810+
1811+
// Days per year.
1812+
if ( array_key_exists( 'year', $value ) ) {
1813+
if ( ! is_numeric( $value['year'] ) ) {
1814+
return new WP_Error( 'year-type-error', __( 'Year must be an integer representing 4 digit year.' ), array( 'status' => 400 ) );
1815+
}
1816+
/*
1817+
* If a year exists in the date query, we can use it to get the days.
1818+
* If multiple years are provided (as in a BETWEEN), use the first one.
1819+
*/
1820+
if ( is_array( $value['year'] ) ) {
1821+
$_year = reset( $value['year'] );
1822+
} else {
1823+
$_year = $value['year'];
1824+
}
1825+
1826+
$max_days_of_year = date( 'z', mktime( 0, 0, 0, 12, 31, $_year ) ) + 1;
1827+
} else {
1828+
// otherwise we use the max of 366 (leap-year)
1829+
$max_days_of_year = 366;
1830+
}
1831+
1832+
$min_max_checks['dayofyear'] = array(
1833+
'min' => 1,
1834+
'max' => $max_days_of_year,
1835+
);
1836+
1837+
// Days per week.
1838+
$min_max_checks['dayofweek'] = array(
1839+
'min' => 1,
1840+
'max' => 7,
1841+
);
1842+
1843+
// Days per week.
1844+
$min_max_checks['dayofweek_iso'] = array(
1845+
'min' => 1,
1846+
'max' => 7,
1847+
);
1848+
1849+
// Months per year.
1850+
$min_max_checks['month'] = array(
1851+
'min' => 1,
1852+
'max' => 12,
1853+
);
1854+
1855+
// Weeks per year.
1856+
if ( isset( $_year ) ) {
1857+
/*
1858+
* If we have a specific year, use it to calculate number of weeks.
1859+
* Note: the number of weeks in a year is the date in which Dec 28 appears.
1860+
*/
1861+
$week_count = date( 'W', mktime( 0, 0, 0, 12, 28, $_year ) );
1862+
1863+
} else {
1864+
// Otherwise set the week-count to a maximum of 53.
1865+
$week_count = 53;
1866+
}
1867+
1868+
$min_max_checks['week'] = array(
1869+
'min' => 1,
1870+
'max' => $week_count,
1871+
);
1872+
1873+
// Days per month.
1874+
$min_max_checks['day'] = array(
1875+
'min' => 1,
1876+
'max' => 31,
1877+
);
1878+
1879+
// Hours per day.
1880+
$min_max_checks['hour'] = array(
1881+
'min' => 0,
1882+
'max' => 23,
1883+
);
1884+
1885+
// Minutes per hour.
1886+
$min_max_checks['minute'] = array(
1887+
'min' => 0,
1888+
'max' => 59,
1889+
);
1890+
1891+
// Seconds per minute.
1892+
$min_max_checks['second'] = array(
1893+
'min' => 0,
1894+
'max' => 59,
1895+
);
1896+
1897+
// Concatenate and throw a notice for each invalid value.
1898+
foreach ( $min_max_checks as $key => $check ) {
1899+
if ( ! array_key_exists( $key, $value ) ) {
1900+
continue;
1901+
}
1902+
1903+
// Throw a notice for each failing value.
1904+
foreach ( (array) $value[ $key ] as $_value ) {
1905+
$is_between = $_value >= $check['min'] && $_value <= $check['max'];
1906+
1907+
if ( ! is_numeric( $_value ) || ! $is_between ) {
1908+
$error = sprintf(
1909+
/* translators: Date query invalid date message: 1: invalid value, 2: type of value, 3: minimum valid value, 4: maximum valid value */
1910+
__( 'Invalid value %1$s for %2$s. Expected value should be between %3$s and %4$s.' ),
1911+
esc_html( $_value ),
1912+
esc_html( $key ),
1913+
esc_html( $check['min'] ),
1914+
esc_html( $check['max'] )
1915+
);
1916+
1917+
return new WP_Error( 'invalid-date-values', $error, array( 'status' => 400 ) );
1918+
1919+
$valid = false;
1920+
}
1921+
}
1922+
}
1923+
1924+
// If we already have invalid date messages, don't bother running through checkdate().
1925+
if ( ! $valid ) {
1926+
return $valid;
1927+
}
1928+
1929+
$day_month_year_error_msg = '';
1930+
1931+
$day_exists = array_key_exists( 'day', $value ) && is_numeric( $value['day'] );
1932+
$month_exists = array_key_exists( 'month', $value ) && is_numeric( $value['month'] );
1933+
$year_exists = array_key_exists( 'year', $value ) && is_numeric( $value['year'] );
1934+
1935+
if ( $day_exists && $month_exists && $year_exists ) {
1936+
// 1. Checking day, month, year combination.
1937+
if ( ! wp_checkdate( $value['month'], $value['day'], $value['year'], sprintf( '%s-%s-%s', $value['year'], $value['month'], $value['day'] ) ) ) {
1938+
/* translators: 1: year, 2: month, 3: day of month */
1939+
$day_month_year_error_msg = sprintf(
1940+
__( 'The following values do not describe a valid date: year %1$s, month %2$s, day %3$s.' ),
1941+
esc_html( $value['year'] ),
1942+
esc_html( $value['month'] ),
1943+
esc_html( $value['day'] )
1944+
);
1945+
1946+
$valid = false;
1947+
}
1948+
} elseif ( $day_exists && $month_exists ) {
1949+
/*
1950+
* 2. checking day, month combination
1951+
* We use 2012 because, as a leap year, it's the most permissive.
1952+
*/
1953+
if ( ! wp_checkdate( $value['month'], $value['day'], 2012, sprintf( '2012-%s-%s', $value['month'], $value['day'] ) ) ) {
1954+
/* translators: 1: month, 2: day of month */
1955+
$day_month_year_error_msg = sprintf(
1956+
__( 'The following values do not describe a valid date: month %1$s, day %2$s.' ),
1957+
esc_html( $value['month'] ),
1958+
esc_html( $value['day'] )
1959+
);
1960+
1961+
$valid = false;
1962+
}
1963+
}
1964+
1965+
if ( ! empty( $day_month_year_error_msg ) ) {
1966+
return new WP_Error( 'invalid-date', $day_month_year_error_msg, array( 'status', 400 ) );
1967+
}
1968+
}
1969+
return $valid;
1970+
}
17661971
}

0 commit comments

Comments
 (0)
0