|null */ private ?array $fixture; /** * Explicit roots are used by portability tests and the standalone collector, * which intentionally does not bootstrap WordPress. In-process callers omit it. * * @param array|null $fixture Exact configured roots. */ public function __construct( ?array $fixture = null ) { $this->fixture = $fixture; } public function wordpress_root(): string { return $this->path( 'wordpress_root', defined( 'ABSPATH' ) ? ABSPATH : '' ); } public function content_root(): string { return $this->path( 'content_root', defined( 'WP_CONTENT_DIR' ) ? WP_CONTENT_DIR : '' ); } public function plugins_root(): string { return $this->path( 'plugins_root', defined( 'WP_PLUGIN_DIR' ) ? WP_PLUGIN_DIR : '' ); } public function mu_plugins_root(): string { return $this->path( 'mu_plugins_root', defined( 'WPMU_PLUGIN_DIR' ) ? WPMU_PLUGIN_DIR : '' ); } public function uploads_root(): string { if ( null !== $this->fixture ) { return $this->path( 'uploads_root', '' ); } if ( ! function_exists( 'wp_get_upload_dir' ) ) { return ''; } $uploads = wp_get_upload_dir(); return isset( $uploads['basedir'] ) && is_string( $uploads['basedir'] ) ? $this->normalize( $uploads['basedir'] ) : ''; } /** @return list */ public function theme_roots(): array { if ( null !== $this->fixture ) { $roots = $this->fixture['theme_roots'] ?? array(); } else { global $wp_theme_directories; $roots = is_array( $wp_theme_directories ) ? $wp_theme_directories : array(); if ( array() === $roots && function_exists( 'get_theme_root' ) ) { $root = get_theme_root(); $roots = is_string( $root ) && '' !== $root ? array( $root ) : array(); } } $result = array(); foreach ( is_array( $roots ) ? $roots : array() as $root ) { if ( is_string( $root ) && '' !== $root ) { $result[] = $this->normalize( $root ); } } return array_values( array_unique( $result ) ); } public function content_file( string $basename ): string { if ( ! $this->safe_relative( $basename ) || basename( $basename ) !== $basename ) { return ''; } $root = $this->content_root(); return '' === $root ? '' : $root . '/' . $basename; } public function plugin_path( string $relative ): string { if ( ! $this->safe_relative( $relative ) ) { return ''; } $root = $this->plugins_root(); return '' === $root ? '' : $root . '/' . ltrim( $relative, '/' ); } public function core_file( string $relative ): string { if ( ! $this->safe_relative( $relative ) ) { return ''; } $root = $this->wordpress_root(); return '' === $root ? '' : $root . '/' . ltrim( $relative, '/' ); } private function path( string $key, string $fallback ): string { $value = null !== $this->fixture ? ( $this->fixture[ $key ] ?? '' ) : $fallback; return is_string( $value ) ? $this->normalize( $value ) : ''; } private function normalize( string $path ): string { if ( '' === $path || false !== strpos( $path, "\0" ) ) { return ''; } $path = function_exists( 'wp_normalize_path' ) ? wp_normalize_path( $path ) : str_replace( '\\', '/', $path ); return rtrim( $path, '/' ); } private function safe_relative( string $path ): bool { return '' !== $path && strlen( $path ) <= 4096 && false === strpos( $path, "\0" ) && '/' !== $path[0] && '\\' !== $path[0] && 1 !== preg_match( '#(?:^|[\\\\/])\.\.(?:[\\\\/]|$)#', $path ) && 1 !== preg_match( '#^[a-zA-Z]:#', $path ); } }