Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Utils
  • none
  • Tracy
    • Bridges
      • Nette

Classes

  • ArrayHash
  • ArrayList
  • Arrays
  • Callback
  • DateTime
  • FileSystem
  • Finder
  • Html
  • Image
  • Json
  • LimitedScope
  • MimeTypeDetector
  • ObjectMixin
  • Paginator
  • Random
  • Reflection
  • Strings
  • TokenIterator
  • Tokenizer
  • Validators

Interfaces

  • IHtmlString

Exceptions

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