Standardizing the validation of BIGINT integers used in the ID column of wp_posts & adding filters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** see attached meta.php for usage sample **/ | |
function is_valid_id( $id ) { | |
// added for a fallback if filter_var is disabled for php build | |
if ( function_exists( 'filter_var' ) ) { | |
$default_opts = array( | |
'options' => array( | |
'default' => false, // value to return if the filter fails | |
'min_range' => 1, | |
'max_range' => PHP_INT_MAX | |
) | |
); | |
apply_filters( ‘is_valid_id_options’, $default_opts ); // alternate filter name: 'filter_var_args' | |
// will cast as an int OR return false if value is not an (int) | |
$checked = filter_var( $id, FILTER_VALIDATE_INT, $default_opts ); | |
} else { | |
// if the id is a collection of only digits, not zero & not starting w/ zero | |
if ( preg_match( '/^[^0][0-9]+$/', $id, $matches ) ) { | |
// set checked val (could typecast as (int) if you want but be aware of BIGINT issues or (float) ). | |
$checked = $matches[0]; | |
} else { | |
// set checked as falsey (or throw exception ) | |
$checked = false; | |
} | |
} | |
return apply_filters( 'is_valid_id', $checked, $id ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* My custom ID validator and sanitizer filter | |
**/ | |
add_filter( 'is_valid_id', 'my_custom_id_validator', 2 ); | |
/** | |
* @param $checked_id The 'ID' that was passed through the validator | |
* @param $id The original value that was passed to the validator | |
* | |
* @return mixed | |
*/ | |
function my_custom_id_validator( $checked_id, $id ) { | |
// if the original validation failed | |
if ( $checked_id === false ) { | |
// check if it's a remote object | |
$remote_type = get_option( 'remote_type' ); // 'REMOTE' | |
preg_match( '/^(\d+)\-(' . $remote_type . ')$/', $id, $matches ); // /^\d+\-('REMOTE')$/ | |
// if our regex returns a match, the ID is good! | |
if ( preg_match( '/^(\d+)\-(' . $remote_type . ')$/', $id, $matches ) ) { | |
/** Note: here you can validate or cast the parts you regexed for as well. | |
* even though the ID passed failed the original check, it was what the developer | |
* intended, thus we will re-set the passed value as the proper ID and return it | |
**/ | |
$checked_id = $id; | |
} | |
} | |
return $checked_id; | |
} | |
/** | |
* The above will add a secondary level of validation to allow non-standard ID numbers to be passed. | |
* I have even considered turning the ID into an array of array('ID' => '1234', 'remote_type' => $matches[1] ) | |
* this would avoid the regex I am currently doing in the WP_Post::get_instance and instead let me just check | |
* if is_array($post_id) | |
* | |
* Alternatively, I have also considered running the wp_cache_get() function from the within this filter and simply | |
* returning the post object right then and there, avoiding entirely that additional function call. | |
**/ | |
/** | |
* getting the remote content and setting as a post. | |
* this is the true use case scenario for allowing | |
* alternative ID formats | |
**/ | |
add_action( 'posts_pre_query', 'get_external_objects_as_posts' ); | |
/** | |
* @param $wp_query | |
* | |
* @return array|null | |
*/ | |
function get_external_objects_as_posts( $wp_query ) { | |
/** | |
* example of a manually created WP_Post with remote post object | |
* Content grabbed from some external API and formatted | |
**/ | |
$result = wp_remote_request( $endpoint, $args ); | |
if ( ! empty( $result ) ) { | |
$external_post = json_decode( $result['body'] ); | |
$external_ID = $external_post->id . '-REMOTE'; | |
$post = new WP_Post( (object) [ | |
'ID' => $external_ID, | |
'post_title' => $external_post->title, | |
'post_excerpt' => $external_post->excerpt, | |
'post_content' => $external_post->content, | |
'post_author' => $external_post->author_id, | |
'post_name' => $external_post->slug, | |
'post_date' => $external_post->date, | |
'post_date_gmt' => $external_post->date_gmt, | |
'post_modified_gmt' => $external_post->date_gmt, | |
'post_modified' => $external_post->date, | |
'post_type' => $external_post->post_type, | |
'filter' => 'raw', | |
'remote_post' => true, | |
'comment_status' => $external_post->comment_status, | |
] ); | |
// record the object in the cache | |
wp_cache_add( $external_ID, $post, 'posts', 0 ); | |
// map extra data from API to custom field key | |
$post_meta = array(); | |
$post_meta['featured_article'] = $external_post->is_featured_article; | |
// record the meta in the cache | |
wp_cache_add( $external_ID, $post_meta, 'post_meta' ); | |
$the_external_posts = array( $post ); | |
// set the WP_Query 'posts' property w/ our newly created WP_Post object | |
$wp_query->set( 'posts', $the_external_posts ); | |
return $the_external_posts; | |
} else{ | |
return null; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* The template for displaying all single posts. | |
* | |
* @package mofflygroup | |
*/ | |
get_header(); ?> | |
<div id="primary" class="content-area"> | |
<main id="main" class="site-main" role="main"> | |
<?php while ( have_posts() ) : the_post(); | |
$is_featured = get_post_meta( the_ID(), 'featured_article', true ); ?> | |
if ( $is_featured ) { | |
echo 'Featured Article!'; | |
} | |
get_template_part( 'content', 'single' ); ?> | |
<?php mofflygroup_post_nav(); ?> | |
<?php endwhile; // end of the loop. ?> | |
</main><!-- #main --> | |
</div><!-- #primary --> | |
<?php get_sidebar(); ?> | |
<?php get_footer(); ?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Implementation of new sanitizer/validator for ID numbers in queries | |
* using the get_meta_data function. | |
**/ | |
function get_meta_data( $meta_type, $object_id, $meta_key = '', $single = false ) { | |
if ( ! $meta_type ) { | |
return false; | |
} | |
/** REMOVED/REPLACED if ( ! is_numeric($object_id) ) **/ | |
if ( ! is_valid_id( $object_id ) ) { | |
return false; | |
} | |
$check = apply_filters( "get_{$meta_type}_metadata", null, $object_id, $meta_key, $single ); | |
if ( null !== $check ) { | |
if ( $single && is_array( $check ) ) { | |
return $check[0]; | |
} else { | |
return $check; | |
} | |
} | |
// this will now grab the record we added earlier on line 37 of 1_create_post_object.php | |
// previously it would not passed the is_numeric() check | |
$meta_cache = wp_cache_get( $object_id, $meta_type . '_meta' ); | |
if ( ! $meta_cache ) { | |
$meta_cache = update_meta_cache( $meta_type, array( $object_id ) ); | |
$meta_cache = $meta_cache[ $object_id ]; | |
} | |
if ( ! $meta_key ) { | |
return $meta_cache; | |
} | |
if ( isset( $meta_cache[ $meta_key ] ) ) { | |
if ( $single ) { | |
return maybe_unserialize( $meta_cache[ $meta_key ][0] ); | |
} else { | |
return array_map( 'maybe_unserialize', $meta_cache[ $meta_key ] ); | |
} | |
} | |
if ( $single ) { | |
return ''; | |
} else { | |
return array(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment