*/ public $token_seen_count = array(); /** * Previous depth. * * @var int */ private $previous_depth = 0; /** * Open stack indices. * * @since n.e.x.t * @var array */ private $open_stack_indices = array(); /** * Gets XPath for the current open tag. * * @return string XPath. */ public function get_xpath(): string { $xpath = ''; foreach ( $this->open_stack_indices as $level ) { $xpath .= sprintf( '/*[%d][self::%s]', $level['index'] + 1, $level['tag_name'] ); } return $xpath; } /** * Gets next token. * * @return bool Whether next token was matched. */ public function next_token(): bool { $result = parent::next_token(); $current_depth = $this->get_current_depth(); $current_tag = $this->get_tag(); $current_depth--; // Because HTML starts at depth 1. if ( $this->get_token_type() === '#tag' ) { $token_name = ( $this->is_tag_closer() ? '-' : '+' ) . $current_tag; } else { $token_name = $this->get_token_name(); } if ( ! isset( $this->token_seen_count[ $token_name ] ) ) { $this->token_seen_count[ $token_name ] = 1; } else { ++$this->token_seen_count[ $token_name ]; } if ( $this->get_token_type() === '#tag' && ! $this->is_tag_closer() ) { if ( $current_depth < $this->previous_depth ) { array_splice( $this->open_stack_indices, $current_depth + 1 ); } if ( ! isset( $this->open_stack_indices[ $current_depth ] ) ) { $this->open_stack_indices[ $current_depth ] = array( 'tag_name' => $current_tag, 'index' => 0, ); } else { $this->open_stack_indices[ $current_depth ]['tag_name'] = $current_tag; ++$this->open_stack_indices[ $current_depth ]['index']; } $this->previous_depth = $current_depth; } return $result; } }