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