diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist
index 7dfcb692f269238decac45ae18a1e6a510736005..6555ad3378423a8fc1a3f1466bb3ee1848986b61 100644
--- a/.phpcs.xml.dist
+++ b/.phpcs.xml.dist
@@ -26,7 +26,7 @@
 	<!-- Rules: WordPress Coding Standards -->
 	<!-- https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards -->
 	<!-- https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/wiki/Customizable-sniff-properties -->
-	<config name="minimum_supported_wp_version" value="3.2.1"/>
+	<config name="minimum_supported_wp_version" value="4.4"/>
 	<rule ref="WordPress" />
 	<rule ref="WordPressVIPMinimum" />
 	<rule ref="WordPress-VIP-Go" />
diff --git a/eth-simple-shortlinks.php b/eth-simple-shortlinks.php
new file mode 100644
index 0000000000000000000000000000000000000000..e0894c591ca7a760532b5208349e40faf97b101e
--- /dev/null
+++ b/eth-simple-shortlinks.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Load ETH Simple Shortlinks.
+ *
+ * @package ETH_Simple_Shortlinks
+ */
+
+/**
+ * Plugin Name: ETH Simple Shortlinks
+ * Plugin URI: https://ethitter.com/plugins/
+ * Description: Simple non-GET shortlinks using post IDs
+ * Author: Erick Hitter
+ * Version: 0.5
+ * Author URI: https://ethitter.com/
+ * Text Domain: eth_simple_shortlinks
+ * Domain Path: /languages/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+require_once __DIR__ . '/inc/class-eth-simple-shortlinks.php';
diff --git a/inc/class-eth-simple-shortlinks.php b/inc/class-eth-simple-shortlinks.php
index 381f07d0915d3884d4b50ff4aca5348c4495181a..85498530f4ff1e2a0d97699f68cc7400c6038376 100644
--- a/inc/class-eth-simple-shortlinks.php
+++ b/inc/class-eth-simple-shortlinks.php
@@ -1,29 +1,13 @@
 <?php
-/*
-Plugin Name: ETH Simple Shortlinks
-Plugin URI: https://ethitter.com/plugins/
-Description: Simple non-GET shortlinks using post IDs
-Author: Erick Hitter
-Version: 0.5
-Author URI: https://ethitter.com/
-Text Domain: eth_simple_shortlinks
-Domain Path: /languages/
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-*/
+/**
+ * Plugin functionality.
+ *
+ * @package ETH_Simple_Shortlinks
+ */
 
+/**
+ * Class ETH_Simple_Shortlinks.
+ */
 class ETH_Simple_Shortlinks {
 	/**
 	 * PLUGIN SETUP
@@ -31,6 +15,8 @@ class ETH_Simple_Shortlinks {
 
 	/**
 	 * Singleton
+	 *
+	 * @var self
 	 */
 	private static $instance = null;
 
@@ -39,115 +25,204 @@ class ETH_Simple_Shortlinks {
 	 */
 	public static function get_instance() {
 		if ( ! is_a( self::$instance, __CLASS__ ) ) {
-			self::$instance = new self;
+			self::$instance = new self();
 		}
 
 		return self::$instance;
 	}
 
 	/**
-	 * Dummy magic methods
+	 * Dummy magic method
 	 */
-	public function __clone() { _doing_it_wrong( __FUNCTION__, __( 'Cheatin&#8217; uh?', 'eth_simple_shortlinks' ), '0.1' ); }
-	public function __wakeup() { _doing_it_wrong( __FUNCTION__, __( 'Cheatin&#8217; uh?', 'eth_simple_shortlinks' ), '0.1' ); }
-	public function __call( $name = '', $args = array() ) { unset( $name, $args ); return null; }
+	public function __clone() {
+		_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; uh?', 'eth_simple_shortlinks' ), '0.1' );
+	}
 
 	/**
-	 * Class properties
+	 * Dummy magic method
 	 */
-	private $name          = 'ETH Simple Shortlinks';
-	private $slug          = 'p';
-	private $rewrite_rule  = null;
-	private $rewrite_match = null;
-	private $qv            = 'eth-shortlink';
+	public function __wakeup() {
+		_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; uh?', 'eth_simple_shortlinks' ), '0.1' );
+	}
 
+	/**
+	 * Dummy magic method
+	 *
+	 * @param string $name Method name.
+	 * @param array  $args Method arguments.
+	 * @return null
+	 */
+	public function __call( $name = '', $args = array() ) {
+		return null;
+	}
+
+	/**
+	 * Plugin name.
+	 *
+	 * @var string
+	 */
+	private $name = 'ETH Simple Shortlinks';
+
+	/**
+	 * Rewrite slug.
+	 *
+	 * @var string
+	 */
+	private $slug = 'p';
+
+	/**
+	 * Rewrite rule.
+	 *
+	 * @var string
+	 */
+	private $rewrite_rule;
+
+	/**
+	 * Rewrite rule match.
+	 *
+	 * @var string
+	 */
+	private $rewrite_match;
+
+	/**
+	 * Query variable.
+	 *
+	 * @var string
+	 */
+	private $qv = 'eth-shortlink';
+
+	/**
+	 * Is environment ready?
+	 *
+	 * @var bool
+	 */
 	private $plugin_supported = false;
 
-	private $supported_post_types    = array();
+	/**
+	 * Post types supported by the plugin.
+	 *
+	 * @var array
+	 */
+	private $supported_post_types = array();
+
+	/**
+	 * Post statuses allowed to redirect to.
+	 *
+	 * @var array
+	 */
 	private $supported_post_statuses = array();
 
 	/**
-	 * Register plugin's setup action
+	 * Register plugin's setup action.
 	 */
 	private function __construct() {
-		// Build rewrite parts using other class properties
+		// Build rewrite parts using other class properties.
 		$this->rewrite_rule  = '^' . $this->slug . '/([\d]+)/?$';
 		$this->rewrite_match = 'index.php?p=$matches[1]&' . $this->qv . '=1';
 
-		// Basic plugin actions
+		// Basic plugin actions.
 		add_action( 'plugins_loaded', array( $this, 'action_plugins_loaded' ) );
 		add_action( 'init', array( $this, 'action_init' ) );
 	}
 
 	/**
-	 * Load plugin translations
+	 * Load plugin translations.
 	 */
 	public function action_plugins_loaded() {
-		load_plugin_textdomain( 'eth_simple_shortlinks', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
+		load_plugin_textdomain(
+			'eth_simple_shortlinks',
+			false,
+			dirname( plugin_basename( __FILE__ ) ) . '/languages/'
+		);
 	}
 
 	/**
-	 * Verify plugin is supported, then register its functionality
+	 * Verify plugin is supported, then register its functionality.
 	 */
 	public function action_init() {
 		global $wp_rewrite;
 
-		// Plugin won't work if site doesn't use pretty permalinks
+		// Plugin won't work if site doesn't use pretty permalinks.
 		if ( empty( $wp_rewrite->permalink_structure ) ) {
 			add_action( 'admin_notices', array( $this, 'action_add_admin_notices' ) );
-		} else {
-			$this->plugin_supported = true;
+			return;
+		}
 
-			// Admin notices
-			add_action( 'admin_notices', array( $this, 'action_add_admin_notices' ) );
+		$this->plugin_supported = true;
 
-			// Register rewrite rule
-			add_rewrite_rule( $this->rewrite_rule, $this->rewrite_match, 'top' );
+		// Admin notices.
+		add_action( 'admin_notices', array( $this, 'action_add_admin_notices' ) );
 
-			// Request handling
-			add_action( 'wp_loaded', array( $this, 'filter_support' ) );
-			add_filter( 'query_vars', array( $this, 'filter_query_vars' ) );
-			add_action( 'parse_request', array( $this, 'action_parse_request' ) );
+		// Register rewrite rule.
+		add_rewrite_rule( $this->rewrite_rule, $this->rewrite_match, 'top' );
 
-			// Shortlink overrides
-			add_filter( 'get_shortlink', array( $this, 'filter_get_shortlink' ), 10, 2 );
-			add_action( 'admin_head-edit.php', array( $this, 'add_admin_header_assets' ) );
-			add_filter( 'post_row_actions', array( $this, 'filter_row_actions' ), 10, 2 );
-			add_filter( 'page_row_actions', array( $this, 'filter_row_actions' ), 10, 2 );
-		}
+		// Request handling.
+		add_action( 'wp_loaded', array( $this, 'filter_support' ) );
+		add_filter( 'query_vars', array( $this, 'filter_query_vars' ) );
+		add_action( 'parse_request', array( $this, 'action_parse_request' ) );
+
+		// Shortlink overrides.
+		add_filter( 'get_shortlink', array( $this, 'filter_get_shortlink' ), 10, 2 );
+		add_action( 'admin_head-edit.php', array( $this, 'add_admin_header_assets' ) );
+		add_filter( 'post_row_actions', array( $this, 'filter_row_actions' ), 10, 2 );
+		add_filter( 'page_row_actions', array( $this, 'filter_row_actions' ), 10, 2 );
 	}
 
 	/**
-	 * Display admin notices if plugin's requirements aren't met
+	 * Display admin notices if plugin's requirements aren't met.
 	 */
 	public function action_add_admin_notices() {
-		// Notices are only relevant if current user can get to the Permalinks and Plugins options screens
-		if ( ! current_user_can( 'manage_options') || ! current_user_can( 'activate_plugins' ) ) {
+		// Notices are only relevant if current user can get to the Permalinks and Plugins options screens.
+		if ( ! current_user_can( 'manage_options' ) || ! current_user_can( 'activate_plugins' ) ) {
 			return;
 		}
 
-		// Build notices
+		// Build notices.
 		$message = '';
 
 		if ( $this->plugin_supported ) {
-			// Check option for the plugin's rule
+			// Check option for the plugin's rule.
 			// The `$wp_rewrite` global will include it in `extra_rules_top` even though it hasn't been saved to the DB, and therefore isn't really active.
 			$rewrites = get_option( 'rewrite_rules' );
 
 			if ( is_array( $rewrites ) && ! array_key_exists( $this->rewrite_rule, $rewrites ) ) {
-				$message = sprintf( __( 'Please visit the <a href="%1$s">Permalinks</a> settings page to refresh your permalinks. Doing so will add the rules this plugin requires.', 'eth_simple_shortlinks' ), admin_url( 'options-permalink.php' ) );
+				$message = sprintf(
+					/* translators: 1: URL of permalink options page. */
+					__(
+						'Please visit the <a href="%1$s">Permalinks</a> settings page to refresh your permalinks. Doing so will add the rules this plugin requires.',
+						'eth_simple_shortlinks'
+					),
+					admin_url( 'options-permalink.php' )
+				);
 			}
 		} else {
-			$message = sprintf( __( 'Please enable <a href="%1$s">pretty permalinks</a>, otherwise disable this plugin as it is not compatible with "Plain" permalinks.', 'eth_simple_shortlinks' ), admin_url( 'options-permalink.php' ) );
+			$message = sprintf(
+				/* translators: 1: URL of permalink options page. */
+				__(
+					'Please enable <a href="%1$s">pretty permalinks</a>, otherwise disable this plugin as it is not compatible with "Plain" permalinks.',
+					'eth_simple_shortlinks'
+				),
+				admin_url( 'options-permalink.php' )
+			);
 		}
 
-		// Display a notice if one exists
+		// Display a notice if one exists.
 		if ( ! empty( $message ) ) {
-			$message = sprintf( __( '<strong>%1$s</strong>: %2$s', 'eth_simple_shortlinks' ), $this->name, $message );
-
-			?><div class="error">
-				<p><?php echo $message; ?></p>
-			</div><?php
+			$message = sprintf(
+				/* translators: 1: Plugin name, 2: Notice text. */
+				__(
+					'<strong>%1$s</strong>: %2$s',
+					'eth_simple_shortlinks'
+				),
+				$this->name,
+				$message
+			);
+
+			?>
+			<div class="error">
+				<p><?php echo wp_kses_post( $message ); ?></p>
+			</div>
+			<?php
 		}
 	}
 
@@ -156,15 +231,29 @@ class ETH_Simple_Shortlinks {
 	 */
 
 	/**
-	 * Allow filtering of supported statuses and types
+	 * Allow filtering of supported statuses and types.
 	 */
 	public function filter_support() {
+		/**
+		 * Filters supported post statuses.
+		 *
+		 * @param array $statuses Supported statuses.
+		 */
 		$this->supported_post_statuses = apply_filters( 'eth_simple_shortlinks_allowed_post_statuses', array( 'publish', 'future' ) );
-		$this->supported_post_types    = apply_filters( 'eth_simple_shortlinks_allowed_post_types',    array( 'post', 'page' ) );
+
+		/**
+		 * Filters supported post types.
+		 *
+		 * @param array $types Post types.
+		 */
+		$this->supported_post_types = apply_filters( 'eth_simple_shortlinks_allowed_post_types', array( 'post', 'page' ) );
 	}
 
 	/**
-	 * Add custom query var to those permitted, so it can be detected at `parse_request`
+	 * Add custom query var to those permitted, so it can be detected at `parse_request`.
+	 *
+	 * @param array $qv Registered query vars.
+	 * @return array
 	 */
 	public function filter_query_vars( $qv ) {
 		$qv[] = $this->qv;
@@ -173,25 +262,50 @@ class ETH_Simple_Shortlinks {
 	}
 
 	/**
-	 * Catch this plugin's requests and issue redirects, otherwise WP will serve content at duplicate URLs
+	 * Catch this plugin's requests and issue redirects, otherwise WP
+	 * will serve content at duplicate URLs.
 	 *
-	 * Allows invalid post IDs fall through to WP's 404 handler, or anything else that might intercede
+	 * Allows invalid post IDs fall through to WP's 404 handler, or
+	 * anything else that might intercede.
 	 *
-	 * URLs aren't validated in case plugins filter permalinks to point to external URLs
+	 * @param WP $request WP object.
 	 */
 	public function action_parse_request( $request ) {
 		if ( isset( $request->query_vars[ $this->qv ] ) ) {
-			$dest = get_permalink( $request->query_vars[ 'p' ] );
+			$dest = get_permalink( $request->query_vars['p'] );
 
 			if ( $dest ) {
-				wp_redirect( $dest, 301 );
+				/**
+				 * Filters the redirect URL.
+				 *
+				 * @param string $dest    Redirect destination.
+				 * @param WP    $request WP object.
+				 */
+				$dest = apply_filters( 'eth_simple_shortlinks_redirect_url', $dest, $request );
+
+				/**
+				 * Filters the redirect status code.
+				 *
+				 * @param int    $status_code Redirect status code.
+				 * @param string $dest        Redirect destination.
+				 * @param WP     $request     WP object.
+				 */
+				$status_code = (int) apply_filters( 'eth_simple_shortlinks_redirect_status', 301, $dest, $request );
+
+				// URLs aren't validated in case plugins filter permalinks to point to external URLs.
+				// phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
+				wp_redirect( $dest, $status_code );
 				exit;
 			}
 		}
 	}
 
 	/**
-	 * Override shortlinks with this plugin's format
+	 * Override shortlinks with this plugin's format.
+	 *
+	 * @param string $shortlink Short URL.
+	 * @param int    $id        Post ID.
+	 * @return string
 	 */
 	public function filter_get_shortlink( $shortlink, $id ) {
 		if ( empty( $id ) ) {
@@ -218,7 +332,7 @@ class ETH_Simple_Shortlinks {
 	}
 
 	/**
-	 * Header assets for shortlink in row actions
+	 * Header assets for shortlink in row actions.
 	 */
 	public function add_admin_header_assets() {
 		global $typenow;
@@ -241,7 +355,11 @@ class ETH_Simple_Shortlinks {
 	}
 
 	/**
-	 * Provide the shortlink in row actions for easy access
+	 * Provide the shortlink in row actions for easy access.
+	 *
+	 * @param array   $actions Row actions.
+	 * @param WP_Post $post Post object.
+	 * @return array
 	 */
 	public function filter_row_actions( $actions, $post ) {
 		if ( ! $this->is_supported_post_type( get_post_type( $post ) ) || ! $this->is_supported_post_status( get_post_status( $post ) ) ) {
@@ -254,24 +372,33 @@ class ETH_Simple_Shortlinks {
 	}
 
 	/**
-	 * Check if given post type is supported
+	 * Check if given post type is supported.
+	 *
+	 * @param string $type Post type.
+	 * @return bool
 	 */
 	private function is_supported_post_type( $type ) {
-		return in_array( $type, $this->supported_post_types );
+		return in_array( $type, $this->supported_post_types, true );
 	}
 
 	/**
-	 * Check if given post status is supported
+	 * Check if given post status is supported.
+	 *
+	 * @param string $status Post status.
+	 * @return bool
 	 */
 	private function is_supported_post_status( $status ) {
-		return in_array( $status, $this->supported_post_statuses );
+		return in_array( $status, $this->supported_post_statuses, true );
 	}
 
 	/**
-	 * Utility method for building permlink
+	 * Utility method for building permalink.
+	 *
+	 * @param int $post_id Post ID.
+	 * @return string
 	 */
 	public function get_shortlink( $post_id ) {
-		// Use Core's default when this plugin can't build a link
+		// Use Core's default when this plugin can't build a link.
 		if ( ! $this->plugin_supported ) {
 			return wp_get_shortlink( $post_id );
 		}
@@ -286,11 +413,24 @@ class ETH_Simple_Shortlinks {
 ETH_Simple_Shortlinks::get_instance();
 
 /**
- * Shortcut for using the shortlink outside of this plugin's considerations
+ * Shortcut for using the shortlink outside of this plugin's considerations.
+ *
+ * @param int $post_id Post ID.
+ * @return string
  */
 function eth_simple_shortlinks_get( $post_id ) {
 	if ( ! did_action( 'wp_loaded' ) ) {
-		_doing_it_wrong( __FUNCTION__, __( 'Shortlinks cannot be generated until after <code>wp_loaded</code>; this ensures that all post types are registered.', 'eth_simple_shortlinks' ), '0.3' );
+		_doing_it_wrong(
+			__FUNCTION__,
+			wp_kses_post(
+				__(
+					'Shortlinks cannot be generated until after <code>wp_loaded</code>; this ensures that all post types are registered.',
+					'eth_simple_shortlinks'
+				)
+			),
+			'0.3'
+		);
+
 		return false;
 	}