Last active
August 29, 2015 14:17
-
-
Save Flushot/7ef7f93a51713052bba9 to your computer and use it in GitHub Desktop.
HTML builder for PHP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * HTML builder | |
| * @author Chris Lyon <flushot@gmail.com> | |
| * | |
| * This functional HTML builder was inspired by JSX and eliminates the need to use the unsightly | |
| * and error-prone string concatenation method of building HTML markup. You can now cleanly use | |
| * nested functions as children and associative arrays as tag attributes. | |
| * | |
| * Example usage: | |
| * | |
| * echo tag('h1', function() { | |
| * return [ | |
| * tag('a', [ | |
| * 'href' => 'http://www.google.com', | |
| * 'class' => ['foo', 'bar'], | |
| * 'style' => [ | |
| * 'text-decoration' => 'underline', | |
| * 'font-style' => 'italic' | |
| * ] | |
| * ], 'hello'), | |
| * tag('br'), | |
| * 'world' | |
| * ]; | |
| * }); | |
| * | |
| * Outputs: | |
| * | |
| * <h1><a href="http://www.google.com" class="foo bar" style="text-decoration: underline; font-style: italic">hello</a><br/>world</h1> | |
| * | |
| * @param $tagName name of the tag. | |
| * @param $attrsOrChild associative array of attributes or a child (see $child). | |
| * @param $child child element(s) to render under this tag. can be a string, array of strings, or callable. | |
| * @return HTML markup as a string. | |
| */ | |
| function tag($tagName, $attrsOrChild=null, $child=null) { | |
| if (!is_string($tagName)) | |
| throw new InvalidArgumentException('$tagName must be a string'); | |
| $realChild = null; | |
| // Handle polymorphic args | |
| if (is_null($attrsOrChild)) { | |
| $attrs = array(); | |
| $realChild = $child; | |
| } | |
| else { | |
| if (is_array($attrsOrChild)) { | |
| // Assume array is associative if keys aren't numeric | |
| $isAssociative = (bool)count(array_filter(array_keys($attrsOrChild), 'is_string')); | |
| // Handle as attributes | |
| if ($isAssociative) { | |
| $attrs = $attrsOrChild; | |
| $realChild = $child; | |
| } | |
| // Handle as array of children | |
| else { | |
| $attrs = array(); | |
| $realChild = $attrsOrChild; | |
| } | |
| } | |
| // Handle as child | |
| elseif (is_string($attrsOrChild) || is_callable($attrsOrChild)) { | |
| if (!is_null($child)) | |
| throw new InvalidArgumentException('Ambiguous children: $attrsOrChild and $child'); | |
| $attrs = array(); | |
| $realChild = $attrsOrChild; | |
| } | |
| } | |
| // Evaluate child | |
| if (!is_null($realChild)) { | |
| if (is_callable($realChild)) | |
| $realChild = $realChild(); | |
| if (is_array($realChild)) | |
| $realChild = implode("\n", $realChild); | |
| if (!is_string($realChild)) | |
| throw new InvalidArgumentException('Child must be a string or a callable'); | |
| } | |
| // Doing this because unclear on how array_reduce() callback can handle associative arrays | |
| $reducedAttrs = ''; | |
| foreach ($attrs as $k => $v) { | |
| if (is_array($v)) { | |
| switch ($k) { | |
| case 'class': | |
| // val1 valN ... | |
| $value = implode(' ', $v); | |
| break; | |
| case 'style': | |
| // key1: val1; keyN: valN; ... | |
| $value = implode('; ', array_map( | |
| function($styleKey) use ($v) { | |
| return "$styleKey: " . | |
| htmlspecialchars($v[$styleKey], ENT_QUOTES, 'UTF-8'); | |
| }, array_keys($v))); // array_map() doesn't iterate keys+vals | |
| break; | |
| default: | |
| throw new InvalidArgumentException('array attributes are only valid for class or style'); | |
| } | |
| } | |
| else { | |
| $value = htmlspecialchars($v, ENT_QUOTES, 'UTF-8'); | |
| } | |
| $reducedAttrs .= " $k=\"$value\""; | |
| } | |
| // These tags allow the short closing "/>" syntax. | |
| // See EMPTY elements defined in: http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_XHTML-1.0-Strict | |
| $emptyTags = array('base', 'meta', 'link', 'hr', 'br', 'param', 'img', 'area', 'input', 'col'); | |
| $markup = '<' . $tagName . $reducedAttrs; | |
| if (is_null($realChild) && in_array(strtolower($tagName), $emptyTags)) { | |
| // Short close tag | |
| $markup .= '/>'; | |
| } | |
| else { | |
| // Render children | |
| $markup .= '>' . $realChild . '</' . $tagName . '>'; | |
| } | |
| return $markup; | |
| } | |
| ?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment