Completed
Push — master ( dfc8a8...b88b69 )
by Yann
02:12
created

GenericHTMLConverter::buildElement()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 2
1
<?php
2
3
namespace OpenMindParser\Converters\HTML;
4
5
use OpenMindParser\Converters\Model\AbstractConverter;
6
use OpenMindParser\Models\Document;
7
use OpenMindParser\Models\Node;
8
use OpenMindParser\Parser;
9
use \DOMDocument;
10
use \InvalidArgumentException;
11
12
/*A singleton to convert a document tree (object Document) in a HTML tree following few options.*/
13
class GenericHTMLConverter extends AbstractConverter
14
{ 
0 ignored issues
show
Coding Style introduced by
The opening class brace should be on a newline by itself.
Loading history...
15
	/**
16
	 * String MAIN_TAG_KEY : An option key name for the list of HTML tags to use.
17
	 */
18
	const MAIN_TAG_KEY = 'tags';
19
	/**
20
	 * String TAG_KEY : An option key name for the HTML tag to use.
21
	 */
22
	const TAG_KEY = 'tag';
23
	/**
24
	 * String ATTRIBUTES_KEY : An option key name for the HTML attributes to use on the tag.
25
	 */
26
	const ATTRIBUTES_KEY = 'attributes'; 
27
	/**
28
	 * String MAIN_TAG_MERGE_STYLE : An option key name for a boolean to choose if document fonts and colors will be used in HTML rendering (true by default).
29
	 */
30
	const MAIN_TAG_MERGE_STYLE = 'merge_style'; 
31
	/**
32
	 * String MAIN_TAG_MERGE_STYLE : An option key name for a boolean to choose if texts set as bold and/or italic in the document will be set as it.
33
	 */
34
	const MAIN_TAG_MERGE_DECORATION = 'merge_decoration'; 
35
	/**
36
	 * String MAIN_ICON_KEY : An option key name for the icon tag to use.
37
	 */
38
	const MAIN_ICON_KEY = 'icon'; 
39
	/**
40
	 * String DISPLAY_ICON_KEY : A boolean to determine if for nodes with an icon, the icon will be added in a img tag before the text.
41
	 */
42
	 const DISPLAY_ICON_KEY = 'display';
43
	/**
44
	 * String PATH_ICON_KEY : Key for options to generate the icon path.
45
	 */
46
	 const PATH_ICON_KEY = 'path';
47
	/**
48
	 * String CALLBACK_PATH_ICON_KEY : A callback to generate the icon uri as wished. 
49
	 * Its first parameter is the icon full name (with extension (i.e : 'button_ok.png')).
50
	 * Signature : function($fullName, $options = null);
51
	 */
52
	 const CALLBACK_PATH_ICON_KEY = 'callback';
53
	/**
54
	 * String OPTIONS_PATH_ICON_KEY : An array of options given as a second parameter to the callback.
55
	 */
56
	 const OPTIONS_PATH_ICON_KEY = 'options';
57
	
58
	/**
59
	 * Call the conversion over the Document instance.
60
	 * 
61
	 * @var Document $document : The document instance to convert.
62
	 * @var Array $options : The options for the conversion. 
63
	 * It must follow this filling : 
64
	 * [
65
	 * 		MAIN_TAG_KEY              =>
66
	 * 			[
67
	 * 				TAG_KEY        => 'First wrapping HTML tag to use (ul, div, ...)',
68
	 * 				ATTRIBUTES_KEY => ['attribute name (class, id, href, ...)' => 'attribute value', ...]
69
	 * 			],
70
	 * 			[
71
	 * 				TAG_KEY        => 'Second wrapping HTML tag to use (ul, div, ...)',
72
	 * 				ATTRIBUTES_KEY => ['attribute name (class, id, href, ...)' => 'attribute value', ...]
73
	 * 			],
74
	 * 			[
75
	 * 				TAG_KEY        => 'Third wrapping HTML tag to use (ul, div, ...) (optionnal)',
76
	 * 				ATTRIBUTES_KEY => ['attribute name (class, id, href, ...)' => 'attribute value', ...]
77
	 * 			],
78
	 * 		MAIN_TAG_MERGE_STYLE      =>  A boolean to choose if document fonts and colors will be used in HTML rendering (true by default),
79
	 * 		MAIN_TAG_MERGE_DECORATION =>  A boolean to choose if texts set as bold and/or italic in the document will be set as it. (true by default),
80
	 * 		MAIN_ICON_KEY        => [
81
	 * 			DISPLAY_ICON_KEY => boolean determining if the icons must be displayed or not,
82
	 * 			PATH_ICON_KEY    => [
83
	 * 				'CALLBACK_PATH_ICON_KEY' => 'the callback to use to determine uri. Its first parameter will be the file full name, and the second an optional array with additional parametrs.',
84
	 * 				'OPTIONS_PATH_ICON_KEY'  => [array of options for the callback],
85
	 * 			],
86
	 * 		],
87
	 * ]. The first two are mandatory, and the style stored in the Node instance (color, font name and font size) will be applied on the second tag).
88
	 * 
89
	 * @return DOMDocument $domDocument : The DOMDocument instance created with the HTML.
90
	 */
91
	protected function convertFromDocumentInstance(Document $document, array $options) {
92
		$domDocument = new DOMDocument();
93
		
94
		$domDocument->appendChild($this->buildHTMLTreeFromNode($domDocument, $document->getRootNode(), $options));
95
		
96
		return $domDocument;
97
	}
98
	
99
	/**
100
	 * Perform the conversion over the Document instance. 
101
	 * Each text will be wrap in at least two HTML tags (<ul><li>, <div><span>, ...) and at most three tags (<ul><li><span>, ...).
102
	 * The style stored in the Node instance (color, font name and font size) will be applied on the second tag and override the "style" attribute if one is given.
103
	 * 
104
	 * @var Document $document : The document instance to convert.
105
	 * @var Array $options : The options for the conversion. 
106
	 * It must follow this filling : 
107
	 * [
108
	 * 		MAIN_TAG_KEY              =>
109
	 * 			[
110
	 * 				TAG_KEY        => 'First wrapping HTML tag to use (ul, div, ...)',
111
	 * 				ATTRIBUTES_KEY => ['attribute name (class, id, href, ...)' => 'attribute value', ...]
112
	 * 			],
113
	 * 			[
114
	 * 				TAG_KEY        => 'Second wrapping HTML tag to use (ul, div, ...)',
115
	 * 				ATTRIBUTES_KEY => ['attribute name (class, id, href, ...)' => 'attribute value', ...]
116
	 * 			],
117
	 * 			[
118
	 * 				TAG_KEY        => 'Third wrapping HTML tag to use (ul, div, ...) (optionnal)',
119
	 * 				ATTRIBUTES_KEY => ['attribute name (class, id, href, ...)' => 'attribute value', ...]
120
	 * 			],
121
	 * 		MAIN_TAG_MERGE_STYLE      =>  A boolean to choose if document fonts and colors will be used in HTML rendering (true by default),
122
	 * 		MAIN_TAG_MERGE_DECORATION =>  A boolean to choose if texts set as bold and/or italic in the document will be set as it. (true by default),
123
	 * 		MAIN_ICON_KEY        => [
124
	 * 			DISPLAY_ICON_KEY => boolean determining if the icons must be displayed or not,
125
	 * 			PATH_ICON_KEY    => [
126
	 * 				'CALLBACK_PATH_ICON_KEY' => 'the callback to use to determine uri. Its first parameter will be the file full name, and the second an optional array with additional parametrs.',
127
	 * 				'OPTIONS_PATH_ICON_KEY'  => [array of options for the callback],
128
	 * 			],
129
	 * 		],
130
	 * ]. The first two are mandatory.
131
	 * 
132
	 * @return DOMDocument $domDocument : The DOMDocument instance created with the HTML.
133
	 */
134
	private function buildHTMLTreeFromNode(DOMDocument $document, Node $node, array $options) {
135
		//Create the first wrapping DOMElement.
136
		$domElementA = $this->buildElement($document, $options[self::MAIN_TAG_KEY][0]);
0 ignored issues
show
Documentation introduced by
$document is of type object<OpenMindParser\Models\Document>, but the function expects a object<DOMDocument>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
137
		
138
		//Merge font size and font family to add inside the second wrapping DOMElement.
139 View Code Duplication
		if(!array_key_exists(self::MAIN_TAG_MERGE_STYLE, $options) || !is_bool($options[self::MAIN_TAG_MERGE_STYLE])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
140
			$options[self::MAIN_TAG_MERGE_STYLE] = true;
141
		}
142
		if($options[self::MAIN_TAG_MERGE_STYLE]) {
143
			$options[self::MAIN_TAG_KEY][1][self::ATTRIBUTES_KEY] = array_merge(
144
				$options[self::MAIN_TAG_KEY][1][self::ATTRIBUTES_KEY], 
145
				[
146
					'style' => 'color:'.$node->getColor().';'.
147
							   ($node->getFontName() ? 'font-family:'.$node->getFontName().';' : '').
148
							   ($node->getFontSize() ? 'font-size:'.$node->getFontSize().';' : ''),
149
				]
150
			);
151
		}
152
		$options[self::MAIN_TAG_KEY][1][self::ATTRIBUTES_KEY] = array_merge($options[self::MAIN_TAG_KEY][1][self::ATTRIBUTES_KEY], ['id' => $node->getId(),]);
153
		//Create the second wrapping DOMElement whom will be append to the first one
154
		$domElementB = $this->buildElement($document, $options[self::MAIN_TAG_KEY][1]);
0 ignored issues
show
Documentation introduced by
$document is of type object<OpenMindParser\Models\Document>, but the function expects a object<DOMDocument>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
155
		
156
		//Set the icon in an img tag whom will be wrapped in the second DOMElement.
157
		if(!empty($node->getIcon()) && $options[self::MAIN_ICON_KEY][self::DISPLAY_ICON_KEY]) {
158
			$icon = $node->getIcon();
159
			$domElementImg = $document->createElement('img');
0 ignored issues
show
Bug introduced by
The method createElement() does not seem to exist on object<OpenMindParser\Models\Document>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
160
			
161
			$iconUri = $icon->getShortUri();
162
			if(array_key_exists(self::PATH_ICON_KEY, $options[self::MAIN_ICON_KEY])) {
163
				if(!is_callable($callback = $options[self::MAIN_ICON_KEY][self::PATH_ICON_KEY][self::CALLBACK_PATH_ICON_KEY])) {
164
					throw new InvalidArgumentException('The argument with the key "'.self::CALLBACK_PATH_ICON_KEY.'" (self::CALLBACK_PATH_ICON_KEY) must be a valid callback.');
165
				}
166
				
167
				$callBackoptions = array_key_exists(self::OPTIONS_PATH_ICON_KEY, $options[self::MAIN_ICON_KEY][self::PATH_ICON_KEY]) ?
168
					$options[self::MAIN_ICON_KEY][self::PATH_ICON_KEY][self::OPTIONS_PATH_ICON_KEY] :
169
					null;
170
				
171
				$iconUri = $callback($icon->getFullName(), $callBackoptions);
172
			}
173
			
174
			$domElementImg->setAttribute('src', $iconUri);
175
			$domElementImg->setAttribute('alt', $icon->getName());
176
			$domElementB->appendChild($domElementImg);
177
		}
178
		
179
		//Define the TextNode to append to the current wrapping elements.
180
		$text = $document->createTextNode($node->getText());
0 ignored issues
show
Bug introduced by
The method createTextNode() does not seem to exist on object<OpenMindParser\Models\Document>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
181
		
182
		//Bold an italic in old HTML way in order not to spread it to all children.
183 View Code Duplication
		if(!array_key_exists(self::MAIN_TAG_MERGE_DECORATION, $options) || !is_bool($options[self::MAIN_TAG_MERGE_DECORATION])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
184
			$options[self::MAIN_TAG_MERGE_DECORATION] = true;
185
		}
186
		if($options[self::MAIN_TAG_MERGE_DECORATION]) {
187
			if($node->isBold()) {
188
				$bTag = $document->createElement('b');
1 ignored issue
show
Bug introduced by
The method createElement() does not seem to exist on object<OpenMindParser\Models\Document>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
189
				$bTag->appendChild($text);
190
				$text = $bTag;
191
			}
192
			if($node->isItalic()) {
193
				$iTag = $document->createElement('i');
1 ignored issue
show
Bug introduced by
The method createElement() does not seem to exist on object<OpenMindParser\Models\Document>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
194
				$iTag->appendChild($text);
195
				$text = $iTag;
196
			}
197
		}
198
		
199
		//Append text node (real one or the one already wrapped inside <b> and/or <i> tags)
200
		if(isset($options[self::MAIN_TAG_KEY][2])) {
201
			$domElementC = $this->buildElement($document, $options[self::MAIN_TAG_KEY][2]);
0 ignored issues
show
Documentation introduced by
$document is of type object<OpenMindParser\Models\Document>, but the function expects a object<DOMDocument>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
202
			$domElementC->appendChild($text);
203
			$domElementB->appendChild($domElementC);
204
		}
205
		else {
206
			$domElementB->appendChild($text);
207
		}
208
		
209
		$domElementA->appendChild($domElementB);
210
		
211
		foreach($node->getChildren() as $child) {
212
			$domElementB->appendChild($this->buildHTMLTreeFromNode($document, $child, $options));
0 ignored issues
show
Documentation introduced by
$document is of type object<OpenMindParser\Models\Document>, but the function expects a object<DOMDocument>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
213
		}
214
		
215
		return $domElementA;
216
	}
217
	
218
	/**
219
	 * Build the HTML tag following its given tag name and attributes stored in $description array.
220
	 * 
221
	 * @param DOMDocument $document : The current DOMDocument in creation with the HTML tree.
222
	 * @param array $description : The array describing the tag to create.
223
	 * 
224
	 * @return DOMElement $domElement : The created DOMElement.
225
	 */
226
	private function buildElement(DOMDocument $document, array $description) {
227
		$domElement = $document->createElement($description[self::TAG_KEY]);
228
		foreach($description[self::ATTRIBUTES_KEY] as $name => $attribute) {
229
			$domElement->setAttribute($name, $attribute);
230
		}
231
		
232
		return $domElement;
233
	}
234
}