Clean the chunk list by verifying that open and closing tags are nested correctly.

@param array $chunks
@param array $wrapper
@return string
518211
protected function _cleanChunks(array $chunks, array $wrapper = array()) {
        $clean = array();
        $openTags = array();
        $prevChunk = array();
        $disallowed = array();
        $parents = array();
        $depths = array();
        $count = count($chunks);
        $i = 0;

        if ($wrapper) {
            $parent = $this->getFilterByTag($wrapper['tag'])->getTag($wrapper['tag']);
            $root = false;
        } else {
            $parent = $this->getFilter('Empty')->getTag('root');
            $root = true;
        }

        while ($i < $count) {
            $chunk = $chunks[$i];
            $tag = isset($chunk['tag']) ? $chunk['tag'] : '';

            switch ($chunk['type']) {
                case self::TAG_NONE:
                    // Disregard deeply nested text nodes if persist is disabled
                    if ($disallowed && !$parent['persistContent']) {
                        break;
                    }

                    if (!$parent['onlyTags']) {
                        if (!empty($prevChunk) && $prevChunk['type'] === self::TAG_NONE) {
                            $chunk['text'] = $prevChunk['text'] . $chunk['text'];
                            array_pop($clean);
                        }

                        $clean[] = $chunk;
                    }
                break;

                case self::TAG_OPEN:
                    if ($parent['maxChildDepth'] >= 0 && !isset($depths[$tag])) {
                        $depths[$tag] = 1;
                        $parent['currentDepth'] = $depths[$tag];

                    } else if (isset($depths[$tag])) {
                        $depths[$tag] += 1;
                        $parent['currentDepth'] = $depths[$tag];
                    }

                    if ($this->_isAllowed($parent, $tag)) {
                        $prevParent = $parent;
                        $parents[] = $parent;
                        $parent = $this->getFilterByTag($tag)->getTag($tag);

                        // Don't parse Decoda tags if preserve is disabled
                        if ($prevParent['preserveTags']) {
                            $chunk['type'] = self::TAG_NONE;
                            $parent['preserveTags'] = true;
                        }

                        $clean[] = $chunk;

                        if ($root) {
                            $openTags[] = array('tag' => $tag, 'index' => $i);
                        }
                    } else {
                        $disallowed[] = array('tag' => $tag, 'index' => $i);
                    }
                break;

                case self::TAG_CLOSE:
                    // Reduce depth
                    if (isset($depths[$tag])) {
                        $depths[$tag] -= 1;
                    }

                    // If something is not allowed, skip the close tag
                    if ($disallowed) {
                        $last = end($disallowed);

                        if ($last['tag'] === $tag) {
                            array_pop($disallowed);
                            break;
                        }
                    }

                    // Return to previous parent before allowing
                    if ($parents) {
                        $parent = array_pop($parents);
                    }

                    // Now check for open tags if the tag is allowed
                    if ($this->_isAllowed($parent, $tag)) {
                        if ($parent['preserveTags']) {
                            $chunk['type'] = self::TAG_NONE;
                        }

                        $clean[] = $chunk;

                        if ($root && $openTags) {
                            $last = end($openTags);

                            if ($last['tag'] === $tag) {
                                array_pop($openTags);
                            } else {
                                while ($openTags) {
                                    $last = array_pop($openTags);

                                    if ($last['tag'] !== $tag) {
                                        $this->_errors[] = array(
                                            'type' => self::ERROR_NESTING,
                                            'tag' => $last['tag']
                                        );

                                        unset($clean[$last['index']]);
                                    }
                                }
                            }
                        }
                    }
                break;

                case self::TAG_SELF_CLOSE:
                    $clean[] = $chunk;
                break;
            }

            $i++;
            $prevChunk = $chunk;
        }

        // Remove any unclosed tags
        while ($openTags) {
            $last = array_pop($openTags);

            $this->_errors[] = array(
                'type' => self::ERROR_CLOSING,
                'tag' => $last['tag']
            );

            unset($clean[$last['index']]);
        }

        return array_values($clean);
    }
1149785
public function getChunkIterator($refresh = false)
    {
        if ($refresh || $this->_chunkIterator === null) {
            $this->_chunkIterator = new \IteratorIterator($this->getChunkCursor($refresh));
            $this->_chunkIterator->rewind();
        }
        return $this->_chunkIterator;
    }
355692
static function stripTags(Parser &$parser, $input = '') {
	    $parser->disableCache ();

	    $output = strip_tags($input, '');
	    $output = trim(str_replace(array("\r\n", "\n", "\r", "\n\r", "\t"), ' ', $output));
	    	    
		return array($output, 'noparse' => true);
	}
10224
def remove_chunk_layer(self):
        
        if self.chunk_layer is not None:
            this_node = self.chunk_layer.get_node()
            self.root.remove(this_node)
            self.chunk_layer = None

        if self.header is not None:
            self.header.remove_lp('chunks')
669062
private function cleanHeaders(array $headers)
    {
        $cleaned = [];

        foreach ($headers as $header => $value) {
            if (is_array($value)) {
                $value = array_map('strval', $value);
            } else {
                $value = [(string) $value];
            }

            $cleaned[strtolower($header)] = $value;
        }

        return $cleaned;
    }
1170170
public static function wrapItems(array $items, $wrapper = "'")
    {
        return array_map(function ($item) use ($wrapper) {
            $item = str_replace($wrapper, '\\' . $wrapper, trim($item, $wrapper));

            return sprintf('%s%s%s', $wrapper, $item, $wrapper);
        }, $items);
    }
940727
protected function Chunks(){
		$level = 0;
		$parenLevel = 0;
		$lastMultiCommentEndBrace = null;
		$lastOpening = null;
		$lastMultiComment = null;
		$lastParen = null;

		for( $this->parserCurrentIndex = 0; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ){
			$cc = $this->CharCode($this->parserCurrentIndex);
			if ((($cc >= 97) && ($cc <= 122)) || ($cc < 34)) {
				// a-z or whitespace
				continue;
			}

			switch ($cc) {

				// (
				case 40:
					$parenLevel++;
					$lastParen = $this->parserCurrentIndex;
					break;

				// )
				case 41:
					$parenLevel--;
					if( $parenLevel < 0 ){
						return $this->fail("missing opening `(`");
					}
					break;

				// ;
				case 59:
					//if (!$parenLevel) { $this->emitChunk();	}
					break;

				// {
				case 123:
					$level++;
					$lastOpening = $this->parserCurrentIndex;
					break;

				// }
				case 125:
					$level--;
					if( $level < 0 ){
						return $this->fail("missing opening `{`");

					}
					//if (!$level && !$parenLevel) { $this->emitChunk(); }
					break;
				// \
				case 92:
					if ($this->parserCurrentIndex < $this->input_len - 1) { $this->parserCurrentIndex++; break; }
					return $this->fail("unescaped `\\`");

				// ", ' and `
				case 34:
				case 39:
				case 96:
					$matched = 0;
					$currentChunkStartIndex = $this->parserCurrentIndex;
					for ($this->parserCurrentIndex = $this->parserCurrentIndex + 1; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
						$cc2 = $this->CharCode($this->parserCurrentIndex);
						if ($cc2 > 96) { continue; }
						if ($cc2 == $cc) { $matched = 1; break; }
						if ($cc2 == 92) {        // \
							if ($this->parserCurrentIndex == $this->input_len - 1) {
								return $this->fail("unescaped `\\`");
							}
							$this->parserCurrentIndex++;
						}
					}
					if ($matched) { break; }
					return $this->fail("unmatched `" . chr($cc) . "`", $currentChunkStartIndex);

				// /, check for comment
				case 47:
					if ($parenLevel || ($this->parserCurrentIndex == $this->input_len - 1)) { break; }
					$cc2 = $this->CharCode($this->parserCurrentIndex+1);
					if ($cc2 == 47) {
						// //, find lnfeed
						for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
							$cc2 = $this->CharCode($this->parserCurrentIndex);
							if (($cc2 <= 13) && (($cc2 == 10) || ($cc2 == 13))) { break; }
						}
					} else if ($cc2 == 42) {
						// /*, find */
						$lastMultiComment = $currentChunkStartIndex = $this->parserCurrentIndex;
						for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len - 1; $this->parserCurrentIndex++) {
							$cc2 = $this->CharCode($this->parserCurrentIndex);
							if ($cc2 == 125) { $lastMultiCommentEndBrace = $this->parserCurrentIndex; }
							if ($cc2 != 42) { continue; }
							if ($this->CharCode($this->parserCurrentIndex+1) == 47) { break; }
						}
						if ($this->parserCurrentIndex == $this->input_len - 1) {
							return $this->fail("missing closing `*/`", $currentChunkStartIndex);
						}
					}
					break;

				// *, check for unmatched */
				case 42:
					if (($this->parserCurrentIndex < $this->input_len - 1) && ($this->CharCode($this->parserCurrentIndex+1) == 47)) {
						return $this->fail("unmatched `/*`");
					}
					break;
			}
		}

		if( $level !== 0 ){
			if( ($lastMultiComment > $lastOpening) && ($lastMultiCommentEndBrace > $lastMultiComment) ){
				return $this->fail("missing closing `}` or `*/`", $lastOpening);
			} else {
				return $this->fail("missing closing `}`", $lastOpening);
			}
		} else if ( $parenLevel !== 0 ){
			return $this->fail("missing closing `)`", $lastParen);
		}


		//chunk didn't fail


		//$this->emitChunk(true);
	}
595766
static function cleanBuffers(?int $targetLevel = null, bool $catchExceptions = false): bool
    {
        $level = 0;
        $stopLevel = $targetLevel ?? 0;

        foreach (self::iterateBufferLevels(PHP_OUTPUT_HANDLER_CLEANABLE) as $level) {
            if ($level <= $stopLevel) {
                break;
            }

            try {
                ob_end_clean();
            } catch (\Throwable $e) {
                if (!$catchExceptions) {
                    throw $e;
                }
            }
        }

        return $targetLevel === null || $level <= $targetLevel;
    }
858826
public function removeWrapper($content, $wrapperTag, $wrapperClass) {
		// Trim content from the white characters, so we can spot the wrapper
		// starting exactly from the beginning.
		$content = trim($content);

		// wrapper strings start && end
		$wrap = array(
			"<$wrapperTag class=\"$wrapperClass\">",
			"</$wrapperTag>",
		);

		// Only proceed if wrapper is found exactly at the beginning of the content
		if (0 !== strpos($content, $wrap[0])) {
			return $content;
		}

		$start  = strlen($wrap[0]);
		$length = strlen($content) - $start - strlen($wrap[1]);
		$content = substr($content, $start, $length);

		// In development context add extra HTML comments with info, what is what
		if ($this->isDevelopmentEnvironment() && ($contentCollectionNode = $this->getContentCollectionNode())) {
			// ContentCollection: add context path info around it
			if (strstr($wrapperClass, 'contentcollection')) {
				$contextPath = $contentCollectionNode->getContextPath();
				$content = "<!-- content collection START: $contextPath -->" . $content . "<!-- content collection END: $contextPath -->";
			}
			// Other NodeTypes: add node element name
			elseif (($node = $this->getNode())) {
				$nodeType = $node->getNodeType()->getName();
				$content  = "<!-- $nodeType -->" . $content . "<!-- /$nodeType -->";
			}
		}

		return $content;
	}
1167080
public function deleteContextEntry($wrapper, $name)
	{
		// Check whether the wrapper is set
		if (isset($this->contextOptions[$wrapper]))
		{
			// Check that entry is set for that wrapper
			if (isset($this->contextOptions[$wrapper][$name]))
			{
				// Unset the item
				unset($this->contextOptions[$wrapper][$name]);

				// Check that there are still items there
				if (!\count($this->contextOptions[$wrapper]))
				{
					// Clean up an empty wrapper context option
					unset($this->contextOptions[$wrapper]);
				}
			}
		}

		// Rebuild the context and apply it to the stream
		$this->_buildContext();
	}
418905
public static String join(final Collection<String> chunks,
    final String delimiter) {
    StringBuilder sb = new StringBuilder();
    if (!chunks.isEmpty()) {
      Iterator<String> itr = chunks.iterator();
      sb.append(itr.next());
      while (itr.hasNext()) {
        sb.append(delimiter);
        sb.append(itr.next());
      }
    }
    return sb.toString();
  }
835769
private function mustSeparate($chunks)
    {
        $found = null;

        foreach ($chunks as $chunk) {
            if ($chunk !== '000') {
                break;
            }
        }

        if ($chunk < 100 || !($chunk % 100)) {
            return true;
        }

        return false;
    }
311482
public static function wrapClosures(&$data, SplObjectStorage $storage = null)
    {
        static::enterContext();

        if($storage === null){
            $storage = static::$context->scope;
        }

        if($data instanceof Closure){
            $data = static::from($data);
        } elseif (is_array($data)){
            if(isset($data[self::ARRAY_RECURSIVE_KEY])){
                return;
            }
            $data[self::ARRAY_RECURSIVE_KEY] = true;
            foreach ($data as $key => &$value){
                if($key === self::ARRAY_RECURSIVE_KEY){
                    continue;
                }
                static::wrapClosures($value, $storage);
            }
            unset($value);
            unset($data[self::ARRAY_RECURSIVE_KEY]);
        } elseif($data instanceof \stdClass){
            if(isset($storage[$data])){
                $data = $storage[$data];
                return;
            }
            $data = $storage[$data] = clone($data);
            foreach ($data as &$value){
                static::wrapClosures($value, $storage);
            }
            unset($value);
        } elseif (is_object($data) && ! $data instanceof static){
            if(isset($storage[$data])){
                $data = $storage[$data];
                return;
            }
            $instance = $data;
            $reflection = new ReflectionObject($instance);
            if(!$reflection->isUserDefined()){
                $storage[$instance] = $data;
                return;
            }
            $storage[$instance] = $data = $reflection->newInstanceWithoutConstructor();

            do{
                if(!$reflection->isUserDefined()){
                    break;
                }
                foreach ($reflection->getProperties() as $property){
                    if($property->isStatic() || !$property->getDeclaringClass()->isUserDefined()){
                        continue;
                    }
                    $property->setAccessible(true);
                    $value = $property->getValue($instance);
                    if(is_array($value) || is_object($value)){
                        static::wrapClosures($value, $storage);
                    }
                    $property->setValue($data, $value);
                };
            } while($reflection = $reflection->getParentClass());
        }

        static::exitContext();
    }
112012
public static function cleanTags($tagsStr)
    {
        $tagsArray = explode("\n", str_replace(["\r", ','], "\n", $tagsStr));
        $cleanTags = [];
        foreach ($tagsArray as $tag) {
            $tag = trim($tag);
            if ($tag !== '') {
                $cleanTags[] = $tag;
            }
        }
        //the leading and trailing line break char is for searching: fvTag like %\ntag\n%
        return isset($cleanTags[0]) ? "\n" . implode("\n", $cleanTags) . "\n" : '';
    }
1032461
protected function postprocess_chunk($data) {

        // Iterate over all the data tags, if any of them is
        // not 'subplugin_XXXX' or has value, then it's a valid chunk,
        // pass it to standard (parent) processing of chunks.
        foreach ($data['tags'] as $key => $value) {
            if (trim($value) !== '' || strpos($key, 'subplugin_') !== 0) {
                parent::postprocess_chunk($data);
                return;
            }
        }
        // Arrived here, all the tags correspond to sublplugins and are empty,
        // skip the chunk, and debug_developer notice
        $this->chunks--; // not counted
        debugging('Missing support on restore for ' . clean_param($data['path'], PARAM_PATH) .
                  ' subplugin (' . implode(', ', array_keys($data['tags'])) .')', DEBUG_DEVELOPER);
    }
269192
public function wrap($str, $wrap)
    {
        return (is_string($str) && $str !== '') ? $wrap.$str.$wrap : $str;
    }
1216659
private function unchunk( string$chunked ):string
	{
		return preg_replace_callback(
			'/(?:(?:\r\n|\n)|^)([0-9A-F]+)(?:\r\n|\n){1,2}(.*?)((?:\r\n|\n)(?:[0-9A-F]+(?:\r\n|\n))|$)/si'
		,
			function( array$matches ){
				return hexdec( $matches[1] )==strlen( $matches[2] )? $matches[2] : $matches[0];
			}
		,
			$chunked
		);
	}