true ); } return '' === $term['text'] ? array() : array( 'search' => $term['text'] ); } if ( ! is_singular( array( 'post', 'page' ) ) ) { return array(); } $post = get_queried_object(); if ( ! $post instanceof \WP_Post ) { return array(); } $categories = get_the_category( $post->ID ); $first = is_array( $categories ) && isset( $categories[0]->name ) ? (string) $categories[0]->name : ''; return array( 'content' => array( 'title' => self::clean( get_the_title( $post ) ), 'type' => self::clean( $post->post_type ), 'category' => self::clean( $first ), ), ); } /** * Cleans a text for sending: entities decoded, tags removed, white space * collapsed. Returns '' when the whole text holds personal data (see * is_personal()) or is not valid UTF-8, otherwise the text cut to * MAX_LENGTH characters. The cut text is checked once more, so nothing * personal can get through on either side of the cut. * * @param string $text The text. */ public static function clean( string $text ): string { return self::inspect( $text )['text']; } /** * The cleaned text (as clean() gives it) and whether it was dropped * because it holds personal data or is not valid UTF-8. A text that is * only empty after cleaning is not personal. * * @param string $text The text. * @return array{text: string, personal: bool} */ private static function inspect( string $text ): array { $text = wp_strip_all_tags( html_entity_decode( $text, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) ); $text = preg_replace( '/\s+/u', ' ', $text ); // Null: not valid UTF-8. Dropped, never sent broken, and not trusted. if ( ! is_string( $text ) ) { return array( 'text' => '', 'personal' => true, ); } $text = trim( $text ); if ( '' === $text ) { return array( 'text' => '', 'personal' => false, ); } if ( self::is_personal( $text ) || 1 !== preg_match( '/^.{0,' . self::MAX_LENGTH . '}/us', $text, $cut ) ) { return array( 'text' => '', 'personal' => true, ); } $cut = trim( $cut[0] ); return self::is_personal( $cut ) ? array( 'text' => '', 'personal' => true, ) : array( 'text' => $cut, 'personal' => false, ); } /** * Whether a text holds an email address (anything shaped like a@b) or a * phone number: seven or more digits in a row once the spaces, dots, * brackets, plus signs, dashes, slashes and underscores between digits are * ignored. A few * ordinary texts are caught too (a run of years, "2026-10-07"); losing * them costs a search term, never an event. * * @param string $text The text, already decoded. */ public static function is_personal( string $text ): bool { if ( 1 === preg_match( '/[^\s@]+@[^\s@]+/u', $text ) ) { return true; } $digits = preg_replace( '/(?<=\p{Nd})[\s.()+\-\/_]+(?=\p{Nd})/u', '', $text ); // Null (invalid UTF-8) is treated as personal: fail closed. return ! is_string( $digits ) || 1 === preg_match( '/\p{Nd}{7,}/u', $digits ); } }