if the content starts with multiple line breaks. if ( preg_match( '/^\s*\n\s*\n\s*/', $content ) ) { $this->end_tag( 'p' ); } // Split up the contents into paragraphs, separated by double line breaks. $paragraphs = preg_split( '/\s*\n\s*\n\s*/', $content ); $paragraphs = array_filter( $paragraphs, static function ( $paragraph ) { return '' !== wpcf7_strip_whitespaces( $paragraph ); } ); $paragraphs = array_values( $paragraphs ); if ( $paragraphs ) { if ( $this->is_inside( 'p' ) ) { $paragraph = array_shift( $paragraphs ); $paragraph = self::normalize_paragraph( $paragraph, $this->options['auto_br'] ); $this->append_preformatted( $paragraph ); } foreach ( $paragraphs as $paragraph ) { $this->start_tag( 'p' ); $paragraph = wpcf7_strip_whitespaces( $paragraph, 'start' ); $paragraph = self::normalize_paragraph( $paragraph, $this->options['auto_br'] ); $this->append_preformatted( $paragraph ); } } // Close
if the content ends with multiple line breaks. if ( preg_match( '/\s*\n\s*\n\s*$/', $content ) ) { $this->end_tag( 'p' ); } // Cases where the content is a single line break. if ( preg_match( '/^\s*\n\s*$/', $content ) ) { $auto_br = $this->options['auto_br'] && $this->is_inside( 'p' ); $content = self::normalize_paragraph( $content, $auto_br ); $this->append_preformatted( $content ); } } else { $auto_br = $this->options['auto_br'] && $this->has_parent( self::br_parent_elements ); $content = self::normalize_paragraph( $content, $auto_br ); $this->append_preformatted( $content ); } } /** * Appends a start tag to the output property. * * @param string $tag A start tag. */ public function start_tag( $tag ) { list( $tag, $tag_name ) = self::normalize_start_tag( $tag ); if ( in_array( $tag_name, self::p_child_elements, true ) and ! $this->is_inside( 'p' ) and ! $this->is_inside( self::p_child_elements ) and ! $this->has_parent( self::p_nonparent_elements ) ) { // Open
if it does not exist. $this->start_tag( 'p' ); } $this->append_start_tag( $tag_name, array(), $tag ); } /** * Appends a start tag to the output property. * * @param string $tag_name Tag name. * @param array $atts Associative array of attribute name and value pairs. * @param string $tag A start tag. */ public function append_start_tag( $tag_name, $atts = array(), $tag = '' ) { if ( ! self::validate_tag_name( $tag_name ) ) { wp_trigger_error( __METHOD__, sprintf( /* translators: %s: Invalid HTML tag name */ __( 'Invalid tag name (%s) is specified.', 'contact-form-7' ), $tag_name ), E_USER_WARNING ); return false; } if ( WP_DEBUG and ! empty( $this->options['allowed_html'] ) ) { $html_disallowance = array(); if ( ! isset( $this->options['allowed_html'][$tag_name] ) ) { $html_disallowance = array( 'element' => $tag_name, ); } else { $atts_allowed = $this->options['allowed_html'][$tag_name]; $atts_disallowed = array_diff_ukey( $atts, $atts_allowed, static function ( $key_1, $key_2 ) use ( $atts_allowed ) { if ( str_starts_with( $key_1, 'data-' ) and ! empty( $atts_allowed['data-*'] ) and preg_match( '/^data-[a-z0-9_-]+$/', $key_1 ) ) { return 0; } else { return $key_1 === $key_2 ? 0 : 1; } } ); if ( ! empty( $atts_disallowed ) ) { $html_disallowance = array( 'element' => $tag_name, 'attributes' => array_keys( $atts_disallowed ), ); } } if ( $html_disallowance ) { $notice = sprintf( /* translators: %s: JSON-formatted array of disallowed HTML */ __( 'HTML Disallowance: %s', 'contact-form-7' ), wp_json_encode( $html_disallowance, JSON_PRETTY_PRINT ) ); wp_trigger_error( __METHOD__, $notice, E_USER_NOTICE ); } } if ( 'p' === $tag_name or in_array( $tag_name, self::p_parent_elements, true ) or in_array( $tag_name, self::p_nonparent_elements, true ) ) { // Close
if it exists. $this->end_tag( 'p' ); } if ( 'dd' === $tag_name or 'dt' === $tag_name ) { // Close
\s*<\/p>$/', '', $this->output ); } /** * Closes all open tags. */ public function close_all_tags() { while ( $element = array_shift( $this->stacked_elements ) ) { $this->append_end_tag( $element ); } } /** * Appends an HTML comment to the output property. * * @param string $tag An HTML comment. */ public function append_comment( $tag ) { $this->append_preformatted( $tag ); } /** * Returns true if it is currently inside one of HTML elements specified * by tag names. * * @param string|array $tag_names A tag name or an array of tag names. */ public function is_inside( $tag_names ) { $tag_names = (array) $tag_names; foreach ( $this->stacked_elements as $element ) { if ( in_array( $element, $tag_names, true ) ) { return true; } } return false; } /** * Returns true if the parent node is one of HTML elements specified * by tag names. * * @param string|array $tag_names A tag name or an array of tag names. */ public function has_parent( $tag_names ) { $tag_names = (array) $tag_names; $parent = reset( $this->stacked_elements ); if ( false === $parent ) { return false; } return in_array( $parent, $tag_names, true ); } /** * Calls the callback given by the first parameter. The buffered output * will be appended to the output property. */ public function call_user_func( $callback, ...$args ) { ob_start(); $result = call_user_func( $callback, ...$args ); $output = ob_get_clean(); if ( false !== $output ) { $this->append_preformatted( "\n" . $output . "\n" ); } return $result; } /** * Closes all remaining tags, returns and resets the output. */ public function output() { $this->close_all_tags(); $output = $this->output; $this->output = ''; return $output; } /** * Prints the output. Returns false if the allowed_html option is empty. */ public function print() { if ( empty( $this->options['allowed_html'] ) ) { return false; } echo wp_kses( $this->output(), $this->options['allowed_html'] ); } /** * Calculates the position of the next chunk based on the position and * length of the current chunk. * * @param array $chunk An associative array of the current chunk. * @return int The position of the next chunk. */ public static function calc_next_position( $chunk ) { return $chunk['position'] + strlen( $chunk['content'] ); } /** * Outputs a set of tabs to indent. * * @param int $level Indentation level. * @return string A series of tabs. */ public static function indent( $level ) { $level = (int) $level; if ( 0 < $level ) { return str_repeat( "\t", $level ); } return ''; } /** * Normalizes a start tag. * * @param string $tag A start tag or a tag name. * @return array An array includes the normalized start tag and tag name. */ public static function normalize_start_tag( $tag ) { if ( preg_match( '/<(.+?)[\s\/>]/', $tag, $matches ) ) { $tag_name = strtolower( $matches[1] ); } else { $tag_name = strtolower( $tag ); $tag = sprintf( '<%s>', $tag_name ); } if ( in_array( $tag_name, self::void_elements, true ) ) { // Normalize void element. $tag = preg_replace( '/\s*\/?>/', ' />', $tag ); } return array( $tag, $tag_name ); } /** * Normalizes a paragraph of text. * * @param string $paragraph A paragraph of text. * @param bool $auto_br Optional. If true, line breaks will be replaced * by a br element. * @return string The normalized paragraph. */ public static function normalize_paragraph( $paragraph, $auto_br = false ) { if ( $auto_br ) { $paragraph = preg_replace( '/\s*\n\s*/', "\n", $paragraph ); } $paragraph = preg_replace( '/[ ]+/', ' ', $paragraph ); return $paragraph; } /** * Returns true if the specified tag name is valid. */ public static function validate_tag_name( $tag_name ) { return preg_match( '/^[a-z][0-9a-z]*(?:[:][a-z][0-9a-z]*)?$/', $tag_name ); } }