Ejemplo n.º 1
0
 protected function parse($usage)
 {
     $tag = new Tag();
     $bbcode = new BBCode();
     $config = array('tag' => $tag, 'bbcode' => $bbcode, 'passthroughToken' => \null);
     $usage = \preg_replace_callback('#(\\{(?>HASH)?MAP=)([^:]+:[^,;}]+(?>,[^:]+:[^,;}]+)*)(?=[;}])#', function ($m) {
         return $m[1] . \base64_encode($m[2]);
     }, $usage);
     $usage = \preg_replace_callback('#(\\{(?:PARSE|REGEXP)=)(' . self::REGEXP . '(?:,' . self::REGEXP . ')*)#', function ($m) {
         return $m[1] . \base64_encode($m[2]);
     }, $usage);
     $regexp = '(^' . '\\[(?<bbcodeName>\\S+?)' . '(?<defaultAttribute>=\\S+?)?' . '(?<attributes>(?:\\s+[^=]+=\\S+?)*?)?' . '\\s*(?:/?\\]|\\]\\s*(?<content>.*?)\\s*(?<endTag>\\[/\\1]))$)i';
     if (!\preg_match($regexp, \trim($usage), $m)) {
         throw new InvalidArgumentException('Cannot interpret the BBCode definition');
     }
     $config['bbcodeName'] = BBCode::normalizeName($m['bbcodeName']);
     $definitions = \preg_split('#\\s+#', \trim($m['attributes']), -1, \PREG_SPLIT_NO_EMPTY);
     if (!empty($m['defaultAttribute'])) {
         \array_unshift($definitions, $m['bbcodeName'] . $m['defaultAttribute']);
     }
     if (!empty($m['content'])) {
         $regexp = '#^\\{' . RegexpBuilder::fromList($this->unfilteredTokens) . '[0-9]*\\}$#D';
         if (\preg_match($regexp, $m['content'])) {
             $config['passthroughToken'] = \substr($m['content'], 1, -1);
         } else {
             $definitions[] = 'content=' . $m['content'];
             $bbcode->contentAttributes[] = 'content';
         }
     }
     $attributeDefinitions = array();
     foreach ($definitions as $definition) {
         $pos = \strpos($definition, '=');
         $name = \substr($definition, 0, $pos);
         $value = \substr($definition, 1 + $pos);
         $value = \preg_replace_callback('#(\\{(?>HASHMAP|MAP|PARSE|REGEXP)=)([A-Za-z0-9+/]+=*)#', function ($m) {
             return $m[1] . \base64_decode($m[2]);
         }, $value);
         if ($name[0] === '$') {
             $optionName = \substr($name, 1);
             $bbcode->{$optionName} = $this->convertValue($value);
         } elseif ($name[0] === '#') {
             $ruleName = \substr($name, 1);
             foreach (\explode(',', $value) as $value) {
                 $tag->rules->{$ruleName}($this->convertValue($value));
             }
         } else {
             $attrName = \strtolower(\trim($name));
             $attributeDefinitions[] = array($attrName, $value);
         }
     }
     $tokens = $this->addAttributes($attributeDefinitions, $bbcode, $tag);
     if (isset($tokens[$config['passthroughToken']])) {
         $config['passthroughToken'] = \null;
     }
     $config['tokens'] = \array_filter($tokens);
     return $config;
 }
Ejemplo n.º 2
0
 /**
  * Get a BBCode and its associated tag from this repository
  *
  * @param  string $name Name of the entry in the repository
  * @param  array  $vars Replacement variables
  * @return array        Array with three elements: "bbcode", "name" and "tag"
  */
 public function get($name, array $vars = [])
 {
     // Everything before # should be a BBCode name
     $name = preg_replace_callback('/^[^#]+/', function ($m) {
         return BBCode::normalizeName($m[0]);
     }, $name);
     $xpath = new DOMXPath($this->dom);
     $node = $xpath->query('//bbcode[@name="' . htmlspecialchars($name) . '"]')->item(0);
     if (!$node instanceof DOMElement) {
         throw new RuntimeException("Could not find '" . $name . "' in repository");
     }
     // Clone the node so we don't end up modifying the node in the repository
     $clonedNode = $node->cloneNode(true);
     // Replace all the <var> descendants if applicable
     foreach ($xpath->query('.//var', $clonedNode) as $varNode) {
         $varName = $varNode->getAttribute('name');
         if (isset($vars[$varName])) {
             $varNode->parentNode->replaceChild($this->dom->createTextNode($vars[$varName]), $varNode);
         }
     }
     // Now we can parse the BBCode usage and prepare the template.
     // Grab the content of the <usage> element then use BBCodeMonkey to parse it
     $usage = $xpath->evaluate('string(usage)', $clonedNode);
     $template = $xpath->evaluate('string(template)', $clonedNode);
     $config = $this->bbcodeMonkey->create($usage, $template);
     $bbcode = $config['bbcode'];
     $bbcodeName = $config['bbcodeName'];
     $tag = $config['tag'];
     // Set the optional tag name
     if ($node->hasAttribute('tagName')) {
         $bbcode->tagName = $node->getAttribute('tagName');
     }
     // Set the rules
     foreach ($xpath->query('rules/*', $node) as $ruleNode) {
         $methodName = $ruleNode->nodeName;
         $args = [];
         if ($ruleNode->textContent) {
             $args[] = $ruleNode->textContent;
         }
         call_user_func_array([$tag->rules, $methodName], $args);
     }
     // Set predefined attributes
     foreach ($node->getElementsByTagName('predefinedAttributes') as $predefinedAttributes) {
         foreach ($predefinedAttributes->attributes as $attribute) {
             $bbcode->predefinedAttributes->set($attribute->name, $attribute->value);
         }
     }
     return ['bbcode' => $bbcode, 'bbcodeName' => $bbcodeName, 'tag' => $tag];
 }
Ejemplo n.º 3
0
 public function get($name, array $vars = array())
 {
     $name = \preg_replace_callback('/^[^#]+/', function ($m) {
         return BBCode::normalizeName($m[0]);
     }, $name);
     $xpath = new DOMXPath($this->dom);
     $node = $xpath->query('//bbcode[@name="' . \htmlspecialchars($name) . '"]')->item(0);
     if (!$node instanceof DOMElement) {
         throw new RuntimeException("Could not find '" . $name . "' in repository");
     }
     $clonedNode = $node->cloneNode(\true);
     foreach ($xpath->query('.//var', $clonedNode) as $varNode) {
         $varName = $varNode->getAttribute('name');
         if (isset($vars[$varName])) {
             $varNode->parentNode->replaceChild($this->dom->createTextNode($vars[$varName]), $varNode);
         }
     }
     $usage = $xpath->evaluate('string(usage)', $clonedNode);
     $template = $xpath->evaluate('string(template)', $clonedNode);
     $config = $this->bbcodeMonkey->create($usage, $template);
     $bbcode = $config['bbcode'];
     $bbcodeName = $config['bbcodeName'];
     $tag = $config['tag'];
     if ($node->hasAttribute('tagName')) {
         $bbcode->tagName = $node->getAttribute('tagName');
     }
     foreach ($xpath->query('rules/*', $node) as $ruleNode) {
         $methodName = $ruleNode->nodeName;
         $args = array();
         if ($ruleNode->textContent) {
             $args[] = $ruleNode->textContent;
         }
         \call_user_func_array(array($tag->rules, $methodName), $args);
     }
     foreach ($node->getElementsByTagName('predefinedAttributes') as $predefinedAttributes) {
         foreach ($predefinedAttributes->attributes as $attribute) {
             $bbcode->predefinedAttributes->set($attribute->name, $attribute->value);
         }
     }
     return array('bbcode' => $bbcode, 'bbcodeName' => $bbcodeName, 'tag' => $tag);
 }
Ejemplo n.º 4
0
 /**
  * {@inheritdoc}
  */
 public function normalizeKey($key)
 {
     return BBCode::normalizeName($key);
 }
Ejemplo n.º 5
0
 /**
  * @testdox asConfig() returns predefinedAttributes in a Dictionary
  */
 public function testAsConfigPredefinedAttributesDictionary()
 {
     $bbcode = new BBCode();
     $bbcode->predefinedAttributes = ['foo' => 'bar'];
     $this->assertEquals(['predefinedAttributes' => new Dictionary(['foo' => 'bar'])], $bbcode->asConfig());
 }
Ejemplo n.º 6
0
 /**
  * Create a BBCode based on its reference usage
  *
  * @param  string $usage BBCode usage, e.g. [B]{TEXT}[/b]
  * @return array
  */
 protected function parse($usage)
 {
     $tag = new Tag();
     $bbcode = new BBCode();
     // This is the config we will return
     $config = ['tag' => $tag, 'bbcode' => $bbcode, 'passthroughToken' => null];
     // Encode maps to avoid special characters to interfere with definitions
     $usage = preg_replace_callback('#(\\{(?>HASH)?MAP=)([^:]+:[^,;}]+(?>,[^:]+:[^,;}]+)*)(?=[;}])#', function ($m) {
         return $m[1] . base64_encode($m[2]);
     }, $usage);
     // Encode regexps to avoid special characters to interfere with definitions
     $usage = preg_replace_callback('#(\\{(?:PARSE|REGEXP)=)(' . self::REGEXP . '(?:,' . self::REGEXP . ')*)#', function ($m) {
         return $m[1] . base64_encode($m[2]);
     }, $usage);
     $regexp = '(^' . '\\[(?<bbcodeName>\\S+?)' . '(?<defaultAttribute>=\\S+?)?' . '(?<attributes>(?:\\s+[^=]+=\\S+?)*?)?' . '\\s*(?:/?\\]|\\]\\s*(?<content>.*?)\\s*(?<endTag>\\[/\\1]))' . '$)i';
     if (!preg_match($regexp, trim($usage), $m)) {
         throw new InvalidArgumentException('Cannot interpret the BBCode definition');
     }
     // Save the BBCode's name
     $config['bbcodeName'] = BBCode::normalizeName($m['bbcodeName']);
     // Prepare the attributes definition, e.g. "foo={BAR}"
     $definitions = preg_split('#\\s+#', trim($m['attributes']), -1, PREG_SPLIT_NO_EMPTY);
     // If there's a default attribute, we prepend it to the list using the BBCode's name as
     // attribute name
     if (!empty($m['defaultAttribute'])) {
         array_unshift($definitions, $m['bbcodeName'] . $m['defaultAttribute']);
     }
     // Append the content token to the attributes list under the name "content" if it's anything
     // but raw {TEXT} (or other unfiltered tokens)
     if (!empty($m['content'])) {
         $regexp = '#^\\{' . RegexpBuilder::fromList($this->unfilteredTokens) . '[0-9]*\\}$#D';
         if (preg_match($regexp, $m['content'])) {
             $config['passthroughToken'] = substr($m['content'], 1, -1);
         } else {
             $definitions[] = 'content=' . $m['content'];
             $bbcode->contentAttributes[] = 'content';
         }
     }
     // Separate the attribute definitions from the BBCode options
     $attributeDefinitions = [];
     foreach ($definitions as $definition) {
         $pos = strpos($definition, '=');
         $name = substr($definition, 0, $pos);
         $value = substr($definition, 1 + $pos);
         // Decode base64-encoded tokens
         $value = preg_replace_callback('#(\\{(?>HASHMAP|MAP|PARSE|REGEXP)=)([A-Za-z0-9+/]+=*)#', function ($m) {
             return $m[1] . base64_decode($m[2]);
         }, $value);
         // If name starts with $ then it's a BBCode options, if it starts with # it's a rule and
         // otherwise it's an attribute definition
         if ($name[0] === '$') {
             $optionName = substr($name, 1);
             $bbcode->{$optionName} = $this->convertValue($value);
         } elseif ($name[0] === '#') {
             $ruleName = substr($name, 1);
             // Supports #denyChild=foo,bar
             foreach (explode(',', $value) as $value) {
                 $tag->rules->{$ruleName}($this->convertValue($value));
             }
         } else {
             $attrName = strtolower(trim($name));
             $attributeDefinitions[] = [$attrName, $value];
         }
     }
     // Add the attributes and get the token translation table
     $tokens = $this->addAttributes($attributeDefinitions, $bbcode, $tag);
     // Test whether the passthrough token is used for something else, in which case we need
     // to unset it
     if (isset($tokens[$config['passthroughToken']])) {
         $config['passthroughToken'] = null;
     }
     // Add the list of known (and only the known) tokens to the config
     $config['tokens'] = array_filter($tokens);
     return $config;
 }