PluginProbe
EasyFonts – Host Google Fonts Locally, Fast & Auto-Optimize, GDPR Compliant / 2.0.1
EasyFonts – Host Google Fonts Locally, Fast & Auto-Optimize, GDPR Compliant v2.0.1
2.0.4 2.0.3 2.0.2 2.0.1 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2 1.3 2.0.0
easyfonts / src / Detect / StylesheetFetcher.php

StylesheetFetcher.php in EasyFonts – Host Google Fonts Locally, Fast & Auto-Optimize, GDPR Compliant 2.0.1, at src/Detect/StylesheetFetcher.php

150 lines 3.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Fetches the *contents* of linked stylesheets so fonts hidden inside theme /
4 * plugin / CDN CSS can be detected — the capability plain output-buffer
5 * scanners miss.
6 *
7 * @package EasyFonts
8 */
9
10 namespace EasyFonts\Detect;
11
12 use EasyFonts\Fonts\Downloader;
13
14 defined( 'ABSPATH' ) || exit;
15
16 /**
17 * Resolves stylesheet URLs to CSS contents (disk for local, HTTP for external).
18 */
19 class StylesheetFetcher {
20
21 /**
22 * Already-processed URLs this request (avoids duplicate work).
23 *
24 * @var array<string,bool>
25 */
26 private array $seen = array();
27
28 /**
29 * Read a same-origin stylesheet from disk.
30 *
31 * @param string $url Absolute or root-relative URL.
32 * @return string|null
33 */
34 public function read_local( string $url ): ?string {
35 $url = $this->normalize( $url );
36
37 if ( isset( $this->seen[ $url ] ) ) {
38 return null;
39 }
40
41 $this->seen[ $url ] = true;
42
43 $path = $this->url_to_path( $url );
44
45 if ( null === $path || ! is_file( $path ) ) {
46 return null;
47 }
48
49 $css = @file_get_contents( $path ); // phpcs:ignore WordPress.WP.AlternativeFunctions
50
51 return false === $css ? null : $css;
52 }
53
54 /**
55 * Fetch an external stylesheet over HTTP (once).
56 *
57 * @param string $url URL.
58 * @param Downloader $downloader Downloader.
59 * @return string|null
60 */
61 public function read_external( string $url, Downloader $downloader ): ?string {
62 $url = $this->normalize( $url );
63
64 if ( isset( $this->seen[ $url ] ) ) {
65 return null;
66 }
67
68 $this->seen[ $url ] = true;
69
70 return $downloader->fetch_css( $url );
71 }
72
73 /**
74 * Is this URL same-origin?
75 *
76 * @param string $url URL.
77 * @return bool
78 */
79 public function is_local( string $url ): bool {
80 $url = $this->normalize( $url );
81 $home = home_url();
82
83 return str_starts_with( $url, $home ) || str_starts_with( $url, '/' );
84 }
85
86 /**
87 * Should we skip this stylesheet entirely (core, our own cache, etc.)?
88 *
89 * @param string $url URL.
90 * @return bool
91 */
92 public function is_skippable( string $url ): bool {
93 foreach ( array( 'wp-includes', '/easyfonts/', 'fonts.googleapis.com', 'fonts.bunny.net' ) as $needle ) {
94 if ( false !== strpos( $url, $needle ) ) {
95 return true;
96 }
97 }
98
99 return false;
100 }
101
102 /**
103 * Map a same-origin URL to a filesystem path.
104 *
105 * @param string $url URL.
106 * @return string|null
107 */
108 private function url_to_path( string $url ): ?string {
109 $url = strtok( $url, '?' ); // Drop query (dynamic CSS handled elsewhere).
110
111 $content_url = content_url();
112
113 if ( str_starts_with( $url, $content_url ) ) {
114 return str_replace( $content_url, WP_CONTENT_DIR, $url );
115 }
116
117 $home = home_url();
118
119 if ( str_starts_with( $url, $home ) ) {
120 return str_replace( $home, untrailingslashit( ABSPATH ), $url );
121 }
122
123 if ( str_starts_with( $url, '/' ) ) {
124 return untrailingslashit( ABSPATH ) . $url;
125 }
126
127 return null;
128 }
129
130 /**
131 * Normalize protocol-relative / root-relative URLs to absolute.
132 *
133 * @param string $url URL.
134 * @return string
135 */
136 private function normalize( string $url ): string {
137 $url = html_entity_decode( $url, ENT_QUOTES, 'UTF-8' );
138
139 if ( str_starts_with( $url, '//' ) ) {
140 return ( is_ssl() ? 'https:' : 'http:' ) . $url;
141 }
142
143 if ( str_starts_with( $url, '/' ) ) {
144 return home_url( $url );
145 }
146
147 return $url;
148 }
149 }
150