1 <?php
2
3 /*
4 * This file is part of the Brickrouge package.
5 *
6 * (c) Olivier Laviale <olivier.laviale@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Brickrouge;
13
14 /**
15 * Custom data attributes are intended to store custom data private to the page or application,
16 * for which there are no more appropriate attributes or elements.
17 *
18 * @see http://www.w3.org/TR/html5/elements.html#embedding-custom-non-visible-data-with-the-data-attributes
19 */
20 class Dataset implements \ArrayAccess, \IteratorAggregate
21 {
22 static protected function serialize_property($property)
23 {
24 return 'data-' . $property;
25 }
26
27 static protected function unserialize_property($property)
28 {
29 return substr($property, 5);
30 }
31
32 /**
33 * The target element.
34 *
35 * @var Element
36 */
37 protected $element;
38
39 /**
40 * Constructor.
41 *
42 * @param Element $element The target element.
43 * @param array $properties[optional] The initial properties of the dataset.
44 */
45 public function __construct(Element $element, array $properties=array())
46 {
47 $this->element = $element;
48
49 foreach ($properties as $property => $value)
50 {
51 $this[$property] = $value;
52 }
53 }
54
55 /**
56 * Sets the value of a property.
57 *
58 * The attribute corresponding to the property is set.
59 */
60 public function offsetSet($property, $value)
61 {
62 $this->element->offsetSet(self::serialize_property($property), $value);
63 }
64
65 /**
66 * Returns the value of a property,
67 *
68 * The value is gotten from the attribute corresponding to the property.
69 */
70 public function offsetGet($property, $default=null)
71 {
72 return $this->element->offsetGet(self::serialize_property($property), $default);
73 }
74
75 public function offsetExists($property)
76 {
77 return $this->element->offsetExists(self::serialize_property($property));
78 }
79
80 public function offsetUnset($property)
81 {
82 return $this->element->offsetUnset(self::serialize_property($property));
83 }
84
85 public function getIterator()
86 {
87 return new \ArrayIterator($this->to_a());
88 }
89
90 /**
91 * Returns an array representation of the dataset.
92 *
93 * @return array[string]mixed
94 */
95 public function to_a()
96 {
97 $properties = array();
98
99 foreach ($this->element->attributes as $attribute => $value)
100 {
101 if (strpos($attribute, 'data-') !== 0)
102 {
103 continue;
104 }
105
106 $properties[self::unserialize_property($attribute)] = $value;
107 }
108
109 return $properties;
110 }
111 }
112
113 /**
114 * An iterator used to traverse {@link Element} descendant.
115 *
116 * The iterator collects all descendant elements excluding non {@link Element} instances.
117 */
118 class Iterator implements \Iterator
119 {
120 protected $children = array();
121 protected $left;
122
123 public function __construct(Element $element)
124 {
125 $children = array();
126
127 foreach ($element->children as $key => $child)
128 {
129 if (!($child instanceof Element))
130 {
131 continue;
132 }
133
134 $children[$key] = $child;
135 }
136
137 $this->children = $children;
138 }
139
140 public function rewind()
141 {
142 reset($this->children);
143
144 $this->left = count($this->children);
145 }
146
147 public function next()
148 {
149 next($this->children);
150
151 $this->left--;
152 }
153
154 public function valid()
155 {
156 return !!$this->left;
157 }
158
159 public function key()
160 {
161 return key($this->children);
162 }
163
164 public function current()
165 {
166 return current($this->children);
167 }
168 }
169
170 /**
171 * An iterator used to traverse {@link Element} descendant in depth.
172 */
173 class RecursiveIterator extends Iterator implements \RecursiveIterator
174 {
175 public function hasChildren()
176 {
177 $current = $this->current();
178
179 return !empty($current->children);
180 }
181
182 public function getChildren()
183 {
184 $current = $this->current();
185
186 return new static($current);
187 }
188 }