Namespaces

  • Nette
    • Application
    • Caching
    • Collections
    • Config
    • Forms
    • IO
    • Loaders
    • Mail
    • Reflection
    • Security
    • Templates
    • Web
  • None
  • PHP

Classes

  • Ftp
  • Html
  • HttpContext
  • HttpRequest
  • HttpResponse
  • HttpUploadedFile
  • Session
  • SessionNamespace
  • Uri
  • UriScript
  • User

Interfaces

  • IHttpRequest
  • IHttpResponse
  • IUser

Exceptions

  • FtpException
  • Overview
  • Namespace
  • Class
  • Tree
  • Other releases
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://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\Web;
 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,'command'=>1,'keygen'=>1,'source'=>1,
 50:         'base'=>1,'col'=>1,'link'=>1,'param'=>1,'basefont'=>1,'frame'=>1,'isindex'=>1,'wbr'=>1,'embed'=>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:             preg_match_all('#([a-z0-9:-]+)(?:=(["\'])?(.*?)(?(2)\\2|\s))?#i', $parts[1] . ' ', $parts, PREG_SET_ORDER);
 75:             foreach ($parts as $m) {
 76:                 $el->attrs[$m[1]] = isset($m[3]) ? $m[3] : TRUE;
 77:             }
 78:         }
 79: 
 80:         return $el;
 81:     }
 82: 
 83: 
 84: 
 85:     /**
 86:      * Changes element's name.
 87:      * @param  string
 88:      * @param  bool  Is element empty?
 89:      * @return Html  provides a fluent interface
 90:      * @throws \InvalidArgumentException
 91:      */
 92:     final public function setName($name, $isEmpty = NULL)
 93:     {
 94:         if ($name !== NULL && !is_string($name)) {
 95:             throw new \InvalidArgumentException("Name must be string or NULL, " . gettype($name) ." given.");
 96:         }
 97: 
 98:         $this->name = $name;
 99:         $this->isEmpty = $isEmpty === NULL ? isset(self::$emptyElements[$name]) : (bool) $isEmpty;
100:         return $this;
101:     }
102: 
103: 
104: 
105:     /**
106:      * Returns element's name.
107:      * @return string
108:      */
109:     final public function getName()
110:     {
111:         return $this->name;
112:     }
113: 
114: 
115: 
116:     /**
117:      * Is element empty?
118:      * @return bool
119:      */
120:     final public function isEmpty()
121:     {
122:         return $this->isEmpty;
123:     }
124: 
125: 
126: 
127:     /**
128:      * Overloaded setter for element's attribute.
129:      * @param  string    HTML attribute name
130:      * @param  mixed     HTML attribute value
131:      * @return void
132:      */
133:     final public function __set($name, $value)
134:     {
135:         $this->attrs[$name] = $value;
136:     }
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:     final public function &__get($name)
146:     {
147:         return $this->attrs[$name];
148:     }
149: 
150: 
151: 
152:     /**
153:      * Overloaded unsetter for element's attribute.
154:      * @param  string    HTML attribute name
155:      * @return void
156:      */
157:     final public function __unset($name)
158:     {
159:         unset($this->attrs[$name]);
160:     }
161: 
162: 
163: 
164:     /**
165:      * Overloaded setter for element's attribute.
166:      * @param  string  HTML attribute name
167:      * @param  array   (string) HTML attribute value or pair?
168:      * @return Html  provides a fluent interface
169:      */
170:     final public function __call($m, $args)
171:     {
172:         $p = substr($m, 0, 3);
173:         if ($p === 'get' || $p === 'set' || $p === 'add') {
174:             $m = substr($m, 3);
175:             $m[0] = $m[0] | "\x20";
176:             if ($p === 'get') {
177:                 return isset($this->attrs[$m]) ? $this->attrs[$m] : NULL;
178: 
179:             } elseif ($p === 'add') {
180:                 $args[] = TRUE;
181:             }
182:         }
183: 
184:         if (count($args) === 0) { // invalid
185: 
186:         } elseif (count($args) === 1) { // set
187:             $this->attrs[$m] = $args[0];
188: 
189:         } elseif ((string) $args[0] === '') {
190:             $tmp = & $this->attrs[$m]; // appending empty value? -> ignore, but ensure it exists
191: 
192:         } elseif (!isset($this->attrs[$m]) || is_array($this->attrs[$m])) { // needs array
193:             $this->attrs[$m][$args[0]] = $args[1];
194: 
195:         } else {
196:             $this->attrs[$m] = array($this->attrs[$m], $args[0] => $args[1]);
197:         }
198: 
199:         return $this;
200:     }
201: 
202: 
203: 
204:     /**
205:      * Special setter for element's attribute.
206:      * @param  string path
207:      * @param  array query
208:      * @return Html  provides a fluent interface
209:      */
210:     final public function href($path, $query = NULL)
211:     {
212:         if ($query) {
213:             $query = http_build_query($query, NULL, '&');
214:             if ($query !== '') $path .= '?' . $query;
215:         }
216:         $this->attrs['href'] = $path;
217:         return $this;
218:     }
219: 
220: 
221: 
222:     /**
223:      * Sets element's HTML content.
224:      * @param  string
225:      * @return Html  provides a fluent interface
226:      * @throws \InvalidArgumentException
227:      */
228:     final public function setHtml($html)
229:     {
230:         if ($html === NULL) {
231:             $html = '';
232: 
233:         } elseif (is_array($html)) {
234:             throw new \InvalidArgumentException("Textual content must be a scalar, " . gettype($html) ." given.");
235: 
236:         } else {
237:             $html = (string) $html;
238:         }
239: 
240:         $this->removeChildren();
241:         $this->children[] = $html;
242:         return $this;
243:     }
244: 
245: 
246: 
247:     /**
248:      * Returns element's HTML content.
249:      * @return string
250:      */
251:     final public function getHtml()
252:     {
253:         $s = '';
254:         foreach ($this->children as $child) {
255:             if (is_object($child)) {
256:                 $s .= $child->render();
257:             } else {
258:                 $s .= $child;
259:             }
260:         }
261:         return $s;
262:     }
263: 
264: 
265: 
266:     /**
267:      * Sets element's textual content.
268:      * @param  string
269:      * @return Html  provides a fluent interface
270:      * @throws \InvalidArgumentException
271:      */
272:     final public function setText($text)
273:     {
274:         if (!is_array($text)) {
275:             $text = htmlspecialchars((string) $text, ENT_NOQUOTES);
276:         }
277:         return $this->setHtml($text);
278:     }
279: 
280: 
281: 
282:     /**
283:      * Returns element's textual content.
284:      * @return string
285:      */
286:     final public function getText()
287:     {
288:         return html_entity_decode(strip_tags($this->getHtml()), ENT_QUOTES, 'UTF-8');
289:     }
290: 
291: 
292: 
293:     /**
294:      * Adds new element's child.
295:      * @param  Html|string child node
296:      * @return Html  provides a fluent interface
297:      */
298:     final public function add($child)
299:     {
300:         return $this->insert(NULL, $child);
301:     }
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 textual content)
309:      * @return Html  created element
310:      */
311:     final public function create($name, $attrs = NULL)
312:     {
313:         $this->insert(NULL, $child = static::el($name, $attrs));
314:         return $child;
315:     }
316: 
317: 
318: 
319:     /**
320:      * Inserts child node.
321:      * @param  int
322:      * @param  Html node
323:      * @param  bool
324:      * @return Html  provides a fluent interface
325:      * @throws \Exception
326:      */
327:     public function insert($index, $child, $replace = FALSE)
328:     {
329:         if ($child instanceof Html || is_scalar($child)) {
330:             if ($index === NULL)  { // append
331:                 $this->children[] = $child;
332: 
333:             } else { // insert or replace
334:                 array_splice($this->children, (int) $index, $replace ? 1 : 0, array($child));
335:             }
336: 
337:         } else {
338:             throw new \InvalidArgumentException("Child node must be scalar or Html object, " . (is_object($child) ? get_class($child) : gettype($child)) ." given.");
339:         }
340: 
341:         return $this;
342:     }
343: 
344: 
345: 
346:     /**
347:      * Inserts (replaces) child node (\ArrayAccess implementation).
348:      * @param  int
349:      * @param  Html node
350:      * @return void
351:      */
352:     final public function offsetSet($index, $child)
353:     {
354:         $this->insert($index, $child, TRUE);
355:     }
356: 
357: 
358: 
359:     /**
360:      * Returns child node (\ArrayAccess implementation).
361:      * @param  int index
362:      * @return mixed
363:      */
364:     final public function offsetGet($index)
365:     {
366:         return $this->children[$index];
367:     }
368: 
369: 
370: 
371:     /**
372:      * Exists child node? (\ArrayAccess implementation).
373:      * @param  int index
374:      * @return bool
375:      */
376:     final public function offsetExists($index)
377:     {
378:         return isset($this->children[$index]);
379:     }
380: 
381: 
382: 
383:     /**
384:      * Removes child node (\ArrayAccess implementation).
385:      * @param  int index
386:      * @return void
387:      */
388:     public function offsetUnset($index)
389:     {
390:         if (isset($this->children[$index])) {
391:             array_splice($this->children, (int) $index, 1);
392:         }
393:     }
394: 
395: 
396: 
397:     /**
398:      * Required by the \Countable interface.
399:      * @return int
400:      */
401:     final public function count()
402:     {
403:         return count($this->children);
404:     }
405: 
406: 
407: 
408:     /**
409:      * Removed all children.
410:      * @return void
411:      */
412:     public function removeChildren()
413:     {
414:         $this->children = array();
415:     }
416: 
417: 
418: 
419:     /**
420:      * Iterates over a elements.
421:      * @param  bool    recursive?
422:      * @param  string  class types filter
423:      * @return \RecursiveIterator
424:      */
425:     final public function getIterator($deep = FALSE)
426:     {
427:         if ($deep) {
428:             $deep = $deep > 0 ? \RecursiveIteratorIterator::SELF_FIRST : \RecursiveIteratorIterator::CHILD_FIRST;
429:             return new \RecursiveIteratorIterator(new Nette\GenericRecursiveIterator(new \ArrayIterator($this->children)), $deep);
430: 
431:         } else {
432:             return new Nette\GenericRecursiveIterator(new \ArrayIterator($this->children));
433:         }
434:     }
435: 
436: 
437: 
438:     /**
439:      * Returns all of children.
440:      * return array
441:      */
442:     final public function getChildren()
443:     {
444:         return $this->children;
445:     }
446: 
447: 
448: 
449:     /**
450:      * Renders element's start tag, content and end tag.
451:      * @param  int indent
452:      * @return string
453:      */
454:     final public function render($indent = NULL)
455:     {
456:         $s = $this->startTag();
457: 
458:         if (!$this->isEmpty) {
459:             // add content
460:             if ($indent !== NULL) {
461:                 $indent++;
462:             }
463:             foreach ($this->children as $child) {
464:                 if (is_object($child)) {
465:                     $s .= $child->render($indent);
466:                 } else {
467:                     $s .= $child;
468:                 }
469:             }
470: 
471:             // add end tag
472:             $s .= $this->endTag();
473:         }
474: 
475:         if ($indent !== NULL) {
476:             return "\n" . str_repeat("\t", $indent - 1) . $s . "\n" . str_repeat("\t", max(0, $indent - 2));
477:         }
478:         return $s;
479:     }
480: 
481: 
482: 
483:     final public function __toString()
484:     {
485:         return $this->render();
486:     }
487: 
488: 
489: 
490:     /**
491:      * Returns element's start tag.
492:      * @return string
493:      */
494:     final public function startTag()
495:     {
496:         if ($this->name) {
497:             return '<' . $this->name . $this->attributes() . (self::$xhtml && $this->isEmpty ? ' />' : '>');
498: 
499:         } else {
500:             return '';
501:         }
502:     }
503: 
504: 
505: 
506:     /**
507:      * Returns element's end tag.
508:      * @return string
509:      */
510:     final public function endTag()
511:     {
512:         return $this->name && !$this->isEmpty ? '</' . $this->name . '>' : '';
513:     }
514: 
515: 
516: 
517:     /**
518:      * Returns element's attributes.
519:      * @return string
520:      */
521:     final public function attributes()
522:     {
523:         if (!is_array($this->attrs)) {
524:             return '';
525:         }
526: 
527:         $s = '';
528:         foreach ($this->attrs as $key => $value)
529:         {
530:             // skip NULLs and false boolean attributes
531:             if ($value === NULL || $value === FALSE) continue;
532: 
533:             // true boolean attribute
534:             if ($value === TRUE) {
535:                 // in XHTML must use unminimized form
536:                 if (self::$xhtml) $s .= ' ' . $key . '="' . $key . '"';
537:                 // in HTML should use minimized form
538:                 else $s .= ' ' . $key;
539:                 continue;
540: 
541:             } elseif (is_array($value)) {
542: 
543:                 // prepare into temporary array
544:                 $tmp = NULL;
545:                 foreach ($value as $k => $v) {
546:                     // skip NULLs & empty string; composite 'style' vs. 'others'
547:                     if ($v == NULL) continue; // intentionally ==
548: 
549:                     //  composite 'style' vs. 'others'
550:                     $tmp[] = $v === TRUE ? $k : (is_string($k) ? $k . ':' . $v : $v);
551:                 }
552:                 if ($tmp === NULL) continue;
553: 
554:                 $value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp);
555: 
556:             } else {
557:                 $value = (string) $value;
558:             }
559: 
560:             $s .= ' ' . $key . '="' . htmlspecialchars($value) . '"';
561:         }
562: 
563:         $s = str_replace('@', '&#64;', $s);
564:         return $s;
565:     }
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 Framework 0.9.7 API documentation generated by ApiGen 2.3.0