Namespaces

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Diagnostics
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
      • Diagnostics
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • PhpGenerator
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
  • NetteModule
  • none

Classes

  • Arrays
  • Callback
  • FileSystem
  • Finder
  • Html
  • Json
  • LimitedScope
  • MimeTypeDetector
  • Neon
  • NeonEntity
  • Paginator
  • Strings
  • Validators

Exceptions

  • AssertionException
  • JsonException
  • NeonException
  • RegexpException
  • TokenizerException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  • Nette homepage
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\Utils;
  9: 
 10: use Nette;
 11: 
 12: 
 13: /**
 14:  * HTML helper.
 15:  *
 16:  * <code>
 17:  * $el = Html::el('a')->href($link)->setText('Nette');
 18:  * $el->class = 'myclass';
 19:  * echo $el;
 20:  *
 21:  * echo $el->startTag(), $el->endTag();
 22:  * </code>
 23:  *
 24:  * @author     David Grudl
 25:  */
 26: class Html extends Nette\Object implements \ArrayAccess, \Countable, \IteratorAggregate
 27: {
 28:     /** @var string  element's name */
 29:     private $name;
 30: 
 31:     /** @var bool  is element empty? */
 32:     private $isEmpty;
 33: 
 34:     /** @var array  element's attributes */
 35:     public $attrs = array();
 36: 
 37:     /** @var array  of Html | string nodes */
 38:     protected $children = array();
 39: 
 40:     /** @var bool  use XHTML syntax? */
 41:     public static $xhtml = FALSE;
 42: 
 43:     /** @var array  empty (void) elements */
 44:     public static $emptyElements = array('img'=>1,'hr'=>1,'br'=>1,'input'=>1,'meta'=>1,'area'=>1,'embed'=>1,'keygen'=>1,
 45:         'source'=>1,'base'=>1,'col'=>1,'link'=>1,'param'=>1,'basefont'=>1,'frame'=>1,'isindex'=>1,'wbr'=>1,'command'=>1,'track'=>1);
 46: 
 47: 
 48:     /**
 49:      * Static factory.
 50:      * @param  string element name (or NULL)
 51:      * @param  array|string element's attributes or plain text content
 52:      * @return self
 53:      */
 54:     public static function el($name = NULL, $attrs = NULL)
 55:     {
 56:         $el = new static;
 57:         $parts = explode(' ', $name, 2);
 58:         $el->setName($parts[0]);
 59: 
 60:         if (is_array($attrs)) {
 61:             $el->attrs = $attrs;
 62: 
 63:         } elseif ($attrs !== NULL) {
 64:             $el->setText($attrs);
 65:         }
 66: 
 67:         if (isset($parts[1])) {
 68:             foreach (Strings::matchAll($parts[1] . ' ', '#([a-z0-9:-]+)(?:=(["\'])?(.*?)(?(2)\\2|\s))?#i') as $m) {
 69:                 $el->attrs[$m[1]] = isset($m[3]) ? $m[3] : TRUE;
 70:             }
 71:         }
 72: 
 73:         return $el;
 74:     }
 75: 
 76: 
 77:     /**
 78:      * Changes element's name.
 79:      * @param  string
 80:      * @param  bool  Is element empty?
 81:      * @return self
 82:      * @throws Nette\InvalidArgumentException
 83:      */
 84:     public function setName($name, $isEmpty = NULL)
 85:     {
 86:         if ($name !== NULL && !is_string($name)) {
 87:             throw new Nette\InvalidArgumentException(sprintf('Name must be string or NULL, %s given.', gettype($name)));
 88:         }
 89: 
 90:         $this->name = $name;
 91:         $this->isEmpty = $isEmpty === NULL ? isset(static::$emptyElements[$name]) : (bool) $isEmpty;
 92:         return $this;
 93:     }
 94: 
 95: 
 96:     /**
 97:      * Returns element's name.
 98:      * @return string
 99:      */
100:     public function getName()
101:     {
102:         return $this->name;
103:     }
104: 
105: 
106:     /**
107:      * Is element empty?
108:      * @return bool
109:      */
110:     public function isEmpty()
111:     {
112:         return $this->isEmpty;
113:     }
114: 
115: 
116:     /**
117:      * Sets multiple attributes.
118:      * @param  array
119:      * @return self
120:      */
121:     public function addAttributes(array $attrs)
122:     {
123:         $this->attrs = array_merge($this->attrs, $attrs);
124:         return $this;
125:     }
126: 
127: 
128:     /**
129:      * Overloaded setter for element's attribute.
130:      * @param  string    HTML attribute name
131:      * @param  mixed     HTML attribute value
132:      * @return void
133:      */
134:     public function __set($name, $value)
135:     {
136:         $this->attrs[$name] = $value;
137:     }
138: 
139: 
140:     /**
141:      * Overloaded getter for element's attribute.
142:      * @param  string    HTML attribute name
143:      * @return mixed     HTML attribute value
144:      */
145:     public function &__get($name)
146:     {
147:         return $this->attrs[$name];
148:     }
149: 
150: 
151:     /**
152:      * Overloaded tester for element's attribute.
153:      * @param  string    HTML attribute name
154:      * @return bool
155:      */
156:     public function __isset($name)
157:     {
158:         return isset($this->attrs[$name]);
159:     }
160: 
161: 
162:     /**
163:      * Overloaded unsetter for element's attribute.
164:      * @param  string    HTML attribute name
165:      * @return void
166:      */
167:     public function __unset($name)
168:     {
169:         unset($this->attrs[$name]);
170:     }
171: 
172: 
173:     /**
174:      * Overloaded setter for element's attribute.
175:      * @param  string  HTML attribute name
176:      * @param  array   (string) HTML attribute value or pair?
177:      * @return self
178:      */
179:     public function __call($m, $args)
180:     {
181:         $p = substr($m, 0, 3);
182:         if ($p === 'get' || $p === 'set' || $p === 'add') {
183:             $m = substr($m, 3);
184:             $m[0] = $m[0] | "\x20";
185:             if ($p === 'get') {
186:                 return isset($this->attrs[$m]) ? $this->attrs[$m] : NULL;
187: 
188:             } elseif ($p === 'add') {
189:                 $args[] = TRUE;
190:             }
191:         }
192: 
193:         if (count($args) === 0) { // invalid
194: 
195:         } elseif (count($args) === 1) { // set
196:             $this->attrs[$m] = $args[0];
197: 
198:         } elseif ((string) $args[0] === '') {
199:             $tmp = & $this->attrs[$m]; // appending empty value? -> ignore, but ensure it exists
200: 
201:         } elseif (!isset($this->attrs[$m]) || is_array($this->attrs[$m])) { // needs array
202:             $this->attrs[$m][$args[0]] = $args[1];
203: 
204:         } else {
205:             $this->attrs[$m] = array($this->attrs[$m], $args[0] => $args[1]);
206:         }
207: 
208:         return $this;
209:     }
210: 
211: 
212:     /**
213:      * Special setter for element's attribute.
214:      * @param  string path
215:      * @param  array query
216:      * @return self
217:      */
218:     public function href($path, $query = NULL)
219:     {
220:         if ($query) {
221:             $query = http_build_query($query, NULL, '&');
222:             if ($query !== '') {
223:                 $path .= '?' . $query;
224:             }
225:         }
226:         $this->attrs['href'] = $path;
227:         return $this;
228:     }
229: 
230: 
231:     /**
232:      * Sets element's HTML content.
233:      * @param  string raw HTML string
234:      * @return self
235:      * @throws Nette\InvalidArgumentException
236:      */
237:     public function setHtml($html)
238:     {
239:         if (is_array($html)) {
240:             throw new Nette\InvalidArgumentException(sprintf('Textual content must be a scalar, %s given.', gettype($html)));
241:         }
242:         $this->removeChildren();
243:         $this->children[] = (string) $html;
244:         return $this;
245:     }
246: 
247: 
248:     /**
249:      * Returns element's HTML content.
250:      * @return string
251:      */
252:     public function getHtml()
253:     {
254:         $s = '';
255:         foreach ($this->children as $child) {
256:             if (is_object($child)) {
257:                 $s .= $child->render();
258:             } else {
259:                 $s .= $child;
260:             }
261:         }
262:         return $s;
263:     }
264: 
265: 
266:     /**
267:      * Sets element's textual content.
268:      * @param  string
269:      * @return self
270:      * @throws Nette\InvalidArgumentException
271:      */
272:     public function setText($text)
273:     {
274:         if (!is_array($text) && !$text instanceof self) {
275:             $text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8');
276:         }
277:         return $this->setHtml($text);
278:     }
279: 
280: 
281:     /**
282:      * Returns element's textual content.
283:      * @return string
284:      */
285:     public function getText()
286:     {
287:         return html_entity_decode(strip_tags($this->getHtml()), ENT_QUOTES, 'UTF-8');
288:     }
289: 
290: 
291:     /**
292:      * Adds new element's child.
293:      * @param  Html|string Html node or raw HTML string
294:      * @return self
295:      */
296:     public function add($child)
297:     {
298:         return $this->insert(NULL, $child);
299:     }
300: 
301: 
302:     /**
303:      * Creates and adds a new Html child.
304:      * @param  string  elements's name
305:      * @param  array|string element's attributes or raw HTML string
306:      * @return self  created element
307:      */
308:     public function create($name, $attrs = NULL)
309:     {
310:         $this->insert(NULL, $child = static::el($name, $attrs));
311:         return $child;
312:     }
313: 
314: 
315:     /**
316:      * Inserts child node.
317:      * @param  int|NULL position of NULL for appending
318:      * @param  Html|string Html node or raw HTML string
319:      * @param  bool
320:      * @return self
321:      * @throws Nette\InvalidArgumentException
322:      */
323:     public function insert($index, $child, $replace = FALSE)
324:     {
325:         if ($child instanceof Html || is_scalar($child)) {
326:             if ($index === NULL) { // append
327:                 $this->children[] = $child;
328: 
329:             } else { // insert or replace
330:                 array_splice($this->children, (int) $index, $replace ? 1 : 0, array($child));
331:             }
332: 
333:         } else {
334:             throw new Nette\InvalidArgumentException(sprintf('Child node must be scalar or Html object, %s given.', is_object($child) ? get_class($child) : gettype($child)));
335:         }
336: 
337:         return $this;
338:     }
339: 
340: 
341:     /**
342:      * Inserts (replaces) child node (\ArrayAccess implementation).
343:      * @param  int|NULL position of NULL for appending
344:      * @param  Html|string Html node or raw HTML string
345:      * @return void
346:      */
347:     public function offsetSet($index, $child)
348:     {
349:         $this->insert($index, $child, TRUE);
350:     }
351: 
352: 
353:     /**
354:      * Returns child node (\ArrayAccess implementation).
355:      * @param  int
356:      * @return self|string
357:      */
358:     public function offsetGet($index)
359:     {
360:         return $this->children[$index];
361:     }
362: 
363: 
364:     /**
365:      * Exists child node? (\ArrayAccess implementation).
366:      * @param  int
367:      * @return bool
368:      */
369:     public function offsetExists($index)
370:     {
371:         return isset($this->children[$index]);
372:     }
373: 
374: 
375:     /**
376:      * Removes child node (\ArrayAccess implementation).
377:      * @param  int
378:      * @return void
379:      */
380:     public function offsetUnset($index)
381:     {
382:         if (isset($this->children[$index])) {
383:             array_splice($this->children, (int) $index, 1);
384:         }
385:     }
386: 
387: 
388:     /**
389:      * Returns children count.
390:      * @return int
391:      */
392:     public function count()
393:     {
394:         return count($this->children);
395:     }
396: 
397: 
398:     /**
399:      * Removed all children.
400:      * @return void
401:      */
402:     public function removeChildren()
403:     {
404:         $this->children = array();
405:     }
406: 
407: 
408:     /**
409:      * Iterates over a elements.
410:      * @param  bool    recursive?
411:      * @param  string  class types filter
412:      * @return \RecursiveIterator
413:      */
414:     public function getIterator($deep = FALSE)
415:     {
416:         if ($deep) {
417:             $deep = $deep > 0 ? \RecursiveIteratorIterator::SELF_FIRST : \RecursiveIteratorIterator::CHILD_FIRST;
418:             return new \RecursiveIteratorIterator(new Nette\Iterators\Recursor(new \ArrayIterator($this->children)), $deep);
419: 
420:         } else {
421:             return new Nette\Iterators\Recursor(new \ArrayIterator($this->children));
422:         }
423:     }
424: 
425: 
426:     /**
427:      * Returns all of children.
428:      * @return array
429:      */
430:     public function getChildren()
431:     {
432:         return $this->children;
433:     }
434: 
435: 
436:     /**
437:      * Renders element's start tag, content and end tag.
438:      * @param  int
439:      * @return string
440:      */
441:     public function render($indent = NULL)
442:     {
443:         $s = $this->startTag();
444: 
445:         if (!$this->isEmpty) {
446:             // add content
447:             if ($indent !== NULL) {
448:                 $indent++;
449:             }
450:             foreach ($this->children as $child) {
451:                 if (is_object($child)) {
452:                     $s .= $child->render($indent);
453:                 } else {
454:                     $s .= $child;
455:                 }
456:             }
457: 
458:             // add end tag
459:             $s .= $this->endTag();
460:         }
461: 
462:         if ($indent !== NULL) {
463:             return "\n" . str_repeat("\t", $indent - 1) . $s . "\n" . str_repeat("\t", max(0, $indent - 2));
464:         }
465:         return $s;
466:     }
467: 
468: 
469:     public function __toString()
470:     {
471:         return $this->render();
472:     }
473: 
474: 
475:     /**
476:      * Returns element's start tag.
477:      * @return string
478:      */
479:     public function startTag()
480:     {
481:         if ($this->name) {
482:             return '<' . $this->name . $this->attributes() . (static::$xhtml && $this->isEmpty ? ' />' : '>');
483: 
484:         } else {
485:             return '';
486:         }
487:     }
488: 
489: 
490:     /**
491:      * Returns element's end tag.
492:      * @return string
493:      */
494:     public function endTag()
495:     {
496:         return $this->name && !$this->isEmpty ? '</' . $this->name . '>' : '';
497:     }
498: 
499: 
500:     /**
501:      * Returns element's attributes.
502:      * @return string
503:      * @internal
504:      */
505:     public function attributes()
506:     {
507:         if (!is_array($this->attrs)) {
508:             return '';
509:         }
510: 
511:         $s = '';
512:         foreach ($this->attrs as $key => $value) {
513:             if ($value === NULL || $value === FALSE) {
514:                 continue;
515: 
516:             } elseif ($value === TRUE) {
517:                 if (static::$xhtml) {
518:                     $s .= ' ' . $key . '="' . $key . '"';
519:                 } else {
520:                     $s .= ' ' . $key;
521:                 }
522:                 continue;
523: 
524:             } elseif (is_array($value)) {
525:                 if ($key === 'data') {
526:                     foreach ($value as $k => $v) {
527:                         if ($v !== NULL && $v !== FALSE) {
528:                             $q = strpos($v, '"') === FALSE ? '"' : "'";
529:                             $s .= ' data-' . $k . '='
530:                                 . $q
531:                                 . str_replace(
532:                                     array('&', $q, '<'),
533:                                     array('&amp;', $q === '"' ? '&quot;' : '&#39;', self::$xhtml ? '&lt;' : '<'),
534:                                     $v
535:                                 )
536:                                 . (strpos($v, '`') !== FALSE && strpbrk($v, ' <>"\'') === FALSE ? ' ' : '')
537:                                 . $q;
538:                         }
539:                     }
540:                     continue;
541:                 }
542: 
543:                 $tmp = NULL;
544:                 foreach ($value as $k => $v) {
545:                     if ($v != NULL) { // intentionally ==, skip NULLs & empty string
546:                         //  composite 'style' vs. 'others'
547:                         $tmp[] = $v === TRUE ? $k : (is_string($k) ? $k . ':' . $v : $v);
548:                     }
549:                 }
550:                 if ($tmp === NULL) {
551:                     continue;
552:                 }
553: 
554:                 $value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp);
555: 
556:             } elseif (is_float($value)) {
557:                 $value = rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.');
558: 
559:             } else {
560:                 $value = (string) $value;
561:             }
562: 
563:             $q = strpos($value, '"') === FALSE ? '"' : "'";
564:             $s .= ' ' . $key . '='
565:                 . $q
566:                 . str_replace(
567:                     array('&', $q, '<'),
568:                     array('&amp;', $q === '"' ? '&quot;' : '&#39;', self::$xhtml ? '&lt;' : '<'),
569:                     $value
570:                 )
571:                 . (strpos($value, '`') !== FALSE && strpbrk($value, ' <>"\'') === FALSE ? ' ' : '')
572:                 . $q;
573:         }
574: 
575:         $s = str_replace('@', '&#64;', $s);
576:         return $s;
577:     }
578: 
579: 
580:     /**
581:      * Clones all children too.
582:      */
583:     public function __clone()
584:     {
585:         foreach ($this->children as $key => $value) {
586:             if (is_object($value)) {
587:                 $this->children[$key] = clone $value;
588:             }
589:         }
590:     }
591: 
592: }
593: 
Nette 2.1 API documentation generated by ApiGen 2.8.0