Namespaces

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

Classes

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

Interfaces

  • IHtmlString

Exceptions

  • AssertionException
  • 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:  * @author     David Grudl
 25:  */
 26: class Html extends Nette\Object implements \ArrayAccess, \Countable, \IteratorAggregate, IHtmlString
 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(
 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 self
 56:      */
 57:     public static function el($name = NULL, $attrs = NULL)
 58:     {
 59:         $el = new static;
 60:         $parts = explode(' ', $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 self
 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 self
123:      */
124:     public function addAttributes(array $attrs)
125:     {
126:         $this->attrs = array_merge($this->attrs, $attrs);
127:         return $this;
128:     }
129: 
130: 
131:     /**
132:      * Overloaded setter for element's attribute.
133:      * @param  string    HTML attribute name
134:      * @param  mixed     HTML attribute value
135:      * @return void
136:      */
137:     public function __set($name, $value)
138:     {
139:         $this->attrs[$name] = $value;
140:     }
141: 
142: 
143:     /**
144:      * Overloaded getter for element's attribute.
145:      * @param  string    HTML attribute name
146:      * @return mixed     HTML attribute value
147:      */
148:     public function &__get($name)
149:     {
150:         return $this->attrs[$name];
151:     }
152: 
153: 
154:     /**
155:      * Overloaded tester for element's attribute.
156:      * @param  string    HTML attribute name
157:      * @return bool
158:      */
159:     public function __isset($name)
160:     {
161:         return isset($this->attrs[$name]);
162:     }
163: 
164: 
165:     /**
166:      * Overloaded unsetter for element's attribute.
167:      * @param  string    HTML attribute name
168:      * @return void
169:      */
170:     public function __unset($name)
171:     {
172:         unset($this->attrs[$name]);
173:     }
174: 
175: 
176:     /**
177:      * Overloaded setter for element's attribute.
178:      * @param  string  HTML attribute name
179:      * @param  array   (string) HTML attribute value or pair?
180:      * @return self
181:      */
182:     public function __call($m, $args)
183:     {
184:         $p = substr($m, 0, 3);
185:         if ($p === 'get' || $p === 'set' || $p === 'add') {
186:             $m = substr($m, 3);
187:             $m[0] = $m[0] | "\x20";
188:             if ($p === 'get') {
189:                 return isset($this->attrs[$m]) ? $this->attrs[$m] : NULL;
190: 
191:             } elseif ($p === 'add') {
192:                 $args[] = TRUE;
193:             }
194:         }
195: 
196:         if (count($args) === 0) { // invalid
197: 
198:         } elseif (count($args) === 1) { // set
199:             $this->attrs[$m] = $args[0];
200: 
201:         } elseif ((string) $args[0] === '') {
202:             $tmp = & $this->attrs[$m]; // appending empty value? -> ignore, but ensure it exists
203: 
204:         } elseif (!isset($this->attrs[$m]) || is_array($this->attrs[$m])) { // needs array
205:             $this->attrs[$m][$args[0]] = $args[1];
206: 
207:         } else {
208:             $this->attrs[$m] = array($this->attrs[$m], $args[0] => $args[1]);
209:         }
210: 
211:         return $this;
212:     }
213: 
214: 
215:     /**
216:      * Special setter for element's attribute.
217:      * @param  string path
218:      * @param  array query
219:      * @return self
220:      */
221:     public function href($path, $query = NULL)
222:     {
223:         if ($query) {
224:             $query = http_build_query($query, NULL, '&');
225:             if ($query !== '') {
226:                 $path .= '?' . $query;
227:             }
228:         }
229:         $this->attrs['href'] = $path;
230:         return $this;
231:     }
232: 
233: 
234:     /**
235:      * Sets element's HTML content.
236:      * @param  string raw HTML string
237:      * @return self
238:      * @throws Nette\InvalidArgumentException
239:      */
240:     public function setHtml($html)
241:     {
242:         if (is_array($html)) {
243:             throw new Nette\InvalidArgumentException(sprintf('Textual content must be a scalar, %s given.', gettype($html)));
244:         }
245:         $this->removeChildren();
246:         $this->children[] = (string) $html;
247:         return $this;
248:     }
249: 
250: 
251:     /**
252:      * Returns element's HTML content.
253:      * @return string
254:      */
255:     public function getHtml()
256:     {
257:         $s = '';
258:         foreach ($this->children as $child) {
259:             if (is_object($child)) {
260:                 $s .= $child->render();
261:             } else {
262:                 $s .= $child;
263:             }
264:         }
265:         return $s;
266:     }
267: 
268: 
269:     /**
270:      * Sets element's textual content.
271:      * @param  string
272:      * @return self
273:      * @throws Nette\InvalidArgumentException
274:      */
275:     public function setText($text)
276:     {
277:         if (!is_array($text) && !$text instanceof self) {
278:             $text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8');
279:         }
280:         return $this->setHtml($text);
281:     }
282: 
283: 
284:     /**
285:      * Returns element's textual content.
286:      * @return string
287:      */
288:     public function getText()
289:     {
290:         return html_entity_decode(strip_tags($this->getHtml()), ENT_QUOTES, 'UTF-8');
291:     }
292: 
293: 
294:     /**
295:      * Adds new element's child.
296:      * @param  Html|string Html node or raw HTML string
297:      * @return self
298:      */
299:     public function add($child)
300:     {
301:         return $this->insert(NULL, $child);
302:     }
303: 
304: 
305:     /**
306:      * Creates and adds a new Html child.
307:      * @param  string  elements's name
308:      * @param  array|string element's attributes or raw HTML string
309:      * @return self  created element
310:      */
311:     public function create($name, $attrs = NULL)
312:     {
313:         $this->insert(NULL, $child = static::el($name, $attrs));
314:         return $child;
315:     }
316: 
317: 
318:     /**
319:      * Inserts child node.
320:      * @param  int|NULL position of NULL for appending
321:      * @param  Html|string Html node or raw HTML string
322:      * @param  bool
323:      * @return self
324:      * @throws Nette\InvalidArgumentException
325:      */
326:     public function insert($index, $child, $replace = FALSE)
327:     {
328:         if ($child instanceof self || is_scalar($child)) {
329:             if ($index === NULL) { // append
330:                 $this->children[] = $child;
331: 
332:             } else { // insert or replace
333:                 array_splice($this->children, (int) $index, $replace ? 1 : 0, array($child));
334:             }
335: 
336:         } else {
337:             throw new Nette\InvalidArgumentException(sprintf('Child node must be scalar or Html object, %s given.', is_object($child) ? get_class($child) : gettype($child)));
338:         }
339: 
340:         return $this;
341:     }
342: 
343: 
344:     /**
345:      * Inserts (replaces) child node (\ArrayAccess implementation).
346:      * @param  int|NULL position of NULL for appending
347:      * @param  Html|string Html node or raw HTML string
348:      * @return void
349:      */
350:     public function offsetSet($index, $child)
351:     {
352:         $this->insert($index, $child, TRUE);
353:     }
354: 
355: 
356:     /**
357:      * Returns child node (\ArrayAccess implementation).
358:      * @param  int
359:      * @return self|string
360:      */
361:     public function offsetGet($index)
362:     {
363:         return $this->children[$index];
364:     }
365: 
366: 
367:     /**
368:      * Exists child node? (\ArrayAccess implementation).
369:      * @param  int
370:      * @return bool
371:      */
372:     public function offsetExists($index)
373:     {
374:         return isset($this->children[$index]);
375:     }
376: 
377: 
378:     /**
379:      * Removes child node (\ArrayAccess implementation).
380:      * @param  int
381:      * @return void
382:      */
383:     public function offsetUnset($index)
384:     {
385:         if (isset($this->children[$index])) {
386:             array_splice($this->children, (int) $index, 1);
387:         }
388:     }
389: 
390: 
391:     /**
392:      * Returns children count.
393:      * @return int
394:      */
395:     public function count()
396:     {
397:         return count($this->children);
398:     }
399: 
400: 
401:     /**
402:      * Removed all children.
403:      * @return void
404:      */
405:     public function removeChildren()
406:     {
407:         $this->children = array();
408:     }
409: 
410: 
411:     /**
412:      * Iterates over a elements.
413:      * @return \ArrayIterator
414:      */
415:     public function getIterator()
416:     {
417:         if (func_num_args() && func_get_arg(0)) {
418:             throw new Nette\DeprecatedException(__METHOD__ . " doesn't support deep iterator any more.");
419:         }
420:         return new \ArrayIterator($this->children);
421:     }
422: 
423: 
424:     /**
425:      * Returns all of children.
426:      * @return array
427:      */
428:     public function getChildren()
429:     {
430:         return $this->children;
431:     }
432: 
433: 
434:     /**
435:      * Renders element's start tag, content and end tag.
436:      * @param  int
437:      * @return string
438:      */
439:     public function render($indent = NULL)
440:     {
441:         $s = $this->startTag();
442: 
443:         if (!$this->isEmpty) {
444:             // add content
445:             if ($indent !== NULL) {
446:                 $indent++;
447:             }
448:             foreach ($this->children as $child) {
449:                 if (is_object($child)) {
450:                     $s .= $child->render($indent);
451:                 } else {
452:                     $s .= $child;
453:                 }
454:             }
455: 
456:             // add end tag
457:             $s .= $this->endTag();
458:         }
459: 
460:         if ($indent !== NULL) {
461:             return "\n" . str_repeat("\t", $indent - 1) . $s . "\n" . str_repeat("\t", max(0, $indent - 2));
462:         }
463:         return $s;
464:     }
465: 
466: 
467:     public function __toString()
468:     {
469:         return $this->render();
470:     }
471: 
472: 
473:     /**
474:      * Returns element's start tag.
475:      * @return string
476:      */
477:     public function startTag()
478:     {
479:         if ($this->name) {
480:             return '<' . $this->name . $this->attributes() . (static::$xhtml && $this->isEmpty ? ' />' : '>');
481: 
482:         } else {
483:             return '';
484:         }
485:     }
486: 
487: 
488:     /**
489:      * Returns element's end tag.
490:      * @return string
491:      */
492:     public function endTag()
493:     {
494:         return $this->name && !$this->isEmpty ? '</' . $this->name . '>' : '';
495:     }
496: 
497: 
498:     /**
499:      * Returns element's attributes.
500:      * @return string
501:      * @internal
502:      */
503:     public function attributes()
504:     {
505:         if (!is_array($this->attrs)) {
506:             return '';
507:         }
508: 
509:         $s = '';
510:         $attrs = $this->attrs;
511:         if (isset($attrs['data']) && is_array($attrs['data'])) { // deprecated
512:             foreach ($attrs['data'] as $key => $value) {
513:                 $attrs['data-' . $key] = $value;
514:             }
515:             unset($attrs['data']);
516:         }
517: 
518:         foreach ($attrs as $key => $value) {
519:             if ($value === NULL || $value === FALSE) {
520:                 continue;
521: 
522:             } elseif ($value === TRUE) {
523:                 if (static::$xhtml) {
524:                     $s .= ' ' . $key . '="' . $key . '"';
525:                 } else {
526:                     $s .= ' ' . $key;
527:                 }
528:                 continue;
529: 
530:             } elseif (is_array($value)) {
531:                 if (strncmp($key, 'data-', 5) === 0) {
532:                     $value = Json::encode($value);
533: 
534:                 } else {
535:                     $tmp = NULL;
536:                     foreach ($value as $k => $v) {
537:                         if ($v != NULL) { // intentionally ==, skip NULLs & empty string
538:                             //  composite 'style' vs. 'others'
539:                             $tmp[] = $v === TRUE ? $k : (is_string($k) ? $k . ':' . $v : $v);
540:                         }
541:                     }
542:                     if ($tmp === NULL) {
543:                         continue;
544:                     }
545: 
546:                     $value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp);
547:                 }
548: 
549:             } elseif (is_float($value)) {
550:                 $value = rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.');
551: 
552:             } else {
553:                 $value = (string) $value;
554:             }
555: 
556:             $q = strpos($value, '"') === FALSE ? '"' : "'";
557:             $s .= ' ' . $key . '=' . $q
558:                 . str_replace(
559:                     array('&', $q, '<'),
560:                     array('&amp;', $q === '"' ? '&quot;' : '&#39;', self::$xhtml ? '&lt;' : '<'),
561:                     $value
562:                 )
563:                 . (strpos($value, '`') !== FALSE && strpbrk($value, ' <>"\'') === FALSE ? ' ' : '')
564:                 . $q;
565:         }
566: 
567:         $s = str_replace('@', '&#64;', $s);
568:         return $s;
569:     }
570: 
571: 
572:     /**
573:      * Clones all children too.
574:      */
575:     public function __clone()
576:     {
577:         foreach ($this->children as $key => $value) {
578:             if (is_object($value)) {
579:                 $this->children[$key] = clone $value;
580:             }
581:         }
582:     }
583: 
584: }
585: 
Nette 2.2 API documentation generated by ApiGen 2.8.0