sfWidget.class.php 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. <?php
  2. /*
  3. * This file is part of the symfony package.
  4. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * sfWidget is the base class for all widgets.
  11. *
  12. * @package symfony
  13. * @subpackage widget
  14. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  15. * @version SVN: $Id: sfWidget.class.php 12518 2008-11-01 07:18:12Z fabien $
  16. */
  17. abstract class sfWidget
  18. {
  19. protected
  20. $requiredOptions = array(),
  21. $attributes = array(),
  22. $options = array();
  23. protected static
  24. $xhtml = true,
  25. $charset = 'UTF-8';
  26. /**
  27. * Constructor.
  28. *
  29. * @param array $options An array of options
  30. * @param array $attributes An array of default HTML attributes
  31. */
  32. public function __construct($options = array(), $attributes = array())
  33. {
  34. $this->configure($options, $attributes);
  35. // check option names
  36. if ($diff = array_diff(array_keys($options), array_merge(array_keys($this->options), $this->requiredOptions)))
  37. {
  38. throw new InvalidArgumentException(sprintf('%s does not support the following options: \'%s\'.', get_class($this), implode('\', \'', $diff)));
  39. }
  40. // check required options
  41. if ($diff = array_diff($this->requiredOptions, array_merge(array_keys($this->options), array_keys($options))))
  42. {
  43. throw new RuntimeException(sprintf('%s requires the following options: \'%s\'.', get_class($this), implode('\', \'', $diff)));
  44. }
  45. $this->options = array_merge($this->options, $options);
  46. $this->attributes = array_merge($this->attributes, $attributes);
  47. }
  48. /**
  49. * Configures the current widget.
  50. *
  51. * This method allows each widget to add options or HTML attributes
  52. * during widget creation.
  53. *
  54. * If some options and HTML attributes are given in the sfWidget constructor
  55. * they will take precedence over the options and HTML attributes you configure
  56. * in this method.
  57. *
  58. * @param array $options An array of options
  59. * @param array $attributes An array of HTML attributes
  60. *
  61. * @see __construct()
  62. */
  63. protected function configure($options = array(), $attributes = array())
  64. {
  65. }
  66. /**
  67. * Renders the widget as HTML.
  68. *
  69. * All subclasses must implement this method.
  70. *
  71. * @param string $name The name of the HTML widget
  72. * @param mixed $value The value of the widget
  73. * @param array $attributes An array of HTML attributes
  74. * @param array $errors An array of errors
  75. *
  76. * @return string A HTML representation of the widget
  77. */
  78. abstract public function render($name, $value = null, $attributes = array(), $errors = array());
  79. /**
  80. * Adds a required option.
  81. *
  82. * @param string $name The option name
  83. */
  84. public function addRequiredOption($name)
  85. {
  86. $this->requiredOptions[] = $name;
  87. }
  88. /**
  89. * Returns all required option names.
  90. *
  91. * @param array An array of required option names
  92. */
  93. public function getRequiredOptions()
  94. {
  95. return $this->requiredOptions;
  96. }
  97. /**
  98. * Adds a new option value with a default value.
  99. *
  100. * @param string $name The option name
  101. * @param mixed $value The default value
  102. */
  103. public function addOption($name, $value = null)
  104. {
  105. $this->options[$name] = $value;
  106. }
  107. /**
  108. * Changes an option value.
  109. *
  110. * @param string $name The option name
  111. * @param mixed $value The value
  112. */
  113. public function setOption($name, $value)
  114. {
  115. if (!in_array($name, array_merge(array_keys($this->options), $this->requiredOptions)))
  116. {
  117. throw new InvalidArgumentException(sprintf('%s does not support the following option: \'%s\'.', get_class($this), $name));
  118. }
  119. $this->options[$name] = $value;
  120. }
  121. /**
  122. * Gets an option value.
  123. *
  124. * @param string The option name
  125. *
  126. * @return mixed The option value
  127. */
  128. public function getOption($name)
  129. {
  130. return isset($this->options[$name]) ? $this->options[$name] : null;
  131. }
  132. /**
  133. * Returns true if the option exists.
  134. *
  135. * @param string $name The option name
  136. *
  137. * @return bool true if the option exists, false otherwise
  138. */
  139. public function hasOption($name)
  140. {
  141. return array_key_exists($name, $this->options);
  142. }
  143. /**
  144. * Gets all options.
  145. *
  146. * @return array An array of named options
  147. */
  148. public function getOptions()
  149. {
  150. return $this->options;
  151. }
  152. /**
  153. * Sets the options.
  154. *
  155. * @param array $options An array of options
  156. */
  157. public function setOptions($options)
  158. {
  159. $this->options = $options;
  160. }
  161. /**
  162. * Returns the default HTML attributes.
  163. *
  164. * @param array An array of HTML attributes
  165. */
  166. public function getAttributes()
  167. {
  168. return $this->attributes;
  169. }
  170. /**
  171. * Sets a default HTML attribute.
  172. *
  173. * @param string $name The attribute name
  174. * @param string $value The attribute value
  175. */
  176. public function setAttribute($name, $value)
  177. {
  178. $this->attributes[$name] = $value;
  179. }
  180. /**
  181. * Returns the HTML attribute value for a given attribute name.
  182. *
  183. * @param string $name The attribute name.
  184. *
  185. * @return string The attribute value, or null if the attribute does not exist
  186. */
  187. public function getAttribute($name)
  188. {
  189. return isset($this->attributes[$name]) ? $this->attributes[$name] : null;
  190. }
  191. /**
  192. * Sets the HTML attributes.
  193. *
  194. * @param array $attributes An array of HTML attributes
  195. */
  196. public function setAttributes($attributes)
  197. {
  198. $this->attributes = $attributes;
  199. }
  200. /**
  201. * Gets the stylesheet paths associated with the widget.
  202. *
  203. * The array keys are files and values are the media names (separated by a ,):
  204. *
  205. * array('/path/to/file.css' => 'all', '/another/file.css' => 'screen,print')
  206. *
  207. * @return array An array of stylesheet paths
  208. */
  209. public function getStylesheets()
  210. {
  211. return array();
  212. }
  213. /**
  214. * Gets the JavaScript paths associated with the widget.
  215. *
  216. * @return array An array of JavaScript paths
  217. */
  218. public function getJavaScripts()
  219. {
  220. return array();
  221. }
  222. /**
  223. * Sets the charset to use when rendering widgets.
  224. *
  225. * @param string $charset The charset
  226. */
  227. static public function setCharset($charset)
  228. {
  229. self::$charset = $charset;
  230. }
  231. /**
  232. * Returns the charset to use when rendering widgets.
  233. *
  234. * @return string The charset (defaults to UTF-8)
  235. */
  236. static public function getCharset()
  237. {
  238. return self::$charset;
  239. }
  240. /**
  241. * Sets the XHTML generation flag.
  242. *
  243. * @param bool $boolean true if widgets must be generated as XHTML, false otherwise
  244. */
  245. static public function setXhtml($boolean)
  246. {
  247. self::$xhtml = (boolean) $boolean;
  248. }
  249. /**
  250. * Returns whether to generate XHTML tags or not.
  251. *
  252. * @return bool true if widgets must be generated as XHTML, false otherwise
  253. */
  254. static public function isXhtml()
  255. {
  256. return self::$xhtml;
  257. }
  258. /**
  259. * Renders a HTML tag.
  260. *
  261. * @param string $tag The tag name
  262. * @param array $attributes An array of HTML attributes to be merged with the default HTML attributes
  263. *
  264. * @param string An HTML tag string
  265. */
  266. public function renderTag($tag, $attributes = array())
  267. {
  268. if (empty($tag))
  269. {
  270. return '';
  271. }
  272. return sprintf('<%s%s%s', $tag, $this->attributesToHtml($attributes), self::$xhtml ? ' />' : (strtolower($tag) == 'input' ? '>' : sprintf('></%s>', $tag)));
  273. }
  274. /**
  275. * Renders a HTML content tag.
  276. *
  277. * @param string $tag The tag name
  278. * @param string $content The content of the tag
  279. * @param array $attributes An array of HTML attributes to be merged with the default HTML attributes
  280. *
  281. * @param string An HTML tag string
  282. */
  283. public function renderContentTag($tag, $content = null, $attributes = array())
  284. {
  285. if (empty($tag))
  286. {
  287. return '';
  288. }
  289. return sprintf('<%s%s>%s</%s>', $tag, $this->attributesToHtml($attributes), $content, $tag);
  290. }
  291. /**
  292. * Escapes a string.
  293. *
  294. * @param string $value string to escape
  295. * @return string escaped string
  296. */
  297. static public function escapeOnce($value)
  298. {
  299. $value = is_object($value) ? $value->__toString() : (string) $value;
  300. return self::fixDoubleEscape(htmlspecialchars($value, ENT_QUOTES, self::getCharset()));
  301. }
  302. /**
  303. * Fixes double escaped strings.
  304. *
  305. * @param string $escaped string to fix
  306. * @return string single escaped string
  307. */
  308. static public function fixDoubleEscape($escaped)
  309. {
  310. return preg_replace('/&amp;([a-z]+|(#\d+)|(#x[\da-f]+));/i', '&$1;', $escaped);
  311. }
  312. /**
  313. * Converts an array of attributes to its HTML representation.
  314. *
  315. * @param array $attributes An array of attributes
  316. *
  317. * @return string The HTML representation of the HTML attribute array.
  318. */
  319. public function attributesToHtml($attributes)
  320. {
  321. $attributes = array_merge($this->attributes, $attributes);
  322. return implode('', array_map(array($this, 'attributesToHtmlCallback'), array_keys($attributes), array_values($attributes)));
  323. }
  324. /**
  325. * Prepares an attribute key and value for HTML representation.
  326. *
  327. * It removes empty attributes, except for the value one.
  328. *
  329. * @param string $k The attribute key
  330. * @param string $v The attribute value
  331. *
  332. * @return string The HTML representation of the HTML key attribute pair.
  333. */
  334. protected function attributesToHtmlCallback($k, $v)
  335. {
  336. return false === $v || is_null($v) || ('' === $v && 'value' != $k) ? '' : sprintf(' %s="%s"', $k, $this->escapeOnce($v));
  337. }
  338. }