How to make download counter and hide link to the file

Code sample to make download counter for hidden links.

How to make download counter and hide link to the file


The question often arises: how to count downloads and hide the real link to the file?

To solve the task we need to two things: intercept a click on the link and send the real file to the user by php.

What happens by the click on the fictitious link, pointing to an non-existent page? WordPress inits own core and tries to show 404 page. At this moment, we need to intervene by our function, update download counter, and send the real file. Here is code of such a function:

/**
 * Rewrite download link by its url.
 * Increase download count.
 */
public function rewrite_download_link() {
	/** @var wpdb $wpdb */
	global $wpdb;

	$uri  = $_SERVER['REQUEST_URI'];
	$path = wp_parse_url( $uri, PHP_URL_PATH );

	if ( 0 === strpos( trailingslashit( $path ), self::$link_base ) ) {
		$download_link = untrailingslashit( $path );
		$url           = self::get_url( $download_link );
		if ( $url ) {
			$count = self::get_count( $url );
			$data  = array(
				'count' => ++ $count,
			);
			$where = array(
				'url' => $url,
			);
			$wpdb->update( self::$table, $data, $where );

			self::download_file( $url );
		}
	}
}

Function rewrite_download_link() works on init event, and checks, if url starts with $link_base. For instance, it could be /downloads/. Any link like http://site.org/downloads/cool.jpg consider as fictitious and is subject to process. Download counter is updated, the real url is calculated, and function to send the real file is called.

Code is below.

/**
 * Download file.
 *
 * @param string $url file url.
 */
private static function download_file( $url ) {
	if ( ! $url ) {
		return;
	}

	$file_path = ABSPATH . wp_make_link_relative( $url );

	$file_name = pathinfo( $file_path, PATHINFO_FILENAME );

	$file_extension = pathinfo( $file_path, PATHINFO_EXTENSION );

	switch ( $file_extension ) {
		case 'png':
			$content_type = 'image/png';
			break;
		case 'gif':
			$content_type = 'image/gif';
			break;
		case 'tiff':
			$content_type = 'image/tiff';
			break;
		case 'jpeg':
		case 'jpg':
			$content_type = 'image/jpg';
			break;
		default:
			$content_type = 'application/force-download';
	}

	header( 'Expires: 0' );
	header( 'Cache-Control: no-cache, no-store, must-revalidate' );
	header( 'Cache-Control: pre-check=0, post-check=0, max-age=0', false );
	header( 'Pragma: no-cache' );
	header( "Content-type: {$content_type}" );
	header( "Content-Disposition:attachment; filename={$file_name}.{$file_extension}" );
	header( 'Content-Type: application/force-download' );

	// @codingStandardsIgnoreLine
	readfile( "{$file_path}" );
	exit();
}

Function download_file() makes link to the file relative and calculates path to the file, and then sends it by php means. Thus, user has no ability to know where is the real file and download it.

The full code of the class, which contains methods discussed above, is available on our GitHub.

 

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.