Selecta   B
last analyzed

Complexity

Total Complexity 38

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 38
c 4
b 0
f 0
lcom 1
cbo 0
dl 0
loc 192
rs 8.3999

14 Methods

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