Esempio n. 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;
 }
Esempio n. 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);
 }
Esempio n. 3
0
 /**
  * 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();
 }
Esempio n. 4
0
 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';
     }
 }
Esempio n. 5
0
 /**
  * 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';
 }
Esempio n. 6
0
 /**
  * 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);
     }
 }
Esempio n. 7
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);
 }
Esempio n. 8
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;
 }
Esempio n. 9
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)
 {
     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;
     }
 }
Esempio n. 10
0
 /**
  * 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;
 }
Esempio n. 11
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;
 }
Esempio n. 12
0
 /**
  * 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++;
             }
         }
     }
 }
Esempio n. 13
0
 public function __isset($name)
 {
     return !is_null(Util::nestedValue($_SESSION, 'flash', $name));
 }
Esempio n. 14
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;
 }
Esempio n. 15
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);
 }
Esempio n. 16
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);
 }
Esempio n. 17
0
 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;
 }
Esempio n. 18
0
 /**
  * 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);
     }
 }
Esempio n. 19
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;
 }
Esempio n. 20
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];
 }
Esempio n. 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
  *
  * @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;
     }
 }
Esempio n. 22
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';
     }
 }
Esempio n. 23
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;
     }
 }
Esempio n. 24
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, $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];
 }
Esempio n. 25
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);
 }