Exemple #1
0
 /**
  * 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;
 }
Exemple #2
0
 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);
 }
Exemple #3
0
 /**
  * Parses the request to figure out the best format for response.
  * Extension, if present, overrides the Accept header
  *
  * @throws RestException
  * @return iFormat
  * @example JsonFormat
  */
 protected function negotiateResponseFormat()
 {
     $metadata = Util::nestedValue($this, 'apiMethodInfo', 'metadata');
     //check if the api method insists on response format using @format comment
     if ($metadata && isset($metadata['format'])) {
         $formats = explode(',', (string) $metadata['format']);
         foreach ($formats as $i => $f) {
             $f = trim($f);
             if (!in_array($f, $this->formatOverridesMap)) {
                 throw new RestException(500, "Given @format is not present in overriding formats. Please call `\$r->setOverridingFormats('{$f}');` first.");
             }
             $formats[$i] = $f;
         }
         call_user_func_array(array($this, 'setSupportedFormats'), $formats);
     }
     // check if client has specified an extension
     /** @var $format iFormat*/
     $format = null;
     $extensions = explode('.', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
     while ($extensions) {
         $extension = array_pop($extensions);
         $extension = explode('/', $extension);
         $extension = array_shift($extension);
         if ($extension && isset($this->formatMap[$extension])) {
             $format = Scope::get($this->formatMap[$extension]);
             $format->setExtension($extension);
             // echo "Extension $extension";
             return $format;
         }
     }
     // check if client has sent list of accepted data formats
     if (isset($_SERVER['HTTP_ACCEPT'])) {
         $acceptList = Util::sortByPriority($_SERVER['HTTP_ACCEPT']);
         foreach ($acceptList as $accept => $quality) {
             if (isset($this->formatMap[$accept])) {
                 $format = Scope::get($this->formatMap[$accept]);
                 $format->setMIME($accept);
                 //echo "MIME $accept";
                 // Tell cache content is based on Accept header
                 @header('Vary: Accept');
                 return $format;
             } elseif (false !== ($index = strrpos($accept, '+'))) {
                 $mime = substr($accept, 0, $index);
                 if (is_string(Defaults::$apiVendor) && 0 === stripos($mime, 'application/vnd.' . Defaults::$apiVendor . '-v')) {
                     $extension = substr($accept, $index + 1);
                     if (isset($this->formatMap[$extension])) {
                         //check the MIME and extract version
                         $version = intval(substr($mime, 18 + strlen(Defaults::$apiVendor)));
                         if ($version > 0 && $version <= $this->apiVersion) {
                             $this->requestedApiVersion = $version;
                             $format = Scope::get($this->formatMap[$extension]);
                             $format->setExtension($extension);
                             // echo "Extension $extension";
                             Defaults::$useVendorMIMEVersioning = true;
                             @header('Vary: Accept');
                             return $format;
                         }
                     }
                 }
             }
         }
     } else {
         // RFC 2616: If no Accept header field is
         // present, then it is assumed that the
         // client accepts all media types.
         $_SERVER['HTTP_ACCEPT'] = '*/*';
     }
     if (strpos($_SERVER['HTTP_ACCEPT'], '*') !== false) {
         if (false !== strpos($_SERVER['HTTP_ACCEPT'], 'application/*')) {
             $format = Scope::get('JsonFormat');
         } elseif (false !== strpos($_SERVER['HTTP_ACCEPT'], 'text/*')) {
             $format = Scope::get('XmlFormat');
         } elseif (false !== strpos($_SERVER['HTTP_ACCEPT'], '*/*')) {
             $format = Scope::get($this->formatMap['default']);
         }
     }
     if (empty($format)) {
         // RFC 2616: If an Accept header field is present, and if the
         // server cannot send a response which is acceptable according to
         // the combined Accept field value, then the server SHOULD send
         // a 406 (not acceptable) response.
         $format = Scope::get($this->formatMap['default']);
         $this->responseFormat = $format;
         throw new RestException(406, 'Content negotiation failed. ' . 'Try `' . $format->getMIME() . '` instead.');
     } else {
         // Tell cache content is based at Accept header
         @header("Vary: Accept");
         return $format;
     }
 }
Exemple #4
0
 private function _model($className, $instance = null)
 {
     $id = $this->_noNamespace($className);
     if (isset($this->_models->{$id})) {
         return;
     }
     $properties = array();
     if (!$instance) {
         if (!class_exists($className)) {
             return;
         }
         $instance = new $className();
     }
     $data = get_object_vars($instance);
     $reflectionClass = new \ReflectionClass($className);
     foreach ($data as $key => $value) {
         $propertyMetaData = null;
         try {
             $property = $reflectionClass->getProperty($key);
             if ($c = $property->getDocComment()) {
                 $propertyMetaData = Util::nestedValue(CommentParser::parse($c), 'var');
             }
         } catch (\ReflectionException $e) {
         }
         if (is_null($propertyMetaData)) {
             $type = $this->getType($value, true);
             $description = '';
         } else {
             $type = Util::nestedValue($propertyMetaData, 'type') ?: $this->getType($value, true);
             $description = Util::nestedValue($propertyMetaData, 'description') ?: '';
             if (class_exists($type)) {
                 $this->_model($type);
                 $type = $this->_noNamespace($type);
             }
         }
         if (isset(static::$dataTypeAlias[$type])) {
             $type = static::$dataTypeAlias[$type];
         }
         $properties[$key] = array('type' => $type, 'description' => $description);
         if (Util::nestedValue($propertyMetaData, CommentParser::$embeddedDataName, 'required')) {
             $properties[$key]['required'] = true;
         }
         if ($type == 'Array') {
             $itemType = Util::nestedValue($propertyMetaData, CommentParser::$embeddedDataName, 'type') ?: (count($value) ? $this->getType(end($value), true) : 'string');
             if (class_exists($itemType)) {
                 $this->_model($itemType);
                 $itemType = $this->_noNamespace($itemType);
             }
             $properties[$key]['item'] = array('type' => $itemType);
         } else {
             if (preg_match('/^Array\\[(.+)\\]$/', $type, $matches)) {
                 $itemType = $matches[1];
                 $properties[$key]['type'] = 'Array';
                 $properties[$key]['item']['type'] = $itemType;
                 if (class_exists($itemType)) {
                     $this->_model($itemType);
                 }
             }
         }
     }
     if (!empty($properties)) {
         $model = new stdClass();
         $model->id = $id;
         $model->properties = $properties;
         $this->_models->{$id} = $model;
     }
 }
Exemple #5
0
 /**
  * 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;
 }
Exemple #6
0
 /**
  * @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);
 }
 /**
  * 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;
     }
 }
Exemple #8
0
 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';
     }
 }
Exemple #9
0
 /**
  * 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];
 }
Exemple #10
0
 /**
  * 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;
     }
 }
Exemple #11
0
 public function __isset($name)
 {
     return !is_null(Util::nestedValue($_SESSION, 'flash', $name));
 }
Exemple #12
0
 /**
  * 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);
 }
Exemple #13
0
 /**
  * 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);
 }
Exemple #14
0
 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;
 }
 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';
     }
 }
Exemple #16
0
 /**
  * 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;
 }
Exemple #17
0
 /**
  * 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;
     }
 }
Exemple #18
0
 /**
  * @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;
 }
Exemple #19
0
 public static function fields($dataOnly = false)
 {
     $m = static::$info->metadata;
     $params = $m['param'];
     $values = static::$info->parameters;
     $r = array();
     foreach ($params as $k => $p) {
         $value = Util::nestedValue($values, $k);
         if (is_scalar($value) || $p['type'] == 'array' && is_array($value) && $value == array_values($value) || is_object($value) && $p['type'] == get_class($value)) {
             $p['value'] = $value;
         }
         static::$validationInfo = $v = new ValidationInfo($p);
         if ($v->from == 'path') {
             continue;
         }
         if (!empty($v->children)) {
             $t = Emmet::make(static::style('fieldset', $m), array('label' => $v->label));
             foreach ($v->children as $n => $c) {
                 $value = Util::nestedValue($v->value, $n);
                 if (is_scalar($value) || $c['type'] == 'array' && is_array($value) && $value == array_values($value) || is_object($value) && $c['type'] == get_class($value)) {
                     $c['value'] = $value;
                 }
                 static::$validationInfo = $vc = new ValidationInfo($c);
                 if ($vc->from == 'path') {
                     continue;
                 }
                 $vc->name = $v->name . '[' . $vc->name . ']';
                 $t[] = static::field($vc, $dataOnly);
             }
             $r[] = $t;
             static::$validationInfo = null;
         } else {
             $f = static::field($v, $dataOnly);
             $r[] = $f;
         }
         static::$validationInfo = null;
     }
     return $r;
 }
 /**
  * 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];
 }
Exemple #21
0
 /**
  * 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);
 }