without_fragment( $decoded ) ); if ( '' === $address ) { return null; } return $this->absolute_web_address( $address ); } public function is_usable_new_url( string $address ): bool { $trimmed = trim( $address ); if ( $this->is_root_relative( $trimmed ) ) { return true; } return 1 === preg_match( self::WEB_SCHEME, $trimmed ) && null !== $this->absolute_web_address( $trimmed ); } public function host_of( string $absolute_address ): string { $host = (string) wp_parse_url( $absolute_address, PHP_URL_HOST ); return strtolower( $host ); } public function is_internal( string $absolute_address ): bool { $home_host = $this->host_of( $this->home_url ); return $this->host_of( $absolute_address ) === $home_host; } private function without_fragment( string $address ): string { $fragment_start = strpos( $address, '#' ); if ( false === $fragment_start ) { return $address; } return substr( $address, 0, $fragment_start ); } private function is_root_relative( string $address ): bool { return str_starts_with( $address, '/' ) && ! str_starts_with( $address, '//' ); } private function absolute_form( string $address ): string { if ( str_starts_with( $address, '//' ) ) { return self::DEFAULT_SCHEME . $address; } if ( 1 === preg_match( self::ANY_SCHEME, $address ) ) { return $address; } if ( $this->is_root_relative( $address ) ) { return $this->site_root() . $address; } return rtrim( $this->home_url, '/' ) . '/' . $address; } private function site_root(): string { $matches = array(); preg_match( self::SITE_ROOT, $this->home_url, $matches ); return $matches[0] ?? rtrim( $this->home_url, '/' ); } private function absolute_web_address( string $address ): string|null { $parts = wp_parse_url( $this->absolute_form( $address ) ); if ( ! is_array( $parts ) ) { return null; } $scheme = strtolower( $parts['scheme'] ?? '' ); $host = strtolower( $parts['host'] ?? '' ); if ( ! in_array( $scheme, self::WEB_SCHEMES, true ) || '' === $host ) { return null; } return $scheme . '://' . $this->user_part( $parts ) . $host . $this->port_part( $parts ) . ( $parts['path'] ?? '' ) . $this->query_part( $parts ); } /** @param array{user?: string, pass?: string} $parts */ private function user_part( array $parts ): string { if ( ! isset( $parts['user'] ) ) { return ''; } $password = isset( $parts['pass'] ) ? ':' . $parts['pass'] : ''; return $parts['user'] . $password . '@'; } /** @param array{port?: int} $parts */ private function port_part( array $parts ): string { return isset( $parts['port'] ) ? ':' . $parts['port'] : ''; } /** @param array{query?: string} $parts */ private function query_part( array $parts ): string { return isset( $parts['query'] ) ? '?' . $parts['query'] : ''; } }