Parses the end of a tag

@param resource $parser
@param string   $tag

@return NULL
345586
protected function end($parser, $tag)
    {
        //Make the tag lower case, for consistency
        $tag = strtolower($tag);

        //Add the data to the paths that require it
        $data = '</'.$tag.'>';
        $this->addData($parser, $data);

        //Loop through each callback and see if the path matches the
        //current path
        foreach ($this->callbacks as $path => $callbacks) {
            //If parsing should continue, and the paths match, then a callback
            //needs to be made
            if ($this->parse && $this->currentPath === $path) {
                if (!$this->fireCallbacks($path, $callbacks)) {
                    break;
                }
            }
        }

        //Unset the path data for this path, as it's no longer needed
        unset($this->pathData[$this->currentPath]);

        //Update the path with the new path (effectively moving up a directory)
        $this->currentPath = substr(
            $this->currentPath,
            0,
            strlen($this->currentPath) - (strlen($tag) + 1)
        );

        return $parser;
    }
41665
function parse() {
    $this->output = '';
    # drop tags
    $this->parser->html = preg_replace('#<('.implode('|', $this->drop).')[^>]*>.*</\\1>#sU', '', $this->parser->html);
    while ($this->parser->nextNode()) {
      switch ($this->parser->nodeType) {
        case 'doctype':
          break;
        case 'pi':
        case 'comment':
          if ($this->keepHTML) {
            $this->flushLinebreaks();
            $this->out($this->parser->node);
            $this->setLineBreaks(2);
          }
          # else drop
          break;
        case 'text':
          $this->handleText();
          break;
        case 'tag':
          if (in_array($this->parser->tagName, $this->ignore)) {
            break;
          }
          if ($this->parser->isStartTag) {
            $this->flushLinebreaks();
          }
          if ($this->skipConversion) {
            $this->isMarkdownable(); # update notConverted
            $this->handleTagToText();
            continue;
          }
          if (!$this->parser->keepWhitespace && $this->parser->isBlockElement && $this->parser->isStartTag) {
            $this->parser->html = ltrim($this->parser->html);
          }
          if ($this->isMarkdownable()) {
            if ($this->parser->isBlockElement && $this->parser->isStartTag && !$this->lastWasBlockTag && !empty($this->output)) {
              if (!empty($this->buffer)) {
                $str =& $this->buffer[count($this->buffer) -1];
              } else {
                $str =& $this->output;
              }
              if (substr($str, -strlen($this->indent)-1) != "\n".$this->indent) {
                $str .= "\n".$this->indent;
              }
            }
            $func = 'handleTag_'.$this->parser->tagName;
            $this->$func();
            if ($this->linksAfterEachParagraph && $this->parser->isBlockElement && !$this->parser->isStartTag && empty($this->parser->openTags)) {
              $this->flushStacked();
            }
            if (!$this->parser->isStartTag) {
              $this->lastClosedTag = $this->parser->tagName;
            }
          } else {
            $this->handleTagToText();
            $this->lastClosedTag = '';
          }
          break;
        default:
          trigger_error('invalid node type', E_USER_ERROR);
          break;
      }
      $this->lastWasBlockTag = $this->parser->nodeType == 'tag' && $this->parser->isStartTag && $this->parser->isBlockElement;
    }
    if (!empty($this->buffer)) {
      trigger_error('buffer was not flushed, this is a bug. please report!', E_USER_WARNING);
      while (!empty($this->buffer)) {
        $this->out($this->unbuffer());
      }
    }
    ### cleanup
    $this->output = rtrim(str_replace('&amp;', '&', str_replace('&lt;', '<', str_replace('&gt;', '>', $this->output))));
    # end parsing, flush stacked tags
    $this->flushStacked();
    $this->stack = array();
  }
53730
function endElement($parser, $name) {
    if (substr($this->elementValue, 0, 1) == "\n") $this->elementValue = substr($this->elementValue, 1);
    $this->deserializedResponse[$this->elementName]= $this->elementValue;
    $this->elementName = "";
    $this->elementValue = "";
  }
1112034
public function parse(Token $token)
    {
        $parser = $this->parser;
        $stream = $parser->getStream();

        $default = null;
        $cases = [];
        $end = false;

        $name = $parser->getExpressionParser()->parseExpression();
        $stream->expect(Token::BLOCK_END_TYPE);
        $stream->expect(Token::TEXT_TYPE);
        $stream->expect(Token::BLOCK_START_TYPE);
        while (!$end) {
            /** @var Token $v */
            $v = $stream->next();
            switch ($v->getValue()) {
                case 'default':
                    $stream->expect(Token::BLOCK_END_TYPE);
                    $default = $parser->subparse([$this, 'decideIfEnd']);
                    break;

                case 'case':
                    $expr = $parser->getExpressionParser()->parseExpression();
                    $stream->expect(Token::BLOCK_END_TYPE);
                    $body = $parser->subparse([$this, 'decideIfFork']);
                    $cases[] = $expr;
                    $cases[] = $body;
                    break;

                case 'endswitch':
                    $end = true;
                    break;

                default:
                    $message = sprintf('Unexpected end of template. Twig was looking for the following tags "case", "default", or "endswitch" to close the "switch" block started at line %d)', $v->getLine());

                    throw new SyntaxError($message, $v->getLine());
            }
        }

        $stream->expect(Token::BLOCK_END_TYPE);

        return new SwitchNode($name, new Node($cases), $default, $token->getLine(), $this->getTag());
    }
1367098
public function endXML($parser, $name)
    {
        --$this->xml_depth;
        if ($this->xml_depth === 1) {
            foreach ($this->xpathhandlers as $handler) {
                if (is_array($this->xmlobj) && array_key_exists(2, $this->xmlobj)) {
                    $searchxml = $this->xmlobj[2];
                    $nstag = array_shift($handler[0]);
                    if (($nstag[0] === null or $searchxml->ns === $nstag[0]) and ($nstag[1] === "*" or $nstag[1] === $searchxml->name)) {
                        foreach ($handler[0] as $nstag) {
                            if ($searchxml !== null and $searchxml->hasSub($nstag[1], $ns = $nstag[0])) {
                                $searchxml = $searchxml->sub($nstag[1], $ns = $nstag[0]);
                            } else {
                                $searchxml = null;
                                break;
                            }
                        }
                        if ($searchxml !== null) {
                            if ($handler[2] === null) {
                                $handler[2] = $this;
                            }
                            $handler[1]($this->xmlobj[2]);
                        }
                    }
                }
            }
            foreach ($this->idhandlers as $id => $handler) {
                if (array_key_exists('id', $this->xmlobj[2]->attrs) and $this->xmlobj[2]->attrs['id'] == $id) {
                    $handler($this->xmlobj[2]);
                    #id handlers are only used once
                    unset($this->idhandlers[$id]);
                    break;
                }
            }
            if (is_array($this->xmlobj)) {
                $this->xmlobj = array_slice($this->xmlobj, 0, 1);
                if (isset($this->xmlobj[0]) && $this->xmlobj[0] instanceof XMLStreamObject) {
                    $this->xmlobj[0]->subs = null;
                }
            }
            unset($this->xmlobj[2]);
        }
        if ($this->xml_depth === 0) {
            $this->event('streamEnd');
        }
    }
1356443
def handle_endtag(self, tag):
        
        self.log.debug( u'Encountered an end tag : {0}'.format(tag) )
        if tag in self.sanitizelist:
            self.level -= 1
            return
        if tag in self.unclosedTags:
            return
        if self.isNotPurify or tag in self.whitelist_keys:
            self.data.append(u'</%s>' % tag)
429547
public function parse(Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        $stream->expect(Token::BLOCK_END_TYPE);
        $try = $this->parser->subparse([$this, 'decideCatch']);
        $stream->next();
        $stream->expect(Token::BLOCK_END_TYPE);
        $catch = $this->parser->subparse([$this, 'decideEnd']);
        $stream->next();
        $stream->expect(Token::BLOCK_END_TYPE);

        return new TwigNodeTryCatch($try, $catch, $lineno, $this->getTag());
    }
311319
function it_should_parse_complete_flashes_tag(
        $parser, $exprParser, $stream, $body, $nodeFactory, $node, $token, $types, $catalog
    )
    {
        $stream->test(\Twig_Token::BLOCK_END_TYPE)->willReturn(false);
        $stream->test(\Twig_Token::NAME_TYPE, 'using')->willReturn(false, true);

        $stream->expect(Argument::any())->will(function() {});
        $stream->expect(Argument::cetera())->will(function() {});

        $exprParser->parseExpression()->willReturn($types, $catalog);

        $parser->subparse(array($this, 'isEndTag'), true)->willReturn($body);

        $nodeFactory->createFlashesNode($types, $catalog, $body, 123)->willReturn($node);

        $this->parse($token)->shouldReturn($node);
    }
824404
private function _parseTag($tag)
        {   
//          return nothing if the tag is disabled
            if(isset($tag['attributes']->disabled) === true && $tag['attributes']->disabled === 'true')
            {
                return '';
            }
            
            $tag_data = false;
            $caching_tag = isset($tag['attributes']->cache) && $tag['attributes']->cache === 'false' ? false : true;
            if($this->_options['cache_tags'] === true && $caching_tag === true)
            {
                if($this->_options['custom_cache_tag_class'] !== false)
                {
                    $tag_data = call_user_func_array(array($this->_options['custom_cache_tag_class'], 'getCache'), array($tag));
                }
                else 
                {
                    $cache_file = $this->_options['cache_directory'].md5(serialize($tag));
                    if(is_file($cache_file) === true)
                    {
                        $tag_data = file_get_contents($cache_file);
                    }
                }
                if($tag_data)
                {
                    $tag['cached'] = true;
                    return $tag_data;
                }
            }    
            
//          look for and load tag function file
            $tag_func_name = ucwords(str_replace(array('_', '-'), ' ', $tag['name']));
            $tag_func_name = strtolower(substr($tag_func_name, 0, 1)).substr($tag_func_name, 1);
            $func_name = str_replace(' ', '', $this->_options['tag_callback_prefix'].$tag_func_name);
            $tag_data = '';
            $collect_tag = false;
            $tag_order = false;
            if(function_exists($func_name) === false)
            {
                $has_resource = false;
                if(is_array($this->_options['tag_directory']) === false)
                {
                    $this->_options['tag_directory'] = array($this->_options['tag_directory']);
                }
                foreach ($this->_options['tag_directory'] as $directory)
                {
                    $tag_file = rtrim($directory, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$tag['name'].DIRECTORY_SEPARATOR.'tag.php';
                    if(is_file($tag_file) === true)
                    {
                        if(isset(self::$_required[$tag['name']]) === false)
                        {
                            self::$_required[$tag['name']] = true;
                            $collect = false;
                            if(function_exists($func_name) === false)
                            {
                                include_once $tag_file;
                            }
                            self::$_tags_to_collect[$tag['name']] = $collect;
                            if(function_exists($func_name) === false)
                            {
                                return self::throwError($tag['name'], 'tag resource "'.$directory.DIRECTORY_SEPARATOR.'tag.php" found but callback "'.$func_name.'" wasn\'t.');
                            }
                            $has_resource = true;
                        }
                    }
                }
                if($has_resource === false)
                {
                    return self::throwError($tag['name'], 'tag resource not found.');
                } 
            }
            
//          do we have to collect this tag for parsing at a later point
            if(self::$_tags_to_collect[$tag['name']] !== false)
            {
                if(isset(self::$tag_collections[$tag['name']]) === false)
                {
                    self::$tag_collections[$tag['name']] = array();
                }
                $tag['collected'] = true;
                $index = array_push(self::$tag_collections[$tag['name']], $tag)-1;
                if($tag_order !== false)
                {
                    if(isset(self::$_tag_order[$block['name']]) === false)
                    {
                        self::$_tag_order[$block['name']] = array();
                    }
                    self::$_tag_order[$block['name']] = $tag_order;
                }
                return self::$tag_collections[$tag['name']][$index]['tag'] = '------@@%'.self::$_instance.'-'.$tag['name'].'-'.$index.'-'.uniqid(time().'-').'%@@------';
            }
            
//          excecute the tag callback
            if($this->_options['tag_global_callback'] !== false)
            {
                $tag_data = trim(call_user_func($this->_options['tag_global_callback'], 'tag', $func_name, $tag));
            }
            else
            {
                $tag_data = trim(call_user_func($func_name, $tag));
            }
            
//          this is where we sniff for buried tags within the returned content
            if(empty($tag_data) === false)
            {
                if($this->_options['sniff_for_buried_tags'] === true && strpos($tag_data, self::$tag_open.$this->_options['tag_name'].':') !== false)
                {
//                  we have the possibility of buried tags so lets parse
//                  but first make sure the output isn't echoed out
                    $old_echo_value = $this->_options['echo_output'];
                    $this->_options['echo_output'] = false; 
//                  parse the tag_data 
                    $tag_data = $this->parse($tag_data, false, true);
//                  restore the echo_output value back to what it was originally
                    $this->_options['echo_output'] = $old_echo_value;
                }
                
                if($this->_options['cache_tags'] === true && $caching_tag === true)
                {
                    if($this->_options['custom_cache_tag_class'] !== false)
                    {
                        call_user_func_array(array($this->_options['custom_cache_tag_class'], 'cache'), array($tag, $tag_data));
                    }
                    else
                    {
                        file_put_contents($this->_options['cache_directory'].md5(serialize($tag)), $tag_data, LOCK_EX);
                    }
                }
            }
            return $tag_data;
        }
999982
protected function endElement($parser, string $name): void
    {
        $method = $name . 'EndHandler';

        if (method_exists($this, $method)) {
            $this->$method();
        }
    }
1053146
private function parse_tag(string $text){
		$res = [];
		$text = $this->clear_text($text);
		$tag_end = strpos($text, ' ');
		if ( $tag_end !== false ){
			// Get tag
			$res['tag'] = substr($text, 1, $tag_end - 1);
			if ( in_array($res['tag'], array_keys($this->tags)) ){
        if (
          $this->tag_has_text($res['tag']) &&
          ($text = substr($text, $tag_end + 1))
        ){
          $res['text'] = $this->clear_text($text);
        }
				else {
					// Get type
					if (
            $this->tag_has_type($res['tag']) &&
            ($type = $this->tag_get_type($text)) &&
            !empty($type[1])
          ){
						$res['type'] = $type[1][0];
					}
					// Get default value
					if (
            $this->tag_has_default($res['tag']) &&
            ($def = $this->tag_get_default($text)) &&
            !empty($def[1])
          ){
						$res['default'] = $def[1][0];
					}
					// Get name
					if ( isset($def[1]) ){
						$n = $def[0][1] + strlen($def[0][0]) + 1;
					}
					else if ( isset($type[1]) ){
						$n = $type[0][1] + strlen($type[0][0]) + 1;
					}
					else {
						$n = $tag_end + 1;
					}
					if (
            $this->tag_has_name($res['tag']) &&
						($name = $this->tag_get_name(substr($text, $n)))
					){
						$res['name'] = $this->clear_text($name[0][0]);
					}
					// Get description
					if ( isset($name[0]) ){
						$d = $n + $name[0][1] + strlen($name[0][0]) + 1;
					}
					else if ( isset($type[1]) ){
						$d = $type[0][1] + strlen($type[0][0]) + 1;
					}
          else {
						$d = $tag_end + 1;
					}
					if (
            $this->tag_has_desc($res['tag']) &&
            ($desc = substr($text, $d))
          ){
						$res['description'] = $this->clear_text($desc);
					}
				}

				return $res;
			}
		}
		return false;
	}
1262534
protected function closeAncestor(Tag $tag)
	{
		if (!empty($this->openTags))
		{
			$tagName   = $tag->getName();
			$tagConfig = $this->tagsConfig[$tagName];

			if (!empty($tagConfig['rules']['closeAncestor']))
			{
				$i = count($this->openTags);

				while (--$i >= 0)
				{
					$ancestor     = $this->openTags[$i];
					$ancestorName = $ancestor->getName();

					if (isset($tagConfig['rules']['closeAncestor'][$ancestorName]))
					{
						++$this->currentFixingCost;

						// We have to close this ancestor. First we reinsert this tag...
						$this->tagStack[] = $tag;

						// ...then we add a new end tag for it with a better priority
						$this->addMagicEndTag($ancestor, $tag->getPos(), $tag->getSortPriority() - 1);

						return true;
					}
				}
			}
		}

		return false;
	}
492580
public function _if($tag,$content) {
        $condition  =   $this->parseCondition($tag['condition']);
        $parseStr   =   '<?php if('.$condition.'): ?>'.$content.'<?php endif; ?>';
        return $parseStr;
    }
60839
private function retrieve_tag($tag)
    {
        if (isset($this->tags[$tag . 'count'])) { //if the openener is not in the Object we ignore it
            $temp_parent = $this->tags['parent']; //check to see if it's a closable tag.
            while ($temp_parent) { //till we reach '' (the initial value);
                if ($tag . $this->tags[$tag . 'count'] === $temp_parent) { //if this is it use it
                    break;
                }
                $temp_parent = isset($this->tags[$temp_parent . 'parent']) ? $this->tags[$temp_parent . 'parent'] : ''; //otherwise keep on climbing up the DOM Tree
            }
            if ($temp_parent) { //if we caught something
                $this->indent_level = $this->tags[$tag . $this->tags[$tag . 'count']]; //set the indent_level accordingly
                $this->tags['parent'] = $this->tags[$temp_parent . 'parent']; //and set the current parent
            }
            unset($this->tags[$tag . $this->tags[$tag . 'count'] . 'parent']); //delete the closed tags parent reference...
            unset($this->tags[$tag . $this->tags[$tag . 'count']]); //...and the tag itself
            if ($this->tags[$tag . 'count'] === 1) {
                unset($this->tags[$tag . 'count']);
            } else {
                $this->tags[$tag . 'count']--;
            }
        }
    }
200540
function readCloseTag() {
		let start = getSourcePoint(),
			match = tagCloseRegex.exec(source.substr(pos))
		if (!match) {
			throwSyntaxError('Invalid close tag')
		}
		advanceTo(pos + match[0].length)
		let end = getSourcePoint()
		return {
			type: 'tag-close',
			start,
			end,
			name: match[1].toLowerCase()
		}
	}
127062
protected function handleTagOpenForObject( $parser, $tag, $attributes )
	{
		$count		= count( $this->last ) - 1;
		$parentNode	=& $this->last[$count];
		$childNode	= new XML_DOM_Node(
			$tag,
			"",
			$attributes
		);
		$parentNode->addChild( $childNode );
		$this->last[]	=& $childNode;
	}
565902
protected function start($parser, $tag, $attributes)
    {
        //Set the tag as lower case, for consistency
        $tag = strtolower($tag);

        //Update the current path
        $this->currentPath .= $tag.'/';

        $this->fireCurrentAttributesCallbacks($attributes);

        //Go through each callback and ensure that path data has been
        //started for it
        foreach ($this->callbacks as $path => $callbacks) {
            if ($path === $this->currentPath) {
                $this->pathData[$this->currentPath] = '';
            }
        }

        //Generate the tag, with attributes. Attribute names are also lower
        //cased, for consistency
        $data = '<'.$tag;
        foreach ($attributes as $key => $val) {
            $options = ENT_QUOTES;
            if (defined('ENT_XML1')) {
                $options |= ENT_XML1;
            }

            $val   = htmlentities($val, $options, "UTF-8");
            $data .= ' '.strtolower($key).'="'.$val.'"';

            if (stripos($key, 'xmlns:') !== false) {
                $key = strtolower($key);
                $key = str_replace('xmlns:', '', $key);
                $this->namespaces[strtolower($key)] = $val;
            }
        }
        $data .= '>';

        //Add the data to the path data required
        $this->addData($parser, $data);

        return $parser;
    }