Packages

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

Classes

  • NFtp
  • NHtml
  • NHttpContext
  • NHttpRequest
  • NHttpResponse
  • NHttpUploadedFile
  • NSession
  • NSessionNamespace
  • NUri
  • NUriScript
  • NUser

Interfaces

  • IHttpRequest
  • IHttpResponse
  • IUser

Exceptions

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