Last active
November 1, 2024 17:35
-
-
Save kagg-design/241fdef594c326a33423ff74f6bd6115 to your computer and use it in GitHub Desktop.
A class to hide downloadable links and count downloads.
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 | |
/** | |
* Class to hide file download links and count download number. | |
* | |
* @package kagg-downloader | |
*/ | |
namespace GTS\Quote; | |
/** | |
* Class Downloader. | |
* | |
* Requires the following table in database: | |
* | |
* CREATE TABLE `wp_downloads` ( | |
* `id` bigint(20) UNSIGNED NOT NULL, | |
* `url` varchar(355) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', | |
* `download_link` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', | |
* `count` bigint(20) UNSIGNED NOT NULL DEFAULT '0' | |
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | |
*/ | |
class Downloader { | |
/** | |
* Class table name. | |
*/ | |
private const TABLE = 'downloads'; | |
/** | |
* Class table name. | |
* | |
* @var string $table | |
*/ | |
protected static string $table; | |
/** | |
* Base of download links. | |
* | |
* @var string $link_base | |
*/ | |
protected static string $link_base = '/downloads/'; | |
/** | |
* Downloader constructor. | |
*/ | |
public function __construct() { | |
global $wpdb; | |
self::$table = $wpdb->prefix . self::TABLE; | |
add_action( 'init', [ $this, 'rewrite_download_link' ] ); | |
} | |
/** | |
* Get download link from url. | |
* | |
* @param string $url Download URL. | |
* | |
* @return string | |
*/ | |
public static function get_link( string $url ): string { | |
global $wpdb; | |
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery | |
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching | |
$record = $wpdb->get_row( | |
$wpdb->prepare( "SELECT download_link FROM {$wpdb->prefix}downloads WHERE url = %s", $url ) | |
); | |
// phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching | |
// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery | |
return $record->download_link ?? ''; | |
} | |
/** | |
* Get url from download link. | |
* | |
* @param string $download_link Download link. | |
* | |
* @return string | |
*/ | |
public static function get_url( string $download_link ): string { | |
global $wpdb; | |
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery | |
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching | |
$record = $wpdb->get_row( | |
$wpdb->prepare( "SELECT url FROM {$wpdb->prefix}downloads WHERE download_link = %s", $download_link ) | |
); | |
// phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching | |
// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery | |
return $record->url ?? ''; | |
} | |
/** | |
* Get download count. | |
* | |
* @param string $url Download URL. | |
* | |
* @return int | |
*/ | |
public static function get_count( string $url ): int { | |
global $wpdb; | |
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery | |
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching | |
$record = $wpdb->get_row( | |
$wpdb->prepare( "SELECT count FROM {$wpdb->prefix}downloads WHERE url = %s", $url ) | |
); | |
// phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching | |
// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery | |
return $record->count ?? - 1; | |
} | |
/** | |
* Create new download link from $url. | |
* | |
* @param string $url Download URL. | |
* | |
* @return string | |
*/ | |
public static function create( string $url ): string { | |
global $wpdb; | |
$download_link = self::get_link( $url ); | |
if ( $download_link ) { | |
return $download_link; | |
} | |
$download_link = self::$link_base . wp_hash( $url ); | |
$data = [ | |
'url' => $url, | |
'download_link' => $download_link, | |
]; | |
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery | |
$result = $wpdb->insert( self::$table, $data ); | |
// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery | |
return $result ? $download_link : ''; | |
} | |
/** | |
* Rewrite download link by its url. | |
* Increase download count. | |
*/ | |
public function rewrite_download_link(): void { | |
$uri = ''; | |
if ( isset( $_SERVER['REQUEST_URI'] ) ) { | |
$uri = filter_var( wp_unslash( $_SERVER['REQUEST_URI'] ), FILTER_SANITIZE_STRING ); | |
} | |
$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 ) { | |
$this->increment_count( $url ); | |
self::download_file( $url ); | |
} | |
} | |
} | |
/** | |
* Download file. | |
* | |
* @param string $url file url. | |
*/ | |
private static function download_file( string $url ): void { | |
if ( ! $url ) { | |
return; | |
} | |
$file_path = realpath( untrailingslashit( ABSPATH ) . wp_make_link_relative( $url ) ); | |
if ( ! $file_path ) { | |
return; | |
} | |
$file_name = rawurlencode( pathinfo( $file_path, PATHINFO_FILENAME ) ); | |
$file_extension = rawurlencode( pathinfo( $file_path, PATHINFO_EXTENSION ) ); | |
$file_size = filesize( $file_path ); | |
$known_content_types = [ | |
'html' => 'text/html', | |
'htm' => 'text/html', | |
'txt' => 'text/plain', | |
'jpg' => 'image/jpg', | |
'jpeg' => 'image/jpg', | |
'png' => 'image/png', | |
'gif' => 'image/gif', | |
'tiff' => 'image/tiff', | |
'pdf' => 'application/pdf', | |
'doc' => 'application/msword', | |
'docx' => 'application/msword', | |
'xls' => 'application/vnd.ms-excel', | |
'xlsx' => 'application/vnd.ms-excel', | |
'ppt' => 'application/vnd.ms-powerpoint', | |
'pptx' => 'application/vnd.ms-powerpoint', | |
'php' => 'text/plain', | |
'exe' => 'application/octet-stream', | |
'zip' => 'application/zip', | |
]; | |
$content_type = 'application/force-download'; | |
if ( array_key_exists( $file_extension, $known_content_types ) ) { | |
$content_type = $known_content_types[ $file_extension ]; | |
} | |
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-Transfer-Encoding: binary' ); | |
header( "Content-Length: {$file_size}" ); | |
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_readfile | |
readfile( $file_path ); | |
exit(); | |
} | |
/** | |
* Increment count. | |
* | |
* @param string $url Download URL. | |
*/ | |
private function increment_count( string $url ): void { | |
global $wpdb; | |
if ( ! apply_filters( 'downloader_increment_count', true, $url ) ) { | |
return; | |
} | |
$count = self::get_count( $url ); | |
$data = [ 'count' => ++ $count ]; | |
$where = [ 'url' => $url ]; | |
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery | |
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching | |
$wpdb->update( self::$table, $data, $where ); | |
// phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching | |
// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment