/vendor/anahkiasen/html-object/src/HtmlObject/Traits/Tag.php

https://bitbucket.org/larryg/powerhut · PHP · 485 lines · 208 code · 74 blank · 203 comment · 24 complexity · 7fc270b375b3cbe404f99044a9a56a36 MD5 · raw file

  1. <?php
  2. namespace HtmlObject\Traits;
  3. use HtmlObject\Element;
  4. use HtmlObject\Text;
  5. /**
  6. * An abstraction of an HTML element
  7. */
  8. abstract class Tag extends TreeObject
  9. {
  10. /**
  11. * The element name
  12. *
  13. * @var string
  14. */
  15. protected $element;
  16. /**
  17. * The object's value
  18. *
  19. * @var string|null|Tag
  20. */
  21. protected $value;
  22. /**
  23. * The object's attribute
  24. *
  25. * @var array
  26. */
  27. protected $attributes = array();
  28. /**
  29. * Whether the element is self closing
  30. *
  31. * @var boolean
  32. */
  33. protected $isSelfClosing = false;
  34. /**
  35. * Whether the current tag is opened or not
  36. *
  37. * @var boolean
  38. */
  39. protected $isOpened = false;
  40. /**
  41. * A list of class properties to be added to attributes
  42. *
  43. * @var array
  44. */
  45. protected $injectedProperties = array('value');
  46. // Configuration options ----------------------------------------- /
  47. /**
  48. * The base configuration inherited by classes
  49. *
  50. * @var array
  51. */
  52. public static $config = array(
  53. 'doctype' => 'html',
  54. );
  55. ////////////////////////////////////////////////////////////////////
  56. //////////////////////////// CORE METHODS //////////////////////////
  57. ////////////////////////////////////////////////////////////////////
  58. /**
  59. * Set up a new tag
  60. *
  61. * @param string $element Its element
  62. * @param string $value Its value
  63. * @param array $attributes Its attributes
  64. */
  65. protected function setTag($element, $value = null, $attributes = array())
  66. {
  67. $this->setValue($value);
  68. $this->setElement($element);
  69. $this->replaceAttributes($attributes);
  70. }
  71. /**
  72. * Wrap the Element in another element
  73. *
  74. * @param string|Element $element The element's tag
  75. *
  76. * @return Element
  77. */
  78. public function wrapWith($element, $name = null)
  79. {
  80. if ($element instanceof Tag) {
  81. return $element->nest($this, $name);
  82. }
  83. return Element::create($element)->nest($this, $name);
  84. }
  85. /**
  86. * Render on string conversion
  87. *
  88. * @return string
  89. */
  90. public function __toString()
  91. {
  92. return $this->render();
  93. }
  94. ////////////////////////////////////////////////////////////////////
  95. ///////////////////////// ELEMENT RENDERING ////////////////////////
  96. ////////////////////////////////////////////////////////////////////
  97. /**
  98. * Opens the Tag
  99. *
  100. * @return string
  101. */
  102. public function open()
  103. {
  104. $this->isOpened = true;
  105. // If self closing, put value as attribute
  106. foreach ($this->injectProperties() as $attribute => $property) {
  107. if (!$this->isSelfClosing and $attribute == 'value') continue;
  108. if (is_null($property) and !is_empty($property)) continue;
  109. $this->attributes[$attribute] = $property;
  110. }
  111. // Invisible tags
  112. if (!$this->element) {
  113. return null;
  114. }
  115. return '<'.$this->element.Helpers::parseAttributes($this->attributes).$this->getTagCloser();
  116. }
  117. /**
  118. * Open the tag tree on a particular child
  119. *
  120. * @param string $onChild The child's key
  121. *
  122. * @return string
  123. */
  124. public function openOn($onChildren)
  125. {
  126. $onChildren = explode('.', $onChildren);
  127. $element = $this->open();
  128. $element .= $this->value;
  129. $subject = $this;
  130. foreach ($onChildren as $onChild) {
  131. foreach ($subject->getChildren() as $childName => $child) {
  132. if ($childName != $onChild) $element .= $child;
  133. else {
  134. $subject = $child;
  135. $element .= $child->open();
  136. break;
  137. }
  138. }
  139. }
  140. return $element;
  141. }
  142. /**
  143. * Check if the tag is opened
  144. *
  145. * @return boolean
  146. */
  147. public function isOpened()
  148. {
  149. return $this->isOpened;
  150. }
  151. /**
  152. * Returns the Tag's content
  153. *
  154. * @return string
  155. */
  156. public function getContent()
  157. {
  158. return $this->value.$this->renderChildren();
  159. }
  160. /**
  161. * Close the Tag
  162. *
  163. * @return string
  164. */
  165. public function close()
  166. {
  167. $this->isOpened = false;
  168. $openedOn = null;
  169. $element = null;
  170. foreach ($this->children as $childName => $child) {
  171. if ($child->isOpened) {
  172. $openedOn = $childName;
  173. $element .= $child->close();
  174. } elseif ($openedOn and $child->isAfter($openedOn)) {
  175. $element .= $child;
  176. }
  177. }
  178. // Invisible tags
  179. if (!$this->element) {
  180. return null;
  181. }
  182. return $element .= '</'.$this->element.'>';
  183. }
  184. /**
  185. * Default rendering method
  186. *
  187. * @return string
  188. */
  189. public function render()
  190. {
  191. if ($this->isSelfClosing) return $this->open();
  192. return $this->open().$this->getContent().$this->close();
  193. }
  194. /**
  195. * Get the preferred way to close a tag
  196. *
  197. * @return string
  198. */
  199. protected function getTagCloser()
  200. {
  201. if ($this->isSelfClosing and static::$config['doctype'] == 'xhtml') {
  202. return ' />';
  203. }
  204. return '>';
  205. }
  206. ////////////////////////////////////////////////////////////////////
  207. /////////////////////////// MAGIC METHODS //////////////////////////
  208. ////////////////////////////////////////////////////////////////////
  209. /**
  210. * Dynamically set attributes
  211. *
  212. * @param string $method An attribute
  213. * @param array $parameters Its value(s)
  214. */
  215. public function __call($method, $parameters)
  216. {
  217. // Replace underscores
  218. $method = str_replace('_', '-', $method);
  219. // Get value and set it
  220. $value = Helpers::arrayGet($parameters, 0, 'true');
  221. $this->$method = $value;
  222. return $this;
  223. }
  224. /**
  225. * Dynamically set an attribute
  226. *
  227. * @param string $attribute The attribute
  228. * @param string $value Its value
  229. */
  230. public function __set($attribute, $value)
  231. {
  232. $this->attributes[$attribute] = $value;
  233. return $this;
  234. }
  235. /**
  236. * Get an attribute or a child
  237. *
  238. * @param string $item The desired child/attribute
  239. *
  240. * @return mixed
  241. */
  242. public function __get($item)
  243. {
  244. if (array_key_exists($item, $this->attributes)) {
  245. return $this->attributes[$item];
  246. }
  247. // Get a child by snake case
  248. $child = preg_replace_callback('/([A-Z])/', function($match) {
  249. return '.'.strtolower($match[1]);
  250. }, $item);
  251. $child = $this->getChild($child);
  252. return $child;
  253. }
  254. ////////////////////////////////////////////////////////////////////
  255. //////////////////////////////// VALUE /////////////////////////////
  256. ////////////////////////////////////////////////////////////////////
  257. /**
  258. * Changes the Tag's element
  259. *
  260. * @param string $element
  261. */
  262. public function setElement($element)
  263. {
  264. $this->element = $element;
  265. return $this;
  266. }
  267. /**
  268. * Change the object's value
  269. *
  270. * @param string $value
  271. */
  272. public function setValue($value)
  273. {
  274. if (is_array($value)) $this->nestChildren($value);
  275. else $this->value = $value;
  276. return $this;
  277. }
  278. /**
  279. * Wrap the value in a tag
  280. *
  281. * @param string $tag The tag
  282. */
  283. public function wrapValue($tag)
  284. {
  285. $this->value = Element::create($tag, $this->value);
  286. return $this;
  287. }
  288. /**
  289. * Get the value
  290. *
  291. * @return string
  292. */
  293. public function getValue()
  294. {
  295. return $this->value;
  296. }
  297. /**
  298. * Get all the children as a string
  299. *
  300. * @return string
  301. */
  302. protected function renderChildren()
  303. {
  304. $children = $this->children;
  305. foreach ($children as $key => $child) {
  306. if ($child instanceof Tag) {
  307. $children[$key] = $child->render();
  308. }
  309. }
  310. return implode($children);
  311. }
  312. ////////////////////////////////////////////////////////////////////
  313. //////////////////////////// ATTRIBUTES ////////////////////////////
  314. ////////////////////////////////////////////////////////////////////
  315. /**
  316. * Return an array of protected properties to bind as attributes
  317. *
  318. * @return array
  319. */
  320. protected function injectProperties()
  321. {
  322. $properties = array();
  323. foreach ($this->injectedProperties as $property) {
  324. if (!isset($this->$property)) continue;
  325. $properties[$property] = $this->$property;
  326. }
  327. return $properties;
  328. }
  329. /**
  330. * Set an attribute
  331. *
  332. * @param string $attribute An attribute
  333. * @param string $value Its value
  334. */
  335. public function setAttribute($attribute, $value = null)
  336. {
  337. $this->attributes[$attribute] = $value;
  338. return $this;
  339. }
  340. /**
  341. * Set a bunch of parameters at once
  342. *
  343. * @param array $attributes The attributes to add to the existing ones
  344. *
  345. * @return Tag
  346. */
  347. public function setAttributes($attributes)
  348. {
  349. $this->attributes = array_merge($this->attributes, (array) $attributes);
  350. return $this;
  351. }
  352. /**
  353. * Get all attributes
  354. *
  355. * @return array
  356. */
  357. public function getAttributes()
  358. {
  359. return $this->attributes;
  360. }
  361. /**
  362. * Replace all attributes with the provided array
  363. *
  364. * @param array $attributes The attributes to replace with
  365. *
  366. * @return Tag
  367. */
  368. public function replaceAttributes($attributes)
  369. {
  370. $this->attributes = (array) $attributes;
  371. return $this;
  372. }
  373. /**
  374. * Add one or more classes to the current field
  375. *
  376. * @param string $class The class(es) to add
  377. */
  378. public function addClass($class)
  379. {
  380. if(is_array($class)) $class = implode(' ', $class);
  381. // Create class attribute if it isn't already
  382. if (!isset($this->attributes['class'])) {
  383. $this->attributes['class'] = null;
  384. }
  385. // Prevent adding a class twice
  386. $classes = explode(' ', $this->attributes['class']);
  387. if (!in_array($class, $classes)) {
  388. $this->attributes['class'] = trim($this->attributes['class']. ' ' .$class);
  389. }
  390. return $this;
  391. }
  392. /**
  393. * Remove one or more classes to the current field
  394. *
  395. * @param string $class The class(es) to remove
  396. */
  397. public function removeClass($class)
  398. {
  399. if (is_array($class)) $class = implode(' ', $class);
  400. // Cancel if there is no class to begin with
  401. if (!isset($this->attributes['class'])) {
  402. return $this;
  403. }
  404. $classes = explode(' ', $this->attributes['class']);
  405. if (in_array($class, $classes)) {
  406. $this->attributes['class'] = str_replace($class, '', $this->attributes['class']);
  407. $this->attributes['class'] = trim($this->attributes['class']);
  408. }
  409. return $this;
  410. }
  411. }