/** * This static method is called for creating an instance of the class by * passing the initiation values as an array. * * @static * * @param array $properties * * @return iValueObject */ public static function __set_state(array $properties) { $task = new Task(); $task->id = Util::nestedValue($properties, 'id'); $task->position = Util::nestedValue($properties, 'position'); $task->text = Util::nestedValue($properties, 'text'); return $task; }
public static function get($for = '', $activeTrail = null) { if (empty(static::$tree)) { /** @var Restler $restler */ $restler = Scope::get('Restler'); if (static::$addExtension) { static::$extension = isset($restler->responseFormat) ? '.' . $restler->responseFormat->getExtension() : '.html'; } static::$url = $restler->getBaseUrl(); if (empty(static::$url)) { static::$url = ''; } static::$activeTrail = $activeTrail = empty($activeTrail) ? empty($restler->url) || $restler->url == 'index' ? static::$root : $restler->url : $activeTrail; if (static::$addExtension) { static::$extension = isset($restler->responseFormat) ? '.' . $restler->responseFormat->getExtension() : '.html'; } static::addUrls(static::$prepends); $map = Routes::findAll(static::$excludedPaths, array('POST', 'DELETE', 'PUT', 'PATCH'), $restler->getRequestedApiVersion()); foreach ($map as $path => $data) { foreach ($data as $item) { $access = $item['access']; $route = $item['route']; $url = $route['url']; if ($access && !Text::contains($url, '{')) { $label = Util::nestedValue($route, 'metadata', CommentParser::$embeddedDataName, 'label'); if (!empty($url)) { $url .= static::$extension; } static::add($url, $label); } } } static::addUrls(static::$appends); } elseif (empty($activeTrail)) { $activeTrail = static::$activeTrail; } $tree = static::$tree; $activeTrail = explode('/', $activeTrail); $nested =& static::nested($tree, $activeTrail); if (is_array($nested)) { $nested['active'] = true; } if (!empty($for)) { $for = explode('/', $for); $tree = static::nested($tree, $for)['children']; } return array_filter($tree); }
/** * Redirect to given url * * @param string $url relative path or full url * @param array $params associative array of query parameters * @param array $flashData associative array of properties to be set in $_SESSION for one time use * @param int $status http status code to send the response with ideally 301 or 302 * * @return array */ public static function to($url, array $params = array(), array $flashData = array(), $status = 302) { $url = ltrim($url, '/'); /** @var $r Restler */ $r = Scope::get('Restler'); $base = $r->getBaseUrl() . '/'; if (0 !== strpos($url, 'http')) { $url = $base . $url; } if (!empty($flashData) || $base . $r->url !== $url || Util::getRequestMethod() != 'GET') { if ($r->responseFormat instanceof JsonFormat) { return array('redirect' => $url); } if (!empty($params)) { $url .= '?' . http_build_query($params); } Flash::set($flashData); header("{$_SERVER['SERVER_PROTOCOL']} {$status} " . (isset(RestException::$codes[$status]) ? RestException::$codes[$status] : '')); header("Location: {$url}"); die(''); } return array(); }
public function __construct(array $info) { $properties = get_object_vars($this); unset($properties['contentType']); foreach ($properties as $property => $value) { $this->{$property} = $this->getProperty($info, $property); } $this->rules = Util::nestedValue($info, 'properties') ?: $info; unset($this->rules['properties']); if (is_string($this->type) && $this->type == 'integer') { $this->type = 'int'; } }
/** * Find the data type of the given value. * * * @param mixed $o given value for finding type * * @param bool $appendToModels if an object is found should we append to * our models list? * * @return string * * @access private */ public function getType($o, $appendToModels = false) { if (is_object($o)) { $oc = get_class($o); if ($appendToModels) { $this->_model($oc, $o); } return $this->_noNamespace($oc); } if (is_array($o)) { if (count($o)) { $child = end($o); if (Util::isObjectOrArray($child)) { $childType = $this->getType($child, $appendToModels); return "Array[{$childType}]"; } } return 'array'; } if (is_bool($o)) { return 'boolean'; } if (is_numeric($o)) { return is_float($o) ? 'float' : 'int'; } return 'string'; }
/** * Add api classes through this method. * * All the public methods that do not start with _ (underscore) * will be will be exposed as the public api by default. * * All the protected methods that do not start with _ (underscore) * will exposed as protected api which will require authentication * * @param string $className name of the service class * @param string $resourcePath optional url prefix for mapping, uses * lowercase version of the class name when * not specified * * @return null * * @throws Exception when supplied with invalid class name */ public function addAPIClass($className, $resourcePath = null) { try { if ($this->productionMode && is_null($this->cached)) { $routes = $this->cache->get('routes'); if (isset($routes) && is_array($routes)) { $this->apiVersionMap = $routes['apiVersionMap']; unset($routes['apiVersionMap']); Routes::fromArray($routes); $this->cached = true; } else { $this->cached = false; } } if (isset(Scope::$classAliases[$className])) { $className = Scope::$classAliases[$className]; } if (!$this->cached) { $maxVersionMethod = '__getMaximumSupportedVersion'; if (class_exists($className)) { if (method_exists($className, $maxVersionMethod)) { $max = $className::$maxVersionMethod(); for ($i = 1; $i <= $max; $i++) { $this->apiVersionMap[$className][$i] = $className; } } else { $this->apiVersionMap[$className][1] = $className; } } //versioned api if (false !== ($index = strrpos($className, '\\'))) { $name = substr($className, 0, $index) . '\\v{$version}' . substr($className, $index); } else { if (false !== ($index = strrpos($className, '_'))) { $name = substr($className, 0, $index) . '_v{$version}' . substr($className, $index); } else { $name = 'v{$version}\\' . $className; } } for ($version = $this->apiMinimumVersion; $version <= $this->apiVersion; $version++) { $versionedClassName = str_replace('{$version}', $version, $name); if (class_exists($versionedClassName)) { Routes::addAPIClass($versionedClassName, Util::getResourcePath($className, $resourcePath), $version); if (method_exists($versionedClassName, $maxVersionMethod)) { $max = $versionedClassName::$maxVersionMethod(); for ($i = $version; $i <= $max; $i++) { $this->apiVersionMap[$className][$i] = $versionedClassName; } } else { $this->apiVersionMap[$className][$version] = $versionedClassName; } } elseif (isset($this->apiVersionMap[$className][$version])) { Routes::addAPIClass($this->apiVersionMap[$className][$version], Util::getResourcePath($className, $resourcePath), $version); } } } } catch (Exception $e) { $e = new Exception("addAPIClass('{$className}') failed. " . $e->getMessage(), $e->getCode(), $e); $this->setSupportedFormats('JsonFormat'); $this->message($e); } }
/** * @param ReflectionClass $class * * @return array * * @access protected */ protected static function getTypeAndModel(ReflectionClass $class) { $children = array(); $props = $class->getProperties(ReflectionProperty::IS_PUBLIC); foreach ($props as $prop) { if ($c = $prop->getDocComment()) { $children[$prop->getName()] = array('name' => $prop->getName()) + Util::nestedValue(CommentParser::parse($c), 'var') + array('type' => 'string'); } } return array($class->getName(), $children); }
/** * Set the id attribute of the current tag * * @param string $value * * @return string */ public function id($value) { $this->attributes['id'] = isset($value) ? (string) $value : Util::nestedValue($this->attributes, 'name'); static::$instances[$value] = $this; return $this; }
/** * Encode the given data in the format * * @param array $data resulting data that needs to * be encoded in the given format * @param boolean $humanReadable set to TRUE when restler * is not running in production mode. * Formatter has to make the encoded * output more human readable * * @throws \Exception * @return string encoded string */ public function encode($data, $humanReadable = false) { try { $events = $this->restler->getEvents(); $data = array_merge(array('response' => Object::toArray($data), 'stages' => $events, 'success' => end($events) != 'message'), static::$data); $params = array(); //print_r($this->restler); if (isset($this->restler->apiMethodInfo->metadata)) { $info = $data['api'] = $this->restler->apiMethodInfo; $metadata = $info->metadata; $params = $metadata['param']; } foreach ($params as $index => &$param) { $index = intval($index); if (is_numeric($index)) { $param['value'] = $this->restler->apiMethodInfo->parameters[$index]; } } $data['request']['parameters'] = $params; $inner = null; if (!$data['success'] && !is_null(static::$errorView)) { self::$view = static::$errorView; } elseif (static::$parseViewMetadata && isset($metadata['view'])) { if (is_array($metadata['view'])) { self::$view = $metadata['view']['description']; if ($value = Util::nestedValue($metadata['view'], 'properties', 'value')) { $inner = explode('.', $value); } } else { self::$view = $metadata['view']; } } if (false === ($i = strpos(self::$view, '.'))) { $extension = self::$format; self::$view .= '.' . $extension; } else { $extension = substr(self::$view, $i + 1); } switch ($extension) { case 'php': $view = self::$viewPath . DIRECTORY_SEPARATOR . self::$view; if (!is_readable($view)) { throw new RestException(500, "view file `{$view}` is not readable. Check for file presence and file permissions"); } $data = $inner ? Util::nestedValue($data, $inner) : $data; $template = function ($view) use($data) { $_ = function () use($data) { extract($data); $args = func_get_args(); $task = array_shift($args); switch ($task) { case 'require': case 'include': $file = HtmlFormat::$viewPath . DIRECTORY_SEPARATOR . $args[0]; if (is_readable($file)) { if (isset($args[1]) && ($arrays = Util::nestedValue($data, $args[1]))) { $str = ''; foreach ($arrays as $arr) { extract($arr); $str .= (include $file); } return $str; } else { return include $file; } } break; case 'if': if (count($args) < 2) { $args[1] = ''; } if (count($args) < 3) { $args[2] = ''; } return $args[0] ? $args[1] : $args[2]; break; default: return call_user_func_array($task, $args); } }; extract($data); return @(include $view); }; $value = $template($view); if (is_string($value)) { echo $value; } break; case 'twig': if (!class_exists('\\Twig_Environment', true)) { throw new RestException(500, 'Twig templates require twig classes to be installed using `composer install`'); } $loader = new \Twig_Loader_Filesystem(static::$viewPath); $twig = new \Twig_Environment($loader, array('cache' => Defaults::$cacheDirectory, 'debug' => true)); $template = $twig->loadTemplate(self::$view); return $template->render($data); case 'handlebar': case 'mustache': if (!class_exists('\\Mustache_Engine', true)) { throw new RestException(500, 'Mustache/Handlebar templates require mustache classes to be installed using `composer install`'); } $view = self::$viewPath . DIRECTORY_SEPARATOR . self::$view; $m = new \Mustache_Engine(); return $m->render(file_get_contents($view), $data); default: throw new RestException(500, "Unsupported template system `{$extension}`"); } } catch (Exception $e) { static::$parseViewMetadata = false; $this->reset(); throw $e; } }
/** * Access verification method. * * API access will be denied when this method returns false * * @return boolean true when api access is allowed false otherwise * * @throws RestException 403 security violation */ public function __isAllowed() { if (session_id() == '') { session_start(); } /** @var Restler $restler */ $restler = $this->restler; $url = $restler->url; foreach (static::$excludedPaths as $exclude) { if (empty($exclude)) { if ($url == $exclude) { return true; } } elseif (String::beginsWith($url, $exclude)) { return true; } } $check = static::$filterFormRequestsOnly ? $restler->requestFormat instanceof UrlEncodedFormat || $restler->requestFormat instanceof UploadFormat : true; if (!empty($_POST) && $check) { if (isset($_POST[static::FORM_KEY]) && ($target = Util::getRequestMethod() . ' ' . $restler->url) && isset($_SESSION[static::FORM_KEY][$target]) && $_POST[static::FORM_KEY] == $_SESSION[static::FORM_KEY][$target]) { return true; } throw new RestException(403, 'Insecure form submission'); } return true; }
/** * @access hybrid * @return \stdClass */ public function index() { if (!static::$accessControlFunction && Defaults::$accessControlFunction) { static::$accessControlFunction = Defaults::$accessControlFunction; } $version = $this->restler->getRequestedApiVersion(); $allRoutes = Util::nestedValue(Routes::toArray(), "v{$version}"); $r = $this->_resourceListing(); $map = array(); if (isset($allRoutes['*'])) { $this->_mapResources($allRoutes['*'], $map, $version); unset($allRoutes['*']); } $this->_mapResources($allRoutes, $map, $version); foreach ($map as $path => $description) { if (!String::contains($path, '{')) { //add id $r->apis[] = array('path' => $path . $this->formatString, 'description' => $description); } } if (Defaults::$useUrlBasedVersioning && static::$listHigherVersions) { $nextVersion = $version + 1; if ($nextVersion <= $this->restler->getApiVersion()) { list($status, $data) = $this->_loadResource("/v{$nextVersion}/resources.json"); if ($status == 200) { $r->apis = array_merge($r->apis, $data->apis); $r->apiVersion = $data->apiVersion; } } } return $r; }
/** * Generates cacheable url to method mapping * * @param string $className * @param string $resourcePath */ protected function generateMap($className, $resourcePath = '') { /* * Mapping Rules - Optional parameters should not be mapped to URL - if * a required parameter is of primitive type - Map them to URL - Do not * create routes with out it - if a required parameter is not primitive * type - Do not include it in URL */ $reflection = new ReflectionClass($className); $classMetadata = CommentParser::parse($reflection->getDocComment()); $methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC + ReflectionMethod::IS_PROTECTED); foreach ($methods as $method) { $methodUrl = strtolower($method->getName()); //method name should not begin with _ if ($methodUrl[0] == '_') { continue; } $doc = $method->getDocComment(); $metadata = CommentParser::parse($doc) + $classMetadata; //@access should not be private if (isset($metadata['access']) && $metadata['access'] == 'private') { continue; } $arguments = array(); $defaults = array(); $params = $method->getParameters(); $position = 0; $ignorePathTill = false; $allowAmbiguity = isset($metadata['smart-auto-routing']) && $metadata['smart-auto-routing'] != 'true' || !Defaults::$smartAutoRouting; $metadata['resourcePath'] = $resourcePath; if (isset($classMetadata['description'])) { $metadata['classDescription'] = $classMetadata['description']; } if (isset($classMetadata['classLongDescription'])) { $metadata['classLongDescription'] = $classMetadata['longDescription']; } if (!isset($metadata['param'])) { $metadata['param'] = array(); } foreach ($params as $param) { $type = $param->isArray() ? 'array' : $param->getClass(); if ($type instanceof ReflectionClass) { $type = $type->getName(); } $arguments[$param->getName()] = $position; $defaults[$position] = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null; if (!isset($metadata['param'][$position])) { $metadata['param'][$position] = array(); } $m =& $metadata['param'][$position]; if (isset($type)) { $m['type'] = $type; } $m['name'] = trim($param->getName(), '$ '); $m['default'] = $defaults[$position]; $m['required'] = !$param->isOptional(); if (isset($m[CommentParser::$embeddedDataName]['from'])) { $from = $m[CommentParser::$embeddedDataName]['from']; } else { if (isset($type) && Util::isObjectOrArray($type) || $param->getName() == Defaults::$fullRequestDataName) { $from = 'body'; } elseif ($m['required']) { $from = 'path'; } else { $from = 'query'; } } $m['from'] = $from; if (!$allowAmbiguity && $from == 'path') { $ignorePathTill = $position + 1; } $position++; } $accessLevel = 0; if ($method->isProtected()) { $accessLevel = 3; } elseif (isset($metadata['access'])) { if ($metadata['access'] == 'protected') { $accessLevel = 2; } elseif ($metadata['access'] == 'hybrid') { $accessLevel = 1; } } elseif (isset($metadata['protected'])) { $accessLevel = 2; } /* echo " access level $accessLevel for $className::" .$method->getName().$method->isProtected().PHP_EOL; */ // take note of the order $call = array('className' => $className, 'path' => rtrim($resourcePath, '/'), 'methodName' => $method->getName(), 'arguments' => $arguments, 'defaults' => $defaults, 'metadata' => $metadata, 'accessLevel' => $accessLevel); // if manual route if (preg_match_all('/@url\\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)' . '[ \\t]*\\/?(\\S*)/s', $doc, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $httpMethod = $match[1]; $url = rtrim($resourcePath . $match[2], '/'); $this->routes[$httpMethod][$url] = $call; } //if auto route enabled, do so } elseif (Defaults::$autoRoutingEnabled) { // no configuration found so use convention if (preg_match_all('/^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)/i', $methodUrl, $matches)) { $httpMethod = strtoupper($matches[0][0]); $methodUrl = substr($methodUrl, strlen($httpMethod)); } else { $httpMethod = 'GET'; } if ($methodUrl == 'index') { $methodUrl = ''; } $url = empty($methodUrl) ? rtrim($resourcePath, '/') : $resourcePath . $methodUrl; if (!$ignorePathTill) { $this->routes[$httpMethod][$url] = $call; } $position = 1; foreach ($params as $param) { $from = $metadata['param'][$position - 1]['from']; if ($from == 'body' && ($httpMethod == 'GET' || $httpMethod == 'DELETE')) { $from = $metadata['param'][$position - 1]['from'] = 'query'; } if (!$allowAmbiguity && $from != 'path') { break; } if (!empty($url)) { $url .= '/'; } $url .= '{' . $param->getName() . '}'; if ($allowAmbiguity || $position == $ignorePathTill) { $this->routes[$httpMethod][$url] = $call; } $position++; } } } }
public function __isset($name) { return !is_null(Util::nestedValue($_SESSION, 'flash', $name)); }
/** * 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 Tag by id * * Retrieve a tag by its id attribute * * @param string $id * * @return Tags|null */ public static function byId($id) { return Util::nestedValue(static::$instances, $id); }
/** * Stage 2: Users response is recorded by the server * * Server redirects the user back with the result. * * If successful, * * Client exchanges the authorization code by a direct call (not through * user's browser) to get access token which can then be used call protected * APIs, if completed it calls a protected api and displays the result * otherwise client ends up showing the error message * * Else * * Client renders the error message to the user * * @param string $code * @param string $error_description * @param string $error_uri * * @return array * * @format HtmlFormat */ public function authorized($code = null, $error_description = null, $error_uri = null) { // the user denied the authorization request if (!$code) { HtmlFormat::$view = 'oauth2/client/denied.twig'; return array('error' => compact('error_description', 'error_uri')); } // exchange authorization code for access token $query = array('grant_type' => 'authorization_code', 'code' => $code, 'client_id' => self::$clientId, 'client_secret' => self::$clientSecret, 'redirect_uri' => self::$authorizeRedirectUrl); //call the API using cURL $curl = new Curl(); $endpoint = self::$serverUrl . '/grant'; $response = $curl->request($endpoint, $query, 'POST'); if (!json_decode($response['response'], true)) { $status = $response['headers']['http_code']; echo '<h1>something went wrong - see the raw response</h1>'; echo '<h2> Http ' . $status . ' - ' . RestException::$codes[$status] . '</h2>'; exit('<pre>' . print_r($response, true) . '</pre>'); } $error = array(); $response = json_decode($response['response'], true); // render error if applicable ($error['error_description'] = Util::nestedValue($response, 'error_description')) || ($error['error_description'] = Util::nestedValue($response, 'error', 'message')) || ($error['error_description'] = Util::nestedValue($response, 'errorMessage')) || ($error['error_description'] = Util::nestedValue($response, 'errorNumber')) || ($error['error_description'] = 'Unknown Error'); $error_uri = Util::nestedValue($response, 'error_uri'); if ($error_uri) { $error['error_uri'] = $error_uri; } // if it is successful, call the API with the retrieved token if ($token = Util::nestedValue($response, 'access_token')) { // make request to the API for awesome data $endpoint = self::$serverUrl . '/access?access_token=' . $token; $response = $curl->request($endpoint); HtmlFormat::$view = 'oauth2/client/granted.twig'; return array('token' => $token, 'endpoint' => $endpoint) + json_decode($response['response'], true); } HtmlFormat::$view = 'oauth2/client/error.twig'; return array('error' => $error); }
private function operationId(array $route) { static $hash = array(); $id = $route['httpMethod'] . ' ' . $route['url']; if (isset($hash[$id])) { return $hash[$id]; } $class = Util::getShortName($route['className']); $method = $route['methodName']; if (isset(static::$prefixes[$method])) { $method = static::$prefixes[$method] . $class; } else { $method = str_replace(array_keys(static::$prefixes), array_values(static::$prefixes), $method); $method = lcfirst($class) . ucfirst($method); } $hash[$id] = $method; return $method; }
/** * Add api classes through this method. * * All the public methods that do not start with _ (underscore) * will be will be exposed as the public api by default. * * All the protected methods that do not start with _ (underscore) * will exposed as protected api which will require authentication * * @param string $className name of the service class * @param string $resourcePath optional url prefix for mapping, uses * lowercase version of the class name when * not specified * * @return null * * @throws Exception when supplied with invalid class name */ public function addAPIClass($className, $resourcePath = null) { try { if ($this->productionMode && is_null($this->cached)) { $routes = $this->cache->get('routes'); if (isset($routes) && is_array($routes)) { Routes::fromArray($routes); $this->cached = true; } else { $this->cached = false; } } if (isset(Util::$classAliases[$className])) { $className = Util::$classAliases[$className]; } if (!$this->cached) { $foundClass = array(); if (class_exists($className)) { $foundClass[$className] = $className; } //versioned api if (false !== ($index = strrpos($className, '\\'))) { $name = substr($className, 0, $index) . '\\v{$version}' . substr($className, $index); } else { if (false !== ($index = strrpos($className, '_'))) { $name = substr($className, 0, $index) . '_v{$version}' . substr($className, $index); } else { $name = 'v{$version}\\' . $className; } } for ($version = $this->apiMinimumVersion; $version <= $this->apiVersion; $version++) { $versionedClassName = str_replace('{$version}', $version, $name); if (class_exists($versionedClassName)) { Routes::addAPIClass($versionedClassName, Util::getResourcePath($className, $resourcePath, "v{$version}/")); $foundClass[$className] = $versionedClassName; } elseif (isset($foundClass[$className])) { Routes::addAPIClass($foundClass[$className], Util::getResourcePath($className, $resourcePath, "v{$version}/")); } } } } catch (Exception $e) { $e = new Exception("addAPIClass('{$className}') failed. " . $e->getMessage(), $e->getCode(), $e); $this->setSupportedFormats('JsonFormat'); $this->message($e); } }
public static function get($for = '', $activeUrl = null) { if (!static::$accessControlFunction && Defaults::$accessControlFunction) { static::$accessControlFunction = Defaults::$accessControlFunction; } /** @var Restler $restler */ $restler = Scope::get('Restler'); if (static::$addExtension) { static::$extension = '.' . $restler->responseFormat->getExtension(); } if (is_null($activeUrl)) { $activeUrl = $restler->url; } $tree = array(); foreach (static::$prepends as $path => $text) { $url = null; if (is_array($text)) { if (isset($text['url'])) { $url = $text['url']; $text = $text['text']; } else { $url = current(array_keys($text)); $text = current($text); } } if (is_numeric($path)) { $path = $text; $text = null; } if (empty($for) || 0 === strpos($path, "{$for}/")) { static::build($tree, $path, $url, $text, $activeUrl); } } $routes = Routes::toArray(); $routes = $routes['v' . $restler->getRequestedApiVersion()]; foreach ($routes as $value) { foreach ($value as $httpMethod => $route) { if ($httpMethod != 'GET') { continue; } $path = $route['url']; if (false !== strpos($path, '{')) { continue; } if ($route['accessLevel'] > 1 && !Util::$restler->_authenticated) { continue; } foreach (static::$excludedPaths as $exclude) { if (empty($exclude)) { if (empty($path)) { continue 2; } } elseif (0 === strpos($path, $exclude)) { continue 2; } } if ($restler->_authenticated && static::$accessControlFunction && !call_user_func(static::$accessControlFunction, $route['metadata'])) { continue; } $text = Util::nestedValue($route, 'metadata', CommentParser::$embeddedDataName, 'label'); if (empty($for) || 0 === strpos($path, "{$for}/")) { static::build($tree, $path, null, $text, $activeUrl); } } } foreach (static::$appends as $path => $text) { $url = null; if (is_array($text)) { if (isset($text['url'])) { $url = $text['url']; $text = $text['text']; } else { $url = current(array_keys($text)); $text = current($text); } } if (is_numeric($path)) { $path = $text; $text = null; } if (empty($for) || 0 === strpos($path, "{$for}/")) { static::build($tree, $path, $url, $text, $activeUrl); } } if (!empty($for)) { $for = explode('/', $for); $p =& $tree; foreach ($for as $f) { if (isset($p[$f]['children'])) { $p =& $p[$f]['children']; } else { return array(); } } return $p; } return $tree; }
/** * Get the type and associated model * * @param ReflectionClass $class * @param array $scope * * @throws RestException * @throws \Exception * @return array * * @access protected */ protected static function getTypeAndModel(ReflectionClass $class, array $scope) { $className = $class->getName(); if (isset(static::$models[$className])) { return static::$models[$className]; } $children = array(); try { $props = $class->getProperties(ReflectionProperty::IS_PUBLIC); foreach ($props as $prop) { $name = $prop->getName(); $child = array('name' => $name); if ($c = $prop->getDocComment()) { $child += Util::nestedValue(CommentParser::parse($c), 'var') ?: []; } else { $o = $class->newInstance(); $p = $prop->getValue($o); if (is_object($p)) { $child['type'] = get_class($p); } elseif (is_array($p)) { $child['type'] = 'array'; if (count($p)) { $pc = reset($p); if (is_object($pc)) { $child['contentType'] = get_class($pc); } } } } $child += array('type' => $child['name'] == 'email' ? 'email' : 'string', 'label' => static::label($child['name'])); isset($child[CommentParser::$embeddedDataName]) ? $child[CommentParser::$embeddedDataName] += array('required' => true) : ($child[CommentParser::$embeddedDataName]['required'] = true); if ($qualified = Scope::resolve($child['type'], $scope)) { list($child['type'], $child['children']) = static::getTypeAndModel(new ReflectionClass($qualified), $scope); } elseif (($contentType = Util::nestedValue($child, CommentParser::$embeddedDataName, 'type')) && ($qualified = Scope::resolve($contentType, $scope))) { list($child['contentType'], $child['children']) = static::getTypeAndModel(new ReflectionClass($qualified), $scope); } $children[$name] = $child; } } catch (Exception $e) { if (String::endsWith($e->getFile(), 'CommentParser.php')) { throw new RestException(500, "Error while parsing comments of `{$className}` class. " . $e->getMessage()); } throw $e; } static::$models[$className] = array($className, $children); return static::$models[$className]; }
/** * Validate the given input * * Validates the input and attempts to fix it when fix is requested * * @param mixed $input * @param ValidationInfo $info * @param null $full * * @throws \Exception * @return array|bool|float|int|mixed|null|number|string */ public static function validate($input, ValidationInfo $info, $full = null) { $html = Scope::get('Restler')->responseFormat instanceof HtmlFormat; $name = $html ? "<strong>{$info->label}</strong>" : "`{$info->name}`"; try { if (is_null($input)) { if ($info->required) { throw new RestException(400, "{$name} is required."); } return null; } $error = isset($info->message) ? $info->message : "Invalid value specified for {$name}"; //if a validation method is specified if (!empty($info->method)) { $method = $info->method; $info->method = ''; $r = self::validate($input, $info); return $info->apiClassInstance->{$method}($r); } // when type is an array check if it passes for any type if (is_array($info->type)) { //trace("types are ".print_r($info->type, true)); $types = $info->type; foreach ($types as $type) { $info->type = $type; try { $r = self::validate($input, $info); if ($r !== false) { return $r; } } catch (RestException $e) { // just continue } } throw new RestException(400, $error); } //patterns are supported only for non numeric types if (isset($info->pattern) && $info->type != 'int' && $info->type != 'float' && $info->type != 'number') { if (!preg_match($info->pattern, $input)) { throw new RestException(400, $error); } } if (isset($info->choice)) { if (is_array($input)) { foreach ($input as $i) { if (!in_array($i, $info->choice)) { $error .= ". Expected one of (" . implode(',', $info->choice) . ")."; throw new RestException(400, $error); } } } elseif (!in_array($input, $info->choice)) { $error .= ". Expected one of (" . implode(',', $info->choice) . ")."; throw new RestException(400, $error); } } if (method_exists($class = get_called_class(), $info->type) && $info->type != 'validate') { try { return call_user_func("{$class}::{$info->type}", $input, $info); } catch (Invalid $e) { throw new RestException(400, $error . '. ' . $e->getMessage()); } } switch ($info->type) { case 'int': case 'float': case 'number': if (!is_numeric($input)) { $error .= '. Expecting ' . ($info->type == 'int' ? 'integer' : 'numeric') . ' value'; break; } if ($info->type == 'int' && (int) $input != $input) { if ($info->fix) { $r = (int) $input; } else { $error .= '. Expecting integer value'; break; } } else { $r = $info->numericValue($input); } if (isset($info->min) && $r < $info->min) { if ($info->fix) { $r = $info->min; } else { $error .= ". Minimum required value is {$info->min}."; break; } } if (isset($info->max) && $r > $info->max) { if ($info->fix) { $r = $info->max; } else { $error .= ". Maximum allowed value is {$info->max}."; break; } } return $r; case 'string': if (!is_string($input)) { $error .= '. Expecting alpha numeric value'; break; } if ($info->required && $input === '') { $error = "{$name} is required."; break; } $r = strlen($input); if (isset($info->min) && $r < $info->min) { if ($info->fix) { $input = str_pad($input, $info->min, $input); } else { $char = $info->min > 1 ? 'characters' : 'character'; $error .= ". Minimum {$info->min} {$char} required."; break; } } if (isset($info->max) && $r > $info->max) { if ($info->fix) { $input = substr($input, 0, $info->max); } else { $char = $info->max > 1 ? 'characters' : 'character'; $error .= ". Maximum {$info->max} {$char} allowed."; break; } } return $input; case 'bool': case 'boolean': if ($input === 'true' || $input === true) { return true; } if (is_numeric($input)) { return $input > 0; } return false; case 'array': if ($info->fix && is_string($input)) { $input = explode(CommentParser::$arrayDelimiter, $input); } if (is_array($input)) { $contentType = Util::nestedValue($info, 'contentType') ?: null; if ($info->fix) { if ($contentType == 'indexed') { $input = $info->filterArray($input, true); } elseif ($contentType == 'associative') { $input = $info->filterArray($input, true); } } elseif ($contentType == 'indexed' && array_values($input) != $input) { $error .= '. Expecting a list of items but an item is given'; break; } elseif ($contentType == 'associative' && array_values($input) == $input && count($input)) { $error .= '. Expecting an item but a list is given'; break; } $r = count($input); if (isset($info->min) && $r < $info->min) { $item = $info->max > 1 ? 'items' : 'item'; $error .= ". Minimum {$info->min} {$item} required."; break; } if (isset($info->max) && $r > $info->max) { if ($info->fix) { $input = array_slice($input, 0, $info->max); } else { $item = $info->max > 1 ? 'items' : 'item'; $error .= ". Maximum {$info->max} {$item} allowed."; break; } } if (isset($contentType) && $contentType != 'associative' && $contentType != 'indexed') { $name = $info->name; $info->type = $contentType; unset($info->contentType); foreach ($input as $key => $chinput) { $info->name = "{$name}[{$key}]"; $input[$key] = static::validate($chinput, $info); } } return $input; } elseif (isset($contentType)) { $error .= '. Expecting items of type ' . ($html ? "<strong>{$contentType}</strong>" : "`{$contentType}`"); break; } break; case 'mixed': case 'unknown_type': case 'unknown': case null: //treat as unknown return $input; default: if (!is_array($input)) { break; } //do type conversion if (class_exists($info->type)) { $input = $info->filterArray($input, false); $implements = class_implements($info->type); if (is_array($implements) && in_array('Luracast\\Restler\\Data\\iValueObject', $implements)) { return call_user_func("{$info->type}::__set_state", $input); } $class = $info->type; $instance = new $class(); if (is_array($info->children)) { if (empty($input) || !is_array($input) || $input === array_values($input)) { $error .= '. Expecting an item of type ' . ($html ? "<strong>{$info->type}</strong>" : "`{$info->type}`"); break; } foreach ($info->children as $key => $value) { $cv = new ValidationInfo($value); if (array_key_exists($key, $input) || $cv->required) { $instance->{$key} = static::validate(Util::nestedValue($input, $key), $cv); } } } return $instance; } } throw new RestException(400, $error); } catch (\Exception $e) { static::$exceptions[] = $e; if (static::$holdException) { return null; } throw $e; } }
private function setType(&$object, ValidationInfo $info) { //TODO: proper type management if ($info->type == 'array') { if ($info->children) { $this->model($info->contentType, $info->children); $object->items = (object) array('$ref' => $info->contentType); } elseif ($info->contentType && $info->contentType == 'associative') { unset($info->contentType); $this->model($info->type = 'Object', array(array('name' => 'property', 'type' => 'string', 'default' => '', 'required' => false, 'description' => ''))); } elseif ($info->contentType && $info->contentType != 'indexed') { $object->items = (object) array('type' => $info->contentType); } else { $object->items = (object) array('type' => 'string'); } } elseif ($info->children) { $this->model($info->type, $info->children); } elseif ($t = Util::nestedValue(static::$dataTypeAlias, strtolower($info->type))) { if (is_array($t)) { $info->type = $t[0]; $object->format = $t[1]; } else { $info->type = $t; } } else { $info->type = 'string'; } $object->type = $info->type; $has64bit = PHP_INT_MAX > 2147483647; if ($object->type == 'integer') { $object->format = $has64bit ? 'int64' : 'int32'; } elseif ($object->type == 'number') { $object->format = $has64bit ? 'double' : 'float'; } }
/** * Encode the given data in the format * * @param array $data resulting data that needs to * be encoded in the given format * @param boolean $humanReadable set to TRUE when restler * is not running in production mode. * Formatter has to make the encoded * output more human readable * * @throws \Exception * @return string encoded string */ public function encode($data, $humanReadable = false) { if (!is_readable(static::$viewPath)) { throw new \Exception('The views directory `' . self::$viewPath . '` should exist with read permission.'); } static::$data['basePath'] = dirname($_SERVER['SCRIPT_NAME']); static::$data['baseUrl'] = $this->restler->getBaseUrl(); static::$data['currentPath'] = $this->restler->url; try { $exception = $this->restler->exception; $success = is_null($exception); $error = $success ? null : $exception->getMessage(); $data = array('response' => static::$convertResponseToArray ? Object::toArray($data) : $data, 'stages' => $this->restler->getEvents(), 'success' => $success, 'error' => $error); $info = $data['api'] = $this->restler->apiMethodInfo; $metadata = Util::nestedValue($this->restler, 'apiMethodInfo', 'metadata'); $view = $success ? 'view' : 'errorView'; $value = false; if (static::$parseViewMetadata && isset($metadata[$view])) { if (is_array($metadata[$view])) { self::$view = $metadata[$view]['description']; $value = Util::nestedValue($metadata[$view], 'properties', 'value'); } else { self::$view = $metadata[$view]; } } elseif (!self::$view) { $file = static::$viewPath . '/' . $this->restler->url . '.' . static::getViewExtension(); self::$view = static::$useSmartViews && is_readable($file) ? $this->restler->url : static::$errorView; } if (isset($metadata['param']) && (!$value || 0 === strpos($value, 'request'))) { $params = $metadata['param']; foreach ($params as $index => &$param) { $index = intval($index); if (is_numeric($index)) { $param['value'] = $this->restler->apiMethodInfo->parameters[$index]; } } $data['request']['parameters'] = $params; } if ($value) { $data = Util::nestedValue($data, explode('.', $value)); } $data += static::$data; if (false === ($i = strrpos(self::$view, '.'))) { $template = self::$template; } else { self::$template = $template = substr(self::$view, $i + 1); self::$view = substr(self::$view, 0, $i); } if (!static::$cacheDirectory) { static::$cacheDirectory = Defaults::$cacheDirectory . DIRECTORY_SEPARATOR . $template; if (!file_exists(static::$cacheDirectory)) { if (!mkdir(static::$cacheDirectory, 0770, true)) { throw new RestException(500, 'Unable to create cache directory `' . static::$cacheDirectory . '`'); } } } if (method_exists($class = get_called_class(), $template)) { if ($template == 'blade') { $this->checkDependency('Illuminate\\View\\View'); } elseif ($template == 'twig') { $this->checkDependency('Twig_Environment'); } elseif ($template == 'mustache' || $template == 'handlebar') { $this->checkDependency('Mustache_Engine'); } return call_user_func("{$class}::{$template}", $data, $humanReadable); } throw new RestException(500, "Unsupported template system `{$template}`"); } catch (Exception $e) { static::$parseViewMetadata = false; $this->reset(); throw $e; } }
/** * Get the type and associated model * * @param ReflectionClass $class * @param array $scope * * @throws RestException * @throws \Exception * @return array * * @access protected */ protected static function getTypeAndModel(ReflectionClass $class, array $scope, $prefix = '', array $rules = array()) { $className = $class->getName(); $dataName = CommentParser::$embeddedDataName; if (isset(static::$models[$prefix . $className])) { return static::$models[$prefix . $className]; } $children = array(); try { if ($magic_properties = static::parseMagic($class, empty($prefix))) { foreach ($magic_properties as $prop) { if (!isset($prop['name'])) { throw new Exception('@property comment is not properly defined in ' . $className . ' class'); } if (!isset($prop[$dataName]['label'])) { $prop[$dataName]['label'] = Text::title($prop['name']); } if (isset(static::$fieldTypesByName[$prop['name']]) && $prop['type'] == 'string' && !isset($prop[$dataName]['type'])) { $prop[$dataName]['type'] = static::$fieldTypesByName[$prop['name']]; } $children[$prop['name']] = $prop; } } else { $props = $class->getProperties(ReflectionProperty::IS_PUBLIC); foreach ($props as $prop) { $name = $prop->getName(); $child = array('name' => $name); if ($c = $prop->getDocComment()) { $child += Util::nestedValue(CommentParser::parse($c), 'var') ?: []; } else { $o = $class->newInstance(); $p = $prop->getValue($o); if (is_object($p)) { $child['type'] = get_class($p); } elseif (is_array($p)) { $child['type'] = 'array'; if (count($p)) { $pc = reset($p); if (is_object($pc)) { $child['contentType'] = get_class($pc); } } } } $child += array('type' => isset(static::$fieldTypesByName[$child['name']]) ? static::$fieldTypesByName[$child['name']] : 'string', 'label' => Text::title($child['name'])); isset($child[$dataName]) ? $child[$dataName] += array('required' => true) : ($child[$dataName]['required'] = true); if ($prop->class != $className && ($qualified = Scope::resolve($child['type'], $scope))) { list($child['type'], $child['children']) = static::getTypeAndModel(new ReflectionClass($qualified), $scope); } elseif (($contentType = Util::nestedValue($child, $dataName, 'type')) && ($qualified = Scope::resolve($contentType, $scope))) { list($child['contentType'], $child['children']) = static::getTypeAndModel(new ReflectionClass($qualified), $scope); } $children[$name] = $child; } } } catch (Exception $e) { if (Text::endsWith($e->getFile(), 'CommentParser.php')) { throw new RestException(500, "Error while parsing comments of `{$className}` class. " . $e->getMessage()); } throw $e; } if ($properties = Util::nestedValue($rules, 'properties')) { if (is_string($properties)) { $properties = array($properties); } $c = array(); foreach ($properties as $property) { if (isset($children[$property])) { $c[$property] = $children[$property]; } } $children = $c; } if ($required = Util::nestedValue($rules, 'required')) { //override required on children if (is_bool($required)) { // true means all are required false means none are required $required = $required ? array_keys($children) : array(); } elseif (is_string($required)) { $required = array($required); } $required = array_fill_keys($required, true); foreach ($children as $name => $child) { $children[$name][$dataName]['required'] = isset($required[$name]); } } static::$models[$prefix . $className] = array($className, $children, $prefix . $className); return static::$models[$prefix . $className]; }
/** * Validate the given input * * Validates the input and attempts to fix it when fix is requested * * @param mixed $input * @param ValidationInfo $info * @param null $full * * @return array|bool|float|int|mixed|null|number|string * @throws \Luracast\Restler\RestException */ public static function validate($input, ValidationInfo $info, $full = null) { if (is_null($input)) { if ($info->required) { throw new RestException(400, "`{$info->name}` is required but missing."); } return null; } $error = isset($info->rules['message']) ? $info->rules['message'] : "invalid value specified for `{$info->name}`"; //if a validation method is specified if (!empty($info->method)) { $method = $info->method; $info->method = ''; $r = self::validate($input, $info); return $info->apiClassInstance->{$method}($r); } // when type is an array check if it passes for any type if (is_array($info->type)) { //trace("types are ".print_r($info->type, true)); $types = $info->type; foreach ($types as $type) { $info->type = $type; try { $r = self::validate($input, $info); if ($r !== false) { return $r; } } catch (RestException $e) { // just continue } } throw new RestException(400, $error); } //patterns are supported only for non numeric types if (isset($info->pattern) && $info->type != 'int' && $info->type != 'float' && $info->type != 'number') { if (!preg_match($info->pattern, $input)) { throw new RestException(400, $error); } } if (isset($info->choice)) { if (!in_array($input, $info->choice)) { throw new RestException(400, $error); } } if (method_exists(__CLASS__, $info->type) && $info->type != 'validate') { try { return call_user_func(__CLASS__ . '::' . $info->type, $input, $info); } catch (Invalid $e) { throw new RestException(400, $error . '. ' . $e->getMessage()); } } switch ($info->type) { case 'int': case 'float': case 'number': if (!is_numeric($input)) { $error .= '. Expecting ' . ($info->type == 'int' ? 'integer' : 'numeric') . ' value'; break; } if ($info->type == 'int' && (int) $input != $input) { if ($info->fix) { $r = (int) $input; } else { $error .= '. Expecting integer value'; break; } } else { $r = $info->numericValue($input); } if (isset($info->min) && $r < $info->min) { if ($info->fix) { $r = $info->min; } else { $error .= '. Given value is too low'; break; } } if (isset($info->max) && $r > $info->max) { if ($info->fix) { $r = $info->max; } else { $error .= '. Given value is too high'; break; } } return $r; case 'string': $r = strlen($input); if (isset($info->min) && $r < $info->min) { if ($info->fix) { $input = str_pad($input, $info->min, $input); } else { $error .= '. Given string is too short'; break; } } if (isset($info->max) && $r > $info->max) { if ($info->fix) { $input = substr($input, 0, $info->max); } else { $error .= '. Given string is too long'; break; } } return $input; case 'bool': case 'boolean': if ($input == 'true') { return true; } if (is_numeric($input)) { return $input > 0; } return false; case 'array': if (is_array($input)) { $contentType = Util::nestedValue($info, 'contentType') ?: null; if ($info->fix) { if ($contentType == 'indexed') { $input = $info->filterArray($input, true); } elseif ($contentType == 'associative') { $input = $info->filterArray($input, true); } } elseif ($contentType == 'indexed' && array_values($input) != $input) { $error .= '. Expecting an array but an object is given'; break; } elseif ($contentType == 'associative' && array_values($input) == $input && count($input)) { $error .= '. Expecting an object but an array is given'; break; } $r = count($input); if (isset($info->min) && $r < $info->min) { $error .= '. Given array is too small'; break; } if (isset($info->max) && $r > $info->max) { if ($info->fix) { $input = array_slice($input, 0, $info->max); } else { $error .= '. Given array is too big'; break; } } if (isset($contentType) && $contentType != 'associative' && $contentType != 'indexed') { $name = $info->name; $info->type = $contentType; unset($info->contentType); foreach ($input as $key => $chinput) { $info->name = "{$name}[{$key}]"; $input[$key] = static::validate($chinput, $info); } } return $input; } elseif (isset($contentType)) { $error .= ". Expecting an array with contents of type `{$contentType}`"; break; } elseif ($info->fix && is_string($input)) { return array($input); } break; case 'mixed': case 'unknown_type': case 'unknown': case null: //treat as unknown return $input; default: if (!is_array($input)) { break; } //do type conversion if (class_exists($info->type)) { $input = $info->filterArray($input, false); $implements = class_implements($info->type); if (is_array($implements) && in_array('Luracast\\Restler\\Data\\iValueObject', $implements)) { return call_user_func("{$info->type}::__set_state", $input); } $class = $info->type; $instance = new $class(); if (is_array($info->children)) { if (empty($input) || !is_array($input) || $input === array_values($input)) { $error .= ". Expecting an object of type `{$info->type}`"; break; } foreach ($info->children as $key => $value) { $instance->{$key} = static::validate(Util::nestedValue($input, $key), new ValidationInfo($value)); } } return $instance; } } throw new RestException(400, $error); }