-
Notifications
You must be signed in to change notification settings - Fork 219
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Co-authored-by: Rémy Perona <remperona@gmail.com>
- Loading branch information
1 parent
a6e21f5
commit 3201ce5
Showing
14 changed files
with
541 additions
and
88 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace WP_Rocket\Engine\Common; | ||
|
||
use RecursiveDirectoryIterator; | ||
use RecursiveIteratorIterator; | ||
use WP_Filesystem_Direct; | ||
|
||
abstract class AbstractFileSystem { | ||
/** | ||
* WP Filesystem instance. | ||
* | ||
* @var WP_Filesystem_Direct | ||
*/ | ||
protected $filesystem; | ||
|
||
/** | ||
* Constructor method. | ||
* Initializes a new instance of the Controller class. | ||
* | ||
* @param WP_Filesystem_Direct $filesystem Filesystem class. | ||
*/ | ||
public function __construct( $filesystem = null ) { | ||
$this->filesystem = $filesystem ?? rocket_direct_filesystem(); | ||
} | ||
|
||
/** | ||
* Write to file. | ||
* | ||
* @param string $file_path File path to store the file. | ||
* @param string $content File content(data). | ||
* | ||
* @return bool | ||
*/ | ||
protected function write_file( string $file_path, string $content ): bool { | ||
return $this->filesystem->put_contents( $file_path, $content, rocket_get_filesystem_perms( 'file' ) ); | ||
} | ||
|
||
/** | ||
* Get the content of a file | ||
* | ||
* @param string $file The file content to get. | ||
* | ||
* @return string | ||
*/ | ||
protected function get_file_content( string $file ): string { | ||
return $this->filesystem->get_contents( $file ); | ||
} | ||
|
||
/** | ||
* Delete file from a directory | ||
* | ||
* @param string $file_path Path to file that would be deleted. | ||
* | ||
* @return bool | ||
*/ | ||
protected function delete_file( string $file_path ): bool { | ||
return $this->filesystem->delete( $file_path, false, 'f' ); | ||
} | ||
|
||
/** | ||
* Checks if the dir path is writable and create dir if it doesn't exist. | ||
* | ||
* @param string $dir_path The directory to check. | ||
* | ||
* @return bool | ||
*/ | ||
protected function is_folder_writable( string $dir_path ): bool { | ||
if ( ! $this->filesystem->exists( $dir_path ) ) { | ||
rocket_mkdir_p( $dir_path ); | ||
} | ||
|
||
return $this->filesystem->is_writable( $dir_path ); | ||
} | ||
|
||
/** | ||
* Deletes all files in a given directory | ||
* | ||
* @param string $dir_path The directory path. | ||
* | ||
* @return void | ||
*/ | ||
public function delete_all_files_from_directory( $dir_path ): void { | ||
try { | ||
$dir = new RecursiveDirectoryIterator( $dir_path, \FilesystemIterator::SKIP_DOTS ); | ||
|
||
$items = new RecursiveIteratorIterator( $dir, RecursiveIteratorIterator::CHILD_FIRST ); | ||
|
||
foreach ( $items as $item ) { | ||
$this->filesystem->delete( $item ); | ||
} | ||
} catch ( \Exception $e ) { | ||
return; | ||
} | ||
} | ||
|
||
/** | ||
* Converts hash to path with filtered number of levels | ||
* | ||
* @since 3.11.4 | ||
* | ||
* @param string $hash md5 hash string. | ||
* | ||
* @return string | ||
*/ | ||
public function hash_to_path( string $hash ): string { | ||
/** | ||
* Filters the number of sub-folders level to create for used CSS storage | ||
* | ||
* @since 3.11.4 | ||
* | ||
* @param int $levels Number of levels. | ||
*/ | ||
$levels = apply_filters( 'rocket_used_css_dir_level', 3 ); | ||
|
||
$base = substr( $hash, 0, $levels ); | ||
$remain = substr( $hash, $levels ); | ||
|
||
$path_array = str_split( $base ); | ||
$path_array[] = $remain; | ||
|
||
return implode( '/', $path_array ); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace WP_Rocket\Engine\Media\Fonts; | ||
|
||
use WP_Filesystem_Direct; | ||
use WP_Rocket\Engine\Common\AbstractFileSystem; | ||
use WP_Rocket\Logger\Logger; | ||
|
||
class Filesystem extends AbstractFileSystem { | ||
/** | ||
* WP Filesystem instance | ||
* | ||
* @var WP_Filesystem_Direct | ||
*/ | ||
protected $filesystem; | ||
|
||
/** | ||
* Path to the fonts storage | ||
* | ||
* @var string | ||
*/ | ||
private $path; | ||
|
||
/** | ||
* Instantiate the class | ||
* | ||
* @param WP_Filesystem_Direct $filesystem WP Filesystem instance. | ||
*/ | ||
public function __construct( $filesystem = null ) { | ||
parent::__construct( is_null( $filesystem ) ? rocket_direct_filesystem() : $filesystem ); | ||
|
||
$this->path = rocket_get_constant( 'WP_ROCKET_CACHE_ROOT_PATH', '' ) . 'fonts/' . get_current_blog_id() . '/'; | ||
} | ||
|
||
/** | ||
* Hashes the url | ||
* | ||
* @param string $url URL to get the hash from. | ||
* | ||
* @return string | ||
*/ | ||
private function hash_url( string $url ): string { | ||
return md5( $url ); | ||
} | ||
|
||
/** | ||
* Writes font css to path | ||
* | ||
* @param string $font_url The font url to save locally. | ||
* @param string $provider The url of the page. | ||
* | ||
* @return bool | ||
*/ | ||
public function write_font_css( string $font_url, string $provider ): bool { | ||
$font_provider_path = $this->get_font_provider_path( $provider ); | ||
$hash_url = $this->hash_url( $font_url ); | ||
$file = $this->get_fonts_full_path( $font_provider_path, $hash_url ); | ||
$css_file_name = $file . '.css'; | ||
$relative_path = $this->get_fonts_relative_path( $font_provider_path, $hash_url ); | ||
|
||
if ( ! rocket_mkdir_p( dirname( $file ) ) ) { | ||
return false; | ||
} | ||
|
||
$start_time = microtime( true ); | ||
|
||
$css_content = $this->get_remote_content( html_entity_decode( $font_url ) ); | ||
|
||
if ( ! $css_content ) { | ||
return false; | ||
} | ||
|
||
preg_match_all( '/url\((https:\/\/[^)]+)\)/', $css_content, $matches ); | ||
$font_urls = $matches[1]; | ||
$local_css = $css_content; | ||
|
||
foreach ( $font_urls as $font_url ) { | ||
$parsed_url = wp_parse_url( $font_url ); | ||
$font_path = $parsed_url['path']; | ||
$local_path = $file . $font_path; | ||
$local_dir = dirname( $local_path ); | ||
|
||
if ( ! rocket_mkdir_p( $local_dir ) ) { | ||
continue; | ||
} | ||
|
||
if ( ! $this->filesystem->exists( $local_path ) ) { | ||
$font_content = $this->get_remote_content( $font_url ); | ||
|
||
if ( ! $font_content ) { | ||
Logger::debug( 'Font download was not successful', [ 'Host Fonts Locally' ] ); | ||
continue; | ||
} | ||
|
||
$this->write_file( $local_path, $font_content ); | ||
} | ||
|
||
$local_url = content_url( $relative_path . $font_path ); | ||
$local_css = str_replace( $font_url, $local_url, $local_css ); | ||
} | ||
|
||
$end_time = microtime( true ); | ||
$duration = $end_time - $start_time; | ||
|
||
// Add for test purpose. | ||
Logger::debug( "Font download and optimization duration in seconds -- $duration", [ 'Host Fonts Locally' ] ); | ||
|
||
return $this->write_file( $css_file_name, $local_css ); | ||
} | ||
|
||
/** | ||
* Gets the remote content of the URL | ||
* | ||
* @param string $url URL to request content for. | ||
* | ||
* @return string | ||
*/ | ||
private function get_remote_content( string $url ): string { | ||
$response = wp_safe_remote_get( | ||
$url, | ||
[ | ||
'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', | ||
'httpversion' => '2.0', | ||
] | ||
); | ||
|
||
if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { | ||
return ''; | ||
} | ||
|
||
return wp_remote_retrieve_body( $response ); | ||
} | ||
|
||
/** | ||
* Get the fonts path for the css file. | ||
* | ||
* @param string $font_provider_path Font provider path. | ||
* @param string $hash Url of the page. | ||
* | ||
* @return string Path for the font file. | ||
*/ | ||
private function get_fonts_full_path( string $font_provider_path, string $hash ): string { | ||
return $this->path . $font_provider_path . $this->hash_to_path( $hash ); | ||
} | ||
|
||
/** | ||
* Get the fonts relative paths | ||
* | ||
* @param string $font_provider_path Font provider path. | ||
* @param string $hash Hash of the font url. | ||
* | ||
* @return string | ||
*/ | ||
private function get_fonts_relative_path( string $font_provider_path, string $hash ): string { | ||
$full_path = $this->path . $font_provider_path; | ||
$wp_content_dir = rocket_get_constant( 'WP_CONTENT_DIR' ); | ||
$relative_path = str_replace( $wp_content_dir, '', $full_path ); | ||
|
||
return $relative_path . $this->hash_to_path( $hash ); | ||
} | ||
|
||
/** | ||
* Get the fonts provider path | ||
* | ||
* @param string $provider The font provider. | ||
* | ||
* @return string | ||
*/ | ||
private function get_font_provider_path( string $provider ): string { | ||
$provider = str_replace( '_', '-', $provider ); | ||
|
||
return $provider . '/'; | ||
} | ||
|
||
/** | ||
* Deletes the locally stored fonts for the corresponding url | ||
* | ||
* @since 3.18 | ||
* | ||
* @param string $url The url of the page to be deleted. | ||
* | ||
* @return bool | ||
*/ | ||
public function delete_font_css( string $url ): bool { | ||
$dir = $this->get_fonts_full_path( $this->get_font_provider_path( $url ), $url ); | ||
|
||
return $this->delete_file( $dir ); | ||
} | ||
} |
Oops, something went wrong.