%PDF- %PDF-
Direktori : /var/www/tif-dev/wp-content/plugins/gravityforms/includes/libraries/ |
Current File : //var/www/tif-dev/wp-content/plugins/gravityforms/includes/libraries/class-dom-parser.php |
<?php namespace Gravity_Forms\Gravity_Forms\Libraries; class Dom_Parser { const NO_PARSE_QUERY_ARG = 'gf_disable_hooks_injection'; /** * The string representation of the current DOM. * * @since 2.5.6 * * @var string $content */ public $content; /** * A DOMDocument object made by calling loadXML(). * * @since 2.5.6 * * @var DOMDocument $dom_xml */ public $dom_xml; /** * A DOMDocument object made by calling loadHTML(). * * @since 2.5.6 * * @var DOMDocument $dom_html */ public $dom_html; /** * Whether the current server has DOMDocument active. * * @since 2.5.6 * * @var bool $has_domdocument */ public $has_domdocument; /** * The position at which to insert the hooks script. * * @since 2.5.6 * * @var int $insert_position */ public $insert_position = 0; /** * GF_Dom_Parser constructor. * * @param string $content */ public function __construct( $content ) { $this->content = $content; $this->has_domdocument = class_exists( 'DOMDocument' ); if ( ! $this->has_domdocument || empty( $this->content ) ) { return; } $this->parse_dom(); } /** * Parse the DOM content into XML and HTML DOMDocuments. * * @since 2.5.6 * * @return void */ private function parse_dom() { libxml_use_internal_errors( true ); $this->dom_xml = $this->get_dom_xml(); $this->dom_html = $this->get_dom_html(); libxml_clear_errors(); } /** * Callback to fire when ob_flush() is called. Allows us to ensure that our Hooks JS has been output on the page, * even in heavily-cached or concatenated environments. * * @since 2.5.6 * * @return string */ public function get_injected_html() { require_once \GFCommon::get_base_path() . '/form_display.php'; $has_printed = \GFFormDisplay::$hooks_js_printed; /** * Allow plugins to force the hook vars to output no matter what. Useful for certain edge-cases. * * @since 2.5.3 * * @param bool $force_output Whether to force the script output. * * @return bool */ $force_output = apply_filters( 'gform_force_hooks_js_output', false ); if ( ! $force_output && ! $has_printed ) { return $this->content; } if ( ! $this->should_inject_hooks_js() ) { return $this->content; } return $this->inject_hooks_js(); } /** * Take the given DOM Content and inject the Hooks JS code in the correct position. * * @return string */ private function inject_hooks_js() { $insert_position = $this->get_insert_position(); $hooks_javascript = \GFCommon::get_hooks_javascript_code(); $content = str_replace( $hooks_javascript, '', $this->content ); $string = \GFCommon::get_inline_script_tag( $hooks_javascript ); $pieces = preg_split( "/\r\n|\n|\r/", $content ); if ( count( $pieces ) > 1 && $insert_position > 0 ) { array_splice( $pieces, $insert_position, 0, $string ); $content = implode( "\n", $pieces ); } else { $content = preg_replace( '/(<[\s]*head(?!e)[^>]*>)/', '$0 ' . $string, $this->content, 1 ); } return $content; } /** * There are some contexts in which we do not want to inject our Hooks JS. This determines * whether we are in one of those contexts. * * @since 2.5.6 * * @return bool */ public function should_inject_hooks_js() { if ( ! $this->is_parseable_request() ) { return false; } if ( $this->is_xml() ) { return false; } if ( ! $this->is_full_html_doc() ) { return false; } if ( $this->is_amp() ) { return false; } return true; } /** * Attempt to parse the DOM Content into a DOMDocument XML model. * * @since 2.5.6 * * @return DOMDocument|false */ private function get_dom_xml() { if ( empty( $this->content ) ) { return false; } try { $xdom = new \DOMDocument(); $xdom->loadXML( $this->content ); return $xdom; } catch ( \Exception $e ) { return false; } } /** * Attempt to parse the DOM Content into a DOMDocument HTML model. * * @since 2.5.6 * * @return DOMDocument|false */ private function get_dom_html() { if ( empty( $this->content ) ) { return false; } try { $dom = new \DOMDocument(); $dom->loadHTML( $this->content ); return $dom; } catch ( \Exception $e ) { return false; } } /** * Get the correct line position at which the Hooks JS should be inserted. A location of 0 will result * in the script being added right after the opening <head> tag, while anything greater will inject it * at the defined line number. * * @since 2.5.6 * * @return int */ private function get_insert_position() { // Default to 0 to inject right after head. $insert_position = 0; $insert_el = false; if ( ! $this->has_domdocument ) { return 0; } if ( ! $this->dom_html ) { return 0; } $meta_els = $this->dom_html->getElementsByTagName( 'meta' ); foreach ( $meta_els as $meta_el ) { if ( // Some charsets are defined via a charset attribute $meta_el->hasAttribute( 'charset' ) || // Other charsets are defined via a combo of http-equiv and content attritbutes ( $meta_el->hasAttribute( 'http-equiv' ) && $meta_el->hasAttribute( 'content' ) ) ) { $insert_position = $meta_el->getLineNo(); $insert_el = $meta_el; } } if ( $insert_position === 0 ) { return $insert_position; } $pieces = preg_split( "/\r\n|\n|\r/", $this->content ); $previous = $pieces[ $insert_position - 1 ]; // Only use injection position if the detected line # actually falls after the meta tag. preg_match( '/<\s*meta[^>]*>$/', $previous, $pos_matches ); if ( empty( $pos_matches ) ) { return 0; } return $insert_position; } /** * Determine if the current server request is one which requires us to add our hooks scripts. * * @since 2.5.6 * * @return bool */ public function is_parseable_request() { if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { return false; } if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { return false; } if ( ! empty( $_POST['gform_ajax'] ) ) { return false; } $query_arg = rgget( self::NO_PARSE_QUERY_ARG ); if ( ! empty( $query_arg ) ) { return false; } if ( empty( $this->content ) ) { return false; } return true; } /** * Determine if the current document is an XML document. * * @since 2.5.6 * * @return bool */ public function is_xml() { if ( ! $this->has_domdocument ) { return false; } if ( ! $this->dom_xml ) { return false; } if ( ! is_null( $this->dom_xml->documentElement ) && $this->dom_xml->documentElement->tagName !== 'html' ) { return true; } return false; } /** * Determine if the current document has the required (<html>, <head>) elements, and thus * should be treated as a full doc. * * @since 2.5.6 * * @return bool */ public function is_full_html_doc() { if ( ! $this->has_domdocument ) { return $this->has_head_regex(); } if ( ! $this->dom_html ) { return false; } $html = $this->dom_html->getElementsByTagName( 'html' ); $head = $this->dom_html->getElementsByTagName( 'head' ); // No HTML tag or head tag - we shouldn't mess with this so we bail. if ( empty( $head->length ) || empty( $html->length ) ) { return false; } return true; } /** * If the current server doesn't have DOMDocument defined, use a regex method to find the * <head> element. Less reliable than the DOMDocument method, but a decent fallback. * * @since 2.5.6 * * @return bool */ private function has_head_regex() { preg_match( '/(<[\s]*head(?!e)[^>]*>)/', $content, $hmatches ); if ( empty( $hmatches ) ) { return false; } return true; } /** * Determine whether the current document is an AMP document. * * @since 2.5.6 * * @return bool */ public function is_amp() { if ( ! $this->has_domdocument || ! $this->dom_html ) { return $this->is_amp_regex(); } $html = $this->dom_html->getElementsByTagName( 'html' ); $html_el = $html[0]; // Markup is AMP using the amp attribute - bail. if ( $html_el->hasAttribute( 'amp' ) ) { return true; } // Pattern copied from the official AMP project Repository: https://github.com/ampproject/amp-toolbox-php $pattern = '/<html\s([^>]*?(?:' . "\xE2\x9A\xA1" . '|' . "\xE2\x9A\xA1\xEF\xB8\x8F" . ')[^>]*?)>/i'; preg_match( $pattern, $this->content, $emoji_matches ); // Markup is AMP using the ⚡ symbol - bail. if ( ! empty( $emoji_matches ) ) { return true; } return false; } /** * If the current server doesn't have DOMDocument defined, use a regex method to detect AMP. * * @since 2.5.6 * * @return bool */ private function is_amp_regex() { // Bail if this markup is AMP'd preg_match( '/^<!DOCTYPE html>[\r\n]*<[\s]*html[\s]+[^>]*amp[=\s>]+/i', trim( $content ), $amatches ); if ( ! empty( $amatches ) ) { return true; } } }