custom permalink/shortlink with base62 encoded post ID

I want my post-links to look like /%post_id62%_%postname%/ where %post_id62% is the post ID in base62 encoding (e.g. http://example.com/y9Rlf_test/) and my shortlinks to consist of /%post_id62% only (e.g. http://example.com/y9Rlf). This is what i’ve got so far (in my functions.php) which obviously doesn’t work:

add_action( 'init', 'yoyo_init' );
function yoyo_init() {
  add_rewrite_tag( '%post_id62%', '([A-Za-z0-9]+)' );
}

add_action('generate_rewrite_rules','yoyo_generate_rewrite_rules');
function yoyo_generate_rewrite_rules($yoyo_rewrite) {
  global $wp_rewrite;
  $new_rules['/([0-9A-Za-z]+)/?$'] = 'index.php?p=' .
    base622dec($wp_rewrite->preg_index(1));  // DOESNT WORK OF COURSE (executed only once when rewrite_rules are generated)
  $wp_rewrite->rules = array_merge($new_rules, $wp_rewrite->rules);
}

add_filter( 'post_link', 'yoyo_post_link', 10, 2 );
function yoyo_post_link( $permalink, $post ) {
  if ( false !== strpos( $permalink, '%post_id62%' ) ) {
    $post_id62 = dec2base62( $post->ID );
    $permalink = str_replace( '%post_id62%', $post_id62, $permalink );
  }
  return $permalink;
}

add_filter('pre_get_shortlink', 'yoyo_custom_shortlink', 10, 4);
function yoyo_custom_shortlink($false, $post_id, $context, $allow_slugs) {
  global $wp_query;
  if($post_id or ($context == 'query' and $post_id = $wp_query->get_queried_object_id())) {
    return home_url('/' . dec2base62($post_id));
  }
  return false;
}

function dec2base62($number) {
  $digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';  $string = '';  while($number > 0) { $string = $digits[$number % 62] . $string;  $number = floor($number / 62); }
  return $string;
}
function base622dec($string) {
  $digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';  $number = 0;  while(strlen($string) and ($number *= 62 or strlen($string))) { $number += strpos($digits, $string[0]);  $string  = substr($string, 1); }
  return $number;
}

Any idea how to tackle the problem?
Bonus points for avoiding the -N slug suffix when there’s another post with the same title created (which might be avoided actually when setting the link structure to “/%post_id62%_%postname%/” but in order to have the shortlink functionality it should be “/%post_id62%” I guess; cf. Turn off %postname% auto-incrementing?)!
Thanks!

Related posts

Leave a Reply

1 comment

  1. You only made a mistake in the final step, when you convert the base62-encoded ID back to a decimal ID. You should not do this by changing the rewrite rules, as they are only patterns, not actual rules per post.

    What you want is take an incoming URL, let WordPress parse it using the rewrite rules, but then before it is passed to the query engine, check for the post_id62 variable and set the actual p (post id) variable based on it. You can do this in the request filter, which is applied in WP::parse_request().

    add_action( 'request', 'wpse22253_request' );
    function wpse22253_request( $query_vars )
    {
        if ( array_key_exists( 'post_id62', $query_vars ) ) {
            $query_vars['p'] = base622dec( $query_vars['post_id62'] );
        }
        return $query_vars;
    }
    

    However, this will not solve the postname-N problem. WordPress will still call wp_unique_post_slug on the post_name field, and post_name will not include the post_id62 part (since the database is independent of the rewrite rules). As Mike suggested in the followup comments to his answer, you should either figure out a way to include the post_id62 part in the post_name field, or (and this might be the better way) ignore the %postname% part in the URL and just slap a postname-with-stripped-number-suffix string at the end.

    You will also run into issues with your shortlinks, since they have the same format (any letter or digit) as a page. Is /banana/ a page with the title “Banana” or a shortlink to the post with ID 34440682410 – which is banana in your base-62 encoding?