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
      • Reflection
      • 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
  • Strings
  • TokenIterator
  • Tokenizer
  • Validators

Interfaces

  • IHtmlString

Exceptions

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