Skip to content

Instantly share code, notes, and snippets.

@LinzardMac
Last active May 30, 2017 01:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LinzardMac/38bbe22feb0b0a3fbabfcf64d797cd80 to your computer and use it in GitHub Desktop.
Save LinzardMac/38bbe22feb0b0a3fbabfcf64d797cd80 to your computer and use it in GitHub Desktop.
Standardizing the validation of BIGINT integers used in the ID column of wp_posts & adding filters
<?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 );
}
<?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;
}
}
<?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(); ?>
<?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