collection = new RulesGeneratorList; $this->collection->append('AutoCloseIfVoid'); $this->collection->append('AutoReopenFormattingElements'); $this->collection->append('BlockElementsCloseFormattingElements'); $this->collection->append('BlockElementsFosterFormattingElements'); $this->collection->append('DisableAutoLineBreaksIfNewLinesArePreserved'); $this->collection->append('EnforceContentModels'); $this->collection->append('EnforceOptionalEndTags'); $this->collection->append('IgnoreTagsInCode'); $this->collection->append('IgnoreTextIfDisallowed'); $this->collection->append('IgnoreWhitespaceAroundBlockElements'); $this->collection->append('TrimFirstLineInCodeBlocks'); } /** * Generate rules for given tag collection * * @param TagCollection $tags Tags collection * @return array */ public function getRules(TagCollection $tags) { $tagInspectors = $this->getTagInspectors($tags); return [ 'root' => $this->generateRootRules($tagInspectors), 'tags' => $this->generateTagRules($tagInspectors) ]; } /** * Generate and return rules based on a set of TemplateInspector * * @param array $tagInspectors Array of [tagName => TemplateInspector] * @return array Array of [tagName => []] */ protected function generateTagRules(array $tagInspectors) { $rules = []; foreach ($tagInspectors as $tagName => $tagInspector) { $rules[$tagName] = $this->generateRuleset($tagInspector, $tagInspectors); } return $rules; } /** * Generate a set of rules to be applied at the root of a document * * @param array $tagInspectors Array of [tagName => TemplateInspector] * @return array */ protected function generateRootRules(array $tagInspectors) { // Create a proxy for the parent markup so that we can determine which tags are allowed at // the root of the text (IOW, with no parent) or even disabled altogether $rootInspector = new TemplateInspector('
'); $rules = $this->generateRuleset($rootInspector, $tagInspectors); // Remove root rules that wouldn't be applied anyway unset($rules['autoClose']); unset($rules['autoReopen']); unset($rules['breakParagraph']); unset($rules['closeAncestor']); unset($rules['closeParent']); unset($rules['fosterParent']); unset($rules['ignoreSurroundingWhitespace']); unset($rules['isTransparent']); unset($rules['requireAncestor']); unset($rules['requireParent']); return $rules; } /** * Generate a set of rules for a single TemplateInspector instance * * @param TemplateInspector $srcInspector Source of the rules * @param array $trgInspectors Array of [tagName => TemplateInspector] * @return array */ protected function generateRuleset(TemplateInspector $srcInspector, array $trgInspectors) { $rules = []; foreach ($this->collection as $rulesGenerator) { if ($rulesGenerator instanceof BooleanRulesGenerator) { foreach ($rulesGenerator->generateBooleanRules($srcInspector) as $ruleName => $bool) { $rules[$ruleName] = $bool; } } if ($rulesGenerator instanceof TargetedRulesGenerator) { foreach ($trgInspectors as $tagName => $trgInspector) { $targetedRules = $rulesGenerator->generateTargetedRules($srcInspector, $trgInspector); foreach ($targetedRules as $ruleName) { $rules[$ruleName][] = $tagName; } } } } return $rules; } /** * Inspect given list of tags * * @param TagCollection $tags Tags collection * @return array Array of [tagName => TemplateInspector] */ protected function getTagInspectors(TagCollection $tags) { $tagInspectors = []; foreach ($tags as $tagName => $tag) { // Use the tag's template if applicable or XSLT's implicit default otherwise $template = $tag->template ?? ''; $tagInspectors[$tagName] = new TemplateInspector($template); } return $tagInspectors; } }