Completed
Push — master ( e2485c...87ab5d )
by Josh
19:03
created

Repository::loadRepository()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 3
nop 1
crap 3
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2018 The s9e Authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\TextFormatter\Plugins\BBCodes\Configurator;
9
10
use DOMDocument;
11
use DOMElement;
12
use DOMXPath;
13
use InvalidArgumentException;
14
use RuntimeException;
15
16
class Repository
17
{
18
	/**
19
	* @var BBCodeMonkey Instance of BBCodeMonkey used to parse definitions
20
	*/
21
	protected $bbcodeMonkey;
22
23
	/**
24
	* @var DOMDocument Repository document
25
	*/
26
	protected $dom;
27
28
	/**
29
	* Constructor
30
	*
31
	* @param  mixed        $value        Either a DOMDocument or the path to a repository's XML file
32
	* @param  BBCodeMonkey $bbcodeMonkey Instance of BBCodeMonkey used to parse definitions
33
	*/
34 15
	public function __construct($value, BBCodeMonkey $bbcodeMonkey)
35
	{
36 15
		$this->bbcodeMonkey = $bbcodeMonkey;
37 15
		$this->dom          = ($value instanceof DOMDocument) ? $value : $this->loadRepository($value);
38 13
	}
39
40
	/**
41
	* Get a BBCode and its associated tag from this repository
42
	*
43
	* @param  string $name Name of the entry in the repository
44
	* @param  array  $vars Replacement variables
45
	* @return array        Array with three elements: "bbcode", "name" and "tag"
46
	*/
47 11
	public function get($name, array $vars = [])
48
	{
49
		// Everything before # should be a BBCode name
50 11
		$name = preg_replace_callback(
51 11
			'/^[^#]+/',
52 11
			function ($m)
53
			{
54 11
				return BBCode::normalizeName($m[0]);
55 11
			},
56 11
			$name
57
		);
58
59 11
		$xpath = new DOMXPath($this->dom);
60 11
		$node  = $xpath->query('//bbcode[@name="' . htmlspecialchars($name) . '"]')->item(0);
61
62 11
		if (!($node instanceof DOMElement))
63
		{
64 1
			throw new RuntimeException("Could not find '" . $name . "' in repository");
65
		}
66
67
		// Clone the node so we don't end up modifying the node in the repository
68 10
		$clonedNode = $node->cloneNode(true);
69
70
		// Replace all the <var> descendants if applicable
71 10
		foreach ($xpath->query('.//var', $clonedNode) as $varNode)
72
		{
73 5
			$varName = $varNode->getAttribute('name');
74
75 5
			if (isset($vars[$varName]))
76
			{
77 4
				$varNode->parentNode->replaceChild(
78 4
					$this->dom->createTextNode($vars[$varName]),
79 5
					$varNode
80
				);
81
			}
82
		}
83
84
		// Now we can parse the BBCode usage and prepare the template.
85
		// Grab the content of the <usage> element then use BBCodeMonkey to parse it
86 10
		$usage      = $xpath->evaluate('string(usage)', $clonedNode);
87 10
		$template   = $xpath->evaluate('string(template)', $clonedNode);
88 10
		$config     = $this->bbcodeMonkey->create($usage, $template);
89 10
		$bbcode     = $config['bbcode'];
90 10
		$bbcodeName = $config['bbcodeName'];
91 10
		$tag        = $config['tag'];
92
93
		// Set the optional tag name
94 10
		if ($node->hasAttribute('tagName'))
95
		{
96 1
			$bbcode->tagName = $node->getAttribute('tagName');
97
		}
98
99
		// Set the rules
100 10
		foreach ($xpath->query('rules/*', $node) as $ruleNode)
101
		{
102 2
			$methodName = $ruleNode->nodeName;
103 2
			$args       = [];
104
105 2
			if ($ruleNode->textContent)
106
			{
107 1
				$args[] = $ruleNode->textContent;
108
			}
109
110 2
			call_user_func_array([$tag->rules, $methodName], $args);
111
		}
112
113
		return [
114 10
			'bbcode'     => $bbcode,
115 10
			'bbcodeName' => $bbcodeName,
116 10
			'tag'        => $tag
117
		];
118
	}
119
120
	/**
121
	* Create an exception for a bad repository file path
122
	*
123
	* @param  mixed $filepath
124
	* @return InvalidArgumentException
125
	*/
126 2
	protected function createRepositoryException($filepath)
127
	{
128 2
		return new InvalidArgumentException(var_export($filepath, true) . ' is not a valid BBCode repository file');
129
	}
130
131
	/**
132
	* Load a repository file into a DOMDocument
133
	*
134
	* @param  string $filepath
135
	* @return DOMDocument
136
	*/
137 5
	protected function loadRepository($filepath)
138
	{
139 5
		if (!file_exists($filepath))
140
		{
141 1
			throw $this->createRepositoryException($filepath);
142
		}
143
144 4
		$dom = new DOMDocument;
145 4
		$dom->preserveWhiteSpace = false;
146 4
		if (!$dom->loadXML(file_get_contents($filepath), LIBXML_NOERROR))
147
		{
148 1
			throw $this->createRepositoryException($filepath);
149
		}
150
151 3
		return $dom;
152
	}
153
}