Completed
Push — master ( d3af35...17363b )
by Drew
02:28
created

Selecta::build_attributes()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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