PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/FluentDOM/Css/Properties.php

http://github.com/ThomasWeinert/FluentDOM
PHP | 244 lines | 129 code | 17 blank | 98 comment | 24 complexity | 1a26c0ce7634b0ee4c19dfdc9602f4b0 MD5 | raw file
  1. <?php
  2. /**
  3. * FluentDOMCssProperties provides an array access to a css style string. It is used to
  4. * modify the attribute values of style attributes.
  5. *
  6. * @license http://www.opensource.org/licenses/mit-license.php The MIT License
  7. * @copyright Copyright (c) 2009-2010 Bastian Feder, Thomas Weinert
  8. *
  9. * @package FluentDOM
  10. */
  11. /**
  12. * FluentDOMCssProperties provides an array access to a css style string. It is used to
  13. * modify the attribute values of style attributes.
  14. *
  15. * @package FluentDOM
  16. */
  17. class FluentDOMCssProperties implements ArrayAccess, IteratorAggregate, Countable {
  18. /**
  19. * Pattern to decode the style property string
  20. */
  21. const STYLE_PATTERN = '((?:^|;)\s*(?P<name>[-\w]+)\s*:\s*(?P<value>[^;]+))';
  22. /**
  23. * property storage
  24. *
  25. * @var array
  26. */
  27. private $_properties = array();
  28. public function __construct($styleString = '') {
  29. $this->setStyleString($styleString);
  30. }
  31. public function __toString() {
  32. return $this->getStyleString();
  33. }
  34. /**
  35. * Decode style attribute to the css properties array.
  36. *
  37. * @param string $styleString
  38. */
  39. public function setStyleString($styleString) {
  40. $this->_properties = array();
  41. if (!empty($styleString)) {
  42. $matches = array();
  43. if (preg_match_all(self::STYLE_PATTERN, $styleString, $matches, PREG_SET_ORDER)) {
  44. foreach ($matches as $match) {
  45. if (isset($match['name']) &&
  46. isset($match['value']) &&
  47. $this->_isCSSProperty($match['name']) &&
  48. trim($match['value']) !== '') {
  49. $this->_properties[$match['name']] = $match['value'];
  50. }
  51. }
  52. }
  53. }
  54. }
  55. /**
  56. * Encode css properties array for the style string.
  57. *
  58. * @return string
  59. */
  60. public function getStyleString() {
  61. $result = '';
  62. if (is_array($this->_properties) && count($this->_properties) > 0) {
  63. uksort($this->_properties, array($this, '_compare'));
  64. foreach ($this->_properties as $name => $value) {
  65. if (trim($value) !== '') {
  66. $result .= ' '.$name.': '.$value.';';
  67. }
  68. }
  69. }
  70. return (string)substr($result, 1);
  71. }
  72. /**
  73. * Get an iterator for the properties
  74. *
  75. * @see IteratorAggregate::getIterator()
  76. * @return ArrayIterator
  77. */
  78. public function getIterator() {
  79. return new ArrayIterator($this->_properties);
  80. }
  81. /**
  82. * Get the property count of the first selected node
  83. *
  84. * @see Countable::count()
  85. * @return integer
  86. */
  87. public function count() {
  88. return count($this->_properties);
  89. }
  90. /**
  91. * Allow to use isset() and array syntax to check if a css property is set on
  92. * the first matched node.
  93. *
  94. * @see ArrayAccess::offsetExists()
  95. */
  96. public function offsetExists($name) {
  97. return isset($this->_properties[$name]);
  98. }
  99. /**
  100. * Allow to use array syntax to read a css property value from first matched node.
  101. *
  102. * @see ArrayAccess::offsetGet()
  103. * @param string $name
  104. * @return $value
  105. */
  106. public function offsetGet($name) {
  107. return $this->_properties[$name];
  108. }
  109. /**
  110. * Set a property
  111. *
  112. * @see ArrayAccess::offsetSet()
  113. * @param string $name
  114. * @param string $value
  115. */
  116. public function offsetSet($name, $value) {
  117. if ($this->_isCSSProperty($name)) {
  118. if (trim($value) !== '') {
  119. $this->_properties[$name] = (string)$value;
  120. } else {
  121. $this->offsetUnset($name);
  122. }
  123. } else {
  124. throw new InvalidArgumentException('Invalid css property name: '.$name);
  125. }
  126. }
  127. /**
  128. * Remove a css properties if it is set.
  129. *
  130. * @see ArrayAccess::offsetUnset()
  131. * @param string $names
  132. */
  133. public function offsetUnset($names) {
  134. if (!is_array($names)) {
  135. $names = array($names);
  136. }
  137. foreach ($names as $property) {
  138. if (array_key_exists($property, $this->_properties)) {
  139. unset($this->_properties[$property]);
  140. }
  141. }
  142. }
  143. /**
  144. * Compile value argument into a string (it can be an callback)
  145. *
  146. * @param string|Callback|Closure $value
  147. * @param DOMElement $node
  148. * @param integer $index
  149. * @param string $currentValue
  150. * @return string
  151. */
  152. public function compileValue($value, $node, $index, $currentValue) {
  153. if (!is_string($value) &&
  154. is_callable($value, TRUE)) {
  155. return (string)call_user_func(
  156. $value,
  157. $node,
  158. $index,
  159. $currentValue
  160. );
  161. }
  162. return (string)$value;
  163. }
  164. /**
  165. * Compare to css property names by name, browser-prefix and level.
  166. *
  167. * @param string $propertyNameOne
  168. * @param string $propertyNameTwo
  169. * @return integer
  170. */
  171. private function _compare($propertyNameOne, $propertyNameTwo) {
  172. $propertyOne = $this->_decodeName($propertyNameOne);
  173. $propertyTwo = $this->_decodeName($propertyNameTwo);
  174. $propertyOneLevels = count($propertyOne);
  175. $propertyTwoLevels = count($propertyTwo);
  176. $maxLevels = ($propertyOneLevels > $propertyTwoLevels)
  177. ? $propertyOneLevels : $propertyTwoLevels;
  178. for ($i = 0; $i < $maxLevels; ++$i) {
  179. if (isset($propertyOne[$i]) &&
  180. isset($propertyTwo[$i])) {
  181. $compare = strnatcasecmp(
  182. $propertyOne[$i],
  183. $propertyTwo[$i]
  184. );
  185. if ($compare != 0) {
  186. return $compare;
  187. }
  188. } else {
  189. break;
  190. }
  191. }
  192. if ($propertyOneLevels > $propertyTwoLevels) {
  193. return 1;
  194. } else {
  195. return -1;
  196. }
  197. }
  198. /**
  199. * Decodes the css property name into an compareable array.
  200. *
  201. * @return array
  202. */
  203. private function _decodeName($propertyName) {
  204. if (substr($propertyName, 0, 1) == '-') {
  205. $pos = strpos($propertyName, '-', 1);
  206. $items = explode('-', substr($propertyName, $pos + 1));
  207. $items[] = substr($propertyName, 1, $pos);
  208. return $items;
  209. } else {
  210. $items = explode('-', $propertyName);
  211. return $items;
  212. }
  213. }
  214. /**
  215. * Check if string is an valid css property name.
  216. *
  217. * @param string $propertyName
  218. * @return boolean
  219. */
  220. private function _isCSSProperty($propertyName) {
  221. $pattern = '(^-?(?:[a-z]+-)*(?:[a-z]+)$)D';
  222. if (preg_match($pattern, $propertyName)) {
  223. return TRUE;
  224. }
  225. return FALSE;
  226. }
  227. }