Namespaces

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

Classes

  • Arrays
  • Finder
  • Html
  • Json
  • LimitedScope
  • MimeTypeDetector
  • Neon
  • NeonEntity
  • Paginator
  • Strings
  • Tokenizer
  • 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 (http://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 = TRUE;
 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 = $attrs + $this->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 ($html === NULL) {
240:             $html = '';
241: 
242:         } elseif (is_array($html)) {
243:             throw new Nette\InvalidArgumentException(sprintf('Textual content must be a scalar, %s given.', gettype($html)));
244: 
245:         } else {
246:             $html = (string) $html;
247:         }
248: 
249:         $this->removeChildren();
250:         $this->children[] = $html;
251:         return $this;
252:     }
253: 
254: 
255:     /**
256:      * Returns element's HTML content.
257:      * @return string
258:      */
259:     public function getHtml()
260:     {
261:         $s = '';
262:         foreach ($this->children as $child) {
263:             if (is_object($child)) {
264:                 $s .= $child->render();
265:             } else {
266:                 $s .= $child;
267:             }
268:         }
269:         return $s;
270:     }
271: 
272: 
273:     /**
274:      * Sets element's textual content.
275:      * @param  string
276:      * @return self
277:      * @throws Nette\InvalidArgumentException
278:      */
279:     public function setText($text)
280:     {
281:         if (!is_array($text)) {
282:             $text = htmlspecialchars((string) $text, ENT_NOQUOTES);
283:         }
284:         return $this->setHtml($text);
285:     }
286: 
287: 
288:     /**
289:      * Returns element's textual content.
290:      * @return string
291:      */
292:     public function getText()
293:     {
294:         return html_entity_decode(strip_tags($this->getHtml()), ENT_QUOTES, 'UTF-8');
295:     }
296: 
297: 
298:     /**
299:      * Adds new element's child.
300:      * @param  Html|string Html node or raw HTML string
301:      * @return self
302:      */
303:     public function add($child)
304:     {
305:         return $this->insert(NULL, $child);
306:     }
307: 
308: 
309:     /**
310:      * Creates and adds a new Html child.
311:      * @param  string  elements's name
312:      * @param  array|string element's attributes or raw HTML string
313:      * @return self  created element
314:      */
315:     public function create($name, $attrs = NULL)
316:     {
317:         $this->insert(NULL, $child = static::el($name, $attrs));
318:         return $child;
319:     }
320: 
321: 
322:     /**
323:      * Inserts child node.
324:      * @param  int|NULL position of NULL for appending
325:      * @param  Html|string Html node or raw HTML string
326:      * @param  bool
327:      * @return self
328:      * @throws Nette\InvalidArgumentException
329:      */
330:     public function insert($index, $child, $replace = FALSE)
331:     {
332:         if ($child instanceof Html || is_scalar($child)) {
333:             if ($index === NULL) { // append
334:                 $this->children[] = $child;
335: 
336:             } else { // insert or replace
337:                 array_splice($this->children, (int) $index, $replace ? 1 : 0, array($child));
338:             }
339: 
340:         } else {
341:             throw new Nette\InvalidArgumentException(sprintf('Child node must be scalar or Html object, %s given.', is_object($child) ? get_class($child) : gettype($child)));
342:         }
343: 
344:         return $this;
345:     }
346: 
347: 
348:     /**
349:      * Inserts (replaces) child node (\ArrayAccess implementation).
350:      * @param  int|NULL position of NULL for appending
351:      * @param  Html|string Html node or raw HTML string
352:      * @return void
353:      */
354:     public function offsetSet($index, $child)
355:     {
356:         $this->insert($index, $child, TRUE);
357:     }
358: 
359: 
360:     /**
361:      * Returns child node (\ArrayAccess implementation).
362:      * @param  int
363:      * @return self|string
364:      */
365:     public function offsetGet($index)
366:     {
367:         return $this->children[$index];
368:     }
369: 
370: 
371:     /**
372:      * Exists child node? (\ArrayAccess implementation).
373:      * @param  int
374:      * @return bool
375:      */
376:     public function offsetExists($index)
377:     {
378:         return isset($this->children[$index]);
379:     }
380: 
381: 
382:     /**
383:      * Removes child node (\ArrayAccess implementation).
384:      * @param  int
385:      * @return void
386:      */
387:     public function offsetUnset($index)
388:     {
389:         if (isset($this->children[$index])) {
390:             array_splice($this->children, (int) $index, 1);
391:         }
392:     }
393: 
394: 
395:     /**
396:      * Returns children count.
397:      * @return int
398:      */
399:     public function count()
400:     {
401:         return count($this->children);
402:     }
403: 
404: 
405:     /**
406:      * Removed all children.
407:      * @return void
408:      */
409:     public function removeChildren()
410:     {
411:         $this->children = array();
412:     }
413: 
414: 
415:     /**
416:      * Iterates over a elements.
417:      * @param  bool    recursive?
418:      * @param  string  class types filter
419:      * @return \RecursiveIterator
420:      */
421:     public function getIterator($deep = FALSE)
422:     {
423:         if ($deep) {
424:             $deep = $deep > 0 ? \RecursiveIteratorIterator::SELF_FIRST : \RecursiveIteratorIterator::CHILD_FIRST;
425:             return new \RecursiveIteratorIterator(new Nette\Iterators\Recursor(new \ArrayIterator($this->children)), $deep);
426: 
427:         } else {
428:             return new Nette\Iterators\Recursor(new \ArrayIterator($this->children));
429:         }
430:     }
431: 
432: 
433:     /**
434:      * Returns all of children.
435:      * @return array
436:      */
437:     public function getChildren()
438:     {
439:         return $this->children;
440:     }
441: 
442: 
443:     /**
444:      * Renders element's start tag, content and end tag.
445:      * @param  int
446:      * @return string
447:      */
448:     public function render($indent = NULL)
449:     {
450:         $s = $this->startTag();
451: 
452:         if (!$this->isEmpty) {
453:             // add content
454:             if ($indent !== NULL) {
455:                 $indent++;
456:             }
457:             foreach ($this->children as $child) {
458:                 if (is_object($child)) {
459:                     $s .= $child->render($indent);
460:                 } else {
461:                     $s .= $child;
462:                 }
463:             }
464: 
465:             // add end tag
466:             $s .= $this->endTag();
467:         }
468: 
469:         if ($indent !== NULL) {
470:             return "\n" . str_repeat("\t", $indent - 1) . $s . "\n" . str_repeat("\t", max(0, $indent - 2));
471:         }
472:         return $s;
473:     }
474: 
475: 
476:     public function __toString()
477:     {
478:         return $this->render();
479:     }
480: 
481: 
482:     /**
483:      * Returns element's start tag.
484:      * @return string
485:      */
486:     public function startTag()
487:     {
488:         if ($this->name) {
489:             return '<' . $this->name . $this->attributes() . (static::$xhtml && $this->isEmpty ? ' />' : '>');
490: 
491:         } else {
492:             return '';
493:         }
494:     }
495: 
496: 
497:     /**
498:      * Returns element's end tag.
499:      * @return string
500:      */
501:     public function endTag()
502:     {
503:         return $this->name && !$this->isEmpty ? '</' . $this->name . '>' : '';
504:     }
505: 
506: 
507:     /**
508:      * Returns element's attributes.
509:      * @return string
510:      * @internal
511:      */
512:     public function attributes()
513:     {
514:         if (!is_array($this->attrs)) {
515:             return '';
516:         }
517: 
518:         $s = '';
519:         foreach ($this->attrs as $key => $value) {
520:             if ($value === NULL || $value === FALSE) {
521:                 continue;
522: 
523:             } elseif ($value === TRUE) {
524:                 if (static::$xhtml) {
525:                     $s .= ' ' . $key . '="' . $key . '"';
526:                 } else {
527:                     $s .= ' ' . $key;
528:                 }
529:                 continue;
530: 
531:             } elseif (is_array($value)) {
532:                 if ($key === 'data') {
533:                     foreach ($value as $k => $v) {
534:                         if ($v !== NULL && $v !== FALSE) {
535:                             $q = strpos($v, '"') === FALSE ? '"' : "'";
536:                             $s .= ' data-' . $k . '='
537:                                 . $q . str_replace(array('&', $q), array('&amp;', $q === '"' ? '&quot;' : '&#39;'), $v)
538:                                 . (strpos($v, '`') !== FALSE && strpbrk($v, ' <>"\'') === FALSE ? ' ' : '')
539:                                 . $q;
540:                         }
541:                     }
542:                     continue;
543:                 }
544: 
545:                 $tmp = NULL;
546:                 foreach ($value as $k => $v) {
547:                     if ($v != NULL) { // intentionally ==, skip NULLs & empty string
548:                         //  composite 'style' vs. 'others'
549:                         $tmp[] = $v === TRUE ? $k : (is_string($k) ? $k . ':' . $v : $v);
550:                     }
551:                 }
552:                 if ($tmp === NULL) {
553:                     continue;
554:                 }
555: 
556:                 $value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp);
557: 
558:             } elseif (is_float($value)) {
559:                 $value = rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.');
560: 
561:             } else {
562:                 $value = (string) $value;
563:             }
564: 
565:             $q = strpos($value, '"') === FALSE ? '"' : "'";
566:             $s .= ' ' . $key . '='
567:                 . $q . str_replace(array('&', $q), array('&amp;', $q === '"' ? '&quot;' : '&#39;'), $value)
568:                 . (strpos($value, '`') !== FALSE && strpbrk($value, ' <>"\'') === FALSE ? ' ' : '')
569:                 . $q;
570:         }
571: 
572:         $s = str_replace('@', '&#64;', $s);
573:         return $s;
574:     }
575: 
576: 
577:     /**
578:      * Clones all children too.
579:      */
580:     public function __clone()
581:     {
582:         foreach ($this->children as $key => $value) {
583:             if (is_object($value)) {
584:                 $this->children[$key] = clone $value;
585:             }
586:         }
587:     }
588: 
589: }
590: 
Nette 2.0 API documentation generated by ApiGen 2.8.0