[a-z]' . '[a-z0-9_/-]*)(?\s+\{.*?\})?\s+(?:/-->|-->(?' . '(?:(?!)#s'; private const PAIRED_MEDIA_PATTERN = '#(?<(?iframe|video' . '|audio)\b[^>]*>).*?\s*>#is'; private const VOID_MEDIA_PATTERN = '#(?<(?:img|embed|source)' . '\b[^>]*>)#is'; private const ANCHOR_PATTERN = '#(?]*>)' . '(?.*?)#is'; private const MEDIA_OPENING = '#<(?:img|iframe|video|audio' . '|embed|source)\b[^>]*>#is'; public function __construct( private AddressMatch $address_match, private BlockAttributes $block_attributes ) { } public function remove( string $content, string $link_url ): string { $without_blocks = (string) preg_replace_callback( self::LEAF_BLOCK_PATTERN, fn( array $block ): string => $this->kept_block( $block, $link_url ), $content, flags: PREG_UNMATCHED_AS_NULL ); $without_media = $this->without_media( $without_blocks, $link_url ); return (string) preg_replace_callback( self::ANCHOR_PATTERN, fn( array $anchor ): string => $this->kept_anchor( $anchor, $link_url ), $without_media ); } private function without_media( string $content, string $link_url ): string { $without_paired = (string) preg_replace_callback( self::PAIRED_MEDIA_PATTERN, fn( array $media ): string => $this->kept_media( $media, $link_url ), $content ); return (string) preg_replace_callback( self::VOID_MEDIA_PATTERN, fn( array $media ): string => $this->kept_media( $media, $link_url ), $without_paired ); } /** @param array{0: string, opening: string, inner: string} $anchor */ private function kept_anchor( array $anchor, string $link_url ): string { $is_target = $this->address_match->tag_points_to( $anchor['opening'], $link_url ); return $is_target ? $anchor['inner'] : $anchor[0]; } /** @param array{0: string, opening: string} $media */ private function kept_media( array $media, string $link_url ): string { $is_target = $this->address_match->tag_points_to( $media['opening'], $link_url ); return $is_target ? '' : $media[0]; } /** * @param array{ * 0: string, * attributes: string|null, * inner: string|null * } $block */ private function kept_block( array $block, string $link_url ): string { $attributes = json_decode( trim( $block['attributes'] ?? '' ), true ); $is_target = is_array( $attributes ) && $this->block_attributes->contain( $attributes, $link_url ); $has_media = $this->has_media( $block['inner'] ?? '', $link_url ); return $is_target || $has_media ? '' : $block[0]; } private function has_media( string $html, string $link_url ): bool { $tags = array(); preg_match_all( self::MEDIA_OPENING, $html, $tags ); foreach ( $tags[0] as $tag ) { if ( $this->address_match->tag_points_to( $tag, $link_url ) ) { return true; } } return false; } }