Overview

Namespaces

  • Jyxo
    • Beholder
      • TestCase
    • Gettext
      • Parser
    • Input
      • Chain
      • Filter
      • Validator
    • Mail
      • Email
        • Attachment
      • Parser
      • Sender
    • Rpc
      • Json
      • Xml
    • Shell
    • Spl
    • Svn
    • Time
    • Webdav
  • PHP

Classes

  • Charset
  • Color
  • Css
  • ErrorHandler
  • ErrorMail
  • FirePhp
  • Html
  • HtmlTag
  • SpamFilter
  • String
  • Timer
  • XmlReader

Exceptions

  • Exception
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * Jyxo PHP Library
  5:  *
  6:  * LICENSE
  7:  *
  8:  * This source file is subject to the new BSD license that is bundled
  9:  * with this package in the file license.txt.
 10:  * It is also available through the world-wide-web at this URL:
 11:  * https://github.com/jyxo/php/blob/master/license.txt
 12:  */
 13: 
 14: namespace Jyxo;
 15: 
 16: /**
 17:  * Class for generating (x)HTML source code.
 18:  * Allows creating HTML tags and its attributes.
 19:  *
 20:  * Example:
 21:  * <code>
 22:  * $p = \Jyxo\HtmlTag::create('p')->setClass('buttons');
 23:  * </code>
 24: 
 25:  * The magic __call() method ensures attributes settings.
 26:  *
 27:  * <code>
 28:  * $p->render();
 29:  * </code>
 30:  *
 31:  * The render() method creates the HTML output.
 32:  *
 33:  * @category Jyxo
 34:  * @package Jyxo\Html
 35:  * @copyright Copyright (c) 2005-2011 Jyxo, s.r.o.
 36:  * @license https://github.com/jyxo/php/blob/master/license.txt
 37:  * @author Roman Řáha
 38:  * @author Štěpán Svoboda
 39:  */
 40: final class HtmlTag
 41: {
 42:     /**
 43:      * Is XHTML output turned on?
 44:      *
 45:      * @var boolean
 46:      */
 47:     private $xhtml = true;
 48: 
 49:     /**
 50:      * Element name.
 51:      *
 52:      * @var string
 53:      */
 54:     private $tag = '';
 55: 
 56:     /**
 57:      * Is the element self closing?
 58:      *
 59:      * @var boolean
 60:      */
 61:     private $isEmptyElement = false;
 62: 
 63:     /**
 64:      * Element attributes.
 65:      *
 66:      * @var array
 67:      */
 68:     private $attributes = array();
 69: 
 70:     /**
 71:      * Array of child elements.
 72:      *
 73:      * @var array
 74:      */
 75:     private $children = array();
 76: 
 77:     /**
 78:      * Array of elements whose value will not be escaped.
 79:      *
 80:      * @var array
 81:      */
 82:     private $noEncode = array();
 83: 
 84:     /**
 85:      * Renders only the contents, not the opening and closing tag.
 86:      *
 87:      * @var boolean
 88:      */
 89:     private $contentOnly = FALSE;
 90: 
 91:     /**
 92:      * List of element attributes.
 93:      *
 94:      * @var array
 95:      */
 96:     private static $attrs = array(
 97:         'accesskey' => true, 'action' => true, 'alt' => true, 'cellpadding' => true, 'cellspacing' => true, 'checked' => true, 'class' => true,
 98:         'cols' => true, 'disabled' => true, 'for' => true, 'href' => true, 'id' => true, 'label' => true, 'method' => true, 'name' => true, 'onblur' => true,
 99:         'onchange' => true, 'onclick' => true, 'onfocus' => true, 'onkeyup' => true, 'onsubmit' => true, 'readonly' => true, 'rel' => true,
100:         'rows' => true, 'selected' => true, 'size' => true, 'src' => true, 'style' => true, 'tabindex' => true, 'title' => true, 'type' => true,
101:         'value' => true, 'width' => true,
102:     );
103: 
104:     /**
105:      * List of self closing elements.
106:      *
107:      * @var array
108:      */
109:     private $emptyElements = array(
110:         'br' => true, 'hr' => true, 'img' => true, 'input' => true, 'meta' => true, 'link' => true
111:     );
112: 
113:     /**
114:      * List of mandatory attributes that will be rendered even if empty.
115:      *
116:      * @var array
117:      */
118:     private $requiredAttrs = array(
119:         'option' => 'value',
120:         'optgroup' => 'label'
121:     );
122: 
123:     /**
124:      * Constructor.
125:      *
126:      * Sets the element name.
127:      *
128:      * @param string $tag
129:      */
130:     private function __construct($tag)
131:     {
132:         $this->tag = (string) $tag;
133:     }
134: 
135:     /**
136:      * Creates an element instance.
137:      *
138:      * @param string $tag HTML element name
139:      * @return \Jyxo\HtmlTag
140:      */
141:     public static function create($tag)
142:     {
143:         return new self($tag);
144:     }
145: 
146:     /**
147:      * Returns an element instance from the given source.
148:      * The first and last tag will be used as the opening and closing tag respectively.
149:      * Anything between those tags will be used as contents.
150:      *
151:      * @param string $html HTML source code
152:      * @return \Jyxo\HtmlTag
153:      * @throws \InvalidArgumentException If an invalid HTML source was given
154:      */
155:     public static function createFromSource($html)
156:     {
157:         if (preg_match('~<(\w+)(\s[^>]*)+>(.*)((<[^>]+>)?[^>]*)$~', $html, $matches)) {
158:             $tag = new self($matches[1]);
159:             if ('' !== $matches[3]) {
160:                 // @todo Maybe some kind of recursion to create a tree of elements
161:                 $tag->setText($matches[3]);
162:             }
163:             if (preg_match_all('/(\w+)\s*=\s*"([^"]+)"/', $matches[2], $submatches, PREG_PATTERN_ORDER)) {
164:                 $attrs = array_combine($submatches[1], $submatches[2]);
165:                 $tag->setAttributes($attrs);
166:             }
167:             return $tag;
168:         }
169:         throw new \InvalidArgumentException('Zadaný text neobsahuje validní html');
170:     }
171: 
172:     /**
173:      * Creates and returns the opening tag.
174:      *
175:      * @return string
176:      */
177:     public function open()
178:     {
179:         if (TRUE === $this->contentOnly) {
180:             return '';
181:         }
182:         $this->isEmptyElement = isset($this->emptyElements[$this->tag]);
183:         $buff = '';
184:         foreach ($this->attributes as $name => $value) {
185:             if (isset(self::$attrs[$name])) {
186:                 if (($name === 'selected' || $name === 'checked' || $name === 'readonly' || $name === 'disabled') && $value) {
187:                     $value = $name;
188:                 }
189:                 $notEmpty = $value !== null && $value !== '' && $value !== false;
190:                 if ($this->isRequiredAttr($this->tag, $name) || $notEmpty) {
191:                     // For not empty attributes and the value attribute by the <option> tag
192:                     if (!isset($this->noEncode[$name])) {
193:                         $value = String::escape($value);
194:                     }
195:                     $attrString = sprintf(' %s="%s"', $name, $value);
196:                     if ($name === 'value') {
197:                         if ($this->tag === 'textarea') {
198:                             $buff .= '';
199:                         } else {
200:                             $buff .= $attrString;
201:                         }
202:                     } else {
203:                         $buff .= $attrString;
204:                     }
205:                 }
206:             }
207:         }
208:         $buff = '<' . $this->tag . $buff . ($this->xhtml ? $this->isEmptyElement ? ' />' : '>' : '>');
209:         return $buff;
210:     }
211: 
212:     /**
213:      * Creates and returns element's contents.
214:      *
215:      * @return string
216:      */
217:     public function content()
218:     {
219:         $buff = '';
220:         if (!$this->isEmptyElement) {
221: 
222:             $hasValue = isset($this->attributes['value']);
223:             $hasText = isset($this->attributes['text']);
224:             if ($hasValue || $hasText) {
225:                 $text = $hasText ? $this->attributes['text'] : $this->attributes['value'];
226:                 $noEncode = isset($this->noEncode['value']) || isset($this->noEncode['text']);
227:                 // <script> contents are not escaped
228:                 $noEncode = 'script' === $this->tag ? true : $noEncode;
229:                 $buff .= $noEncode ? $text : String::escape($text);
230:             }
231:         }
232:         if (!$this->isEmptyElement && !empty($this->children)) {
233:             foreach ($this->children as $element) {
234:                 $buff .= $element->render();
235:             }
236:         }
237:         return $buff;
238:     }
239: 
240:     /**
241:      * Creates and returns the closing tag.
242:      *
243:      * @return string
244:      */
245:     public function close()
246:     {
247:         if (true === $this->contentOnly) {
248:             return '';
249:         }
250:         $close = '</' . $this->tag . '>';
251:         if ($this->xhtml) {
252:             $buff = !$this->isEmptyElement ? $close : '';
253:         } else {
254:             $buff = $close;
255:         }
256:         $buff .= "\n";
257:         return $buff;
258:     }
259: 
260:     /**
261:      * Renders the element.
262:      *
263:      * @return string
264:      */
265:     public function render()
266:     {
267:         return $this->open() . $this->content() . $this->close();
268:     }
269: 
270:     /**
271:      * Adds a child element.
272:      *
273:      * @param \Jyxo\HtmlTag $element Child element to be added
274:      * @return \Jyxo\HtmlTag
275:      */
276:     public function addChild(\Jyxo\HtmlTag $element)
277:     {
278:         $this->children[] = $element;
279:         return $this;
280:     }
281: 
282:     /**
283:      * Adds multiple child elements.
284:      *
285:      * @param array $elements Array of child elements
286:      * @return \Jyxo\HtmlTag
287:      */
288:     public function addChildren(array $elements)
289:     {
290:         foreach ($elements as $element) {
291:             $this->addChild($element);
292:         }
293:         return $this;
294:     }
295: 
296:     /**
297:      * Imports attributes from the given array.
298:      *
299:      * @param array $attributes Associative array of attributes and their values
300:      * @return \Jyxo\HtmlTag
301:      */
302:     public function setAttributes(array $attributes)
303:     {
304:         foreach ($attributes as $name => $value) {
305:             $this->attributes[strtolower($name)] = $value;
306:         }
307:         return $this;
308:     }
309: 
310:     /**
311:      * Sets an attribute to not be espaced on output.
312:      *
313:      * @param string $attribute Attribute name
314:      * @return \Jyxo\HtmlTag
315:      */
316:     public function setNoEncode($attribute)
317:     {
318:         $this->noEncode[$attribute] = true;
319: 
320:         return $this;
321:     }
322: 
323:     /**
324:      * Sets if only the contents should be rendered.
325:      *
326:      * @param boolean $contentOnly Should only the contents be rendered
327:      * @return \Jyxo\HtmlTag
328:      */
329:     public function setContentOnly($contentOnly)
330:     {
331:         $this->contentOnly = (bool) $contentOnly;
332:         return $this;
333:     }
334: 
335:     /**
336:      * Renders the HTML element.
337:      *
338:      * @return string
339:      */
340:     public function __toString()
341:     {
342:         return $this->render();
343:     }
344: 
345:     /**
346:      * Sets or returns the attribute.
347:      *
348:      * @param string $method Method name
349:      * @param array $args Method attributes
350:      * @return mixed string|Jyxo_HtmlTag
351:      */
352:     public function __call($method, $args)
353:     {
354:         $type = $method[0] === 's' ? 'set' : 'get';
355:         if ($type === 'set') {
356:             $this->attributes[strtolower(substr($method, 3))] = $args[0];
357:             return $this;
358:         } else {
359:             if (isset($this->attributes[strtolower(substr($method, 3))])) {
360:                 return $this->attributes[strtolower(substr($method, 3))];
361:             }
362:             return '';
363:         }
364:     }
365: 
366:     /**
367:      * Returns an attribute value.
368:      *
369:      * @param string $name Attribute name
370:      * @return mixed string|null
371:      */
372:     public function __get($name)
373:     {
374:         // An empty attribute is always null
375:         return isset($this->attributes[$name]) ? $this->attributes[$name] : null;
376:     }
377: 
378:     /**
379:      * Returns if the given attribute is mandatory.
380:      *
381:      * @param string $tag HTML tag name
382:      * @param string $attr Attribute name
383:      * @return boolean
384:      */
385:     private function isRequiredAttr($tag, $attr)
386:     {
387:         if (isset($this->requiredAttrs[$tag])) {
388:             if (is_array($this->requiredAttrs[$tag])) {
389:                 return in_array($attr, $this->requiredAttrs[$tag]);
390:             }
391:             return $attr == $this->requiredAttrs[$tag];
392:         }
393:         return false;
394:     }
395: }
396: 
Jyxo PHP Library API documentation generated by ApiGen 2.3.0