PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.0.13
Kirki – Freeform Page Builder, Website Builder & Customizer v6.0.13
6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / customizer / packages / modules / webfonts / src / Webfonts / Downloader.php
kirki / customizer / packages / modules / webfonts / src / Webfonts Last commit date
Async.php 2 months ago Downloader.php 5 months ago Embed.php 2 months ago Fonts.php 5 months ago Google.php 2 months ago
Downloader.php
291 lines
1 <?php
2 /**
3 * Manage fonts downloading.
4 *
5 * @package Kirki
6 * @category Core
7 * @author Themeum
8 * @copyright Copyright (c) 2023, Themeum
9 * @license https://opensource.org/licenses/MIT
10 * @since 3.1.0
11 */
12
13 namespace Kirki\Module\Webfonts;
14
15 /**
16 * Fonts-downloading manager.
17 *
18 * @since 3.1.0
19 */
20 class Downloader {
21
22 /**
23 * Get styles from URL.
24 *
25 * @access public
26 * @since 3.1.0
27 * @param string $url The URL.
28 * @return string
29 */
30 public function get_styles( $url ) {
31 $css = $this->get_cached_url_contents( $url );
32 return $this->get_local_font_styles( $css );
33 }
34
35 /**
36 * Get styles with fonts downloaded locally.
37 *
38 * @access protected
39 * @since 3.1.0
40 * @param string $css The styles.
41 * @return string
42 */
43 protected function get_local_font_styles( $css ) {
44 $files = $this->get_local_files_from_css( $css );
45
46 // Convert paths to URLs.
47 foreach ( $files as $remote => $local ) {
48 $files[ $remote ] = str_replace( WP_CONTENT_DIR, content_url(), $local );
49 }
50
51 return str_replace(
52 array_keys( $files ),
53 array_values( $files ),
54 $css
55 );
56 }
57
58 /**
59 * Download files mentioned in our CSS locally.
60 *
61 * @access protected
62 * @since 3.1.0
63 * @param string $css The CSS we want to parse.
64 * @return array Returns an array of remote URLs and their local counterparts.
65 */
66 protected function get_local_files_from_css( $css ) {
67 $font_files = $this->get_files_from_css( $css );
68 $stored = get_option( 'kirki_downloaded_font_files', array() );
69 $change = false; // If in the end this is true, we need to update the cache option.
70
71 // If the fonts folder don't exist, create it.
72 if ( ! file_exists( WP_CONTENT_DIR . '/fonts' ) ) {
73 $this->get_filesystem()->mkdir( WP_CONTENT_DIR . '/fonts', FS_CHMOD_DIR );
74 }
75
76 foreach ( $font_files as $font_family => $files ) {
77
78 // The folder path for this font-family.
79 $folder_path = WP_CONTENT_DIR . '/fonts/' . $font_family;
80
81 // If the folder doesn't exist, create it.
82 if ( ! file_exists( $folder_path ) ) {
83 $this->get_filesystem()->mkdir( $folder_path, FS_CHMOD_DIR );
84 }
85
86 foreach ( $files as $url ) {
87
88 // Get the filename.
89 $filename = basename( wp_parse_url( $url, PHP_URL_PATH ) );
90 $font_path = $folder_path . '/' . $filename;
91
92 if ( file_exists( $font_path ) ) {
93
94 // Skip if already cached.
95 if ( isset( $stored[ $url ] ) ) {
96 continue;
97 }
98
99 $stored[ $url ] = $font_path;
100 $change = true;
101 }
102
103 if ( ! function_exists( 'download_url' ) ) {
104 require_once wp_normalize_path( ABSPATH . '/wp-admin/includes/file.php' );
105 }
106
107 // Download file to temporary location.
108 $tmp_path = download_url( $url );
109
110 // Make sure there were no errors.
111 if ( is_wp_error( $tmp_path ) ) {
112 continue;
113 }
114
115 // Move temp file to final destination.
116 $success = $this->get_filesystem()->move( $tmp_path, $font_path, true );
117 if ( $success ) {
118 $stored[ $url ] = $font_path;
119 $change = true;
120 }
121 }
122 }
123
124 if ( $change ) {
125 update_option( 'kirki_downloaded_font_files', $stored );
126 }
127
128 return $stored;
129 }
130
131 /**
132 * Get cached url contents.
133 * If a cache doesn't already exist, get the URL contents from remote
134 * and cache the result.
135 *
136 * @access public
137 * @since 3.1.0
138 * @param string $url The URL we want to get the contents from.
139 * @param string $user_agent The user-agent to use for our request.
140 * @return string Returns the remote URL contents.
141 */
142 public function get_cached_url_contents( $url = '', $user_agent = null ) {
143
144 // Try to retrieved cached response from the gfonts API.
145 $contents = false;
146 $cached_responses = get_transient( 'kirki_remote_url_contents' );
147 $cached_responses = ( $cached_responses && is_array( $cached_responses ) ) ? $cached_responses : array();
148 if ( isset( $cached_responses[ md5( $url . $user_agent ) ] ) ) {
149 return $cached_responses[ md5( $url . $user_agent ) ];
150 }
151
152 // Get the contents from remote.
153 $contents = $this->get_url_contents( $url, $user_agent );
154
155 // If we got the contents successfully, store them in a transient.
156 // We're using a transient and not an option because fonts get updated
157 // so we want to be able to get the latest version weekly.
158 if ( $contents ) {
159 $cached_responses[ md5( $url . $user_agent ) ] = $contents;
160 set_transient( 'kirki_remote_url_contents', $cached_responses, WEEK_IN_SECONDS );
161 }
162
163 return $contents;
164 }
165
166 /**
167 * Get remote file contents.
168 *
169 * @access public
170 * @since 3.1.0
171 * @param string $url The URL we want to get the contents from.
172 * @param string $user_agent The user-agent to use for our request.
173 * @return string Returns the remote URL contents.
174 */
175 public function get_url_contents( $url = '', $user_agent = null ) {
176
177 if ( ! $user_agent ) {
178
179 /**
180 * The user-agent we want to use.
181 *
182 * For woff2 format, use'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0'.
183 * The default user-agent is the only one compatible with woff (not woff2)
184 * which also supports unicode ranges.
185 */
186 $user_agent = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0';
187
188 }
189
190 // Get the response.
191 $response = wp_remote_get( $url, array( 'user-agent' => $user_agent ) );
192
193 // Early exit if there was an error.
194 if ( is_wp_error( $response ) ) {
195 return;
196 }
197
198 // Get the CSS from our response.
199 $contents = wp_remote_retrieve_body( $response );
200
201 // Early exit if there was an error.
202 if ( is_wp_error( $contents ) ) {
203 return;
204 }
205
206 return $contents;
207 }
208
209 /**
210 * Get font files from the CSS.
211 *
212 * @access public
213 * @since 3.1.0
214 * @param string $css The CSS we want to parse.
215 * @return array Returns an array of font-families and the font-files used.
216 */
217 public function get_files_from_css( $css ) {
218
219 $font_faces = explode( '@font-face', $css );
220
221 $result = array();
222
223 // Loop all our font-face declarations.
224 foreach ( $font_faces as $font_face ) {
225
226 // Make sure we only process styles inside this declaration.
227 $style = explode( '}', $font_face )[0];
228
229 // Sanity check.
230 if ( false === strpos( $style, 'font-family' ) ) {
231 continue;
232 }
233
234 // Get an array of our font-families.
235 preg_match_all( '/font-family.*?\;/', $style, $matched_font_families );
236
237 // Get an array of our font-files.
238 preg_match_all( '/url\(.*?\)/i', $style, $matched_font_files );
239
240 // Get the font-family name.
241 $font_family = 'unknown';
242 if ( isset( $matched_font_families[0] ) && isset( $matched_font_families[0][0] ) ) {
243 $font_family = rtrim( ltrim( $matched_font_families[0][0], 'font-family:' ), ';' );
244 $font_family = trim( str_replace( array( "'", ';' ), '', $font_family ) );
245 $font_family = sanitize_key( strtolower( str_replace( ' ', '-', $font_family ) ) );
246 }
247
248 // Make sure the font-family is set in our array.
249 if ( ! isset( $result[ $font_family ] ) ) {
250 $result[ $font_family ] = array();
251 }
252
253 // Get files for this font-family and add them to the array.
254
255 foreach ( $matched_font_files as $match ) {
256
257 // Sanity check.
258 if ( ! isset( $match[0] ) ) {
259 continue;
260 }
261
262 // Add the file URL.
263 $result[ $font_family ][] = rtrim( ltrim( $match[0], 'url(' ), ')' );
264 }
265
266 // Make sure we have unique items.
267 // We're using array_flip here instead of array_unique for improved performance.
268 $result[ $font_family ] = array_flip( array_flip( $result[ $font_family ] ) );
269 }
270 return $result;
271 }
272
273 /**
274 * Get the filesystem.
275 *
276 * @access protected
277 * @since 3.1.0
278 * @return WP_Filesystem
279 */
280 protected function get_filesystem() {
281 global $wp_filesystem;
282 if ( ! $wp_filesystem ) {
283 if ( ! function_exists( 'WP_Filesystem' ) ) {
284 require_once wp_normalize_path( ABSPATH . '/wp-admin/includes/file.php' );
285 }
286 WP_Filesystem();
287 }
288 return $wp_filesystem;
289 }
290 }
291