42 ); |
42 ); |
43 |
43 |
44 /** |
44 /** |
45 * Default themes. |
45 * Default themes. |
46 * |
46 * |
47 * @var array |
47 * @since 3.4.0 |
|
48 * @since 3.5.0 Added the Twenty Twelve theme. |
|
49 * @since 3.6.0 Added the Twenty Thirteen theme. |
|
50 * @since 3.8.0 Added the Twenty Fourteen theme. |
|
51 * @since 4.1.0 Added the Twenty Fifteen theme. |
|
52 * @since 4.4.0 Added the Twenty Sixteen theme. |
|
53 * @since 4.7.0 Added the Twenty Seventeen theme. |
|
54 * @since 5.0.0 Added the Twenty Nineteen theme. |
|
55 * @since 5.3.0 Added the Twenty Twenty theme. |
|
56 * @since 5.6.0 Added the Twenty Twenty-One theme. |
|
57 * @since 5.9.0 Added the Twenty Twenty-Two theme. |
|
58 * @var string[] |
48 */ |
59 */ |
49 private static $default_themes = array( |
60 private static $default_themes = array( |
50 'classic' => 'WordPress Classic', |
61 'classic' => 'WordPress Classic', |
51 'default' => 'WordPress Default', |
62 'default' => 'WordPress Default', |
52 'twentyten' => 'Twenty Ten', |
63 'twentyten' => 'Twenty Ten', |
58 'twentysixteen' => 'Twenty Sixteen', |
69 'twentysixteen' => 'Twenty Sixteen', |
59 'twentyseventeen' => 'Twenty Seventeen', |
70 'twentyseventeen' => 'Twenty Seventeen', |
60 'twentynineteen' => 'Twenty Nineteen', |
71 'twentynineteen' => 'Twenty Nineteen', |
61 'twentytwenty' => 'Twenty Twenty', |
72 'twentytwenty' => 'Twenty Twenty', |
62 'twentytwentyone' => 'Twenty Twenty-One', |
73 'twentytwentyone' => 'Twenty Twenty-One', |
|
74 'twentytwentytwo' => 'Twenty Twenty-Two', |
63 ); |
75 ); |
64 |
76 |
65 /** |
77 /** |
66 * Renamed theme tags. |
78 * Renamed theme tags. |
67 * |
79 * |
68 * @var array |
80 * @since 3.8.0 |
|
81 * @var string[] |
69 */ |
82 */ |
70 private static $tag_map = array( |
83 private static $tag_map = array( |
71 'fixed-width' => 'fixed-layout', |
84 'fixed-width' => 'fixed-layout', |
72 'flexible-width' => 'fluid-layout', |
85 'flexible-width' => 'fluid-layout', |
73 ); |
86 ); |
74 |
87 |
75 /** |
88 /** |
76 * Absolute path to the theme root, usually wp-content/themes |
89 * Absolute path to the theme root, usually wp-content/themes |
77 * |
90 * |
|
91 * @since 3.4.0 |
78 * @var string |
92 * @var string |
79 */ |
93 */ |
80 private $theme_root; |
94 private $theme_root; |
81 |
95 |
82 /** |
96 /** |
83 * Header data from the theme's style.css file. |
97 * Header data from the theme's style.css file. |
84 * |
98 * |
|
99 * @since 3.4.0 |
85 * @var array |
100 * @var array |
86 */ |
101 */ |
87 private $headers = array(); |
102 private $headers = array(); |
88 |
103 |
89 /** |
104 /** |
90 * Header data from the theme's style.css file after being sanitized. |
105 * Header data from the theme's style.css file after being sanitized. |
91 * |
106 * |
|
107 * @since 3.4.0 |
92 * @var array |
108 * @var array |
93 */ |
109 */ |
94 private $headers_sanitized; |
110 private $headers_sanitized; |
95 |
111 |
96 /** |
112 /** |
97 * Header name from the theme's style.css after being translated. |
113 * Header name from the theme's style.css after being translated. |
98 * |
114 * |
99 * Cached due to sorting functions running over the translated name. |
115 * Cached due to sorting functions running over the translated name. |
100 * |
116 * |
|
117 * @since 3.4.0 |
101 * @var string |
118 * @var string |
102 */ |
119 */ |
103 private $name_translated; |
120 private $name_translated; |
104 |
121 |
105 /** |
122 /** |
106 * Errors encountered when initializing the theme. |
123 * Errors encountered when initializing the theme. |
107 * |
124 * |
|
125 * @since 3.4.0 |
108 * @var WP_Error |
126 * @var WP_Error |
109 */ |
127 */ |
110 private $errors; |
128 private $errors; |
111 |
129 |
112 /** |
130 /** |
113 * The directory name of the theme's files, inside the theme root. |
131 * The directory name of the theme's files, inside the theme root. |
114 * |
132 * |
115 * In the case of a child theme, this is directory name of the child theme. |
133 * In the case of a child theme, this is directory name of the child theme. |
116 * Otherwise, 'stylesheet' is the same as 'template'. |
134 * Otherwise, 'stylesheet' is the same as 'template'. |
117 * |
135 * |
|
136 * @since 3.4.0 |
118 * @var string |
137 * @var string |
119 */ |
138 */ |
120 private $stylesheet; |
139 private $stylesheet; |
121 |
140 |
122 /** |
141 /** |
123 * The directory name of the theme's files, inside the theme root. |
142 * The directory name of the theme's files, inside the theme root. |
124 * |
143 * |
125 * In the case of a child theme, this is the directory name of the parent theme. |
144 * In the case of a child theme, this is the directory name of the parent theme. |
126 * Otherwise, 'template' is the same as 'stylesheet'. |
145 * Otherwise, 'template' is the same as 'stylesheet'. |
127 * |
146 * |
|
147 * @since 3.4.0 |
128 * @var string |
148 * @var string |
129 */ |
149 */ |
130 private $template; |
150 private $template; |
131 |
151 |
132 /** |
152 /** |
133 * A reference to the parent theme, in the case of a child theme. |
153 * A reference to the parent theme, in the case of a child theme. |
134 * |
154 * |
|
155 * @since 3.4.0 |
135 * @var WP_Theme |
156 * @var WP_Theme |
136 */ |
157 */ |
137 private $parent; |
158 private $parent; |
138 |
159 |
139 /** |
160 /** |
140 * URL to the theme root, usually an absolute URL to wp-content/themes |
161 * URL to the theme root, usually an absolute URL to wp-content/themes |
141 * |
162 * |
|
163 * @since 3.4.0 |
142 * @var string |
164 * @var string |
143 */ |
165 */ |
144 private $theme_root_uri; |
166 private $theme_root_uri; |
145 |
167 |
146 /** |
168 /** |
147 * Flag for whether the theme's textdomain is loaded. |
169 * Flag for whether the theme's textdomain is loaded. |
148 * |
170 * |
|
171 * @since 3.4.0 |
149 * @var bool |
172 * @var bool |
150 */ |
173 */ |
151 private $textdomain_loaded; |
174 private $textdomain_loaded; |
152 |
175 |
153 /** |
176 /** |
154 * Stores an md5 hash of the theme root, to function as the cache key. |
177 * Stores an md5 hash of the theme root, to function as the cache key. |
155 * |
178 * |
|
179 * @since 3.4.0 |
156 * @var string |
180 * @var string |
157 */ |
181 */ |
158 private $cache_hash; |
182 private $cache_hash; |
159 |
183 |
160 /** |
184 /** |
161 * Flag for whether the themes cache bucket should be persistently cached. |
185 * Flag for whether the themes cache bucket should be persistently cached. |
162 * |
186 * |
163 * Default is false. Can be set with the {@see 'wp_cache_themes_persistently'} filter. |
187 * Default is false. Can be set with the {@see 'wp_cache_themes_persistently'} filter. |
164 * |
188 * |
|
189 * @since 3.4.0 |
165 * @var bool |
190 * @var bool |
166 */ |
191 */ |
167 private static $persistently_cache; |
192 private static $persistently_cache; |
168 |
193 |
169 /** |
194 /** |
170 * Expiration time for the themes cache bucket. |
195 * Expiration time for the themes cache bucket. |
171 * |
196 * |
172 * By default the bucket is not cached, so this value is useless. |
197 * By default the bucket is not cached, so this value is useless. |
173 * |
198 * |
|
199 * @since 3.4.0 |
174 * @var bool |
200 * @var bool |
175 */ |
201 */ |
176 private static $cache_expiration = 1800; |
202 private static $cache_expiration = 1800; |
177 |
203 |
178 /** |
204 /** |
254 'stylesheet' => $this->stylesheet, |
280 'stylesheet' => $this->stylesheet, |
255 'template' => $this->template, |
281 'template' => $this->template, |
256 ) |
282 ) |
257 ); |
283 ); |
258 if ( ! file_exists( $this->theme_root ) ) { // Don't cache this one. |
284 if ( ! file_exists( $this->theme_root ) ) { // Don't cache this one. |
259 $this->errors->add( 'theme_root_missing', __( 'Error: The themes directory is either empty or doesn’t exist. Please check your installation.' ) ); |
285 $this->errors->add( 'theme_root_missing', __( '<strong>Error</strong>: The themes directory is either empty or does not exist. Please check your installation.' ) ); |
260 } |
286 } |
261 return; |
287 return; |
262 } elseif ( ! is_readable( $this->theme_root . '/' . $theme_file ) ) { |
288 } elseif ( ! is_readable( $this->theme_root . '/' . $theme_file ) ) { |
263 $this->headers['Name'] = $this->stylesheet; |
289 $this->headers['Name'] = $this->stylesheet; |
264 $this->errors = new WP_Error( 'theme_stylesheet_not_readable', __( 'Stylesheet is not readable.' ) ); |
290 $this->errors = new WP_Error( 'theme_stylesheet_not_readable', __( 'Stylesheet is not readable.' ) ); |
311 $this->template = $this->headers['Template']; |
337 $this->template = $this->headers['Template']; |
312 } |
338 } |
313 |
339 |
314 if ( ! $this->template ) { |
340 if ( ! $this->template ) { |
315 $this->template = $this->stylesheet; |
341 $this->template = $this->stylesheet; |
316 if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet . '/index.php' ) ) { |
342 $theme_path = $this->theme_root . '/' . $this->stylesheet; |
|
343 |
|
344 if ( |
|
345 ! file_exists( $theme_path . '/templates/index.html' ) |
|
346 && ! file_exists( $theme_path . '/block-templates/index.html' ) // Deprecated path support since 5.9.0. |
|
347 && ! file_exists( $theme_path . '/index.php' ) |
|
348 ) { |
317 $error_message = sprintf( |
349 $error_message = sprintf( |
318 /* translators: 1: index.php, 2: Documentation URL, 3: style.css */ |
350 /* translators: 1: templates/index.html, 2: index.php, 3: Documentation URL, 4: Template, 5: style.css */ |
319 __( 'Template is missing. Standalone themes need to have a %1$s template file. <a href="%2$s">Child themes</a> need to have a Template header in the %3$s stylesheet.' ), |
351 __( 'Template is missing. Standalone themes need to have a %1$s or %2$s template file. <a href="%3$s">Child themes</a> need to have a %4$s header in the %5$s stylesheet.' ), |
|
352 '<code>templates/index.html</code>', |
320 '<code>index.php</code>', |
353 '<code>index.php</code>', |
321 __( 'https://developer.wordpress.org/themes/advanced-topics/child-themes/' ), |
354 __( 'https://developer.wordpress.org/themes/advanced-topics/child-themes/' ), |
|
355 '<code>Template</code>', |
322 '<code>style.css</code>' |
356 '<code>style.css</code>' |
323 ); |
357 ); |
324 $this->errors = new WP_Error( 'theme_no_index', $error_message ); |
358 $this->errors = new WP_Error( 'theme_no_index', $error_message ); |
325 $this->cache_add( |
359 $this->cache_add( |
326 'theme', |
360 'theme', |
530 * @since 3.4.0 |
564 * @since 3.4.0 |
531 * |
565 * |
532 * @param mixed $offset |
566 * @param mixed $offset |
533 * @param mixed $value |
567 * @param mixed $value |
534 */ |
568 */ |
|
569 #[ReturnTypeWillChange] |
535 public function offsetSet( $offset, $value ) {} |
570 public function offsetSet( $offset, $value ) {} |
536 |
571 |
537 /** |
572 /** |
538 * Method to implement ArrayAccess for keys formerly returned by get_themes() |
573 * Method to implement ArrayAccess for keys formerly returned by get_themes() |
539 * |
574 * |
540 * @since 3.4.0 |
575 * @since 3.4.0 |
541 * |
576 * |
542 * @param mixed $offset |
577 * @param mixed $offset |
543 */ |
578 */ |
|
579 #[ReturnTypeWillChange] |
544 public function offsetUnset( $offset ) {} |
580 public function offsetUnset( $offset ) {} |
545 |
581 |
546 /** |
582 /** |
547 * Method to implement ArrayAccess for keys formerly returned by get_themes() |
583 * Method to implement ArrayAccess for keys formerly returned by get_themes() |
548 * |
584 * |
549 * @since 3.4.0 |
585 * @since 3.4.0 |
550 * |
586 * |
551 * @param mixed $offset |
587 * @param mixed $offset |
552 * @return bool |
588 * @return bool |
553 */ |
589 */ |
|
590 #[ReturnTypeWillChange] |
554 public function offsetExists( $offset ) { |
591 public function offsetExists( $offset ) { |
555 static $keys = array( |
592 static $keys = array( |
556 'Name', |
593 'Name', |
557 'Version', |
594 'Version', |
558 'Status', |
595 'Status', |
723 $this->headers = array(); |
761 $this->headers = array(); |
724 $this->__construct( $this->stylesheet, $this->theme_root ); |
762 $this->__construct( $this->stylesheet, $this->theme_root ); |
725 } |
763 } |
726 |
764 |
727 /** |
765 /** |
728 * Get a raw, unformatted theme header. |
766 * Gets a raw, unformatted theme header. |
729 * |
767 * |
730 * The header is sanitized, but is not translated, and is not marked up for display. |
768 * The header is sanitized, but is not translated, and is not marked up for display. |
731 * To get a theme header for display, use the display() method. |
769 * To get a theme header for display, use the display() method. |
732 * |
770 * |
733 * Use the get_template() method, not the 'Template' header, for finding the template. |
771 * Use the get_template() method, not the 'Template' header, for finding the template. |
950 'red' => __( 'Red' ), |
987 'red' => __( 'Red' ), |
951 'silver' => __( 'Silver' ), |
988 'silver' => __( 'Silver' ), |
952 'tan' => __( 'Tan' ), |
989 'tan' => __( 'Tan' ), |
953 'white' => __( 'White' ), |
990 'white' => __( 'White' ), |
954 'yellow' => __( 'Yellow' ), |
991 'yellow' => __( 'Yellow' ), |
955 'dark' => __( 'Dark' ), |
992 'dark' => _x( 'Dark', 'color scheme' ), |
956 'light' => __( 'Light' ), |
993 'light' => _x( 'Light', 'color scheme' ), |
957 'fixed-layout' => __( 'Fixed Layout' ), |
994 'fixed-layout' => __( 'Fixed Layout' ), |
958 'fluid-layout' => __( 'Fluid Layout' ), |
995 'fluid-layout' => __( 'Fluid Layout' ), |
959 'responsive-layout' => __( 'Responsive Layout' ), |
996 'responsive-layout' => __( 'Responsive Layout' ), |
960 'blavatar' => __( 'Blavatar' ), |
997 'blavatar' => __( 'Blavatar' ), |
961 'photoblogging' => __( 'Photoblogging' ), |
998 'photoblogging' => __( 'Photoblogging' ), |
1173 |
1210 |
1174 if ( $search_parent && $this->parent() ) { |
1211 if ( $search_parent && $this->parent() ) { |
1175 $files += (array) self::scandir( $this->get_template_directory(), $type, $depth ); |
1212 $files += (array) self::scandir( $this->get_template_directory(), $type, $depth ); |
1176 } |
1213 } |
1177 |
1214 |
1178 return $files; |
1215 return array_filter( $files ); |
1179 } |
1216 } |
1180 |
1217 |
1181 /** |
1218 /** |
1182 * Returns the theme's post templates. |
1219 * Returns the theme's post templates. |
1183 * |
1220 * |
1184 * @since 4.7.0 |
1221 * @since 4.7.0 |
1185 * @since 5.8.0 Include block templates. |
1222 * @since 5.8.0 Include block templates. |
1186 * |
1223 * |
1187 * @return string[] Array of page templates, keyed by filename and post type, |
1224 * @return array[] Array of page template arrays, keyed by post type and filename, |
1188 * with the value of the translated header name. |
1225 * with the value of the translated header name. |
1189 */ |
1226 */ |
1190 public function get_post_templates() { |
1227 public function get_post_templates() { |
1191 // If you screw up your current theme and we invalidate your parent, most things still work. Let it slide. |
1228 // If you screw up your active theme and we invalidate your parent, most things still work. Let it slide. |
1192 if ( $this->errors() && $this->errors()->get_error_codes() !== array( 'theme_parent_invalid' ) ) { |
1229 if ( $this->errors() && $this->errors()->get_error_codes() !== array( 'theme_parent_invalid' ) ) { |
1193 return array(); |
1230 return array(); |
1194 } |
1231 } |
1195 |
1232 |
1196 $post_templates = $this->cache_get( 'post_templates' ); |
1233 $post_templates = $this->cache_get( 'post_templates' ); |
1427 return true; |
1472 return true; |
1428 } |
1473 } |
1429 } |
1474 } |
1430 |
1475 |
1431 return false; |
1476 return false; |
|
1477 } |
|
1478 |
|
1479 /** |
|
1480 * Returns whether this theme is a block-based theme or not. |
|
1481 * |
|
1482 * @since 5.9.0 |
|
1483 * |
|
1484 * @return bool |
|
1485 */ |
|
1486 public function is_block_theme() { |
|
1487 $paths_to_index_block_template = array( |
|
1488 $this->get_file_path( '/block-templates/index.html' ), |
|
1489 $this->get_file_path( '/templates/index.html' ), |
|
1490 ); |
|
1491 |
|
1492 foreach ( $paths_to_index_block_template as $path_to_index_block_template ) { |
|
1493 if ( is_file( $path_to_index_block_template ) && is_readable( $path_to_index_block_template ) ) { |
|
1494 return true; |
|
1495 } |
|
1496 } |
|
1497 |
|
1498 return false; |
|
1499 } |
|
1500 |
|
1501 /** |
|
1502 * Retrieves the path of a file in the theme. |
|
1503 * |
|
1504 * Searches in the stylesheet directory before the template directory so themes |
|
1505 * which inherit from a parent theme can just override one file. |
|
1506 * |
|
1507 * @since 5.9.0 |
|
1508 * |
|
1509 * @param string $file Optional. File to search for in the stylesheet directory. |
|
1510 * @return string The path of the file. |
|
1511 */ |
|
1512 public function get_file_path( $file = '' ) { |
|
1513 $file = ltrim( $file, '/' ); |
|
1514 |
|
1515 $stylesheet_directory = $this->get_stylesheet_directory(); |
|
1516 $template_directory = $this->get_template_directory(); |
|
1517 |
|
1518 if ( empty( $file ) ) { |
|
1519 $path = $stylesheet_directory; |
|
1520 } elseif ( file_exists( $stylesheet_directory . '/' . $file ) ) { |
|
1521 $path = $stylesheet_directory . '/' . $file; |
|
1522 } else { |
|
1523 $path = $template_directory . '/' . $file; |
|
1524 } |
|
1525 |
|
1526 /** This filter is documented in wp-includes/link-template.php */ |
|
1527 return apply_filters( 'theme_file_path', $path, $file ); |
1432 } |
1528 } |
1433 |
1529 |
1434 /** |
1530 /** |
1435 * Determines the latest WordPress default theme that is installed. |
1531 * Determines the latest WordPress default theme that is installed. |
1436 * |
1532 * |