Completed
Push — master ( 3c67f0...0fedf0 )
by Drew
03:05
created

Selecta::break_into_parts()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
namespace DrewM\Selecta;
4
 
5
class Selecta
6
{
7
	public static $single_tags    = array('img', 'br', 'hr', 'input');
8
	public static $pseudo_classes = array('disabled', 'checked');
9
	public static $meta_map       = array('.'=>'class', '#'=>'id');
10
11
	public static function build($selector, $contents='', $open_tags=true, $close_tags=true)
12
	{
13
		$parts = self::break_into_parts($selector);
14
		
15
		if (count($parts)) {
16
			$parts = array_reverse($parts);
17
			foreach($parts as $part) {
18
				$contents = self::tagify($part, $contents, $open_tags, $close_tags);
19
			}
20
		}
21
		return $contents;
22
	}
23
24
	public static function wrap($selector, $contents='')
25
	{
26
		return self::build($selector, $contents, true, true);
27
	}
28
29
	public static function open($selector)
30
	{
31
		return self::build($selector, '', true, false);
32
	}
33
34
	public static function close($selector)
35
	{
36
		return self::build($selector, '', false, true);
37
	}
38
39
	private static function break_into_parts($selector)
40
	{
41
		$selector = self::sanitise_attribute_metas($selector);
42
		return explode(' ', $selector);
43
	}
44
45
	private static function tagify($selector='', $contents='', $open_tags=true, $close_tags=true)
46
	{
47
		$attrs = array();
48
49
		$metas   = '\.\#\[\:';
50
		$pattern = '/(['.$metas.'])([^'.$metas.']*)?/';
51
		preg_match_all($pattern, $selector, $matches, PREG_SET_ORDER);
52
53
		if (count($matches)) {
54
			foreach($matches as $match) {
55
				$attrs = self::build_attributes($match[1], $match[2], $attrs);
56
			}
57
58
			// reduce selector to just tag name
59
			$parts    = preg_split('/['.$metas.']/', $selector);
60
			$selector = $parts[0];
61
		}
62
63
		return self::build_tag($selector, $attrs, $contents, $open_tags, $close_tags);
64
	}
65
66
	private static function build_attributes($meta_char, $value, $attrs)
67
	{
68
		$key = false;
69
		if (isset(self::$meta_map[$meta_char])) {
70
			$key = self::$meta_map[$meta_char];
71
		}else{
72
			switch ($meta_char) {
73
				
74
				// Attribute selectors
75
				case '[':
76
					list($key, $value) = self::build_attribute_selector_attribute($value);
77
					break;
78
79
				// Pseudo-class selectors
80
				case ':':
81
					list($key, $value) = self::build_pseudo_class_attribute($value);
82
					break;
83
			}
84
		}
85
86
		if ($key){
87
			if (isset($attrs[$key])) {
88
				$attrs[$key] .= ' '.$value;
89
			}else{
90
				$attrs[$key] = $value;
91
			}
92
		}
93
94
		return $attrs;
95
	}
96
97
	private static function build_attribute_selector_attribute($value)
98
	{
99
		$value = rtrim($value, ']');
100
101
		if (strpos($value, '=')) {
102
			$parts = explode('=', $value, 2);
103
			$key   = $parts[0];
104
			$value = self::unsanitise_attribute_metas($parts[1]);
105
		}else{
106
			$key   = $value;
107
			$value = false;
108
		}
109
110
		return array($key, $value);
111
	}
112
113
	private static function build_pseudo_class_attribute($pclass='')
114
	{
115
		if (in_array($pclass, self::$pseudo_classes)) {
116
			return array($pclass, false);
117
		}
118
119
		return array(false, false);
120
	}
121
122
	private static function build_tag($name, $attributes=array(), $contents='', $open_tag=true, $close_tag=true)
123
	{
124
		$tag = '';
125
126
		if ($open_tag) {
127
			$tag = self::open_tag($name, $attributes);
128
		}
129
		
130
		if (in_array($name, self::$single_tags)) { 
131
			return $contents.$tag;
132
		}
133
134
		$tag .= $contents;
135
136
		if ($close_tag) {
137
			$tag .= self::close_tag($name);
138
		}
139
140
		return $tag;
141
	}
142
143
	private static function open_tag($name, $attributes=array())
144
	{
145
		$tag = '<'.self::html($name);
146
		if (count($attributes)) {
147
			// do attributes
148
			$attpairs = array();
149
			foreach($attributes as $key=>$val) {
150
				if ($val!='') {
151
					$attpairs[] = self::html($key).'="'.self::html($val, true).'"';
152
				}else{
153
					$attpairs[] = self::html($key);
154
				}
155
			}
156
			$tag .= ' '.implode(' ', $attpairs);
157
		}
158
		$tag .= '>';
159
160
		return $tag;
161
	}
162
163
	private static function close_tag($name)
164
	{
165
		return  '</'.self::html($name).'>';		
166
	}
167
168
	private static function html($s, $quotes=false, $double_encode=false)
169
	{	    
170
		if ($s || (is_string($s) && strlen($s))) {
171
			return htmlspecialchars($s, ($quotes?ENT_QUOTES:ENT_NOQUOTES), 'UTF-8', $double_encode);
172
		}
173
	    return '';
174
	}
175
176
	private static function sanitise_attribute_metas($selector)
177
	{
178
		if (strpos($selector, '[')!==false) {
179
			preg_match_all('/\[.*?\]/', $selector, $matches, PREG_SET_ORDER);
180
			if (count($matches)) {
181
				foreach($matches as $match) {
182
					$exact    = $match[0];
183
					$new      = str_replace(
184
									self::metas_from(), 
185
									self::metas_to(), 
186
								$exact);
187
					$selector = str_replace($exact, $new, $selector);
188
				}
189
			}
190
		}
191
		return $selector;
192
	}
193
194
	private static function unsanitise_attribute_metas($string)
195
	{
196
		return str_replace(self::metas_to(), self::metas_from(), $string);
197
	}
198
199
	private static function metas_from()
200
	{
201
		return array(
202
				'.', ':', '#', ' '
203
			);
204
	}
205
206
	private static function metas_to()
207
	{
208
		return array(
209
				'__DOT__', '__COLON__', '__HASH__', '__SPACE__'
210
			);
211
	}
212
213
}