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
  • CallbackFilterIterator
  • DateTime
  • FileSystem
  • Finder
  • Html
  • Image
  • Json
  • LimitedScope
  • MimeTypeDetector
  • ObjectMixin
  • Paginator
  • Random
  • RecursiveCallbackFilterIterator
  • Strings
  • TokenIterator
  • Tokenizer
  • Validators

Interfaces

  • IHtmlString

Exceptions

  • AssertionException
  • JsonException
  • RegexpException
  • TokenizerException
  • UnknownImageFileException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (http://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, 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('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 textual content)
 52:      * @return Html
 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
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);
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 child node
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 textual content)
306:      * @return Html  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
318:      * @param  Html node
319:      * @param  bool
320:      * @return self
321:      * @throws \Exception
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
344:      * @param  Html node
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 index
356:      * @return mixed
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 index
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 index
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:      * Required by the \Countable interface.
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:      * @return \ArrayIterator
411:      */
412:     public function getIterator()
413:     {
414:         if (func_num_args() && func_get_arg(0)) {
415:             throw new Nette\DeprecatedException(__METHOD__ . " doesn't support deep iterator any more.");
416:         }
417:         return new \ArrayIterator($this->children);
418:     }
419: 
420: 
421:     /**
422:      * Returns all of children.
423:      * @return array
424:      */
425:     public function getChildren()
426:     {
427:         return $this->children;
428:     }
429: 
430: 
431:     /**
432:      * Renders element's start tag, content and end tag.
433:      * @param  int indent
434:      * @return string
435:      */
436:     public function render($indent = NULL)
437:     {
438:         $s = $this->startTag();
439: 
440:         if (!$this->isEmpty) {
441:             // add content
442:             if ($indent !== NULL) {
443:                 $indent++;
444:             }
445:             foreach ($this->children as $child) {
446:                 if (is_object($child)) {
447:                     $s .= $child->render($indent);
448:                 } else {
449:                     $s .= $child;
450:                 }
451:             }
452: 
453:             // add end tag
454:             $s .= $this->endTag();
455:         }
456: 
457:         if ($indent !== NULL) {
458:             return "\n" . str_repeat("\t", $indent - 1) . $s . "\n" . str_repeat("\t", max(0, $indent - 2));
459:         }
460:         return $s;
461:     }
462: 
463: 
464:     public function __toString()
465:     {
466:         return $this->render();
467:     }
468: 
469: 
470:     /**
471:      * Returns element's start tag.
472:      * @return string
473:      */
474:     public function startTag()
475:     {
476:         if ($this->name) {
477:             return '<' . $this->name . $this->attributes() . (static::$xhtml && $this->isEmpty ? ' />' : '>');
478: 
479:         } else {
480:             return '';
481:         }
482:     }
483: 
484: 
485:     /**
486:      * Returns element's end tag.
487:      * @return string
488:      */
489:     public function endTag()
490:     {
491:         return $this->name && !$this->isEmpty ? '</' . $this->name . '>' : '';
492:     }
493: 
494: 
495:     /**
496:      * Returns element's attributes.
497:      * @return string
498:      * @internal
499:      */
500:     public function attributes()
501:     {
502:         if (!is_array($this->attrs)) {
503:             return '';
504:         }
505: 
506:         $s = '';
507:         foreach ($this->attrs as $key => $value) {
508:             if ($value === NULL || $value === FALSE) {
509:                 continue;
510: 
511:             } elseif ($value === TRUE) {
512:                 if (static::$xhtml) {
513:                     $s .= ' ' . $key . '="' . $key . '"';
514:                 } else {
515:                     $s .= ' ' . $key;
516:                 }
517:                 continue;
518: 
519:             } elseif (is_array($value)) {
520:                 if ($key === 'data') { // deprecated
521:                     foreach ($value as $k => $v) {
522:                         if ($v !== NULL && $v !== FALSE) {
523:                             if (is_array($v)) {
524:                                 $v = Json::encode($v);
525:                             }
526:                             $q = strpos($v, '"') === FALSE ? '"' : "'";
527:                             $s .= ' data-' . $k . '='
528:                                 . $q . str_replace(array('&', $q), array('&amp;', $q === '"' ? '&quot;' : '&#39;'), $v)
529:                                 . (strpos($v, '`') !== FALSE && strpbrk($v, ' <>"\'') === FALSE ? ' ' : '')
530:                                 . $q;
531:                         }
532:                     }
533:                     continue;
534: 
535:                 } elseif (strncmp($key, 'data-', 5) === 0) {
536:                     $value = Json::encode($value);
537: 
538:                 } else {
539:                     $tmp = NULL;
540:                     foreach ($value as $k => $v) {
541:                         if ($v != NULL) { // intentionally ==, skip NULLs & empty string
542:                             //  composite 'style' vs. 'others'
543:                             $tmp[] = $v === TRUE ? $k : (is_string($k) ? $k . ':' . $v : $v);
544:                         }
545:                     }
546:                     if ($tmp === NULL) {
547:                         continue;
548:                     }
549: 
550:                     $value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp);
551:                 }
552: 
553:             } else {
554:                 $value = (string) $value;
555:             }
556: 
557:             $q = strpos($value, '"') === FALSE ? '"' : "'";
558:             $s .= ' ' . $key . '='
559:                 . $q . str_replace(array('&', $q), array('&amp;', $q === '"' ? '&quot;' : '&#39;'), $value)
560:                 . (strpos($value, '`') !== FALSE && strpbrk($value, ' <>"\'') === FALSE ? ' ' : '')
561:                 . $q;
562:         }
563: 
564:         $s = str_replace('@', '&#64;', $s);
565:         return $s;
566:     }
567: 
568: 
569:     /**
570:      * Clones all children too.
571:      */
572:     public function __clone()
573:     {
574:         foreach ($this->children as $key => $value) {
575:             if (is_object($value)) {
576:                 $this->children[$key] = clone $value;
577:             }
578:         }
579:     }
580: 
581: }
582: 
Nette 2.2.6 API API documentation generated by ApiGen 2.8.0