tag. * * @package Postlinkly * @since 1.0.0 */ declare(strict_types=1); namespace Postlinkly\Link; /** * Parses attributes out of an existing `` tag. * * Used during link removal and update operations to inspect what's * already in the content. The parser is intentionally tolerant — it * accepts both single- and double-quoted attribute values, ignores * malformed input, and never throws. * * @since 1.0.0 */ final class LinkAttributeParser { /** * Parses the attribute list out of an `` opening tag. * * Input may be either a complete tag (`y`) or just * the opening tag (``). Anything that isn't an `` * tag returns an empty array. * * @since 1.0.0 * * @param string $tag The tag markup. * * @return array Map of lower-cased attribute name to raw value. */ public function parse( string $tag ): array { // Capture the opening tag content between ``. $matched = preg_match( '/]*)>/i', $tag, $m ); if ( 1 !== $matched || ! isset( $m[1] ) ) { return array(); } $inside = $m[1]; $out = array(); // Match name="value" or name='value' attributes. if ( preg_match_all( '/([a-zA-Z][a-zA-Z0-9_:-]*)\s*=\s*("([^"]*)"|\'([^\']*)\')/', $inside, $matches, PREG_SET_ORDER ) ) { foreach ( $matches as $match ) { $name = strtolower( $match[1] ); $value = isset( $match[3] ) && '' !== $match[3] ? $match[3] : ( $match[4] ?? '' ); $out[ $name ] = $value; } } return $out; } /** * Returns whether a tag was created or modified by Postlinkly. * * Detected via the {@see LinkAttributeBuilder::PLUGIN_MARKER_ATTRIBUTE} * attribute. Useful when the link-removal flow needs to skip links the * user added by hand. * * @since 1.0.0 * * @param string $tag The tag markup. * * @return bool */ public function is_plugin_link( string $tag ): bool { $attrs = $this->parse( $tag ); return isset( $attrs[ LinkAttributeBuilder::PLUGIN_MARKER_ATTRIBUTE ] ); } /** * Returns the href value from a tag, or empty string when missing. * * @since 1.0.0 * * @param string $tag The tag markup. * * @return string */ public function get_href( string $tag ): string { $attrs = $this->parse( $tag ); return $attrs['href'] ?? ''; } /** * Returns the inner text of an `...` tag, or empty string on failure. * * Does not decode HTML entities — callers can apply `html_entity_decode` * themselves when they need plain text. * * @since 1.0.0 * * @param string $tag Complete `...` markup. * * @return string */ public function get_inner_text( string $tag ): string { if ( 1 !== preg_match( '/]*>(.*?)<\/a>/is', $tag, $m ) ) { return ''; } return $m[1]; } }