/** * Create the needed tag hierarchy from emmet string * * @param string $string * * @param array|string $data * * @return array|T */ public static function make($string, $data = null) { if (!strlen($string)) { return array(); } $implicitTag = function () use(&$tag) { if (empty($tag->tag)) { switch ($tag->parent->tag) { case 'ul': case 'ol': $tag->tag = 'li'; break; case 'em': $tag->tag = 'span'; break; case 'table': case 'tbody': case 'thead': case 'tfoot': $tag->tag = 'tr'; break; case 'tr': $tag->tag = 'td'; break; case 'select': case 'optgroup': $tag->tag = 'option'; break; default: $tag->tag = 'div'; } } }; $parseText = function ($text, $round, $total, $data, $delimiter = null) use(&$tokens, &$tag) { $digits = 0; if ($delimiter == null) { $delimiter = array('.' => true, '#' => true, '*' => true, '>' => true, '+' => true, '^' => true, '[' => true, ']' => true, '=' => true); } while (!empty($tokens) && !isset($delimiter[$t = array_shift($tokens)])) { while ('$' === $t) { $digits++; $t = array_shift($tokens); } if ($digits) { $negative = false; $offset = 0; if ('@' == $t) { if ('-' == ($t = array_shift($tokens))) { $negative = true; if (is_numeric(reset($tokens))) { $offset = array_shift($tokens); } } elseif (is_numeric($t)) { $offset = $t; } else { array_unshift($tokens, $t); } } elseif ('#' == ($h = array_shift($tokens))) { if (!empty($t)) { $data = Util::nestedValue($data, $t); if (is_null($data)) { return null; } } if (is_numeric($data)) { $text .= sprintf("%0{$digits}d", (int) $data); } elseif (is_string($data)) { $text .= $data; } $digits = 0; continue; } else { array_unshift($tokens, $t, $h); } if ($negative) { $n = $total + 1 - $round + $offset; } else { $n = $round + $offset; } $text .= sprintf("%0{$digits}d", $n); $digits = 0; } else { $text .= $t; } } if (isset($t)) { array_unshift($tokens, $t); } return $text; }; $parseAttributes = function (callable $self, $round, $total, $data) use(&$tokens, &$tag, $parseText) { $a = $parseText('', $round, $total, $data); if (is_null($a)) { return; } if ('=' == ($v = array_shift($tokens))) { //value if ('"' == ($v = array_shift($tokens))) { $text = ''; $tag->{$a}($parseText($text, $round, $total, $data, array('"' => true))); } else { array_unshift($tokens, $v); $text = ''; $tag->{$a}($parseText($text, $round, $total, $data, array(' ' => true, ']' => true))); } if (' ' == ($v = array_shift($tokens))) { $self($self, $round, $total, $data); } } elseif (']' == $v) { //end $tag->{$a}(''); return; } elseif (' ' == $v) { $tag->{$a}(''); $self($self, $round, $total, $data); } }; $tokens = static::tokenize($string); $tag = new T(array_shift($tokens)); $parent = $root = new T(); $parse = function (callable $self, $round = 1, $total = 1) use(&$tokens, &$parent, &$tag, &$data, $parseAttributes, $implicitTag, $parseText) { $offsetTokens = null; $parent[] = $tag; $isInChild = false; while ($tokens) { switch (array_shift($tokens)) { //class case '.': $offsetTokens = array_values($tokens); array_unshift($offsetTokens, '.'); $implicitTag(); $e = array_filter(explode(' ', $tag->class)); $e[] = $parseText('', $round, $total, $data); $tag->class(implode(' ', array_unique($e))); break; //id //id case '#': $offsetTokens = array_values($tokens); array_unshift($offsetTokens, '#'); $implicitTag(); $tag->id($parseText(array_shift($tokens), $round, $total, $data)); break; //attributes //attributes case '[': $offsetTokens = array_values($tokens); array_unshift($offsetTokens, '['); $implicitTag(); $parseAttributes($parseAttributes, $round, $total, $data); break; //child //child case '{': $text = ''; $tag[] = $parseText($text, $round, $total, $data, array('}' => true)); break; case '>': $isInChild = true; $offsetTokens = null; if ('{' == ($t = array_shift($tokens))) { array_unshift($tokens, $t); $child = new T(); $tag[] = $child; $parent = $tag; $tag = $child; } elseif ('[' == $t) { array_unshift($tokens, $t); } else { $child = new T($t); $tag[] = $child; $parent = $tag; $tag = $child; } break; //sibling //sibling case '+': $offsetTokens = null; if (!$isInChild && $round != $total) { $tokens = array(); break; } if ('{' == ($t = array_shift($tokens))) { $tag = $tag->parent; array_unshift($tokens, $t); break; } elseif ('[' == $t) { array_unshift($tokens, $t); } else { $child = new T($t); $tag = $tag->parent; $tag[] = $child; $tag = $child; } break; //sibling of parent //sibling of parent case '^': if ($round != $total) { $tokens = array(); break; } $tag = $tag->parent; if ($tag->parent) { $tag = $tag->parent; } while ('^' == ($t = array_shift($tokens))) { if ($tag->parent) { $tag = $tag->parent; } } $child = new T($t); $tag[] = $child; $tag = $child; break; //clone //clone case '*': $times = array_shift($tokens); $removeCount = 2; $delimiter = array('.' => true, '#' => true, '*' => true, '>' => true, '+' => true, '^' => true, '[' => true, ']' => true, '=' => true); if (!is_numeric($times)) { if (is_string($times)) { if (!isset($delimiter[$times])) { $data = Util::nestedValue($data, $times) ?: $data; } else { array_unshift($tokens, $times); $removeCount = 1; } } $indexed = array_values($data); $times = is_array($data) && $indexed == $data ? count($data) : 0; } $source = $tag; if (!empty($offsetTokens)) { if (false !== strpos($source->class, ' ')) { $class = explode(' ', $source->class); array_pop($class); $class = implode(' ', $class); } else { $class = null; } $tag->class($class); $star = array_search('*', $offsetTokens); array_splice($offsetTokens, $star, $removeCount); $remainingTokens = $offsetTokens; } else { $remainingTokens = $tokens; } $source->parent = null; $sourceData = $data; $currentParent = $parent; for ($i = 1; $i <= $times; $i++) { $tag = clone $source; $parent = $currentParent; $data = is_array($sourceData) && isset($sourceData[$i - 1]) ? $sourceData[$i - 1] : @(string) $sourceData; $tokens = array_values($remainingTokens); $self($self, $i, $times); } $round = 1; $offsetTokens = null; $tag = $source; $tokens = array(); //$remainingTokens; break; } } }; $parse($parse); return count($root) == 1 ? $root[0] : $root; }
/** * Get the form * * @param string $method http method to submit the form * @param string $action relative path from the web root. When set to null * it uses the current api method's path * @param bool $dataOnly if you want to render the form yourself use this * option * @param string $prefix used for adjusting the spacing in front of * form elements * @param string $indent used for adjusting indentation * * @return array|T * * @throws \Luracast\Restler\RestException */ public static function get($method = 'POST', $action = null, $dataOnly = false, $prefix = '', $indent = ' ') { if (!static::$style) { static::$style = FormStyles::$html; } try { /** @var Restler $restler */ $restler = Scope::get('Restler'); if (is_null($action)) { $action = $restler->url; } $info = $restler->url == $action && Util::getRequestMethod() == $method ? $restler->apiMethodInfo : Routes::find(trim($action, '/'), $method, $restler->getRequestedApiVersion(), static::$preFill || $restler->requestMethod == $method && $restler->url == $action ? $restler->getRequestData() : array()); } catch (RestException $e) { //echo $e->getErrorMessage(); $info = false; } if (!$info) { throw new RestException(500, 'invalid action path for form `' . $method . ' ' . $action . '`'); } static::$info = $info; $m = $info->metadata; $r = static::fields($dataOnly); if ($method != 'GET' && $method != 'POST') { if (empty(Defaults::$httpMethodOverrideProperty)) { throw new RestException(500, 'Forms require `Defaults::\\$httpMethodOverrideProperty`' . "for supporting HTTP {$method}"); } if ($dataOnly) { $r[] = array('tag' => 'input', 'name' => Defaults::$httpMethodOverrideProperty, 'type' => 'hidden', 'value' => 'method'); } else { $r[] = T::input()->name(Defaults::$httpMethodOverrideProperty)->value($method)->type('hidden'); } $method = 'POST'; } if (session_id() != '') { $form_key = static::key($method, $action); if ($dataOnly) { $r[] = array('tag' => 'input', 'name' => static::FORM_KEY, 'type' => 'hidden', 'value' => 'hidden'); } else { $key = T::input()->name(static::FORM_KEY)->type('hidden')->value($form_key); $r[] = $key; } } $s = array('tag' => 'button', 'type' => 'submit', 'label' => Util::nestedValue($m, 'return', CommentParser::$embeddedDataName, 'label') ?: 'Submit'); if (!$dataOnly) { $s = Emmet::make(static::style('submit', $m), $s); } $r[] = $s; $t = array('action' => $restler->getBaseUrl() . '/' . rtrim($action, '/'), 'method' => $method); if (static::$fileUpload) { static::$fileUpload = false; $t['enctype'] = 'multipart/form-data'; } if (!$dataOnly) { $t = Emmet::make(static::style('form', $m), $t); $t->prefix = $prefix; $t->indent = $indent; $t[] = $r; } else { $t['fields'] = $r; } return $t; }