PageRenderTime 55ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/View/Helper/HeadLink.php

https://github.com/praveenuniyal/zf2
PHP | 477 lines | 251 code | 59 blank | 167 comment | 50 complexity | 875ae717faa0dabc99b033400e05f024 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\View\Helper;
  10. use stdClass;
  11. use Zend\View;
  12. use Zend\View\Exception;
  13. /**
  14. * Zend_Layout_View_Helper_HeadLink
  15. *
  16. * @see http://www.w3.org/TR/xhtml1/dtds.html
  17. *
  18. * Creates the following virtual methods:
  19. * @method HeadLink appendStylesheet($href, $media, $conditionalStylesheet, $extras)
  20. * @method HeadLink offsetSetStylesheet($index, $href, $media, $conditionalStylesheet, $extras)
  21. * @method HeadLink prependStylesheet($href, $media, $conditionalStylesheet, $extras)
  22. * @method HeadLink setStylesheet($href, $media, $conditionalStylesheet, $extras)
  23. * @method HeadLink appendAlternate($href, $type, $title, $extras)
  24. * @method HeadLink offsetSetAlternate($index, $href, $type, $title, $extras)
  25. * @method HeadLink prependAlternate($href, $type, $title, $extras)
  26. * @method HeadLink setAlternate($href, $type, $title, $extras)
  27. */
  28. class HeadLink extends Placeholder\Container\AbstractStandalone
  29. {
  30. /**
  31. * Allowed attributes
  32. *
  33. * @var array
  34. */
  35. protected $itemKeys = array('charset', 'href', 'hreflang', 'id', 'media', 'rel', 'rev', 'type', 'title', 'extras');
  36. /**
  37. * Registry key for placeholder
  38. *
  39. * @var string
  40. */
  41. protected $regKey = 'Zend_View_Helper_HeadLink';
  42. /**
  43. * Constructor
  44. *
  45. * Use PHP_EOL as separator
  46. */
  47. public function __construct()
  48. {
  49. parent::__construct();
  50. $this->setSeparator(PHP_EOL);
  51. }
  52. /**
  53. * Proxy to __invoke()
  54. *
  55. * Allows calling $helper->headLink(), but, more importantly, chaining calls
  56. * like ->appendStylesheet()->headLink().
  57. *
  58. * @param array $attributes
  59. * @param string $placement
  60. * @return HeadLink
  61. */
  62. public function headLink(array $attributes = null, $placement = Placeholder\Container\AbstractContainer::APPEND)
  63. {
  64. return call_user_func_array(array($this, '__invoke'), func_get_args());
  65. }
  66. /**
  67. * headLink() - View Helper Method
  68. *
  69. * Returns current object instance. Optionally, allows passing array of
  70. * values to build link.
  71. *
  72. * @param array $attributes
  73. * @param string $placement
  74. * @return HeadLink
  75. */
  76. public function __invoke(array $attributes = null, $placement = Placeholder\Container\AbstractContainer::APPEND)
  77. {
  78. if (null !== $attributes) {
  79. $item = $this->createData($attributes);
  80. switch ($placement) {
  81. case Placeholder\Container\AbstractContainer::SET:
  82. $this->set($item);
  83. break;
  84. case Placeholder\Container\AbstractContainer::PREPEND:
  85. $this->prepend($item);
  86. break;
  87. case Placeholder\Container\AbstractContainer::APPEND:
  88. default:
  89. $this->append($item);
  90. break;
  91. }
  92. }
  93. return $this;
  94. }
  95. /**
  96. * Overload method access
  97. *
  98. * Items that may be added in the future:
  99. * - Navigation? need to find docs on this
  100. * - public function appendStart()
  101. * - public function appendContents()
  102. * - public function appendPrev()
  103. * - public function appendNext()
  104. * - public function appendIndex()
  105. * - public function appendEnd()
  106. * - public function appendGlossary()
  107. * - public function appendAppendix()
  108. * - public function appendHelp()
  109. * - public function appendBookmark()
  110. * - Other?
  111. * - public function appendCopyright()
  112. * - public function appendChapter()
  113. * - public function appendSection()
  114. * - public function appendSubsection()
  115. *
  116. * @param mixed $method
  117. * @param mixed $args
  118. * @throws Exception\BadMethodCallException
  119. * @return void
  120. */
  121. public function __call($method, $args)
  122. {
  123. if (preg_match('/^(?P<action>set|(ap|pre)pend|offsetSet)(?P<type>Stylesheet|Alternate|Prev|Next)$/', $method, $matches)) {
  124. $argc = count($args);
  125. $action = $matches['action'];
  126. $type = $matches['type'];
  127. $index = null;
  128. if ('offsetSet' == $action) {
  129. if (0 < $argc) {
  130. $index = array_shift($args);
  131. --$argc;
  132. }
  133. }
  134. if (1 > $argc) {
  135. throw new Exception\BadMethodCallException(sprintf(
  136. '%s requires at least one argument',
  137. $method
  138. ));
  139. }
  140. if (is_array($args[0])) {
  141. $item = $this->createData($args[0]);
  142. } else {
  143. $dataMethod = 'createData' . $type;
  144. $item = $this->$dataMethod($args);
  145. }
  146. if ($item) {
  147. if ('offsetSet' == $action) {
  148. $this->offsetSet($index, $item);
  149. } else {
  150. $this->$action($item);
  151. }
  152. }
  153. return $this;
  154. }
  155. return parent::__call($method, $args);
  156. }
  157. /**
  158. * Check if value is valid
  159. *
  160. * @param mixed $value
  161. * @return bool
  162. */
  163. protected function isValid($value)
  164. {
  165. if (!$value instanceof stdClass) {
  166. return false;
  167. }
  168. $vars = get_object_vars($value);
  169. $keys = array_keys($vars);
  170. $intersection = array_intersect($this->itemKeys, $keys);
  171. if (empty($intersection)) {
  172. return false;
  173. }
  174. return true;
  175. }
  176. /**
  177. * append()
  178. *
  179. * @param array $value
  180. * @throws Exception\InvalidArgumentException
  181. * @return void
  182. */
  183. public function append($value)
  184. {
  185. if (!$this->isValid($value)) {
  186. throw new Exception\InvalidArgumentException(
  187. 'append() expects a data token; please use one of the custom append*() methods'
  188. );
  189. }
  190. return $this->getContainer()->append($value);
  191. }
  192. /**
  193. * offsetSet()
  194. *
  195. * @param string|int $index
  196. * @param array $value
  197. * @throws Exception\InvalidArgumentException
  198. * @return void
  199. */
  200. public function offsetSet($index, $value)
  201. {
  202. if (!$this->isValid($value)) {
  203. throw new Exception\InvalidArgumentException(
  204. 'offsetSet() expects a data token; please use one of the custom offsetSet*() methods'
  205. );
  206. }
  207. return $this->getContainer()->offsetSet($index, $value);
  208. }
  209. /**
  210. * prepend()
  211. *
  212. * @param array $value
  213. * @throws Exception\InvalidArgumentException
  214. * @return HeadLink
  215. */
  216. public function prepend($value)
  217. {
  218. if (!$this->isValid($value)) {
  219. throw new Exception\InvalidArgumentException(
  220. 'prepend() expects a data token; please use one of the custom prepend*() methods'
  221. );
  222. }
  223. return $this->getContainer()->prepend($value);
  224. }
  225. /**
  226. * set()
  227. *
  228. * @param array $value
  229. * @throws Exception\InvalidArgumentException
  230. * @return HeadLink
  231. */
  232. public function set($value)
  233. {
  234. if (!$this->isValid($value)) {
  235. throw new Exception\InvalidArgumentException(
  236. 'set() expects a data token; please use one of the custom set*() methods'
  237. );
  238. }
  239. return $this->getContainer()->set($value);
  240. }
  241. /**
  242. * Create HTML link element from data item
  243. *
  244. * @param stdClass $item
  245. * @return string
  246. */
  247. public function itemToString(stdClass $item)
  248. {
  249. $attributes = (array) $item;
  250. $link = '<link';
  251. foreach ($this->itemKeys as $itemKey) {
  252. if (isset($attributes[$itemKey])) {
  253. if (is_array($attributes[$itemKey])) {
  254. foreach ($attributes[$itemKey] as $key => $value) {
  255. $link .= sprintf(' %s="%s"', $key, ($this->autoEscape) ? $this->escape($value) : $value);
  256. }
  257. } else {
  258. $link .= sprintf(' %s="%s"', $itemKey, ($this->autoEscape) ? $this->escape($attributes[$itemKey]) : $attributes[$itemKey]);
  259. }
  260. }
  261. }
  262. if (method_exists($this->view, 'plugin')) {
  263. $link .= ($this->view->plugin('doctype')->isXhtml()) ? ' />' : '>';
  264. } else {
  265. $link .= ' />';
  266. }
  267. if (($link == '<link />') || ($link == '<link>')) {
  268. return '';
  269. }
  270. if (isset($attributes['conditionalStylesheet'])
  271. && !empty($attributes['conditionalStylesheet'])
  272. && is_string($attributes['conditionalStylesheet']))
  273. {
  274. $link = '<!--[if ' . $attributes['conditionalStylesheet'] . ']> ' . $link . '<![endif]-->';
  275. }
  276. return $link;
  277. }
  278. /**
  279. * Render link elements as string
  280. *
  281. * @param string|int $indent
  282. * @return string
  283. */
  284. public function toString($indent = null)
  285. {
  286. $indent = (null !== $indent)
  287. ? $this->getWhitespace($indent)
  288. : $this->getIndent();
  289. $items = array();
  290. $this->getContainer()->ksort();
  291. foreach ($this as $item) {
  292. $items[] = $this->itemToString($item);
  293. }
  294. return $indent . implode($this->escape($this->getSeparator()) . $indent, $items);
  295. }
  296. /**
  297. * Create data item for stack
  298. *
  299. * @param array $attributes
  300. * @return stdClass
  301. */
  302. public function createData(array $attributes)
  303. {
  304. return (object) $attributes;
  305. }
  306. /**
  307. * Create item for stylesheet link item
  308. *
  309. * @param array $args
  310. * @return stdClass|false Returns false if stylesheet is a duplicate
  311. */
  312. public function createDataStylesheet(array $args)
  313. {
  314. $rel = 'stylesheet';
  315. $type = 'text/css';
  316. $media = 'screen';
  317. $conditionalStylesheet = false;
  318. $href = array_shift($args);
  319. if ($this->isDuplicateStylesheet($href)) {
  320. return false;
  321. }
  322. if (0 < count($args)) {
  323. $media = array_shift($args);
  324. if (is_array($media)) {
  325. $media = implode(',', $media);
  326. } else {
  327. $media = (string) $media;
  328. }
  329. }
  330. if (0 < count($args)) {
  331. $conditionalStylesheet = array_shift($args);
  332. if (!empty($conditionalStylesheet) && is_string($conditionalStylesheet)) {
  333. $conditionalStylesheet = (string) $conditionalStylesheet;
  334. } else {
  335. $conditionalStylesheet = null;
  336. }
  337. }
  338. if (0 < count($args) && is_array($args[0])) {
  339. $extras = array_shift($args);
  340. $extras = (array) $extras;
  341. }
  342. $attributes = compact('rel', 'type', 'href', 'media', 'conditionalStylesheet', 'extras');
  343. return $this->createData($attributes);
  344. }
  345. /**
  346. * Is the linked stylesheet a duplicate?
  347. *
  348. * @param string $uri
  349. * @return bool
  350. */
  351. protected function isDuplicateStylesheet($uri)
  352. {
  353. foreach ($this->getContainer() as $item) {
  354. if (($item->rel == 'stylesheet') && ($item->href == $uri)) {
  355. return true;
  356. }
  357. }
  358. return false;
  359. }
  360. /**
  361. * Create item for alternate link item
  362. *
  363. * @param array $args
  364. * @throws Exception\InvalidArgumentException
  365. * @return stdClass
  366. */
  367. public function createDataAlternate(array $args)
  368. {
  369. if (3 > count($args)) {
  370. throw new Exception\InvalidArgumentException(sprintf(
  371. 'Alternate tags require 3 arguments; %s provided',
  372. count($args)
  373. ));
  374. }
  375. $rel = 'alternate';
  376. $href = array_shift($args);
  377. $type = array_shift($args);
  378. $title = array_shift($args);
  379. if (0 < count($args) && is_array($args[0])) {
  380. $extras = array_shift($args);
  381. $extras = (array) $extras;
  382. if (isset($extras['media']) && is_array($extras['media'])) {
  383. $extras['media'] = implode(',', $extras['media']);
  384. }
  385. }
  386. $href = (string) $href;
  387. $type = (string) $type;
  388. $title = (string) $title;
  389. $attributes = compact('rel', 'href', 'type', 'title', 'extras');
  390. return $this->createData($attributes);
  391. }
  392. /**
  393. * Create item for a prev relationship (mainly used for pagination)
  394. *
  395. * @param array $args
  396. * @return stdClass
  397. */
  398. public function createDataPrev(array $args)
  399. {
  400. $rel = 'prev';
  401. $href = (string) array_shift($args);
  402. $attributes = compact('rel', 'href');
  403. return $this->createData($attributes);
  404. }
  405. /**
  406. * Create item for a prev relationship (mainly used for pagination)
  407. *
  408. * @param array $args
  409. * @return stdClass
  410. */
  411. public function createDataNext(array $args)
  412. {
  413. $rel = 'next';
  414. $href = (string) array_shift($args);
  415. $attributes = compact('rel', 'href');
  416. return $this->createData($attributes);
  417. }
  418. }