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
  • PHP

Classes

  • Arrays
  • Finder
  • Html
  • Json
  • LimitedScope
  • MimeTypeDetector
  • Neon
  • NeonEntity
  • Paginator
  • Strings
  • Tokenizer
  • Validators

Exceptions

  • AssertionException
  • JsonException
  • NeonException
  • RegexpException
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (http://nette.org)
  5:  *
  6:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  7:  *
  8:  * For the full copyright and license information, please view
  9:  * the file license.txt that was distributed with this source code.
 10:  */
 11: 
 12: namespace Nette\Utils;
 13: 
 14: use Nette;
 15: 
 16: 
 17: 
 18: /**
 19:  * HTML helper.
 20:  *
 21:  * <code>
 22:  * $anchor = Html::el('a')->href($link)->setText('Nette');
 23:  * $el->class = 'myclass';
 24:  * echo $el;
 25:  *
 26:  * echo $el->startTag(), $el->endTag();
 27:  * </code>
 28:  *
 29:  * @author     David Grudl
 30:  */
 31: class Html extends Nette\Object implements \ArrayAccess, \Countable, \IteratorAggregate
 32: {
 33:     /** @var string  element's name */
 34:     private $name;
 35: 
 36:     /** @var bool  is element empty? */
 37:     private $isEmpty;
 38: 
 39:     /** @var array  element's attributes */
 40:     public $attrs = array();
 41: 
 42:     /** @var array  of Html | string nodes */
 43:     protected $children = array();
 44: 
 45:     /** @var bool  use XHTML syntax? */
 46:     public static $xhtml = TRUE;
 47: 
 48:     /** @var array  empty elements */
 49:     public static $emptyElements = array('img'=>1,'hr'=>1,'br'=>1,'input'=>1,'meta'=>1,'area'=>1,'embed'=>1,'keygen'=>1,
 50:         'source'=>1,'base'=>1,'col'=>1,'link'=>1,'param'=>1,'basefont'=>1,'frame'=>1,'isindex'=>1,'wbr'=>1,'command'=>1);
 51: 
 52: 
 53: 
 54:     /**
 55:      * Static factory.
 56:      * @param  string element name (or NULL)
 57:      * @param  array|string element's attributes (or textual content)
 58:      * @return Html
 59:      */
 60:     public static function el($name = NULL, $attrs = NULL)
 61:     {
 62:         $el = new static;
 63:         $parts = explode(' ', $name, 2);
 64:         $el->setName($parts[0]);
 65: 
 66:         if (is_array($attrs)) {
 67:             $el->attrs = $attrs;
 68: 
 69:         } elseif ($attrs !== NULL) {
 70:             $el->setText($attrs);
 71:         }
 72: 
 73:         if (isset($parts[1])) {
 74:             foreach (Strings::matchAll($parts[1] . ' ', '#([a-z0-9:-]+)(?:=(["\'])?(.*?)(?(2)\\2|\s))?#i') as $m) {
 75:                 $el->attrs[$m[1]] = isset($m[3]) ? $m[3] : TRUE;
 76:             }
 77:         }
 78: 
 79:         return $el;
 80:     }
 81: 
 82: 
 83: 
 84:     /**
 85:      * Changes element's name.
 86:      * @param  string
 87:      * @param  bool  Is element empty?
 88:      * @return Html  provides a fluent interface
 89:      * @throws Nette\InvalidArgumentException
 90:      */
 91:     final public function setName($name, $isEmpty = NULL)
 92:     {
 93:         if ($name !== NULL && !is_string($name)) {
 94:             throw new Nette\InvalidArgumentException("Name must be string or NULL, " . gettype($name) ." given.");
 95:         }
 96: 
 97:         $this->name = $name;
 98:         $this->isEmpty = $isEmpty === NULL ? isset(static::$emptyElements[$name]) : (bool) $isEmpty;
 99:         return $this;
100:     }
101: 
102: 
103: 
104:     /**
105:      * Returns element's name.
106:      * @return string
107:      */
108:     final public function getName()
109:     {
110:         return $this->name;
111:     }
112: 
113: 
114: 
115:     /**
116:      * Is element empty?
117:      * @return bool
118:      */
119:     final public function isEmpty()
120:     {
121:         return $this->isEmpty;
122:     }
123: 
124: 
125: 
126:     /**
127:      * Sets multiple attributes.
128:      * @param  array
129:      * @return Html  provides a fluent interface
130:      */
131:     public function addAttributes(array $attrs)
132:     {
133:         $this->attrs = $attrs + $this->attrs;
134:         return $this;
135:     }
136: 
137: 
138: 
139:     /**
140:      * Overloaded setter for element's attribute.
141:      * @param  string    HTML attribute name
142:      * @param  mixed     HTML attribute value
143:      * @return void
144:      */
145:     final public function __set($name, $value)
146:     {
147:         $this->attrs[$name] = $value;
148:     }
149: 
150: 
151: 
152:     /**
153:      * Overloaded getter for element's attribute.
154:      * @param  string    HTML attribute name
155:      * @return mixed     HTML attribute value
156:      */
157:     final public function &__get($name)
158:     {
159:         return $this->attrs[$name];
160:     }
161: 
162: 
163: 
164:     /**
165:      * Overloaded unsetter for element's attribute.
166:      * @param  string    HTML attribute name
167:      * @return void
168:      */
169:     final public function __unset($name)
170:     {
171:         unset($this->attrs[$name]);
172:     }
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 Html  provides a fluent interface
181:      */
182:     final 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:     /**
217:      * Special setter for element's attribute.
218:      * @param  string path
219:      * @param  array query
220:      * @return Html  provides a fluent interface
221:      */
222:     final public function href($path, $query = NULL)
223:     {
224:         if ($query) {
225:             $query = http_build_query($query, NULL, '&');
226:             if ($query !== '') {
227:                 $path .= '?' . $query;
228:             }
229:         }
230:         $this->attrs['href'] = $path;
231:         return $this;
232:     }
233: 
234: 
235: 
236:     /**
237:      * Sets element's HTML content.
238:      * @param  string
239:      * @return Html  provides a fluent interface
240:      * @throws Nette\InvalidArgumentException
241:      */
242:     final public function setHtml($html)
243:     {
244:         if ($html === NULL) {
245:             $html = '';
246: 
247:         } elseif (is_array($html)) {
248:             throw new Nette\InvalidArgumentException("Textual content must be a scalar, " . gettype($html) ." given.");
249: 
250:         } else {
251:             $html = (string) $html;
252:         }
253: 
254:         $this->removeChildren();
255:         $this->children[] = $html;
256:         return $this;
257:     }
258: 
259: 
260: 
261:     /**
262:      * Returns element's HTML content.
263:      * @return string
264:      */
265:     final public function getHtml()
266:     {
267:         $s = '';
268:         foreach ($this->children as $child) {
269:             if (is_object($child)) {
270:                 $s .= $child->render();
271:             } else {
272:                 $s .= $child;
273:             }
274:         }
275:         return $s;
276:     }
277: 
278: 
279: 
280:     /**
281:      * Sets element's textual content.
282:      * @param  string
283:      * @return Html  provides a fluent interface
284:      * @throws Nette\InvalidArgumentException
285:      */
286:     final public function setText($text)
287:     {
288:         if (!is_array($text)) {
289:             $text = htmlspecialchars((string) $text, ENT_NOQUOTES);
290:         }
291:         return $this->setHtml($text);
292:     }
293: 
294: 
295: 
296:     /**
297:      * Returns element's textual content.
298:      * @return string
299:      */
300:     final public function getText()
301:     {
302:         return html_entity_decode(strip_tags($this->getHtml()), ENT_QUOTES, 'UTF-8');
303:     }
304: 
305: 
306: 
307:     /**
308:      * Adds new element's child.
309:      * @param  Html|string child node
310:      * @return Html  provides a fluent interface
311:      */
312:     final public function add($child)
313:     {
314:         return $this->insert(NULL, $child);
315:     }
316: 
317: 
318: 
319:     /**
320:      * Creates and adds a new Html child.
321:      * @param  string  elements's name
322:      * @param  array|string element's attributes (or textual content)
323:      * @return Html  created element
324:      */
325:     final public function create($name, $attrs = NULL)
326:     {
327:         $this->insert(NULL, $child = static::el($name, $attrs));
328:         return $child;
329:     }
330: 
331: 
332: 
333:     /**
334:      * Inserts child node.
335:      * @param  int
336:      * @param  Html node
337:      * @param  bool
338:      * @return Html  provides a fluent interface
339:      * @throws \Exception
340:      */
341:     public function insert($index, $child, $replace = FALSE)
342:     {
343:         if ($child instanceof Html || is_scalar($child)) {
344:             if ($index === NULL) { // append
345:                 $this->children[] = $child;
346: 
347:             } else { // insert or replace
348:                 array_splice($this->children, (int) $index, $replace ? 1 : 0, array($child));
349:             }
350: 
351:         } else {
352:             throw new Nette\InvalidArgumentException("Child node must be scalar or Html object, " . (is_object($child) ? get_class($child) : gettype($child)) ." given.");
353:         }
354: 
355:         return $this;
356:     }
357: 
358: 
359: 
360:     /**
361:      * Inserts (replaces) child node (\ArrayAccess implementation).
362:      * @param  int
363:      * @param  Html node
364:      * @return void
365:      */
366:     final public function offsetSet($index, $child)
367:     {
368:         $this->insert($index, $child, TRUE);
369:     }
370: 
371: 
372: 
373:     /**
374:      * Returns child node (\ArrayAccess implementation).
375:      * @param  int index
376:      * @return mixed
377:      */
378:     final public function offsetGet($index)
379:     {
380:         return $this->children[$index];
381:     }
382: 
383: 
384: 
385:     /**
386:      * Exists child node? (\ArrayAccess implementation).
387:      * @param  int index
388:      * @return bool
389:      */
390:     final public function offsetExists($index)
391:     {
392:         return isset($this->children[$index]);
393:     }
394: 
395: 
396: 
397:     /**
398:      * Removes child node (\ArrayAccess implementation).
399:      * @param  int index
400:      * @return void
401:      */
402:     public function offsetUnset($index)
403:     {
404:         if (isset($this->children[$index])) {
405:             array_splice($this->children, (int) $index, 1);
406:         }
407:     }
408: 
409: 
410: 
411:     /**
412:      * Required by the \Countable interface.
413:      * @return int
414:      */
415:     final public function count()
416:     {
417:         return count($this->children);
418:     }
419: 
420: 
421: 
422:     /**
423:      * Removed all children.
424:      * @return void
425:      */
426:     public function removeChildren()
427:     {
428:         $this->children = array();
429:     }
430: 
431: 
432: 
433:     /**
434:      * Iterates over a elements.
435:      * @param  bool    recursive?
436:      * @param  string  class types filter
437:      * @return \RecursiveIterator
438:      */
439:     final public function getIterator($deep = FALSE)
440:     {
441:         if ($deep) {
442:             $deep = $deep > 0 ? \RecursiveIteratorIterator::SELF_FIRST : \RecursiveIteratorIterator::CHILD_FIRST;
443:             return new \RecursiveIteratorIterator(new Nette\Iterators\Recursor(new \ArrayIterator($this->children)), $deep);
444: 
445:         } else {
446:             return new Nette\Iterators\Recursor(new \ArrayIterator($this->children));
447:         }
448:     }
449: 
450: 
451: 
452:     /**
453:      * Returns all of children.
454:      * return array
455:      */
456:     final public function getChildren()
457:     {
458:         return $this->children;
459:     }
460: 
461: 
462: 
463:     /**
464:      * Renders element's start tag, content and end tag.
465:      * @param  int indent
466:      * @return string
467:      */
468:     final public function render($indent = NULL)
469:     {
470:         $s = $this->startTag();
471: 
472:         if (!$this->isEmpty) {
473:             // add content
474:             if ($indent !== NULL) {
475:                 $indent++;
476:             }
477:             foreach ($this->children as $child) {
478:                 if (is_object($child)) {
479:                     $s .= $child->render($indent);
480:                 } else {
481:                     $s .= $child;
482:                 }
483:             }
484: 
485:             // add end tag
486:             $s .= $this->endTag();
487:         }
488: 
489:         if ($indent !== NULL) {
490:             return "\n" . str_repeat("\t", $indent - 1) . $s . "\n" . str_repeat("\t", max(0, $indent - 2));
491:         }
492:         return $s;
493:     }
494: 
495: 
496: 
497:     final public function __toString()
498:     {
499:         return $this->render();
500:     }
501: 
502: 
503: 
504:     /**
505:      * Returns element's start tag.
506:      * @return string
507:      */
508:     final public function startTag()
509:     {
510:         if ($this->name) {
511:             return '<' . $this->name . $this->attributes() . (static::$xhtml && $this->isEmpty ? ' />' : '>');
512: 
513:         } else {
514:             return '';
515:         }
516:     }
517: 
518: 
519: 
520:     /**
521:      * Returns element's end tag.
522:      * @return string
523:      */
524:     final public function endTag()
525:     {
526:         return $this->name && !$this->isEmpty ? '</' . $this->name . '>' : '';
527:     }
528: 
529: 
530: 
531:     /**
532:      * Returns element's attributes.
533:      * @return string
534:      */
535:     final public function attributes()
536:     {
537:         if (!is_array($this->attrs)) {
538:             return '';
539:         }
540: 
541:         $s = '';
542:         foreach ($this->attrs as $key => $value) {
543:             if ($value === NULL || $value === FALSE) {
544:                 continue;
545: 
546:             } elseif ($value === TRUE) {
547:                 if (static::$xhtml) {
548:                     $s .= ' ' . $key . '="' . $key . '"';
549:                 } else {
550:                     $s .= ' ' . $key;
551:                 }
552:                 continue;
553: 
554:             } elseif (is_array($value)) {
555:                 if ($key === 'data') {
556:                     foreach ($value as $k => $v) {
557:                         if ($v !== NULL && $v !== FALSE) {
558:                             $s .= ' data-' . $k . '="' . htmlspecialchars((string) $v) . '"';
559:                         }
560:                     }
561:                     continue;
562:                 }
563: 
564:                 $tmp = NULL;
565:                 foreach ($value as $k => $v) {
566:                     if ($v != NULL) { // intentionally ==, skip NULLs & empty string
567:                         //  composite 'style' vs. 'others'
568:                         $tmp[] = $v === TRUE ? $k : (is_string($k) ? $k . ':' . $v : $v);
569:                     }
570:                 }
571:                 if ($tmp === NULL) {
572:                     continue;
573:                 }
574: 
575:                 $value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp);
576: 
577:             } else {
578:                 $value = (string) $value;
579:             }
580: 
581:             $s .= ' ' . $key . '="' . htmlspecialchars($value) . '"';
582:         }
583: 
584:         $s = str_replace('@', '&#64;', $s);
585:         return $s;
586:     }
587: 
588: 
589: 
590:     /**
591:      * Clones all children too.
592:      */
593:     public function __clone()
594:     {
595:         foreach ($this->children as $key => $value) {
596:             if (is_object($value)) {
597:                 $this->children[$key] = clone $value;
598:             }
599:         }
600:     }
601: 
602: }
603: 
Nette Framework 2.0.0 API API documentation generated by ApiGen 2.7.0