forked from Wavyzz/dolibarr
NEW : add restler framework
First step to build REST API into Dolibarr
This commit is contained in:
114
htdocs/includes/restler/ApcCache.php
Normal file
114
htdocs/includes/restler/ApcCache.php
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
use Luracast\Restler\iCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ApcCache provides an APC based cache for Restler
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author Joel R. Simpson <joel.simpson@gmail.com>
|
||||||
|
* @copyright 2013 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class ApcCache implements iCache
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The namespace that all of the cached entries will be stored under. This allows multiple APIs to run concurrently.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
static public $namespace = 'restler';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* store data in the cache
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param mixed $data
|
||||||
|
*
|
||||||
|
* @return boolean true if successful
|
||||||
|
*/
|
||||||
|
public function set($name, $data)
|
||||||
|
{
|
||||||
|
function_exists('apc_store') || $this->apcNotAvailable();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return apc_store(self::$namespace . "-" . $name, $data);
|
||||||
|
} catch
|
||||||
|
(\Exception $exception) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function apcNotAvailable()
|
||||||
|
{
|
||||||
|
throw new \Exception('APC is not available for use as Restler Cache. Please make sure the module is installed. http://php.net/manual/en/apc.installation.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retrieve data from the cache
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param bool $ignoreErrors
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get($name, $ignoreErrors = false)
|
||||||
|
{
|
||||||
|
function_exists('apc_fetch') || $this->apcNotAvailable();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return apc_fetch(self::$namespace . "-" . $name);
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
if (!$ignoreErrors) {
|
||||||
|
throw $exception;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete data from the cache
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param bool $ignoreErrors
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
* @return boolean true if successful
|
||||||
|
*/
|
||||||
|
public function clear($name, $ignoreErrors = false)
|
||||||
|
{
|
||||||
|
function_exists('apc_delete') || $this->apcNotAvailable();
|
||||||
|
|
||||||
|
try {
|
||||||
|
apc_delete(self::$namespace . "-" . $name);
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
if (!$ignoreErrors) {
|
||||||
|
throw $exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if the given name is cached
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return boolean true if cached
|
||||||
|
*/
|
||||||
|
public function isCached($name)
|
||||||
|
{
|
||||||
|
function_exists('apc_exists') || $this->apcNotAvailable();
|
||||||
|
return apc_exists(self::$namespace . "-" . $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
438
htdocs/includes/restler/AutoLoader.php
Normal file
438
htdocs/includes/restler/AutoLoader.php
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that implements spl_autoload facilities and multiple
|
||||||
|
* conventions support.
|
||||||
|
* Supports composer libraries and 100% PSR-0 compliant.
|
||||||
|
* In addition we enable namespace prefixing and class aliases.
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage helper
|
||||||
|
* @author Nick Lombard <github@jigsoft.co.za>
|
||||||
|
* @copyright 2012 Luracast
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class AutoLoader
|
||||||
|
{
|
||||||
|
protected static $instance, // the singleton instance reference
|
||||||
|
$perfectLoaders, // used to keep the ideal list of loaders
|
||||||
|
$rogueLoaders = array(), // other auto loaders now unregistered
|
||||||
|
$classMap = array(), // the class to include file mapping
|
||||||
|
$aliases = array( // aliases and prefixes instead of null list aliases
|
||||||
|
'Luracast\\Restler' => null,
|
||||||
|
'Luracast\\Restler\\Format' => null,
|
||||||
|
'Luracast\\Restler\\Data' => null,
|
||||||
|
'Luracast\\Restler\\Filter' => null,
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton instance facility.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
* @return AutoLoader the current instance or new instance if none exists.
|
||||||
|
*/
|
||||||
|
public static function instance()
|
||||||
|
{
|
||||||
|
static::$instance = static::$instance ?: new static();
|
||||||
|
return static::thereCanBeOnlyOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to add a path to the include path.
|
||||||
|
* AutoLoader uses the include path to discover classes.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*
|
||||||
|
* @param $path string absolute or relative path.
|
||||||
|
*
|
||||||
|
* @return bool false if the path cannot be resolved
|
||||||
|
* or the resolved absolute path.
|
||||||
|
*/
|
||||||
|
public static function addPath($path) {
|
||||||
|
if (false === $path = stream_resolve_include_path($path))
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
set_include_path($path.PATH_SEPARATOR.get_include_path());
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Other autoLoaders interfere and cause duplicate class loading.
|
||||||
|
* AutoLoader is capable enough to handle all standards so no need
|
||||||
|
* for others stumbling about.
|
||||||
|
*
|
||||||
|
* @return callable the one true auto loader.
|
||||||
|
*/
|
||||||
|
public static function thereCanBeOnlyOne() {
|
||||||
|
if (static::$perfectLoaders === spl_autoload_functions())
|
||||||
|
return static::$instance;
|
||||||
|
|
||||||
|
if (false !== $loaders = spl_autoload_functions())
|
||||||
|
if (0 < $count = count($loaders))
|
||||||
|
for ($i = 0, static::$rogueLoaders += $loaders;
|
||||||
|
$i < $count && false != ($loader = $loaders[$i]);
|
||||||
|
$i++)
|
||||||
|
if ($loader !== static::$perfectLoaders[0])
|
||||||
|
spl_autoload_unregister($loader);
|
||||||
|
|
||||||
|
return static::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seen this before cache handler.
|
||||||
|
* Facilitates both lookup and persist operations as well as convenience,
|
||||||
|
* load complete map functionality. The key can only be given a non falsy
|
||||||
|
* value once, this will be truthy for life.
|
||||||
|
*
|
||||||
|
* @param $key mixed class name considered or a collection of
|
||||||
|
* classMap entries
|
||||||
|
* @param $value mixed optional not required when doing a query on
|
||||||
|
* key. Default is false we haven't seen this
|
||||||
|
* class. Most of the time it will be the filename
|
||||||
|
* for include and is set to true if we are unable
|
||||||
|
* to load this class iow true == it does not exist.
|
||||||
|
* value may also be a callable auto loader function.
|
||||||
|
*
|
||||||
|
* @return mixed The known value for the key or false if key has no value
|
||||||
|
*/
|
||||||
|
public static function seen($key, $value = false)
|
||||||
|
{
|
||||||
|
if (is_array($key)) {
|
||||||
|
static::$classMap = $key + static::$classMap;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty(static::$classMap[$key]))
|
||||||
|
static::$classMap[$key] = $value;
|
||||||
|
|
||||||
|
if (is_string($alias = static::$classMap[$key]))
|
||||||
|
if (isset(static::$classMap[$alias]))
|
||||||
|
return static::$classMap[$alias];
|
||||||
|
|
||||||
|
return static::$classMap[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protected constructor to enforce singleton pattern.
|
||||||
|
* Populate a default include path.
|
||||||
|
* All possible includes cant possibly be catered for and if you
|
||||||
|
* require another path then simply add it calling set_include_path.
|
||||||
|
*/
|
||||||
|
protected function __construct()
|
||||||
|
{
|
||||||
|
static::$perfectLoaders = array($this);
|
||||||
|
|
||||||
|
if (false === static::seen('__include_path')) {
|
||||||
|
|
||||||
|
$paths = explode(PATH_SEPARATOR, get_include_path());
|
||||||
|
$slash = DIRECTORY_SEPARATOR;
|
||||||
|
$dir = dirname(__DIR__);
|
||||||
|
$source_dir = dirname($dir);
|
||||||
|
$dir = dirname($source_dir);
|
||||||
|
|
||||||
|
foreach (
|
||||||
|
array(
|
||||||
|
array($source_dir),
|
||||||
|
array($dir, '..', '..', 'composer'),
|
||||||
|
array($dir, 'vendor', 'composer'),
|
||||||
|
array($dir, '..', '..', '..', 'php'),
|
||||||
|
array($dir, 'vendor', 'php'))
|
||||||
|
as $includePath)
|
||||||
|
if (false !== $path = stream_resolve_include_path(
|
||||||
|
implode($slash, $includePath)
|
||||||
|
))
|
||||||
|
if ('composer' == end($includePath) &&
|
||||||
|
false !== $classmapPath = stream_resolve_include_path(
|
||||||
|
"$path{$slash}autoload_classmap.php"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
static::seen(static::loadFile(
|
||||||
|
$classmapPath
|
||||||
|
));
|
||||||
|
$paths = array_merge(
|
||||||
|
$paths,
|
||||||
|
array_values(static::loadFile(
|
||||||
|
"$path{$slash}autoload_namespaces.php"
|
||||||
|
))
|
||||||
|
);
|
||||||
|
} else
|
||||||
|
$paths[] = $path;
|
||||||
|
|
||||||
|
$paths = array_filter(array_map(
|
||||||
|
function ($path) {
|
||||||
|
if (false == $realPath = @realpath($path))
|
||||||
|
return null;
|
||||||
|
return $realPath . DIRECTORY_SEPARATOR;
|
||||||
|
},
|
||||||
|
$paths
|
||||||
|
));
|
||||||
|
natsort($paths);
|
||||||
|
static::seen(
|
||||||
|
'__include_path',
|
||||||
|
implode(PATH_SEPARATOR, array_unique($paths))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_include_path(static::seen('__include_path'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to include the path location.
|
||||||
|
* Called from a static context which will not expose the AutoLoader
|
||||||
|
* instance itself.
|
||||||
|
*
|
||||||
|
* @param $path string location of php file on the include path
|
||||||
|
*
|
||||||
|
* @return bool|mixed returns reference obtained from the include or false
|
||||||
|
*/
|
||||||
|
private static function loadFile($path)
|
||||||
|
{
|
||||||
|
return \Luracast_Restler_autoloaderInclude($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to load class with namespace prefixes.
|
||||||
|
*
|
||||||
|
* @param $className string class name
|
||||||
|
*
|
||||||
|
* @return bool|mixed reference to discovered include or false
|
||||||
|
*/
|
||||||
|
private function loadPrefixes($className)
|
||||||
|
{
|
||||||
|
$currentClass = $className;
|
||||||
|
if (false !== $pos = strrpos($className, '\\'))
|
||||||
|
$className = substr($className, $pos);
|
||||||
|
else
|
||||||
|
$className = "\\$className";
|
||||||
|
|
||||||
|
for (
|
||||||
|
$i = 0,
|
||||||
|
$file = false,
|
||||||
|
$count = count(static::$aliases),
|
||||||
|
$prefixes = array_keys(static::$aliases);
|
||||||
|
$i < $count
|
||||||
|
&& false === $file
|
||||||
|
&& false === $file = $this->discover(
|
||||||
|
$variant = $prefixes[$i++].$className,
|
||||||
|
$currentClass
|
||||||
|
);
|
||||||
|
$file = $this->loadAliases($variant)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to load configured aliases based on namespace part of class name.
|
||||||
|
*
|
||||||
|
* @param $className string fully qualified class name.
|
||||||
|
*
|
||||||
|
* @return bool|mixed reference to discovered include or false
|
||||||
|
*/
|
||||||
|
private function loadAliases($className)
|
||||||
|
{
|
||||||
|
$file = false;
|
||||||
|
if (preg_match('/(.+)(\\\\\w+$)/U', $className, $parts))
|
||||||
|
for (
|
||||||
|
$i = 0,
|
||||||
|
$aliases = isset(static::$aliases[$parts[1]])
|
||||||
|
? static::$aliases[$parts[1]] : array(),
|
||||||
|
$count = count($aliases);
|
||||||
|
$i < $count && false === $file;
|
||||||
|
$file = $this->discover(
|
||||||
|
"{$aliases[$i++]}$parts[2]",
|
||||||
|
$className
|
||||||
|
)
|
||||||
|
) ;
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load from rogueLoaders as last resort.
|
||||||
|
* It may happen that a custom auto loader may load classes in a unique way,
|
||||||
|
* these classes cannot be seen otherwise nor should we attempt to cover every
|
||||||
|
* possible deviation. If we still can't find a class, as a last resort, we will
|
||||||
|
* run through the list of rogue loaders and verify if we succeeded.
|
||||||
|
*
|
||||||
|
* @param $className string className that can't be found
|
||||||
|
* @param null $loader callable loader optional when the loader is known
|
||||||
|
*
|
||||||
|
* @return bool false unless className now exists
|
||||||
|
*/
|
||||||
|
private function loadLastResort($className, $loader = null) {
|
||||||
|
$loaders = array_unique(static::$rogueLoaders);
|
||||||
|
if (isset($loader)) {
|
||||||
|
if (false === array_search($loader, $loaders))
|
||||||
|
static::$rogueLoaders[] = $loader;
|
||||||
|
return $this->loadThisLoader($className, $loader);
|
||||||
|
}
|
||||||
|
foreach ($loaders as $loader)
|
||||||
|
if (false !== $file = $this->loadThisLoader($className, $loader))
|
||||||
|
return $file;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for loadLastResort.
|
||||||
|
* Use loader with $className and see if className exists.
|
||||||
|
*
|
||||||
|
* @param $className string name of a class to load
|
||||||
|
* @param $loader callable autoLoader method
|
||||||
|
*
|
||||||
|
* @return bool false unless className exists
|
||||||
|
*/
|
||||||
|
private function loadThisLoader($className, $loader) {
|
||||||
|
if (is_callable($loader)
|
||||||
|
&& false !== $file = $loader($className)
|
||||||
|
&& $this->exists($className, $loader))
|
||||||
|
return $file;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an alias for class.
|
||||||
|
*
|
||||||
|
* @param $className string the name of the alias class
|
||||||
|
* @param $currentClass string the current class this alias references
|
||||||
|
*/
|
||||||
|
private function alias($className, $currentClass)
|
||||||
|
{
|
||||||
|
if ($className != $currentClass
|
||||||
|
&& false !== strpos($className, $currentClass))
|
||||||
|
if (!class_exists($currentClass, false)
|
||||||
|
&& class_alias($className, $currentClass))
|
||||||
|
static::seen($currentClass, $className);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discovery process.
|
||||||
|
*
|
||||||
|
* @param $className string class name to discover
|
||||||
|
* @param $currentClass string optional name of current class when
|
||||||
|
* looking up an alias
|
||||||
|
*
|
||||||
|
* @return bool|mixed resolved include reference or false
|
||||||
|
*/
|
||||||
|
private function discover($className, $currentClass = null)
|
||||||
|
{
|
||||||
|
$currentClass = $currentClass ?: $className;
|
||||||
|
|
||||||
|
/** The short version we've done this before and found it in cache */
|
||||||
|
if (false !== $file = static::seen($className)) {
|
||||||
|
if (!$this->exists($className))
|
||||||
|
if (is_callable($file))
|
||||||
|
$file = $this->loadLastResort($className, $file);
|
||||||
|
elseif($file = stream_resolve_include_path($file))
|
||||||
|
$file = static::loadFile($file);
|
||||||
|
|
||||||
|
$this->alias($className, $currentClass);
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** We did not find it in cache, lets look for it shall we */
|
||||||
|
|
||||||
|
/** replace \ with / and _ in CLASS NAME with / = PSR-0 in 3 lines */
|
||||||
|
$file = preg_replace("/\\\|_(?=\w+$)/", DIRECTORY_SEPARATOR, $className);
|
||||||
|
if (false === $file = stream_resolve_include_path("$file.php"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/** have we loaded this file before could this be an alias */
|
||||||
|
if (in_array($file, get_included_files())) {
|
||||||
|
if (false !== $sameFile = array_search($file, static::$classMap))
|
||||||
|
if (!$this->exists($className, $file))
|
||||||
|
if (false !== strpos($sameFile, $className))
|
||||||
|
$this->alias($sameFile, $className);
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
$state = array_merge(get_declared_classes(), get_declared_interfaces());
|
||||||
|
|
||||||
|
if (false !== $result = static::loadFile($file)) {
|
||||||
|
|
||||||
|
if ($this->exists($className, $file))
|
||||||
|
$this->alias($className, $currentClass);
|
||||||
|
elseif (false != $diff = array_diff(
|
||||||
|
array_merge(get_declared_classes(), get_declared_interfaces()), $state))
|
||||||
|
foreach ($diff as $autoLoaded)
|
||||||
|
if ($this->exists($autoLoaded, $file))
|
||||||
|
if (false !== strpos($autoLoaded, $className))
|
||||||
|
$this->alias($autoLoaded, $className);
|
||||||
|
|
||||||
|
if (!$this->exists($currentClass))
|
||||||
|
$result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether supplied string exists in a loaded class or interface.
|
||||||
|
* As a convenience the supplied $mapping can be the value for seen.
|
||||||
|
*
|
||||||
|
* @param $className string The class or interface to verify
|
||||||
|
* @param $mapping string (optional) value for map/seen if found to exist
|
||||||
|
*
|
||||||
|
* @return bool whether the class/interface exists without calling auto loader
|
||||||
|
*/
|
||||||
|
private function exists($className, $mapping = null)
|
||||||
|
{
|
||||||
|
if (class_exists($className, false)
|
||||||
|
|| interface_exists($className, false))
|
||||||
|
if (isset($mapping))
|
||||||
|
return static::seen($className, $mapping);
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto loader callback through __invoke object as function.
|
||||||
|
*
|
||||||
|
* @param $className string class/interface name to auto load
|
||||||
|
*
|
||||||
|
* @return mixed|null the reference from the include or null
|
||||||
|
*/
|
||||||
|
public function __invoke($className)
|
||||||
|
{
|
||||||
|
if (empty($className))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (false !== $includeReference = $this->discover($className))
|
||||||
|
return $includeReference;
|
||||||
|
|
||||||
|
static::thereCanBeOnlyOne();
|
||||||
|
|
||||||
|
if (false !== $includeReference = $this->loadAliases($className))
|
||||||
|
return $includeReference;
|
||||||
|
|
||||||
|
if (false !== $includeReference = $this->loadPrefixes($className))
|
||||||
|
return $includeReference;
|
||||||
|
|
||||||
|
if (false !== $includeReference = $this->loadLastResort($className))
|
||||||
|
return $includeReference;
|
||||||
|
|
||||||
|
static::seen($className, true);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/**
|
||||||
|
* Include function in the root namespace to include files optimized
|
||||||
|
* for the global context.
|
||||||
|
*
|
||||||
|
* @param $path string path of php file to include into the global context.
|
||||||
|
*
|
||||||
|
* @return mixed|bool false if the file could not be included.
|
||||||
|
*/
|
||||||
|
function Luracast_Restler_autoloaderInclude($path) {
|
||||||
|
return include $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
466
htdocs/includes/restler/CommentParser.php
Normal file
466
htdocs/includes/restler/CommentParser.php
Normal file
@@ -0,0 +1,466 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the PHPDoc comments for metadata. Inspired by `Documentor` code base.
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage Helper
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class CommentParser
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* name for the embedded data
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public static $embeddedDataName = 'properties';
|
||||||
|
/**
|
||||||
|
* Regular Expression pattern for finding the embedded data and extract
|
||||||
|
* the inner information. It is used with preg_match.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public static $embeddedDataPattern
|
||||||
|
= '/```(\w*)[\s]*(([^`]*`{0,2}[^`]+)*)```/ms';
|
||||||
|
/**
|
||||||
|
* Pattern will have groups for the inner details of embedded data
|
||||||
|
* this index is used to locate the data portion.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public static $embeddedDataIndex = 2;
|
||||||
|
/**
|
||||||
|
* Delimiter used to split the array data.
|
||||||
|
*
|
||||||
|
* When the name portion is of the embedded data is blank auto detection
|
||||||
|
* will be used and if URLEncodedFormat is detected as the data format
|
||||||
|
* the character specified will be used as the delimiter to find split
|
||||||
|
* array data.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public static $arrayDelimiter = ',';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* character sequence used to escape \@
|
||||||
|
*/
|
||||||
|
const escapedAtChar = '\\@';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* character sequence used to escape end of comment
|
||||||
|
*/
|
||||||
|
const escapedCommendEnd = '{@*}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of Restler class injected at runtime.
|
||||||
|
*
|
||||||
|
* @var Restler
|
||||||
|
*/
|
||||||
|
public $restler;
|
||||||
|
/**
|
||||||
|
* Comment information is parsed and stored in to this array.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $_data = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the comment and extract the data.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*
|
||||||
|
* @param $comment
|
||||||
|
* @param bool $isPhpDoc
|
||||||
|
*
|
||||||
|
* @return array associative array with the extracted values
|
||||||
|
*/
|
||||||
|
public static function parse($comment, $isPhpDoc = true)
|
||||||
|
{
|
||||||
|
$p = new self();
|
||||||
|
if (empty($comment)) {
|
||||||
|
return $p->_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($isPhpDoc) {
|
||||||
|
$comment = self::removeCommentTags($comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
$p->extractData($comment);
|
||||||
|
return $p->_data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the comment tags from each line of the comment.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*
|
||||||
|
* @param string $comment PhpDoc style comment
|
||||||
|
*
|
||||||
|
* @return string comments with out the tags
|
||||||
|
*/
|
||||||
|
public static function removeCommentTags($comment)
|
||||||
|
{
|
||||||
|
$pattern = '/(^\/\*\*)|(^\s*\**[ \/]?)|\s(?=@)|\s\*\//m';
|
||||||
|
return preg_replace($pattern, '', $comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts description and long description, uses other methods to get
|
||||||
|
* parameters.
|
||||||
|
*
|
||||||
|
* @param $comment
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function extractData($comment)
|
||||||
|
{
|
||||||
|
//to use @ as part of comment we need to
|
||||||
|
$comment = str_replace(
|
||||||
|
array(self::escapedCommendEnd, self::escapedAtChar),
|
||||||
|
array('*/', '@'),
|
||||||
|
$comment);
|
||||||
|
|
||||||
|
$description = array();
|
||||||
|
$longDescription = array();
|
||||||
|
$params = array();
|
||||||
|
|
||||||
|
$mode = 0; // extract short description;
|
||||||
|
$comments = preg_split("/(\r?\n)/", $comment);
|
||||||
|
// remove first blank line;
|
||||||
|
array_shift($comments);
|
||||||
|
$addNewline = false;
|
||||||
|
foreach ($comments as $line) {
|
||||||
|
$line = trim($line);
|
||||||
|
$newParam = false;
|
||||||
|
if (empty ($line)) {
|
||||||
|
if ($mode == 0) {
|
||||||
|
$mode++;
|
||||||
|
} else {
|
||||||
|
$addNewline = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} elseif ($line{0} == '@') {
|
||||||
|
$mode = 2;
|
||||||
|
$newParam = true;
|
||||||
|
}
|
||||||
|
switch ($mode) {
|
||||||
|
case 0 :
|
||||||
|
$description[] = $line;
|
||||||
|
if (count($description) > 3) {
|
||||||
|
// if more than 3 lines take only first line
|
||||||
|
$longDescription = $description;
|
||||||
|
$description[] = array_shift($longDescription);
|
||||||
|
$mode = 1;
|
||||||
|
} elseif (substr($line, -1) == '.') {
|
||||||
|
$mode = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1 :
|
||||||
|
if ($addNewline) {
|
||||||
|
$line = ' ' . $line;
|
||||||
|
}
|
||||||
|
$longDescription[] = $line;
|
||||||
|
break;
|
||||||
|
case 2 :
|
||||||
|
$newParam
|
||||||
|
? $params[] = $line
|
||||||
|
: $params[count($params) - 1] .= ' ' . $line;
|
||||||
|
}
|
||||||
|
$addNewline = false;
|
||||||
|
}
|
||||||
|
$description = implode(' ', $description);
|
||||||
|
$longDescription = implode(' ', $longDescription);
|
||||||
|
$description = preg_replace('/\s+/msu', ' ', $description);
|
||||||
|
$longDescription = preg_replace('/\s+/msu', ' ', $longDescription);
|
||||||
|
list($description, $d1)
|
||||||
|
= $this->parseEmbeddedData($description);
|
||||||
|
list($longDescription, $d2)
|
||||||
|
= $this->parseEmbeddedData($longDescription);
|
||||||
|
$this->_data = compact('description', 'longDescription');
|
||||||
|
$d2 += $d1;
|
||||||
|
if (!empty($d2)) {
|
||||||
|
$this->_data[self::$embeddedDataName] = $d2;
|
||||||
|
}
|
||||||
|
foreach ($params as $key => $line) {
|
||||||
|
list(, $param, $value) = preg_split('/\@|\s/', $line, 3)
|
||||||
|
+ array('', '', '');
|
||||||
|
list($value, $embedded) = $this->parseEmbeddedData($value);
|
||||||
|
$value = array_filter(preg_split('/\s+/msu', $value));
|
||||||
|
$this->parseParam($param, $value, $embedded);
|
||||||
|
}
|
||||||
|
return $this->_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse parameters that begin with (at)
|
||||||
|
*
|
||||||
|
* @param $param
|
||||||
|
* @param array $value
|
||||||
|
* @param array $embedded
|
||||||
|
*/
|
||||||
|
private function parseParam($param, array $value, array $embedded)
|
||||||
|
{
|
||||||
|
$data = & $this->_data;
|
||||||
|
$allowMultiple = false;
|
||||||
|
switch ($param) {
|
||||||
|
case 'param' :
|
||||||
|
$value = $this->formatParam($value);
|
||||||
|
$allowMultiple = true;
|
||||||
|
break;
|
||||||
|
case 'var' :
|
||||||
|
$value = $this->formatVar($value);
|
||||||
|
break;
|
||||||
|
case 'return' :
|
||||||
|
$value = $this->formatReturn($value);
|
||||||
|
break;
|
||||||
|
case 'class' :
|
||||||
|
$data = & $data[$param];
|
||||||
|
list ($param, $value) = $this->formatClass($value);
|
||||||
|
break;
|
||||||
|
case 'access' :
|
||||||
|
$value = reset($value);
|
||||||
|
break;
|
||||||
|
case 'expires' :
|
||||||
|
case 'status' :
|
||||||
|
$value = intval(reset($value));
|
||||||
|
break;
|
||||||
|
case 'throws' :
|
||||||
|
$value = $this->formatThrows($value);
|
||||||
|
$allowMultiple = true;
|
||||||
|
break;
|
||||||
|
case 'author':
|
||||||
|
$value = $this->formatAuthor($value);
|
||||||
|
$allowMultiple = true;
|
||||||
|
break;
|
||||||
|
case 'header' :
|
||||||
|
case 'link':
|
||||||
|
case 'example':
|
||||||
|
case 'todo':
|
||||||
|
$allowMultiple = true;
|
||||||
|
//don't break, continue with code for default:
|
||||||
|
default :
|
||||||
|
$value = implode(' ', $value);
|
||||||
|
}
|
||||||
|
if (!empty($embedded)) {
|
||||||
|
if (is_string($value)) {
|
||||||
|
$value = array('description' => $value);
|
||||||
|
}
|
||||||
|
$value[self::$embeddedDataName] = $embedded;
|
||||||
|
}
|
||||||
|
if (empty ($data[$param])) {
|
||||||
|
if ($allowMultiple) {
|
||||||
|
$data[$param] = array(
|
||||||
|
$value
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$data[$param] = $value;
|
||||||
|
}
|
||||||
|
} elseif ($allowMultiple) {
|
||||||
|
$data[$param][] = $value;
|
||||||
|
} elseif ($param == 'param') {
|
||||||
|
$arr = array(
|
||||||
|
$data[$param],
|
||||||
|
$value
|
||||||
|
);
|
||||||
|
$data[$param] = $arr;
|
||||||
|
} else {
|
||||||
|
if (!is_string($value) && isset($value[self::$embeddedDataName])
|
||||||
|
&& isset($data[$param][self::$embeddedDataName])
|
||||||
|
) {
|
||||||
|
$value[self::$embeddedDataName]
|
||||||
|
+= $data[$param][self::$embeddedDataName];
|
||||||
|
}
|
||||||
|
$data[$param] = $value + $data[$param];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the inline php doc comments and embedded data.
|
||||||
|
*
|
||||||
|
* @param $subject
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function parseEmbeddedData($subject)
|
||||||
|
{
|
||||||
|
$data = array();
|
||||||
|
|
||||||
|
//parse {@pattern } tags specially
|
||||||
|
while (preg_match('|(?s-m)({@pattern (/.+/[imsxuADSUXJ]*)})|', $subject, $matches)) {
|
||||||
|
$subject = str_replace($matches[0], '', $subject);
|
||||||
|
$data['pattern'] = $matches[2];
|
||||||
|
}
|
||||||
|
while (preg_match('/{@(\w+)\s?([^}]*)}/ms', $subject, $matches)) {
|
||||||
|
$subject = str_replace($matches[0], '', $subject);
|
||||||
|
if ($matches[2] == 'true' || $matches[2] == 'false') {
|
||||||
|
$matches[2] = $matches[2] == 'true';
|
||||||
|
} elseif ($matches[2] == '') {
|
||||||
|
$matches[2] = true;
|
||||||
|
}
|
||||||
|
if ($matches[1] == 'pattern') {
|
||||||
|
throw new Exception('Inline pattern tag should follow {@pattern /REGEX_PATTERN_HERE/} format and can optionally include PCRE modifiers following the ending `/`');
|
||||||
|
} elseif (false !== strpos($matches[2], static::$arrayDelimiter)) {
|
||||||
|
$matches[2] = explode(static::$arrayDelimiter, $matches[2]);
|
||||||
|
}
|
||||||
|
$data[$matches[1]] = $matches[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
while (preg_match(self::$embeddedDataPattern, $subject, $matches)) {
|
||||||
|
$subject = str_replace($matches[0], '', $subject);
|
||||||
|
$str = $matches[self::$embeddedDataIndex];
|
||||||
|
if (isset ($this->restler)
|
||||||
|
&& self::$embeddedDataIndex > 1
|
||||||
|
&& !empty ($matches[1])
|
||||||
|
) {
|
||||||
|
$extension = $matches[1];
|
||||||
|
$formatMap = $this->restler->getFormatMap();
|
||||||
|
if (isset ($formatMap[$extension])) {
|
||||||
|
/**
|
||||||
|
* @var \Luracast\Restler\Format\iFormat
|
||||||
|
*/
|
||||||
|
$format = $formatMap[$extension];
|
||||||
|
$format = new $format();
|
||||||
|
$data = $format->decode($str);
|
||||||
|
}
|
||||||
|
} else { // auto detect
|
||||||
|
if ($str{0} == '{') {
|
||||||
|
$d = json_decode($str, true);
|
||||||
|
if (json_last_error() != JSON_ERROR_NONE) {
|
||||||
|
throw new Exception('Error parsing embedded JSON data'
|
||||||
|
. " $str");
|
||||||
|
}
|
||||||
|
$data = $d + $data;
|
||||||
|
} else {
|
||||||
|
parse_str($str, $d);
|
||||||
|
//clean up
|
||||||
|
$d = array_filter($d);
|
||||||
|
foreach ($d as $key => $val) {
|
||||||
|
$kt = trim($key);
|
||||||
|
if ($kt != $key) {
|
||||||
|
unset($d[$key]);
|
||||||
|
$key = $kt;
|
||||||
|
$d[$key] = $val;
|
||||||
|
}
|
||||||
|
if (is_string($val)) {
|
||||||
|
if ($val == 'true' || $val == 'false') {
|
||||||
|
$d[$key] = $val == 'true' ? true : false;
|
||||||
|
} else {
|
||||||
|
$val = explode(self::$arrayDelimiter, $val);
|
||||||
|
if (count($val) > 1) {
|
||||||
|
$d[$key] = $val;
|
||||||
|
} else {
|
||||||
|
$d[$key] =
|
||||||
|
preg_replace('/\s+/msu', ' ',
|
||||||
|
$d[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$data = $d + $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return array($subject, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function formatThrows(array $value)
|
||||||
|
{
|
||||||
|
$r = array();
|
||||||
|
$r['code'] = count($value) && is_numeric($value[0])
|
||||||
|
? intval(array_shift($value)) : 500;
|
||||||
|
$reason = implode(' ', $value);
|
||||||
|
$r['reason'] = empty($reason) ? '' : $reason;
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function formatClass(array $value)
|
||||||
|
{
|
||||||
|
$param = array_shift($value);
|
||||||
|
|
||||||
|
if (empty($param)) {
|
||||||
|
$param = 'Unknown';
|
||||||
|
}
|
||||||
|
$value = implode(' ', $value);
|
||||||
|
return array(
|
||||||
|
ltrim($param, '\\'),
|
||||||
|
array('description' => $value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function formatAuthor(array $value)
|
||||||
|
{
|
||||||
|
$r = array();
|
||||||
|
$email = end($value);
|
||||||
|
if ($email{0} == '<') {
|
||||||
|
$email = substr($email, 1, -1);
|
||||||
|
array_pop($value);
|
||||||
|
$r['email'] = $email;
|
||||||
|
}
|
||||||
|
$r['name'] = implode(' ', $value);
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function formatReturn(array $value)
|
||||||
|
{
|
||||||
|
$data = explode('|', array_shift($value));
|
||||||
|
$r = array(
|
||||||
|
'type' => count($data) == 1 ? $data[0] : $data
|
||||||
|
);
|
||||||
|
$r['description'] = implode(' ', $value);
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function formatParam(array $value)
|
||||||
|
{
|
||||||
|
$r = array();
|
||||||
|
$data = array_shift($value);
|
||||||
|
if (empty($data)) {
|
||||||
|
$r['type'] = 'mixed';
|
||||||
|
} elseif ($data{0} == '$') {
|
||||||
|
$r['name'] = substr($data, 1);
|
||||||
|
$r['type'] = 'mixed';
|
||||||
|
} else {
|
||||||
|
$data = explode('|', $data);
|
||||||
|
$r['type'] = count($data) == 1 ? $data[0] : $data;
|
||||||
|
|
||||||
|
$data = array_shift($value);
|
||||||
|
if (!empty($data) && $data{0} == '$') {
|
||||||
|
$r['name'] = substr($data, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($value) {
|
||||||
|
$r['description'] = implode(' ', $value);
|
||||||
|
}
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function formatVar(array $value)
|
||||||
|
{
|
||||||
|
$r = array();
|
||||||
|
$data = array_shift($value);
|
||||||
|
if (empty($data)) {
|
||||||
|
$r['type'] = 'mixed';
|
||||||
|
} elseif ($data{0} == '$') {
|
||||||
|
$r['name'] = substr($data, 1);
|
||||||
|
$r['type'] = 'mixed';
|
||||||
|
} else {
|
||||||
|
$data = explode('|', $data);
|
||||||
|
$r['type'] = count($data) == 1 ? $data[0] : $data;
|
||||||
|
}
|
||||||
|
if ($value) {
|
||||||
|
$r['description'] = implode(' ', $value);
|
||||||
|
}
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
}
|
||||||
71
htdocs/includes/restler/Compose.php
Normal file
71
htdocs/includes/restler/Compose.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Composer to provide standard structure for all HTTP responses
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage result
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
*/
|
||||||
|
class Compose implements iCompose
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var bool When restler is not running in production mode, this value will
|
||||||
|
* be checked to include the debug information on error response
|
||||||
|
*/
|
||||||
|
public static $includeDebugInfo = true;
|
||||||
|
/**
|
||||||
|
* Current Restler instance
|
||||||
|
* Injected at runtime
|
||||||
|
*
|
||||||
|
* @var Restler
|
||||||
|
*/
|
||||||
|
public $restler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of an api call is passed to this method
|
||||||
|
* to create a standard structure for the data
|
||||||
|
*
|
||||||
|
* @param mixed $result can be a primitive or array or object
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function response($result)
|
||||||
|
{
|
||||||
|
//TODO: check Defaults::language and change result accordingly
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the api call results in RestException this method
|
||||||
|
* will be called to return the error message
|
||||||
|
*
|
||||||
|
* @param RestException $exception exception that has reasons for failure
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function message(RestException $exception)
|
||||||
|
{
|
||||||
|
//TODO: check Defaults::language and change result accordingly
|
||||||
|
$r = array(
|
||||||
|
'error' => array(
|
||||||
|
'code' => $exception->getCode(),
|
||||||
|
'message' => $exception->getErrorMessage(),
|
||||||
|
) + $exception->getDetails()
|
||||||
|
);
|
||||||
|
if (!Scope::get('Restler')->getProductionMode() && self::$includeDebugInfo) {
|
||||||
|
$r += array(
|
||||||
|
'debug' => array(
|
||||||
|
'source' => $exception->getSource(),
|
||||||
|
'stages' => $exception->getStages(),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
}
|
||||||
55
htdocs/includes/restler/Data/ApiMethodInfo.php
Normal file
55
htdocs/includes/restler/Data/ApiMethodInfo.php
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ValueObject for api method info. All needed information about a api method
|
||||||
|
* is stored here
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class ApiMethodInfo extends ValueObject
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string target url
|
||||||
|
*/
|
||||||
|
public $url;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $className;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $methodName;
|
||||||
|
/**
|
||||||
|
* @var array parameters to be passed to the api method
|
||||||
|
*/
|
||||||
|
public $parameters = array();
|
||||||
|
/**
|
||||||
|
* @var array information on parameters in the form of array(name => index)
|
||||||
|
*/
|
||||||
|
public $arguments = array();
|
||||||
|
/**
|
||||||
|
* @var array default values for parameters if any
|
||||||
|
* in the form of array(index => value)
|
||||||
|
*/
|
||||||
|
public $defaults = array();
|
||||||
|
/**
|
||||||
|
* @var array key => value pair of method meta information
|
||||||
|
*/
|
||||||
|
public $metadata = array();
|
||||||
|
/**
|
||||||
|
* @var int access level
|
||||||
|
* 0 - @public - available for all
|
||||||
|
* 1 - @hybrid - both public and protected (enhanced info for authorized)
|
||||||
|
* 2 - @protected comment - only for authenticated users
|
||||||
|
* 3 - protected method - only for authenticated users
|
||||||
|
*/
|
||||||
|
public $accessLevel = 0;
|
||||||
|
}
|
||||||
33
htdocs/includes/restler/Data/Arr.php
Normal file
33
htdocs/includes/restler/Data/Arr.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience class for Array manipulation
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
*/
|
||||||
|
class Arr
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Deep copy given array
|
||||||
|
*
|
||||||
|
* @param array $arr
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function copy(array $arr)
|
||||||
|
{
|
||||||
|
$copy = array();
|
||||||
|
foreach ($arr as $key => $value) {
|
||||||
|
if (is_array($value)) $copy[$key] = static::copy($value);
|
||||||
|
else if (is_object($value)) $copy[$key] = clone $value;
|
||||||
|
else $copy[$key] = $value;
|
||||||
|
}
|
||||||
|
return $copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
htdocs/includes/restler/Data/Invalid.php
Normal file
20
htdocs/includes/restler/Data/Invalid.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Data;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalid Exception
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class Invalid extends Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
157
htdocs/includes/restler/Data/Object.php
Normal file
157
htdocs/includes/restler/Data/Object.php
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience class that converts the given object
|
||||||
|
* in to associative array
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class Object
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var bool|string|callable
|
||||||
|
*/
|
||||||
|
public static $stringEncoderFunction = false;
|
||||||
|
/**
|
||||||
|
* @var bool|string|callable
|
||||||
|
*/
|
||||||
|
public static $numberEncoderFunction = false;
|
||||||
|
/**
|
||||||
|
* @var array key value pairs for fixing value types using functions.
|
||||||
|
* For example
|
||||||
|
*
|
||||||
|
* 'id'=>'intval' will make sure all values of the id properties
|
||||||
|
* will be converted to integers intval function
|
||||||
|
* 'password'=> null will remove all the password entries
|
||||||
|
*/
|
||||||
|
public static $fix = array();
|
||||||
|
/**
|
||||||
|
* @var string character that is used to identify sub objects
|
||||||
|
*
|
||||||
|
* For example
|
||||||
|
*
|
||||||
|
* when Object::$separatorChar = '.';
|
||||||
|
*
|
||||||
|
* array('my.object'=>true) will result in
|
||||||
|
*
|
||||||
|
* array(
|
||||||
|
* 'my'=>array('object'=>true)
|
||||||
|
* );
|
||||||
|
*/
|
||||||
|
public static $separatorChar = null;
|
||||||
|
/**
|
||||||
|
* @var bool set it to true when empty arrays, blank strings, null values
|
||||||
|
* to be automatically removed from response
|
||||||
|
*/
|
||||||
|
public static $removeEmpty = false;
|
||||||
|
/**
|
||||||
|
* @var bool set it to true to remove all null values from the result
|
||||||
|
*/
|
||||||
|
public static $removeNull = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function that converts the given object
|
||||||
|
* in to associative array
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*
|
||||||
|
* @param mixed $object that needs to be converted
|
||||||
|
*
|
||||||
|
* @param bool $forceObjectTypeWhenEmpty when set to true outputs
|
||||||
|
* actual type (array or
|
||||||
|
* object) rather than
|
||||||
|
* always an array when the
|
||||||
|
* array/object is empty
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function toArray($object,
|
||||||
|
$forceObjectTypeWhenEmpty = false)
|
||||||
|
{
|
||||||
|
//if ($object instanceof JsonSerializable) { //wont work on PHP < 5.4
|
||||||
|
if (is_object($object)) {
|
||||||
|
if (method_exists($object, 'jsonSerialize')) {
|
||||||
|
$object = $object->jsonSerialize();
|
||||||
|
} elseif (method_exists($object, '__sleep')) {
|
||||||
|
$properties = $object->__sleep();
|
||||||
|
$array = array();
|
||||||
|
foreach ($properties as $key) {
|
||||||
|
$value = self::toArray($object->{$key},
|
||||||
|
$forceObjectTypeWhenEmpty);
|
||||||
|
if (self::$stringEncoderFunction && is_string($value)) {
|
||||||
|
$value = self::$stringEncoderFunction ($value);
|
||||||
|
} elseif (self::$numberEncoderFunction && is_numeric($value)) {
|
||||||
|
$value = self::$numberEncoderFunction ($value);
|
||||||
|
}
|
||||||
|
$array [$key] = $value;
|
||||||
|
}
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_array($object) || is_object($object)) {
|
||||||
|
$count = 0;
|
||||||
|
$array = array();
|
||||||
|
foreach ($object as $key => $value) {
|
||||||
|
if (
|
||||||
|
is_string(self::$separatorChar) &&
|
||||||
|
false !== strpos($key, self::$separatorChar)
|
||||||
|
) {
|
||||||
|
list($key, $obj) = explode(self::$separatorChar, $key, 2);
|
||||||
|
$object[$key][$obj] = $value;
|
||||||
|
$value = $object[$key];
|
||||||
|
}
|
||||||
|
if (self::$removeEmpty && empty($value) && !is_numeric($value) && !is_bool($value)) {
|
||||||
|
continue;
|
||||||
|
} elseif (self::$removeNull && is_null($value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (array_key_exists($key, self::$fix)) {
|
||||||
|
if (isset(self::$fix[$key])) {
|
||||||
|
$value = call_user_func(self::$fix[$key], $value);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$value = self::toArray($value, $forceObjectTypeWhenEmpty);
|
||||||
|
if (self::$stringEncoderFunction && is_string($value)) {
|
||||||
|
$value = self::$encoderFunctionName ($value);
|
||||||
|
} elseif (self::$numberEncoderFunction && is_numeric($value)) {
|
||||||
|
$value = self::$numberEncoderFunction ($value);
|
||||||
|
}
|
||||||
|
$array [$key] = $value;
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
return $forceObjectTypeWhenEmpty && $count == 0 ? $object : $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($name)
|
||||||
|
{
|
||||||
|
isset(self::$fix[$name]) ? self::$fix[$name] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($name, $function)
|
||||||
|
{
|
||||||
|
self::$fix[$name] = $function;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __isset($name)
|
||||||
|
{
|
||||||
|
return isset(self::$fix[$name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __unset($name)
|
||||||
|
{
|
||||||
|
unset(self::$fix[$name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
84
htdocs/includes/restler/Data/String.php
Normal file
84
htdocs/includes/restler/Data/String.php
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience class for String manipulation
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
*/
|
||||||
|
class String
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Given haystack contains the needle or not?
|
||||||
|
*
|
||||||
|
* @param string $haystack
|
||||||
|
* @param string $needle
|
||||||
|
* @param bool $caseSensitive
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function contains($haystack, $needle, $caseSensitive = true)
|
||||||
|
{
|
||||||
|
if (empty($needle))
|
||||||
|
return true;
|
||||||
|
return $caseSensitive
|
||||||
|
? strpos($haystack, $needle) !== false
|
||||||
|
: stripos($haystack, $needle) !== false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given haystack begins with the needle or not?
|
||||||
|
*
|
||||||
|
* @param string $haystack
|
||||||
|
* @param string $needle
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function beginsWith($haystack, $needle)
|
||||||
|
{
|
||||||
|
$length = strlen($needle);
|
||||||
|
return (substr($haystack, 0, $length) === $needle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given haystack ends with the needle or not?
|
||||||
|
*
|
||||||
|
* @param string $haystack
|
||||||
|
* @param string $needle
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function endsWith($haystack, $needle)
|
||||||
|
{
|
||||||
|
$length = strlen($needle);
|
||||||
|
if ($length == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return (substr($haystack, -$length) === $needle);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert camelCased or underscored string in to a title
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function title($name)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
ucwords(
|
||||||
|
preg_replace(
|
||||||
|
array('/(?<=[^A-Z])([A-Z])/', '/(?<=[^0-9])([0-9])/', '/(_)/'),
|
||||||
|
array(' $0', ' $0', ' '),
|
||||||
|
$name
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
273
htdocs/includes/restler/Data/ValidationInfo.php
Normal file
273
htdocs/includes/restler/Data/ValidationInfo.php
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Data;
|
||||||
|
|
||||||
|
use Luracast\Restler\CommentParser;
|
||||||
|
use Luracast\Restler\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ValueObject for validation information. An instance is created and
|
||||||
|
* populated by Restler to pass it to iValidate implementing classes for
|
||||||
|
* validation
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class ValidationInfo implements iValueObject
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var mixed given value for the parameter
|
||||||
|
*/
|
||||||
|
public $value;
|
||||||
|
/**
|
||||||
|
* @var string proper name for given parameter
|
||||||
|
*/
|
||||||
|
public $label;
|
||||||
|
/**
|
||||||
|
* @var string html element that can be used to represent the parameter for
|
||||||
|
* input
|
||||||
|
*/
|
||||||
|
public $field;
|
||||||
|
/**
|
||||||
|
* @var mixed default value for the parameter
|
||||||
|
*/
|
||||||
|
public $default;
|
||||||
|
/**
|
||||||
|
* Name of the variable being validated
|
||||||
|
*
|
||||||
|
* @var string variable name
|
||||||
|
*/
|
||||||
|
public $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool is it required or not
|
||||||
|
*/
|
||||||
|
public $required;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string body or header or query where this parameter is coming from
|
||||||
|
* in the http request
|
||||||
|
*/
|
||||||
|
public $from;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data type of the variable being validated.
|
||||||
|
* It will be mostly string
|
||||||
|
*
|
||||||
|
* @var string|array multiple types are specified it will be of
|
||||||
|
* type array otherwise it will be a string
|
||||||
|
*/
|
||||||
|
public $type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the type is array, this field is used to define the type of the
|
||||||
|
* contents of the array
|
||||||
|
*
|
||||||
|
* @var string|null when all the items in an array are of certain type, we
|
||||||
|
* can set this property. It will be null if the items can be of any type
|
||||||
|
*/
|
||||||
|
public $contentType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should we attempt to fix the value?
|
||||||
|
* When set to false validation class should throw
|
||||||
|
* an exception or return false for the validate call.
|
||||||
|
* When set to true it will attempt to fix the value if possible
|
||||||
|
* or throw an exception or return false when it cant be fixed.
|
||||||
|
*
|
||||||
|
* @var boolean true or false
|
||||||
|
*/
|
||||||
|
public $fix = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array of children to be validated
|
||||||
|
*/
|
||||||
|
public $children = null;
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// VALUE RANGE
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Given value should match one of the values in the array
|
||||||
|
*
|
||||||
|
* @var array of choices to match to
|
||||||
|
*/
|
||||||
|
public $choice;
|
||||||
|
/**
|
||||||
|
* If the type is string it will set the lower limit for length
|
||||||
|
* else will specify the lower limit for the value
|
||||||
|
*
|
||||||
|
* @var number minimum value
|
||||||
|
*/
|
||||||
|
public $min;
|
||||||
|
/**
|
||||||
|
* If the type is string it will set the upper limit limit for length
|
||||||
|
* else will specify the upper limit for the value
|
||||||
|
*
|
||||||
|
* @var number maximum value
|
||||||
|
*/
|
||||||
|
public $max;
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// REGEX VALIDATION
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* RegEx pattern to match the value
|
||||||
|
*
|
||||||
|
* @var string regular expression
|
||||||
|
*/
|
||||||
|
public $pattern;
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// CUSTOM VALIDATION
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Rules specified for the parameter in the php doc comment.
|
||||||
|
* It is passed to the validation method as the second parameter
|
||||||
|
*
|
||||||
|
* @var array custom rule set
|
||||||
|
*/
|
||||||
|
public $rules;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifying a custom error message will override the standard error
|
||||||
|
* message return by the validator class
|
||||||
|
*
|
||||||
|
* @var string custom error response
|
||||||
|
*/
|
||||||
|
public $message;
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// METHODS
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the method to be used for validation.
|
||||||
|
* It will be receiving two parameters $input, $rules (array)
|
||||||
|
*
|
||||||
|
* @var string validation method name
|
||||||
|
*/
|
||||||
|
public $method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of the API class currently being called. It will be null most of
|
||||||
|
* the time. Only when method is defined it will contain an instance.
|
||||||
|
* This behavior is for lazy loading of the API class
|
||||||
|
*
|
||||||
|
* @var null|object will be null or api class instance
|
||||||
|
*/
|
||||||
|
public $apiClassInstance = null;
|
||||||
|
|
||||||
|
public static function numericValue($value)
|
||||||
|
{
|
||||||
|
return ( int )$value == $value
|
||||||
|
? ( int )$value
|
||||||
|
: floatval($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function arrayValue($value)
|
||||||
|
{
|
||||||
|
return is_array($value) ? $value : array(
|
||||||
|
$value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function stringValue($value, $glue = ',')
|
||||||
|
{
|
||||||
|
return is_array($value)
|
||||||
|
? implode($glue, $value)
|
||||||
|
: ( string )$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function booleanValue($value)
|
||||||
|
{
|
||||||
|
return is_bool($value)
|
||||||
|
? $value
|
||||||
|
: $value !== 'false';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function filterArray(array $data, $keepNumericKeys)
|
||||||
|
{
|
||||||
|
$r = array();
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
if (is_numeric($key)) {
|
||||||
|
if ($keepNumericKeys) {
|
||||||
|
$r[$key] = $value;
|
||||||
|
}
|
||||||
|
} elseif (!$keepNumericKeys) {
|
||||||
|
$r[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return ' new ValidationInfo() ';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getProperty(array &$from, $property)
|
||||||
|
{
|
||||||
|
$p = Util::nestedValue($from, $property);
|
||||||
|
unset($from[$property]);
|
||||||
|
$p2 = Util::nestedValue(
|
||||||
|
$from, CommentParser::$embeddedDataName, $property
|
||||||
|
);
|
||||||
|
unset($from[CommentParser::$embeddedDataName][$property]);
|
||||||
|
|
||||||
|
if ($property == 'type' && $p == 'array' && $p2) {
|
||||||
|
$this->contentType = $p2;
|
||||||
|
return $p;
|
||||||
|
}
|
||||||
|
$r = is_null($p2) ? (is_null($p) ? null : $p) : $p2;
|
||||||
|
if (!is_null($r)) {
|
||||||
|
if ($property == 'min' || $property == 'max') {
|
||||||
|
return static::numericValue($r);
|
||||||
|
} elseif ($property == 'required' || $property == 'fix') {
|
||||||
|
return static::booleanValue($r);
|
||||||
|
} elseif ($property == 'choice') {
|
||||||
|
return static::arrayValue($r);
|
||||||
|
} elseif ($property == 'pattern') {
|
||||||
|
return static::stringValue($r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct(array $info)
|
||||||
|
{
|
||||||
|
$properties = get_object_vars($this);
|
||||||
|
unset($properties['contentType']);
|
||||||
|
foreach ($properties as $property => $value) {
|
||||||
|
$this->{$property} = $this->getProperty($info, $property);
|
||||||
|
}
|
||||||
|
$inner = Util::nestedValue($info, 'properties');
|
||||||
|
$this->rules = !empty($inner) ? $inner + $info : $info;
|
||||||
|
unset($this->rules['properties']);
|
||||||
|
if (is_string($this->type) && $this->type == 'integer') {
|
||||||
|
$this->type = 'int';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magic Method used for creating instance at run time
|
||||||
|
*/
|
||||||
|
public static function __set_state(array $info)
|
||||||
|
{
|
||||||
|
$o = new self ($info);
|
||||||
|
return $o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
626
htdocs/includes/restler/Data/Validator.php
Normal file
626
htdocs/includes/restler/Data/Validator.php
Normal file
@@ -0,0 +1,626 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Data;
|
||||||
|
|
||||||
|
use Luracast\Restler\CommentParser;
|
||||||
|
use Luracast\Restler\Format\HtmlFormat;
|
||||||
|
use Luracast\Restler\RestException;
|
||||||
|
use Luracast\Restler\Scope;
|
||||||
|
use Luracast\Restler\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Validator class used by Restler. It can be replaced by any
|
||||||
|
* iValidate implementing class by setting Defaults::$validatorClass
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class Validator implements iValidate
|
||||||
|
{
|
||||||
|
public static $holdException = false;
|
||||||
|
public static $exceptions = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate alphabetic characters.
|
||||||
|
*
|
||||||
|
* Check that given value contains only alphabetic characters.
|
||||||
|
*
|
||||||
|
* @param $input
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @throws Invalid
|
||||||
|
*/
|
||||||
|
public static function alpha($input, ValidationInfo $info = null)
|
||||||
|
{
|
||||||
|
if (ctype_alpha($input)) {
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
if ($info && $info->fix) {
|
||||||
|
//remove non alpha characters
|
||||||
|
return preg_replace("/[^a-z]/i", "", $input);
|
||||||
|
}
|
||||||
|
throw new Invalid('Expecting only alphabetic characters.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate alpha numeric characters.
|
||||||
|
*
|
||||||
|
* Check that given value contains only alpha numeric characters.
|
||||||
|
*
|
||||||
|
* @param $input
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @throws Invalid
|
||||||
|
*/
|
||||||
|
public static function alphanumeric($input, ValidationInfo $info = null)
|
||||||
|
{
|
||||||
|
if (ctype_alnum($input)) {
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
if ($info && $info->fix) {
|
||||||
|
//remove non alpha numeric and space characters
|
||||||
|
return preg_replace("/[^a-z0-9 ]/i", "", $input);
|
||||||
|
}
|
||||||
|
throw new Invalid('Expecting only alpha numeric characters.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate printable characters.
|
||||||
|
*
|
||||||
|
* Check that given value contains only printable characters.
|
||||||
|
*
|
||||||
|
* @param $input
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @throws Invalid
|
||||||
|
*/
|
||||||
|
public static function printable($input, ValidationInfo $info = null)
|
||||||
|
{
|
||||||
|
if (ctype_print($input)) {
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
if ($info && $info->fix) {
|
||||||
|
//remove non printable characters
|
||||||
|
return preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $input);
|
||||||
|
}
|
||||||
|
throw new Invalid('Expecting only printable characters.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate hexadecimal digits.
|
||||||
|
*
|
||||||
|
* Check that given value contains only hexadecimal digits.
|
||||||
|
*
|
||||||
|
* @param $input
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @throws Invalid
|
||||||
|
*/
|
||||||
|
public static function hex($input, ValidationInfo $info = null)
|
||||||
|
{
|
||||||
|
if (ctype_xdigit($input)) {
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
throw new Invalid('Expecting only hexadecimal digits.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate Telephone number
|
||||||
|
*
|
||||||
|
* Check if the given value is numeric with or without a `+` prefix
|
||||||
|
*
|
||||||
|
* @param $input
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @throws Invalid
|
||||||
|
*/
|
||||||
|
public static function tel($input, ValidationInfo $info = null)
|
||||||
|
{
|
||||||
|
if (is_numeric($input) && '-' != substr($input, 0, 1)) {
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
throw new Invalid('Expecting phone number, a numeric value ' .
|
||||||
|
'with optional `+` prefix');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate Email
|
||||||
|
*
|
||||||
|
* Check if the given string is a valid email
|
||||||
|
*
|
||||||
|
* @param String $input
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws Invalid
|
||||||
|
*/
|
||||||
|
public static function email($input, ValidationInfo $info = null)
|
||||||
|
{
|
||||||
|
$r = filter_var($input, FILTER_VALIDATE_EMAIL);
|
||||||
|
if ($r) {
|
||||||
|
return $r;
|
||||||
|
} elseif ($info && $info->fix) {
|
||||||
|
$r = filter_var($input, FILTER_SANITIZE_EMAIL);
|
||||||
|
return static::email($r);
|
||||||
|
}
|
||||||
|
throw new Invalid('Expecting email in `name@example.com` format');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate IP Address
|
||||||
|
*
|
||||||
|
* Check if the given string is a valid ip address
|
||||||
|
*
|
||||||
|
* @param String $input
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws Invalid
|
||||||
|
*/
|
||||||
|
public static function ip($input, ValidationInfo $info = null)
|
||||||
|
{
|
||||||
|
$r = filter_var($input, FILTER_VALIDATE_IP);
|
||||||
|
if ($r)
|
||||||
|
return $r;
|
||||||
|
|
||||||
|
throw new Invalid('Expecting IP address in IPV6 or IPV4 format');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate Url
|
||||||
|
*
|
||||||
|
* Check if the given string is a valid url
|
||||||
|
*
|
||||||
|
* @param String $input
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws Invalid
|
||||||
|
*/
|
||||||
|
public static function url($input, ValidationInfo $info = null)
|
||||||
|
{
|
||||||
|
$r = filter_var($input, FILTER_VALIDATE_URL);
|
||||||
|
if ($r) {
|
||||||
|
return $r;
|
||||||
|
} elseif ($info && $info->fix) {
|
||||||
|
$r = filter_var($input, FILTER_SANITIZE_URL);
|
||||||
|
return static::url($r);
|
||||||
|
}
|
||||||
|
throw new Invalid('Expecting url in `http://example.com` format');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MySQL Date
|
||||||
|
*
|
||||||
|
* Check if the given string is a valid date in YYYY-MM-DD format
|
||||||
|
*
|
||||||
|
* @param String $input
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws Invalid
|
||||||
|
*/
|
||||||
|
public static function date($input, ValidationInfo $info = null)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
preg_match(
|
||||||
|
'#^(?P<year>\d{2}|\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})$#',
|
||||||
|
$input,
|
||||||
|
$date
|
||||||
|
)
|
||||||
|
&& checkdate($date['month'], $date['day'], $date['year'])
|
||||||
|
) {
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
throw new Invalid(
|
||||||
|
'Expecting date in `YYYY-MM-DD` format, such as `'
|
||||||
|
. date("Y-m-d") . '`'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MySQL DateTime
|
||||||
|
*
|
||||||
|
* Check if the given string is a valid date and time in YYY-MM-DD HH:MM:SS format
|
||||||
|
*
|
||||||
|
* @param String $input
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws Invalid
|
||||||
|
*/
|
||||||
|
public static function datetime($input, ValidationInfo $info = null)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
preg_match('/^(?P<year>19\d\d|20\d\d)\-(?P<month>0[1-9]|1[0-2])\-' .
|
||||||
|
'(?P<day>0\d|[1-2]\d|3[0-1]) (?P<h>0\d|1\d|2[0-3]' .
|
||||||
|
')\:(?P<i>[0-5][0-9])\:(?P<s>[0-5][0-9])$/',
|
||||||
|
$input, $date)
|
||||||
|
&& checkdate($date['month'], $date['day'], $date['year'])
|
||||||
|
) {
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
throw new Invalid(
|
||||||
|
'Expecting date and time in `YYYY-MM-DD HH:MM:SS` format, such as `'
|
||||||
|
. date("Y-m-d H:i:s") . '`'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias for Time
|
||||||
|
*
|
||||||
|
* Check if the given string is a valid time in HH:MM:SS format
|
||||||
|
*
|
||||||
|
* @param String $input
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws Invalid
|
||||||
|
*/
|
||||||
|
public static function time24($input, ValidationInfo $info = null)
|
||||||
|
{
|
||||||
|
return static::time($input, $info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time
|
||||||
|
*
|
||||||
|
* Check if the given string is a valid time in HH:MM:SS format
|
||||||
|
*
|
||||||
|
* @param String $input
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws Invalid
|
||||||
|
*/
|
||||||
|
public static function time($input, ValidationInfo $info = null)
|
||||||
|
{
|
||||||
|
if (preg_match('/^([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/', $input)) {
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
throw new Invalid(
|
||||||
|
'Expecting time in `HH:MM:SS` format, such as `'
|
||||||
|
. date("H:i:s") . '`'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time in 12 hour format
|
||||||
|
*
|
||||||
|
* Check if the given string is a valid time 12 hour format
|
||||||
|
*
|
||||||
|
* @param String $input
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws Invalid
|
||||||
|
*/
|
||||||
|
public static function time12($input, ValidationInfo $info = null)
|
||||||
|
{
|
||||||
|
if (preg_match(
|
||||||
|
'/^([1-9]|1[0-2]|0[1-9]){1}(:[0-5][0-9])?\s?([aApP][mM]{1})?$/',
|
||||||
|
$input)
|
||||||
|
) {
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
throw new Invalid(
|
||||||
|
'Expecting time in 12 hour format, such as `08:00AM` and `10:05:11`'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unix Timestamp
|
||||||
|
*
|
||||||
|
* Check if the given value is a valid timestamp
|
||||||
|
*
|
||||||
|
* @param String $input
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
* @throws Invalid
|
||||||
|
*/
|
||||||
|
public static function timestamp($input, ValidationInfo $info = null)
|
||||||
|
{
|
||||||
|
if ((string)(int)$input == $input
|
||||||
|
&& ($input <= PHP_INT_MAX)
|
||||||
|
&& ($input >= ~PHP_INT_MAX)
|
||||||
|
) {
|
||||||
|
return (int)$input;
|
||||||
|
}
|
||||||
|
throw new Invalid('Expecting unix timestamp, such as ' . time());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
61
htdocs/includes/restler/Data/ValueObject.php
Normal file
61
htdocs/includes/restler/Data/ValueObject.php
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ValueObject base class, you may use this class to create your
|
||||||
|
* iValueObjects quickly
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class ValueObject implements iValueObject
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return ' new ' . get_called_class() . '() ';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function __set_state(array $properties)
|
||||||
|
{
|
||||||
|
$class = get_called_class();
|
||||||
|
$instance = new $class ();
|
||||||
|
$vars = get_object_vars($instance);
|
||||||
|
foreach ($properties as $property => $value) {
|
||||||
|
if (property_exists($instance, $property)) {
|
||||||
|
// see if the property is accessible
|
||||||
|
if (array_key_exists($property, $vars)) {
|
||||||
|
$instance->{$property} = $value;
|
||||||
|
} else {
|
||||||
|
$method = 'set' . ucfirst($property);
|
||||||
|
if (method_exists($instance, $method)) {
|
||||||
|
call_user_func(array(
|
||||||
|
$instance,
|
||||||
|
$method
|
||||||
|
), $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toArray()
|
||||||
|
{
|
||||||
|
$r = get_object_vars($this);
|
||||||
|
$methods = get_class_methods($this);
|
||||||
|
foreach ($methods as $m) {
|
||||||
|
if (substr($m, 0, 3) == 'get') {
|
||||||
|
$r [lcfirst(substr($m, 3))] = @$this->{$m} ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
32
htdocs/includes/restler/Data/iValidate.php
Normal file
32
htdocs/includes/restler/Data/iValidate.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validation classes should implement this interface
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
interface iValidate {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* method used for validation.
|
||||||
|
*
|
||||||
|
* @param mixed $input
|
||||||
|
* data that needs to be validated
|
||||||
|
* @param ValidationInfo $info
|
||||||
|
* information to be used for validation
|
||||||
|
* @return boolean false in case of failure or fixed value in the expected
|
||||||
|
* type
|
||||||
|
* @throws \Luracast\Restler\RestException 400 with information about the
|
||||||
|
* failed
|
||||||
|
* validation
|
||||||
|
*/
|
||||||
|
public static function validate($input, ValidationInfo $info);
|
||||||
|
}
|
||||||
|
|
||||||
39
htdocs/includes/restler/Data/iValueObject.php
Normal file
39
htdocs/includes/restler/Data/iValueObject.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restler is using many ValueObjects across to make it easy for the developers
|
||||||
|
* to use them with the help of code hinting etc.,
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
interface iValueObject
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This static method is called for creating an instance of the class by
|
||||||
|
* passing the initiation values as an array.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
* @abstract
|
||||||
|
*
|
||||||
|
* @param array $properties
|
||||||
|
*
|
||||||
|
* @return iValueObject
|
||||||
|
*/
|
||||||
|
public static function __set_state(array $properties);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method provides a string representation for the instance
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString();
|
||||||
|
}
|
||||||
|
|
||||||
360
htdocs/includes/restler/Defaults.php
Normal file
360
htdocs/includes/restler/Defaults.php
Normal file
@@ -0,0 +1,360 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
use Luracast\Restler\Data\ValidationInfo;
|
||||||
|
use Luracast\Restler\Data\Validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static class to hold all restler defaults, change the values to suit your
|
||||||
|
* needs in the gateway file (index.php), you may also allow the api users to
|
||||||
|
* change them per request by adding the properties to Defaults::$overridables
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class Defaults
|
||||||
|
{
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// Class Mappings
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string of name of the class that implements
|
||||||
|
* \Luracast\Restler\iCache the cache class to be used
|
||||||
|
*/
|
||||||
|
public static $cacheClass = 'Luracast\\Restler\\HumanReadableCache';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string full path of the directory where all the generated files will
|
||||||
|
* be kept. When set to null (default) it will use the cache folder that is
|
||||||
|
* in the same folder as index.php (gateway)
|
||||||
|
*/
|
||||||
|
public static $cacheDirectory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string of name of the class that implements
|
||||||
|
* \Luracast\Restler\Data\iValidate the validator class to be used
|
||||||
|
*/
|
||||||
|
public static $validatorClass = 'Luracast\\Restler\\Data\\Validator';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string name of the class that implements \Luracast\Restler\iCompose
|
||||||
|
* the class to be used to compose the response
|
||||||
|
*/
|
||||||
|
public static $composeClass = 'Luracast\\Restler\\Compose';
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// Routing
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool should auto routing for public and protected api methods
|
||||||
|
* should be enabled by default or not. Set this to false to get
|
||||||
|
* Restler 1.0 style behavior
|
||||||
|
*/
|
||||||
|
public static $autoRoutingEnabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean avoids creating multiple routes that can increase the
|
||||||
|
* ambiguity when set to true. when a method parameter is optional it is
|
||||||
|
* not mapped to the url and should only be used in request body or as
|
||||||
|
* query string `/resource?id=value`. When a parameter is required and is
|
||||||
|
* scalar, it will be mapped as part of the url `/resource/{id}`
|
||||||
|
*/
|
||||||
|
public static $smartAutoRouting = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean enables more ways of finding the parameter data in the request.
|
||||||
|
* If you need backward compatibility with Restler 2 or below turn this off
|
||||||
|
*/
|
||||||
|
public static $smartParameterParsing = true;
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// API Version Management
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var null|string name that is used for vendor specific media type and
|
||||||
|
* api version using the Accept Header for example
|
||||||
|
* application/vnd.{vendor}-v1+json
|
||||||
|
*
|
||||||
|
* Keep this null if you do not want to use vendor MIME for specifying api version
|
||||||
|
*/
|
||||||
|
public static $apiVendor = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool set it to true to force vendor specific MIME for versioning.
|
||||||
|
* It will be automatically set to true when Defaults::$vendor is not
|
||||||
|
* null and client is requesting for the custom MIME type
|
||||||
|
*/
|
||||||
|
public static $useVendorMIMEVersioning = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool set it to true to use enableUrl based versioning
|
||||||
|
*/
|
||||||
|
public static $useUrlBasedVersioning = false;
|
||||||
|
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// Request
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string name to be used for the method parameter to capture the
|
||||||
|
* entire request data
|
||||||
|
*/
|
||||||
|
public static $fullRequestDataName = 'request_data';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string name of the property that can sent through $_GET or $_POST to
|
||||||
|
* override the http method of the request. Set it to null or
|
||||||
|
* blank string to disable http method override through request
|
||||||
|
* parameters.
|
||||||
|
*/
|
||||||
|
public static $httpMethodOverrideProperty = 'http_method';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool should auto validating api parameters should be enabled by
|
||||||
|
* default or not. Set this to false to avoid validation.
|
||||||
|
*/
|
||||||
|
public static $autoValidationEnabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string name of the class that implements iUser interface to identify
|
||||||
|
* the user for caching purposes
|
||||||
|
*/
|
||||||
|
public static $userIdentifierClass = 'Luracast\\Restler\\User';
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// Response
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool HTTP status codes are set on all responses by default.
|
||||||
|
* Some clients (like flash, mobile) have trouble dealing with non-200
|
||||||
|
* status codes on error responses.
|
||||||
|
*
|
||||||
|
* You can set it to true to force a HTTP 200 status code on all responses,
|
||||||
|
* even when errors occur. If you suppress status codes, look for an error
|
||||||
|
* response to determine if an error occurred.
|
||||||
|
*/
|
||||||
|
public static $suppressResponseCode = false;
|
||||||
|
|
||||||
|
public static $supportedCharsets = array('utf-8', 'iso-8859-1');
|
||||||
|
public static $supportedLanguages = array('en', 'en-US');
|
||||||
|
|
||||||
|
public static $charset = 'utf-8';
|
||||||
|
public static $language = 'en';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool when set to true, it will exclude the response body
|
||||||
|
*/
|
||||||
|
public static $emptyBodyForNullResponse = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool enables CORS support
|
||||||
|
*/
|
||||||
|
public static $crossOriginResourceSharing = false;
|
||||||
|
public static $accessControlAllowOrigin = '*';
|
||||||
|
public static $accessControlAllowMethods =
|
||||||
|
'GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD';
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// Header
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array default Cache-Control template that used to set the
|
||||||
|
* Cache-Control header and has two values, first one is used when
|
||||||
|
* Defaults::$headerExpires is 0 and second one when it has some time
|
||||||
|
* value specified. When only one value is specified it will be used for
|
||||||
|
* both cases
|
||||||
|
*/
|
||||||
|
public static $headerCacheControl = array(
|
||||||
|
'no-cache, must-revalidate',
|
||||||
|
|
||||||
|
/* "public, " or "private, " will be prepended based on api method
|
||||||
|
* called (public or protected)
|
||||||
|
*/
|
||||||
|
'max-age={expires}, must-revalidate',
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int sets the content to expire immediately when set to zero
|
||||||
|
* alternatively you can specify the number of seconds the content will
|
||||||
|
* expire. This setting can be altered at api level using php doc comment
|
||||||
|
* with @expires numOfSeconds
|
||||||
|
*/
|
||||||
|
public static $headerExpires = 0;
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// Access Control
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var null|callable if the api methods are under access control mechanism
|
||||||
|
* you can attach a function here that returns true or false to determine
|
||||||
|
* visibility of a protected api method. this function will receive method
|
||||||
|
* info as the only parameter.
|
||||||
|
*/
|
||||||
|
public static $accessControlFunction = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int set the default api access mode
|
||||||
|
* value of 0 = public api
|
||||||
|
* value of 1 = hybrid api using `@access hybrid` comment
|
||||||
|
* value of 2 = protected api using `@access protected` comment
|
||||||
|
* value of 3 = protected api using `protected function` method
|
||||||
|
*/
|
||||||
|
public static $apiAccessLevel = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string authentication method to be called in iAuthenticate
|
||||||
|
* Interface
|
||||||
|
*/
|
||||||
|
public static $authenticationMethod = '__isAllowed';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int time in milliseconds for bandwidth throttling,
|
||||||
|
* which is the minimum response time for each api request. You can
|
||||||
|
* change it per api method by setting `@throttle 3000` in php doc
|
||||||
|
* comment either at the method level or class level
|
||||||
|
*/
|
||||||
|
public static $throttle = 0;
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// Overrides for API User
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array use 'alternativeName'=> 'actualName' to set alternative
|
||||||
|
* names that can be used to represent the api method parameters and/or
|
||||||
|
* static properties of Defaults
|
||||||
|
*/
|
||||||
|
public static $aliases = array(
|
||||||
|
/**
|
||||||
|
* suppress_response_codes=true as an URL parameter to force
|
||||||
|
* a HTTP 200 status code on all responses
|
||||||
|
*/
|
||||||
|
'suppress_response_codes' => 'suppressResponseCode',
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array determines the defaults that can be overridden by the api
|
||||||
|
* user by passing them as URL parameters
|
||||||
|
*/
|
||||||
|
public static $overridables = array(
|
||||||
|
'suppressResponseCode',
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array contains validation details for defaults to be used when
|
||||||
|
* set through URL parameters
|
||||||
|
*/
|
||||||
|
public static $validation = array(
|
||||||
|
'suppressResponseCode' => array('type' => 'bool'),
|
||||||
|
'headerExpires' => array('type' => 'int', 'min' => 0),
|
||||||
|
);
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// Overrides API Developer
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array determines what are the phpdoc comment tags that will
|
||||||
|
* override the Defaults here with their values
|
||||||
|
*/
|
||||||
|
public static $fromComments = array(
|
||||||
|
|
||||||
|
/**
|
||||||
|
* use PHPDoc comments such as the following
|
||||||
|
* `
|
||||||
|
*
|
||||||
|
* @cache no-cache, must-revalidate` to set the Cache-Control header
|
||||||
|
* for a specific api method
|
||||||
|
*/
|
||||||
|
'cache' => 'headerCacheControl',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* use PHPDoc comments such as the following
|
||||||
|
* `
|
||||||
|
*
|
||||||
|
* @expires 50` to set the Expires header
|
||||||
|
* for a specific api method
|
||||||
|
*/
|
||||||
|
'expires' => 'headerExpires',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* use PHPDoc comments such as the following
|
||||||
|
* `
|
||||||
|
*
|
||||||
|
* @throttle 300`
|
||||||
|
* to set the bandwidth throttling for 300 milliseconds
|
||||||
|
* for a specific api method
|
||||||
|
*/
|
||||||
|
'throttle' => 'throttle',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enable or disable smart auto routing from method comments
|
||||||
|
* this one is hardwired so cant be turned off
|
||||||
|
* it is placed here just for documentation purpose
|
||||||
|
*/
|
||||||
|
'smart-auto-routing' => 'smartAutoRouting',
|
||||||
|
);
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// Util
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this method to set value to a static properly of Defaults when
|
||||||
|
* you want to make sure only proper values are taken in with the help of
|
||||||
|
* validation
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*
|
||||||
|
* @param string $name name of the static property
|
||||||
|
* @param mixed $value value to set the property to
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function setProperty($name, $value)
|
||||||
|
{
|
||||||
|
if (!property_exists(__CLASS__, $name)) return false;
|
||||||
|
if (@is_array(Defaults::$validation[$name])) {
|
||||||
|
$info = new ValidationInfo(Defaults::$validation[$name]);
|
||||||
|
$value = Validator::validate($value, $info);
|
||||||
|
}
|
||||||
|
Defaults::$$name = $value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
98
htdocs/includes/restler/EventDispatcher.php
Normal file
98
htdocs/includes/restler/EventDispatcher.php
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
/**
|
||||||
|
* Static event broadcasting system for Restler
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
use Closure;
|
||||||
|
|
||||||
|
class EventDispatcher
|
||||||
|
{
|
||||||
|
private $listeners = array();
|
||||||
|
protected static $_waitList = array();
|
||||||
|
|
||||||
|
public static $self;
|
||||||
|
protected $events = array();
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
static::$self = $this;
|
||||||
|
if (!empty(static::$_waitList)) {
|
||||||
|
foreach (static::$_waitList as $param) {
|
||||||
|
call_user_func_array(array($this,$param[0]), $param[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function __callStatic($eventName, $params)
|
||||||
|
{
|
||||||
|
if (0 === strpos($eventName, 'on')) {
|
||||||
|
if(static::$self){
|
||||||
|
return call_user_func_array(array(static::$self, $eventName), $params);
|
||||||
|
}
|
||||||
|
static::$_waitList[] = func_get_args();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($eventName, $params)
|
||||||
|
{
|
||||||
|
if (0 === strpos($eventName, 'on')) {
|
||||||
|
if (!@is_array($this->listeners[$eventName]))
|
||||||
|
$this->listeners[$eventName] = array();
|
||||||
|
$this->listeners[$eventName][] = $params[0];
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function addListener($eventName, Closure $callback)
|
||||||
|
{
|
||||||
|
return static::$eventName($callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function on(array $eventHandlers)
|
||||||
|
{
|
||||||
|
for (
|
||||||
|
$count = count($eventHandlers),
|
||||||
|
$events = array_map(
|
||||||
|
'ucfirst',
|
||||||
|
$keys = array_keys(
|
||||||
|
$eventHandlers = array_change_key_case(
|
||||||
|
$eventHandlers,
|
||||||
|
CASE_LOWER
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
$i = 0;
|
||||||
|
$i < $count;
|
||||||
|
call_user_func(
|
||||||
|
array($this, "on{$events[$i]}"),
|
||||||
|
$eventHandlers[$keys[$i++]]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fire an event to notify all listeners
|
||||||
|
*
|
||||||
|
* @param string $eventName name of the event
|
||||||
|
* @param array $params event related data
|
||||||
|
*/
|
||||||
|
protected function dispatch($eventName, array $params = array())
|
||||||
|
{
|
||||||
|
$this->events[] = $eventName;
|
||||||
|
$params = func_get_args();
|
||||||
|
$eventName = 'on'.ucfirst(array_shift($params));
|
||||||
|
if (isset($this->listeners[$eventName]))
|
||||||
|
foreach ($this->listeners[$eventName] as $callback)
|
||||||
|
call_user_func_array($callback, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
178
htdocs/includes/restler/Filter/RateLimit.php
Normal file
178
htdocs/includes/restler/Filter/RateLimit.php
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Filter;
|
||||||
|
|
||||||
|
use Luracast\Restler\Defaults;
|
||||||
|
use Luracast\Restler\iFilter;
|
||||||
|
use Luracast\Restler\iUseAuthentication;
|
||||||
|
use Luracast\Restler\RestException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describe the purpose of this class/interface/trait
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class RateLimit implements iFilter, iUseAuthentication
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Luracast\Restler\Restler;
|
||||||
|
*/
|
||||||
|
public $restler;
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public static $usagePerUnit = 1200;
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public static $authenticatedUsagePerUnit = 5000;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public static $unit = 'hour';
|
||||||
|
/**
|
||||||
|
* @var string group the current api belongs to
|
||||||
|
*/
|
||||||
|
public static $group = 'common';
|
||||||
|
|
||||||
|
protected static $units = array(
|
||||||
|
'second' => 1,
|
||||||
|
'minute' => 60,
|
||||||
|
'hour' => 3600, // 60*60 seconds
|
||||||
|
'day' => 86400, // 60*60*24 seconds
|
||||||
|
'week' => 604800, // 60*60*24*7 seconds
|
||||||
|
'month' => 2592000, // 60*60*24*30 seconds
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array all paths beginning with any of the following will be excluded
|
||||||
|
* from documentation
|
||||||
|
*/
|
||||||
|
public static $excludedPaths = array('resources');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $unit
|
||||||
|
* @param int $usagePerUnit
|
||||||
|
* @param int $authenticatedUsagePerUnit set it to false to give unlimited access
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function setLimit(
|
||||||
|
$unit, $usagePerUnit, $authenticatedUsagePerUnit = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
static::$unit = $unit;
|
||||||
|
static::$usagePerUnit = $usagePerUnit;
|
||||||
|
static::$authenticatedUsagePerUnit =
|
||||||
|
is_null($authenticatedUsagePerUnit) ? $usagePerUnit : $authenticatedUsagePerUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __isAllowed()
|
||||||
|
{
|
||||||
|
if (static::$authenticatedUsagePerUnit
|
||||||
|
== static::$usagePerUnit
|
||||||
|
) return $this->check();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __setAuthenticationStatus($isAuthenticated = false)
|
||||||
|
{
|
||||||
|
header('X-Auth-Status: ' . ($isAuthenticated ? 'true' : 'false'));
|
||||||
|
$this->check($isAuthenticated);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function validate($unit)
|
||||||
|
{
|
||||||
|
if (!isset(static::$units[$unit]))
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'Rate Limit time unit should be '
|
||||||
|
. implode('|', array_keys(static::$units)) . '.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function check($isAuthenticated = false)
|
||||||
|
{
|
||||||
|
$path = $this->restler->url;
|
||||||
|
foreach (static::$excludedPaths as $exclude) {
|
||||||
|
if (empty($exclude) && empty($path)) {
|
||||||
|
return true;
|
||||||
|
} elseif (0 === strpos($path, $exclude)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static::validate(static::$unit);
|
||||||
|
$timeUnit = static::$units[static::$unit];
|
||||||
|
$maxPerUnit = $isAuthenticated
|
||||||
|
? static::$authenticatedUsagePerUnit
|
||||||
|
: static::$usagePerUnit;
|
||||||
|
if ($maxPerUnit) {
|
||||||
|
$user = Defaults::$userIdentifierClass;
|
||||||
|
if (!method_exists($user, 'getUniqueIdentifier')) {
|
||||||
|
throw new \UnexpectedValueException('`Defaults::$userIdentifierClass` must implement `iIdentifyUser` interface');
|
||||||
|
}
|
||||||
|
$id = "RateLimit_" . $maxPerUnit . '_per_' . static::$unit
|
||||||
|
. '_for_' . static::$group
|
||||||
|
. '_' . $user::getUniqueIdentifier();
|
||||||
|
$lastRequest = $this->restler->cache->get($id, true)
|
||||||
|
? : array('time' => 0, 'used' => 0);
|
||||||
|
$time = $lastRequest['time'];
|
||||||
|
$diff = time() - $time; # in seconds
|
||||||
|
$used = $lastRequest['used'];
|
||||||
|
|
||||||
|
header("X-RateLimit-Limit: $maxPerUnit per " . static::$unit);
|
||||||
|
if ($diff >= $timeUnit) {
|
||||||
|
$used = 1;
|
||||||
|
$time = time();
|
||||||
|
} elseif ($used >= $maxPerUnit) {
|
||||||
|
header("X-RateLimit-Remaining: 0");
|
||||||
|
$wait = $timeUnit - $diff;
|
||||||
|
sleep(1);
|
||||||
|
throw new RestException(429,
|
||||||
|
'Rate limit of ' . $maxPerUnit . ' request' .
|
||||||
|
($maxPerUnit > 1 ? 's' : '') . ' per '
|
||||||
|
. static::$unit . ' exceeded. Please wait for '
|
||||||
|
. static::duration($wait) . '.'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$used++;
|
||||||
|
}
|
||||||
|
$remainingPerUnit = $maxPerUnit - $used;
|
||||||
|
header("X-RateLimit-Remaining: $remainingPerUnit");
|
||||||
|
$this->restler->cache->set($id,
|
||||||
|
array('time' => $time, 'used' => $used));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function duration($secs)
|
||||||
|
{
|
||||||
|
$units = array(
|
||||||
|
'week' => (int)($secs / 86400 / 7),
|
||||||
|
'day' => $secs / 86400 % 7,
|
||||||
|
'hour' => $secs / 3600 % 24,
|
||||||
|
'minute' => $secs / 60 % 60,
|
||||||
|
'second' => $secs % 60);
|
||||||
|
|
||||||
|
$ret = array();
|
||||||
|
|
||||||
|
//$unit = 'days';
|
||||||
|
foreach ($units as $k => $v) {
|
||||||
|
if ($v > 0) {
|
||||||
|
$ret[] = $v > 1 ? "$v {$k}s" : "$v $k";
|
||||||
|
//$unit = $k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$i = count($ret) - 1;
|
||||||
|
if ($i) {
|
||||||
|
$ret[$i] = 'and ' . $ret[$i];
|
||||||
|
}
|
||||||
|
return implode(' ', $ret); //." $unit.";
|
||||||
|
}
|
||||||
|
}
|
||||||
146
htdocs/includes/restler/Flash.php
Normal file
146
htdocs/includes/restler/Flash.php
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
use Luracast\Restler\Format\HtmlFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storing and retrieving a message or array of key value pairs for one time use using $_SESSION
|
||||||
|
*
|
||||||
|
* They are typically used in view templates when using HtmlFormat
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class Flash //implements \JsonSerializable
|
||||||
|
{
|
||||||
|
const SUCCESS = 'success';
|
||||||
|
const INFO = 'info';
|
||||||
|
const WARNING = 'warning';
|
||||||
|
const DANGER = 'danger';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Flash
|
||||||
|
*/
|
||||||
|
private static $instance;
|
||||||
|
private $usedOnce = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flash a success message to user
|
||||||
|
*
|
||||||
|
* @param string $message
|
||||||
|
* @param string $header
|
||||||
|
*
|
||||||
|
* @return Flash
|
||||||
|
*/
|
||||||
|
public static function success($message, $header = '')
|
||||||
|
{
|
||||||
|
return static::message($message, $header, Flash::SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flash a info message to user
|
||||||
|
*
|
||||||
|
* @param string $message
|
||||||
|
* @param string $header
|
||||||
|
*
|
||||||
|
* @return Flash
|
||||||
|
*/
|
||||||
|
public static function info($message, $header = '')
|
||||||
|
{
|
||||||
|
return static::message($message, $header, Flash::INFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flash a warning message to user
|
||||||
|
*
|
||||||
|
* @param string $message
|
||||||
|
* @param string $header
|
||||||
|
*
|
||||||
|
* @return Flash
|
||||||
|
*/
|
||||||
|
public static function warning($message, $header = '')
|
||||||
|
{
|
||||||
|
return static::message($message, $header, Flash::WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flash a error message to user
|
||||||
|
*
|
||||||
|
* @param string $message
|
||||||
|
* @param string $header
|
||||||
|
*
|
||||||
|
* @return Flash
|
||||||
|
*/
|
||||||
|
public static function danger($message, $header = '')
|
||||||
|
{
|
||||||
|
return static::message($message, $header, Flash::DANGER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flash a message to user
|
||||||
|
*
|
||||||
|
* @param string $text message text
|
||||||
|
* @param string $header
|
||||||
|
* @param string $type
|
||||||
|
*
|
||||||
|
* @return Flash
|
||||||
|
*/
|
||||||
|
public static function message($text, $header = '', $type = Flash::WARNING)
|
||||||
|
{
|
||||||
|
return static::set(array('message' => $text, 'header' => $header, 'type' => $type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set some data for one time use
|
||||||
|
*
|
||||||
|
* @param array $data array of key value pairs {@type associative}
|
||||||
|
*
|
||||||
|
* @return Flash
|
||||||
|
*/
|
||||||
|
public static function set(array $data)
|
||||||
|
{
|
||||||
|
if (!static::$instance)
|
||||||
|
static::$instance = new Flash();
|
||||||
|
if (!isset($_SESSION['flash']))
|
||||||
|
$_SESSION['flash'] = array();
|
||||||
|
$_SESSION['flash'] += $data;
|
||||||
|
HtmlFormat::$data['flash'] = static::$instance;
|
||||||
|
return static::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($name)
|
||||||
|
{
|
||||||
|
$this->usedOnce = true;
|
||||||
|
return Util::nestedValue($_SESSION, 'flash', $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __isset($name)
|
||||||
|
{
|
||||||
|
return !is_null(Util::nestedValue($_SESSION, 'flash', $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
if ($this->usedOnce)
|
||||||
|
unset($_SESSION['flash']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify data which should be serialized to JSON
|
||||||
|
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||||
|
* @return mixed data which can be serialized by <b>json_encode</b>,
|
||||||
|
* which is a value of any type other than a resource.
|
||||||
|
*/
|
||||||
|
public function jsonSerialize()
|
||||||
|
{
|
||||||
|
$this->usedOnce = true;
|
||||||
|
return isset($_SESSION['flash'])
|
||||||
|
? $_SESSION['flash']
|
||||||
|
: array();
|
||||||
|
}
|
||||||
|
}
|
||||||
45
htdocs/includes/restler/Format/AmfFormat.php
Normal file
45
htdocs/includes/restler/Format/AmfFormat.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
|
||||||
|
use ZendAmf\Parser\Amf3\Deserializer;
|
||||||
|
use ZendAmf\Parser\Amf3\Serializer;
|
||||||
|
use ZendAmf\Parser\InputStream;
|
||||||
|
use ZendAmf\Parser\OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AMF Binary Format for Restler Framework.
|
||||||
|
* Native format supported by Adobe Flash and Adobe AIR
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage format
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class AmfFormat extends Format
|
||||||
|
{
|
||||||
|
const MIME = 'application/x-amf';
|
||||||
|
const EXTENSION = 'amf';
|
||||||
|
|
||||||
|
public function encode($data, $humanReadable = false)
|
||||||
|
{
|
||||||
|
|
||||||
|
$stream = new OutputStream();
|
||||||
|
$serializer = new Serializer($stream);
|
||||||
|
$serializer->writeTypeMarker($data);
|
||||||
|
|
||||||
|
return $stream->getStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function decode($data)
|
||||||
|
{
|
||||||
|
$stream = new InputStream(substr($data, 1));
|
||||||
|
$deserializer = new Deserializer($stream);
|
||||||
|
|
||||||
|
return $deserializer->readTypeMarker();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
181
htdocs/includes/restler/Format/CsvFormat.php
Normal file
181
htdocs/includes/restler/Format/CsvFormat.php
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
|
||||||
|
|
||||||
|
use Luracast\Restler\Data\Object;
|
||||||
|
use Luracast\Restler\RestException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comma Separated Value Format
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage format
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class CsvFormat extends Format implements iDecodeStream
|
||||||
|
{
|
||||||
|
|
||||||
|
const MIME = 'text/csv';
|
||||||
|
const EXTENSION = 'csv';
|
||||||
|
public static $delimiter = ',';
|
||||||
|
public static $enclosure = '"';
|
||||||
|
public static $escape = '\\';
|
||||||
|
public static $haveHeaders = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode the given data in the csv 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
|
||||||
|
*
|
||||||
|
* @return string encoded string
|
||||||
|
*
|
||||||
|
* @throws RestException 500 on unsupported data
|
||||||
|
*/
|
||||||
|
public function encode($data, $humanReadable = false)
|
||||||
|
{
|
||||||
|
$char = Object::$separatorChar;
|
||||||
|
Object::$separatorChar = false;
|
||||||
|
$data = Object::toArray($data);
|
||||||
|
Object::$separatorChar = $char;
|
||||||
|
if (is_array($data) && array_values($data) == $data) {
|
||||||
|
//if indexed array
|
||||||
|
$lines = array();
|
||||||
|
$row = array_shift($data);
|
||||||
|
if (array_values($row) != $row) {
|
||||||
|
$lines[] = static::putRow(array_keys($row));
|
||||||
|
}
|
||||||
|
$lines[] = static::putRow(array_values($row));
|
||||||
|
foreach ($data as $row) {
|
||||||
|
$lines[] = static::putRow(array_values($row));
|
||||||
|
}
|
||||||
|
return implode(PHP_EOL, $lines) . PHP_EOL;
|
||||||
|
}
|
||||||
|
throw new RestException(
|
||||||
|
500,
|
||||||
|
'Unsupported data for ' . strtoupper(static::EXTENSION) . ' format'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function putRow($data)
|
||||||
|
{
|
||||||
|
$fp = fopen('php://temp', 'r+');
|
||||||
|
fputcsv($fp, $data, static::$delimiter, static::$enclosure);
|
||||||
|
rewind($fp);
|
||||||
|
$data = fread($fp, 1048576);
|
||||||
|
fclose($fp);
|
||||||
|
return rtrim($data, PHP_EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the given data from the csv format
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* data sent from client to
|
||||||
|
* the api in the given format.
|
||||||
|
*
|
||||||
|
* @return array associative array of the parsed data
|
||||||
|
*/
|
||||||
|
public function decode($data)
|
||||||
|
{
|
||||||
|
$decoded = array();
|
||||||
|
|
||||||
|
if (empty($data)) {
|
||||||
|
return $decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
$lines = array_filter(explode(PHP_EOL, $data));
|
||||||
|
|
||||||
|
$keys = false;
|
||||||
|
$row = static::getRow(array_shift($lines));
|
||||||
|
|
||||||
|
if (is_null(static::$haveHeaders)) {
|
||||||
|
//try to guess with the given data
|
||||||
|
static::$haveHeaders = !count(array_filter($row, 'is_numeric'));
|
||||||
|
}
|
||||||
|
|
||||||
|
static::$haveHeaders ? $keys = $row : $decoded[] = $row;
|
||||||
|
|
||||||
|
while (($row = static::getRow(array_shift($lines), $keys)) !== FALSE)
|
||||||
|
$decoded [] = $row;
|
||||||
|
|
||||||
|
$char = Object::$separatorChar;
|
||||||
|
Object::$separatorChar = false;
|
||||||
|
$decoded = Object::toArray($decoded);
|
||||||
|
Object::$separatorChar = $char;
|
||||||
|
return $decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function getRow($data, $keys = false)
|
||||||
|
{
|
||||||
|
if (empty($data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$line = str_getcsv(
|
||||||
|
$data,
|
||||||
|
static::$delimiter,
|
||||||
|
static::$enclosure,
|
||||||
|
static::$escape
|
||||||
|
);
|
||||||
|
|
||||||
|
$row = array();
|
||||||
|
foreach ($line as $key => $value) {
|
||||||
|
if (is_numeric($value))
|
||||||
|
$value = floatval($value);
|
||||||
|
if ($keys) {
|
||||||
|
if (isset($keys [$key]))
|
||||||
|
$row [$keys [$key]] = $value;
|
||||||
|
} else {
|
||||||
|
$row [$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($keys) {
|
||||||
|
for ($i = count($row); $i < count($keys); $i++) {
|
||||||
|
$row[$keys[$i]] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the given data stream
|
||||||
|
*
|
||||||
|
* @param string $stream A stream resource with data
|
||||||
|
* sent from client to the api
|
||||||
|
* in the given format.
|
||||||
|
*
|
||||||
|
* @return array associative array of the parsed data
|
||||||
|
*/
|
||||||
|
public function decodeStream($stream)
|
||||||
|
{
|
||||||
|
$decoded = array();
|
||||||
|
|
||||||
|
$keys = false;
|
||||||
|
$row = static::getRow(stream_get_line($stream, 0, PHP_EOL));
|
||||||
|
if (is_null(static::$haveHeaders)) {
|
||||||
|
//try to guess with the given data
|
||||||
|
static::$haveHeaders = !count(array_filter($row, 'is_numeric'));
|
||||||
|
}
|
||||||
|
|
||||||
|
static::$haveHeaders ? $keys = $row : $decoded[] = $row;
|
||||||
|
|
||||||
|
while (($row = static::getRow(stream_get_line($stream, 0, PHP_EOL), $keys)) !== FALSE)
|
||||||
|
$decoded [] = $row;
|
||||||
|
|
||||||
|
$char = Object::$separatorChar;
|
||||||
|
Object::$separatorChar = false;
|
||||||
|
$decoded = Object::toArray($decoded);
|
||||||
|
Object::$separatorChar = $char;
|
||||||
|
return $decoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
140
htdocs/includes/restler/Format/Format.php
Normal file
140
htdocs/includes/restler/Format/Format.php
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class to implement common methods of iFormat
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
abstract class Format implements iFormat
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* override in the extending class
|
||||||
|
*/
|
||||||
|
const MIME = 'text/plain';
|
||||||
|
/**
|
||||||
|
* override in the extending class
|
||||||
|
*/
|
||||||
|
const EXTENSION = 'txt';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string charset encoding defaults to UTF8
|
||||||
|
*/
|
||||||
|
protected $charset='utf-8';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injected at runtime
|
||||||
|
*
|
||||||
|
* @var \Luracast\Restler\Restler
|
||||||
|
*/
|
||||||
|
public $restler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get MIME type => Extension mappings as an associative array
|
||||||
|
*
|
||||||
|
* @return array list of mime strings for the format
|
||||||
|
* @example array('application/json'=>'json');
|
||||||
|
*/
|
||||||
|
public function getMIMEMap()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
static::MIME => static::EXTENSION
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected MIME type
|
||||||
|
*
|
||||||
|
* @param string $mime
|
||||||
|
* MIME type
|
||||||
|
*/
|
||||||
|
public function setMIME($mime)
|
||||||
|
{
|
||||||
|
//do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content-Type field of the HTTP header can send a charset
|
||||||
|
* parameter in the HTTP header to specify the character
|
||||||
|
* encoding of the document.
|
||||||
|
* This information is passed
|
||||||
|
* here so that Format class can encode data accordingly
|
||||||
|
* Format class may choose to ignore this and use its
|
||||||
|
* default character set.
|
||||||
|
*
|
||||||
|
* @param string $charset
|
||||||
|
* Example utf-8
|
||||||
|
*/
|
||||||
|
public function setCharset($charset)
|
||||||
|
{
|
||||||
|
$this->charset = $charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content-Type accepted by the Format class
|
||||||
|
*
|
||||||
|
* @return string $charset Example utf-8
|
||||||
|
*/
|
||||||
|
public function getCharset()
|
||||||
|
{
|
||||||
|
return $this->charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get selected MIME type
|
||||||
|
*/
|
||||||
|
public function getMIME()
|
||||||
|
{
|
||||||
|
return static::MIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected file extension
|
||||||
|
*
|
||||||
|
* @param string $extension
|
||||||
|
* file extension
|
||||||
|
*/
|
||||||
|
public function setExtension($extension)
|
||||||
|
{
|
||||||
|
//do nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the selected file extension
|
||||||
|
*
|
||||||
|
* @return string file extension
|
||||||
|
*/
|
||||||
|
public function getExtension()
|
||||||
|
{
|
||||||
|
return static::EXTENSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return boolean is parsing the request supported?
|
||||||
|
*/
|
||||||
|
public function isReadable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return boolean is composing response supported?
|
||||||
|
*/
|
||||||
|
public function isWritable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return $this->getExtension();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
485
htdocs/includes/restler/Format/HtmlFormat.php
Normal file
485
htdocs/includes/restler/Format/HtmlFormat.php
Normal file
@@ -0,0 +1,485 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Events\Dispatcher;
|
||||||
|
use Illuminate\Filesystem\Filesystem;
|
||||||
|
use Illuminate\View\Compilers\BladeCompiler;
|
||||||
|
use Illuminate\View\Engines\CompilerEngine;
|
||||||
|
use Illuminate\View\Engines\EngineResolver;
|
||||||
|
use Illuminate\View\Factory;
|
||||||
|
use Illuminate\View\FileViewFinder;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
use Luracast\Restler\Data\ApiMethodInfo;
|
||||||
|
use Luracast\Restler\Data\Object;
|
||||||
|
use Luracast\Restler\Defaults;
|
||||||
|
use Luracast\Restler\RestException;
|
||||||
|
use Luracast\Restler\Restler;
|
||||||
|
use Luracast\Restler\Scope;
|
||||||
|
use Luracast\Restler\UI\Nav;
|
||||||
|
use Luracast\Restler\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Html template format
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage format
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class HtmlFormat extends Format
|
||||||
|
{
|
||||||
|
public static $mime = 'text/html';
|
||||||
|
public static $extension = 'html';
|
||||||
|
public static $view;
|
||||||
|
public static $errorView = 'debug.php';
|
||||||
|
public static $template = 'php';
|
||||||
|
public static $handleSession = true;
|
||||||
|
|
||||||
|
public static $useSmartViews = true;
|
||||||
|
/**
|
||||||
|
* @var null|string defaults to template named folder in Defaults::$cacheDirectory
|
||||||
|
*/
|
||||||
|
public static $cacheDirectory = null;
|
||||||
|
/**
|
||||||
|
* @var array global key value pair to be supplied to the templates. All
|
||||||
|
* keys added here will be available as a variable inside the template
|
||||||
|
*/
|
||||||
|
public static $data = array();
|
||||||
|
/**
|
||||||
|
* @var string set it to the location of your the view files. Defaults to
|
||||||
|
* views folder which is same level as vendor directory.
|
||||||
|
*/
|
||||||
|
public static $viewPath;
|
||||||
|
/**
|
||||||
|
* @var array template and its custom extension key value pair
|
||||||
|
*/
|
||||||
|
public static $customTemplateExtensions = array('blade' => 'blade.php');
|
||||||
|
/**
|
||||||
|
* @var bool used internally for error handling
|
||||||
|
*/
|
||||||
|
protected static $parseViewMetadata = true;
|
||||||
|
/**
|
||||||
|
* @var Restler;
|
||||||
|
*/
|
||||||
|
public $restler;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
//============ SESSION MANAGEMENT =============//
|
||||||
|
if (static::$handleSession) {
|
||||||
|
if (session_start() && isset($_SESSION['flash'])) {
|
||||||
|
static::$data['flash'] = $_SESSION['flash'];
|
||||||
|
unset($_SESSION['flash']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!static::$viewPath) {
|
||||||
|
$array = explode('vendor', __DIR__, 2);
|
||||||
|
static::$viewPath = $array[0] . 'views';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function blade(array $data, $debug = true)
|
||||||
|
{
|
||||||
|
if (!class_exists('\Illuminate\View\View', true))
|
||||||
|
throw new RestException(500,
|
||||||
|
'Blade templates require laravel view classes to be installed using `composer install`');
|
||||||
|
$resolver = new EngineResolver();
|
||||||
|
$files = new Filesystem();
|
||||||
|
$compiler = new BladeCompiler($files, static::$cacheDirectory);
|
||||||
|
$engine = new CompilerEngine($compiler);
|
||||||
|
$resolver->register('blade', function () use ($engine) {
|
||||||
|
return $engine;
|
||||||
|
});
|
||||||
|
|
||||||
|
/** @var Restler $restler */
|
||||||
|
$restler = Scope::get('Restler');
|
||||||
|
|
||||||
|
//Lets expose shortcuts for our classes
|
||||||
|
spl_autoload_register(function ($className) use ($restler) {
|
||||||
|
if (isset($restler->apiMethodInfo->metadata['scope'][$className])) {
|
||||||
|
return class_alias($restler->apiMethodInfo->metadata['scope'][$className], $className);
|
||||||
|
}
|
||||||
|
if (isset(Scope::$classAliases[$className])) {
|
||||||
|
return class_alias(Scope::$classAliases[$className], $className);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}, true, true);
|
||||||
|
|
||||||
|
$viewFinder = new FileViewFinder($files, array(static::$viewPath));
|
||||||
|
$factory = new Factory($resolver, $viewFinder, new Dispatcher());
|
||||||
|
$path = $viewFinder->find(self::$view);
|
||||||
|
$view = new View($factory, $engine, self::$view, $path, $data);
|
||||||
|
$factory->callCreator($view);
|
||||||
|
return $view->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function twig(array $data, $debug = true)
|
||||||
|
{
|
||||||
|
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' => static::$cacheDirectory,
|
||||||
|
'debug' => $debug,
|
||||||
|
'use_strict_variables' => $debug,
|
||||||
|
));
|
||||||
|
if ($debug)
|
||||||
|
$twig->addExtension(new \Twig_Extension_Debug());
|
||||||
|
|
||||||
|
$twig->addFunction(
|
||||||
|
new \Twig_SimpleFunction(
|
||||||
|
'form',
|
||||||
|
'Luracast\Restler\UI\Forms::get',
|
||||||
|
array('is_safe' => array('html'))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$twig->addFunction(
|
||||||
|
new \Twig_SimpleFunction(
|
||||||
|
'form_key',
|
||||||
|
'Luracast\Restler\UI\Forms::key'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$twig->addFunction(
|
||||||
|
new \Twig_SimpleFunction(
|
||||||
|
'nav',
|
||||||
|
'Luracast\Restler\UI\Nav::get'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$twig->registerUndefinedFunctionCallback(function ($name) {
|
||||||
|
if (
|
||||||
|
isset(HtmlFormat::$data[$name]) &&
|
||||||
|
is_callable(HtmlFormat::$data[$name])
|
||||||
|
) {
|
||||||
|
return new \Twig_SimpleFunction(
|
||||||
|
$name,
|
||||||
|
HtmlFormat::$data[$name]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
$template = $twig->loadTemplate(static::getViewFile());
|
||||||
|
return $template->render($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function handlebar(array $data, $debug = true)
|
||||||
|
{
|
||||||
|
return static::mustache($data, $debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function mustache(array $data, $debug = true)
|
||||||
|
{
|
||||||
|
if (!class_exists('\Mustache_Engine', true))
|
||||||
|
throw new RestException(
|
||||||
|
500,
|
||||||
|
'Mustache/Handlebar templates require mustache classes ' .
|
||||||
|
'to be installed using `composer install`'
|
||||||
|
);
|
||||||
|
if (!isset($data['nav']))
|
||||||
|
$data['nav'] = array_values(Nav::get());
|
||||||
|
$options = array(
|
||||||
|
'loader' => new \Mustache_Loader_FilesystemLoader(
|
||||||
|
static::$viewPath,
|
||||||
|
array('extension' => static::getViewExtension())
|
||||||
|
),
|
||||||
|
'helpers' => array(
|
||||||
|
'form' => function ($text, \Mustache_LambdaHelper $m) {
|
||||||
|
$params = explode(',', $m->render($text));
|
||||||
|
return call_user_func_array(
|
||||||
|
'Luracast\Restler\UI\Forms::get',
|
||||||
|
$params
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (!$debug)
|
||||||
|
$options['cache'] = static::$cacheDirectory;
|
||||||
|
$m = new \Mustache_Engine($options);
|
||||||
|
return $m->render(static::getViewFile(), $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function php(array $data, $debug = true)
|
||||||
|
{
|
||||||
|
if (static::$view == 'debug')
|
||||||
|
static::$viewPath = dirname(__DIR__) . '/views';
|
||||||
|
$view = static::getViewFile(true);
|
||||||
|
|
||||||
|
if (!is_readable($view)) {
|
||||||
|
throw new RestException(
|
||||||
|
500,
|
||||||
|
"view file `$view` is not readable. " .
|
||||||
|
'Check for file presence and file permissions'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = static::$viewPath . DIRECTORY_SEPARATOR;
|
||||||
|
$template = function ($view) use ($data, $path) {
|
||||||
|
$form = function () {
|
||||||
|
return call_user_func_array(
|
||||||
|
'Luracast\Restler\UI\Forms::get',
|
||||||
|
func_get_args()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
if (!isset($data['form']))
|
||||||
|
$data['form'] = $form;
|
||||||
|
$nav = function () {
|
||||||
|
return call_user_func_array(
|
||||||
|
'Luracast\Restler\UI\Nav::get',
|
||||||
|
func_get_args()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
if (!isset($data['nav']))
|
||||||
|
$data['nav'] = $nav;
|
||||||
|
|
||||||
|
$_ = function () use ($data, $path) {
|
||||||
|
extract($data);
|
||||||
|
$args = func_get_args();
|
||||||
|
$task = array_shift($args);
|
||||||
|
switch ($task) {
|
||||||
|
case 'require':
|
||||||
|
case 'include':
|
||||||
|
$file = $path . $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:
|
||||||
|
if (isset($data[$task]) && is_callable($data[$task]))
|
||||||
|
return call_user_func_array($data[$task], $args);
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
extract($data);
|
||||||
|
return @include $view;
|
||||||
|
};
|
||||||
|
$value = $template($view);
|
||||||
|
if (is_string($value))
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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' => Object::toArray($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)) {
|
||||||
|
throw new RestException(500, 'Unable to create cache directory `' . static::$cacheDirectory . '`');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method_exists($class = get_called_class(), $template)) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getViewExtension()
|
||||||
|
{
|
||||||
|
return isset(static::$customTemplateExtensions[static::$template])
|
||||||
|
? static::$customTemplateExtensions[static::$template]
|
||||||
|
: static::$template;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getViewFile($fullPath = false, $includeExtension = true)
|
||||||
|
{
|
||||||
|
$v = $fullPath ? static::$viewPath . '/' : '';
|
||||||
|
$v .= static::$view;
|
||||||
|
if ($includeExtension)
|
||||||
|
$v .= '.' . static::getViewExtension();
|
||||||
|
return $v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function reset()
|
||||||
|
{
|
||||||
|
static::$mime = 'text/html';
|
||||||
|
static::$extension = 'html';
|
||||||
|
static::$view = 'debug';
|
||||||
|
static::$template = 'php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the given data from the format
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* data sent from client to
|
||||||
|
* the api in the given format.
|
||||||
|
*
|
||||||
|
* @return array associative array of the parsed data
|
||||||
|
*
|
||||||
|
* @throws RestException
|
||||||
|
*/
|
||||||
|
public function decode($data)
|
||||||
|
{
|
||||||
|
throw new RestException(500, 'HtmlFormat is write only');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool false as HTML format is write only
|
||||||
|
*/
|
||||||
|
public function isReadable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get MIME type => Extension mappings as an associative array
|
||||||
|
*
|
||||||
|
* @return array list of mime strings for the format
|
||||||
|
* @example array('application/json'=>'json');
|
||||||
|
*/
|
||||||
|
public function getMIMEMap()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
static::$mime => static::$extension
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected MIME type
|
||||||
|
*
|
||||||
|
* @param string $mime MIME type
|
||||||
|
*/
|
||||||
|
public function setMIME($mime)
|
||||||
|
{
|
||||||
|
static::$mime = $mime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get selected MIME type
|
||||||
|
*/
|
||||||
|
public function getMIME()
|
||||||
|
{
|
||||||
|
return static::$mime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the selected file extension
|
||||||
|
*
|
||||||
|
* @return string file extension
|
||||||
|
*/
|
||||||
|
public function getExtension()
|
||||||
|
{
|
||||||
|
return static::$extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected file extension
|
||||||
|
*
|
||||||
|
* @param string $extension file extension
|
||||||
|
*/
|
||||||
|
public function setExtension($extension)
|
||||||
|
{
|
||||||
|
static::$extension = $extension;
|
||||||
|
}
|
||||||
|
}
|
||||||
48
htdocs/includes/restler/Format/JsFormat.php
Normal file
48
htdocs/includes/restler/Format/JsFormat.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Javascript Object Notation Packaged in a method (JSONP)
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage format
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class JsFormat extends JsonFormat
|
||||||
|
{
|
||||||
|
const MIME = 'text/javascript';
|
||||||
|
const EXTENSION = 'js';
|
||||||
|
|
||||||
|
public static $callbackMethodName = 'parseResponse';
|
||||||
|
public static $callbackOverrideQueryString = 'callback';
|
||||||
|
public static $includeHeaders = true;
|
||||||
|
|
||||||
|
public function encode($data, $human_readable = false)
|
||||||
|
{
|
||||||
|
$r = array();
|
||||||
|
if (static::$includeHeaders) {
|
||||||
|
$r['meta'] = array();
|
||||||
|
foreach (headers_list() as $header) {
|
||||||
|
list($h, $v) = explode(': ', $header, 2);
|
||||||
|
$r['meta'][$h] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$r['data'] = $data;
|
||||||
|
if (isset($_GET[static::$callbackOverrideQueryString])) {
|
||||||
|
static::$callbackMethodName
|
||||||
|
= (string) $_GET[static::$callbackOverrideQueryString];
|
||||||
|
}
|
||||||
|
return static::$callbackMethodName . '('
|
||||||
|
. parent::encode($r, $human_readable) . ');';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isReadable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
210
htdocs/includes/restler/Format/JsonFormat.php
Normal file
210
htdocs/includes/restler/Format/JsonFormat.php
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
|
||||||
|
use Luracast\Restler\Data\Object;
|
||||||
|
use Luracast\Restler\RestException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Javascript Object Notation Format
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage format
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class JsonFormat extends Format
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var boolean|null shim for json_encode option JSON_PRETTY_PRINT set
|
||||||
|
* it to null to use smart defaults
|
||||||
|
*/
|
||||||
|
public static $prettyPrint = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean|null shim for json_encode option JSON_UNESCAPED_SLASHES
|
||||||
|
* set it to null to use smart defaults
|
||||||
|
*/
|
||||||
|
public static $unEscapedSlashes = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean|null shim for json_encode JSON_UNESCAPED_UNICODE set it
|
||||||
|
* to null to use smart defaults
|
||||||
|
*/
|
||||||
|
public static $unEscapedUnicode = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean|null shim for json_decode JSON_BIGINT_AS_STRING set it to
|
||||||
|
* null to
|
||||||
|
* use smart defaults
|
||||||
|
*/
|
||||||
|
public static $bigIntAsString = null;
|
||||||
|
|
||||||
|
const MIME = 'application/json';
|
||||||
|
const EXTENSION = 'json';
|
||||||
|
|
||||||
|
public function encode($data, $humanReadable = false)
|
||||||
|
{
|
||||||
|
if (!is_null(self::$prettyPrint)) {
|
||||||
|
$humanReadable = self::$prettyPrint;
|
||||||
|
}
|
||||||
|
if (is_null(self::$unEscapedSlashes)) {
|
||||||
|
self::$unEscapedSlashes = $humanReadable;
|
||||||
|
}
|
||||||
|
if (is_null(self::$unEscapedUnicode)) {
|
||||||
|
self::$unEscapedUnicode = $this->charset == 'utf-8';
|
||||||
|
}
|
||||||
|
$options = 0;
|
||||||
|
if ((PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) // PHP >= 5.4
|
||||||
|
|| PHP_MAJOR_VERSION > 5 // PHP >= 6.0
|
||||||
|
) {
|
||||||
|
if ($humanReadable) $options |= JSON_PRETTY_PRINT;
|
||||||
|
if (self::$unEscapedSlashes) $options |= JSON_UNESCAPED_SLASHES;
|
||||||
|
if (self::$bigIntAsString) $options |= JSON_BIGINT_AS_STRING;
|
||||||
|
if (self::$unEscapedUnicode) $options |= JSON_UNESCAPED_UNICODE;
|
||||||
|
return json_encode(
|
||||||
|
Object::toArray($data, true), $options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = json_encode(Object::toArray($data, true));
|
||||||
|
if ($humanReadable) $result = $this->formatJson($result);
|
||||||
|
if (self::$unEscapedUnicode) {
|
||||||
|
$result = preg_replace_callback('/\\\u(\w\w\w\w)/',
|
||||||
|
function($matches)
|
||||||
|
{
|
||||||
|
if (function_exists('mb_convert_encoding'))
|
||||||
|
{
|
||||||
|
return mb_convert_encoding(pack('H*', $matches[1]), 'UTF-8', 'UTF-16BE');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return iconv('UTF-16BE','UTF-8',pack('H*', $matches[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, $result);
|
||||||
|
}
|
||||||
|
if (self::$unEscapedSlashes) $result = str_replace('\/', '/', $result);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function decode($data)
|
||||||
|
{
|
||||||
|
$options = 0;
|
||||||
|
if (self::$bigIntAsString) {
|
||||||
|
if ((PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) // PHP >= 5.4
|
||||||
|
|| PHP_MAJOR_VERSION > 5 // PHP >= 6.0
|
||||||
|
) {
|
||||||
|
$options |= JSON_BIGINT_AS_STRING;
|
||||||
|
} else {
|
||||||
|
$data = preg_replace(
|
||||||
|
'/:\s*(\-?\d+(\.\d+)?([e|E][\-|\+]\d+)?)/',
|
||||||
|
': "$1"', $data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$decoded = json_decode($data, $options);
|
||||||
|
if (function_exists('json_last_error')) {
|
||||||
|
switch (json_last_error()) {
|
||||||
|
case JSON_ERROR_NONE :
|
||||||
|
return Object::toArray($decoded);
|
||||||
|
break;
|
||||||
|
case JSON_ERROR_DEPTH :
|
||||||
|
$message = 'maximum stack depth exceeded';
|
||||||
|
break;
|
||||||
|
case JSON_ERROR_STATE_MISMATCH :
|
||||||
|
$message = 'underflow or the modes mismatch';
|
||||||
|
break;
|
||||||
|
case JSON_ERROR_CTRL_CHAR :
|
||||||
|
$message = 'unexpected control character found';
|
||||||
|
break;
|
||||||
|
case JSON_ERROR_SYNTAX :
|
||||||
|
$message = 'malformed JSON';
|
||||||
|
break;
|
||||||
|
case JSON_ERROR_UTF8 :
|
||||||
|
$message = 'malformed UTF-8 characters, possibly ' .
|
||||||
|
'incorrectly encoded';
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
$message = 'unknown error';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw new RestException (400, 'Error parsing JSON, ' . $message);
|
||||||
|
} elseif (strlen($data) && $decoded === null || $decoded === $data) {
|
||||||
|
throw new RestException (400, 'Error parsing JSON');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object::toArray($decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pretty print JSON string
|
||||||
|
*
|
||||||
|
* @param string $json
|
||||||
|
*
|
||||||
|
* @return string formatted json
|
||||||
|
*/
|
||||||
|
private function formatJson($json)
|
||||||
|
{
|
||||||
|
$tab = ' ';
|
||||||
|
$newJson = '';
|
||||||
|
$indentLevel = 0;
|
||||||
|
$inString = false;
|
||||||
|
$len = strlen($json);
|
||||||
|
for ($c = 0; $c < $len; $c++) {
|
||||||
|
$char = $json [$c];
|
||||||
|
switch ($char) {
|
||||||
|
case '{' :
|
||||||
|
case '[' :
|
||||||
|
if (!$inString) {
|
||||||
|
$newJson .= $char . "\n" .
|
||||||
|
str_repeat($tab, $indentLevel + 1);
|
||||||
|
$indentLevel++;
|
||||||
|
} else {
|
||||||
|
$newJson .= $char;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '}' :
|
||||||
|
case ']' :
|
||||||
|
if (!$inString) {
|
||||||
|
$indentLevel--;
|
||||||
|
$newJson .= "\n" .
|
||||||
|
str_repeat($tab, $indentLevel) . $char;
|
||||||
|
} else {
|
||||||
|
$newJson .= $char;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ',' :
|
||||||
|
if (!$inString) {
|
||||||
|
$newJson .= ",\n" .
|
||||||
|
str_repeat($tab, $indentLevel);
|
||||||
|
} else {
|
||||||
|
$newJson .= $char;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ':' :
|
||||||
|
if (!$inString) {
|
||||||
|
$newJson .= ': ';
|
||||||
|
} else {
|
||||||
|
$newJson .= $char;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '"' :
|
||||||
|
if ($c == 0) {
|
||||||
|
$inString = true;
|
||||||
|
} elseif ($c > 0 && $json [$c - 1] != '\\') {
|
||||||
|
$inString = !$inString;
|
||||||
|
}
|
||||||
|
default :
|
||||||
|
$newJson .= $char;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $newJson;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
144
htdocs/includes/restler/Format/MultiFormat.php
Normal file
144
htdocs/includes/restler/Format/MultiFormat.php
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
/**
|
||||||
|
* Describe the purpose of this class/interface/trait
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
abstract class MultiFormat implements iFormat
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* override in the extending class
|
||||||
|
*/
|
||||||
|
const MIME = 'text/plain,text/html';
|
||||||
|
/**
|
||||||
|
* override in the extending class
|
||||||
|
*/
|
||||||
|
const EXTENSION = 'txt,html';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string charset encoding defaults to UTF8
|
||||||
|
*/
|
||||||
|
protected $charset='utf-8';
|
||||||
|
|
||||||
|
public static $mime;
|
||||||
|
public static $extension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injected at runtime
|
||||||
|
*
|
||||||
|
* @var \Luracast\Restler\Restler
|
||||||
|
*/
|
||||||
|
public $restler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get MIME type => Extension mappings as an associative array
|
||||||
|
*
|
||||||
|
* @return array list of mime strings for the format
|
||||||
|
* @example array('application/json'=>'json');
|
||||||
|
*/
|
||||||
|
public function getMIMEMap()
|
||||||
|
{
|
||||||
|
$extensions = explode(',',static::EXTENSION);
|
||||||
|
$mimes = explode(',',static::MIME);
|
||||||
|
$count = max(count($extensions), count($mimes));
|
||||||
|
$extensions += array_fill(0, $count, end($extensions));
|
||||||
|
$mimes += array_fill(0, $count, end($mimes));
|
||||||
|
return array_combine($mimes,$extensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected MIME type
|
||||||
|
*
|
||||||
|
* @param string $mime
|
||||||
|
* MIME type
|
||||||
|
*/
|
||||||
|
public function setMIME($mime)
|
||||||
|
{
|
||||||
|
static::$mime = $mime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content-Type field of the HTTP header can send a charset
|
||||||
|
* parameter in the HTTP header to specify the character
|
||||||
|
* encoding of the document.
|
||||||
|
* This information is passed
|
||||||
|
* here so that Format class can encode data accordingly
|
||||||
|
* Format class may choose to ignore this and use its
|
||||||
|
* default character set.
|
||||||
|
*
|
||||||
|
* @param string $charset
|
||||||
|
* Example utf-8
|
||||||
|
*/
|
||||||
|
public function setCharset($charset)
|
||||||
|
{
|
||||||
|
$this->charset = $charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content-Type accepted by the Format class
|
||||||
|
*
|
||||||
|
* @return string $charset Example utf-8
|
||||||
|
*/
|
||||||
|
public function getCharset()
|
||||||
|
{
|
||||||
|
return $this->charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get selected MIME type
|
||||||
|
*/
|
||||||
|
public function getMIME()
|
||||||
|
{
|
||||||
|
return static::$mime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected file extension
|
||||||
|
*
|
||||||
|
* @param string $extension
|
||||||
|
* file extension
|
||||||
|
*/
|
||||||
|
public function setExtension($extension)
|
||||||
|
{
|
||||||
|
static::$extension = $extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the selected file extension
|
||||||
|
*
|
||||||
|
* @return string file extension
|
||||||
|
*/
|
||||||
|
public function getExtension()
|
||||||
|
{
|
||||||
|
return static::$extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return boolean is parsing the request supported?
|
||||||
|
*/
|
||||||
|
public function isReadable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return boolean is composing response supported?
|
||||||
|
*/
|
||||||
|
public function isWritable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return $this->getExtension();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
91
htdocs/includes/restler/Format/PlistFormat.php
Normal file
91
htdocs/includes/restler/Format/PlistFormat.php
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
|
||||||
|
use Luracast\Restler\Data\Object;
|
||||||
|
use CFPropertyList\CFTypeDetector;
|
||||||
|
use CFPropertyList\CFPropertyList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plist Format for Restler Framework.
|
||||||
|
* Plist is the native data exchange format for Apple iOS and Mac platform.
|
||||||
|
* Use this format to talk to mac applications and iOS devices.
|
||||||
|
* This class is capable of serving both xml plist and binary plist.
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage format
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class PlistFormat extends MultiFormat
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var boolean set it to true binary plist is preferred
|
||||||
|
*/
|
||||||
|
public static $compact = null;
|
||||||
|
const MIME = 'application/xml,application/x-plist';
|
||||||
|
const EXTENSION = 'plist';
|
||||||
|
|
||||||
|
public function setMIME($mime)
|
||||||
|
{
|
||||||
|
static::$mime = $mime;
|
||||||
|
static::$compact = $mime == 'application/x-plist';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode the given data in plist format
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* resulting data that needs to
|
||||||
|
* be encoded in plist 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
|
||||||
|
*
|
||||||
|
* @return string encoded string
|
||||||
|
*/
|
||||||
|
public function encode($data, $humanReadable = false)
|
||||||
|
{
|
||||||
|
//require_once 'CFPropertyList.php';
|
||||||
|
if (!isset(self::$compact)) {
|
||||||
|
self::$compact = !$humanReadable;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @var CFPropertyList
|
||||||
|
*/
|
||||||
|
$plist = new CFPropertyList ();
|
||||||
|
$td = new CFTypeDetector ();
|
||||||
|
$guessedStructure = $td->toCFType(
|
||||||
|
Object::toArray($data)
|
||||||
|
);
|
||||||
|
$plist->add($guessedStructure);
|
||||||
|
|
||||||
|
return self::$compact
|
||||||
|
? $plist->toBinary()
|
||||||
|
: $plist->toXML(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the given data from plist format
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* data sent from client to
|
||||||
|
* the api in the given format.
|
||||||
|
*
|
||||||
|
* @return array associative array of the parsed data
|
||||||
|
*/
|
||||||
|
public function decode($data)
|
||||||
|
{
|
||||||
|
//require_once 'CFPropertyList.php';
|
||||||
|
$plist = new CFPropertyList ();
|
||||||
|
$plist->parse($data);
|
||||||
|
|
||||||
|
return $plist->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
24
htdocs/includes/restler/Format/TsvFormat.php
Normal file
24
htdocs/includes/restler/Format/TsvFormat.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tab Separated Value Format
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage format
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class TsvFormat extends CsvFormat
|
||||||
|
{
|
||||||
|
const MIME = 'text/csv';
|
||||||
|
const EXTENSION = 'csv';
|
||||||
|
public static $delimiter = "\t";
|
||||||
|
public static $enclosure = '"';
|
||||||
|
public static $escape = '\\';
|
||||||
|
public static $haveHeaders = null;
|
||||||
|
}
|
||||||
145
htdocs/includes/restler/Format/UploadFormat.php
Normal file
145
htdocs/includes/restler/Format/UploadFormat.php
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
|
||||||
|
use Luracast\Restler\RestException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support for Multi Part Form Data and File Uploads
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage format
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class UploadFormat extends Format
|
||||||
|
{
|
||||||
|
const MIME = 'multipart/form-data';
|
||||||
|
const EXTENSION = 'post';
|
||||||
|
public static $errors = array(
|
||||||
|
0 => false,
|
||||||
|
1 => "The uploaded file exceeds the maximum allowed size",
|
||||||
|
2 => "The uploaded file exceeds the maximum allowed size",
|
||||||
|
3 => "The uploaded file was only partially uploaded",
|
||||||
|
4 => "No file was uploaded",
|
||||||
|
6 => "Missing a temporary folder"
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* use it if you need to restrict uploads based on file type
|
||||||
|
* setting it as an empty array allows all file types
|
||||||
|
* default is to allow only png and jpeg images
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public static $allowedMimeTypes = array('image/jpeg', 'image/png');
|
||||||
|
/**
|
||||||
|
* use it to restrict uploads based on file size
|
||||||
|
* set it to 0 to allow all sizes
|
||||||
|
* please note that it upload restrictions in the server
|
||||||
|
* takes precedence so it has to be lower than or equal to that
|
||||||
|
* default value is 1MB (1024x1024)bytes
|
||||||
|
* usual value for the server is 8388608
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public static $maximumFileSize = 1048576;
|
||||||
|
/**
|
||||||
|
* Your own validation function for validating each uploaded file
|
||||||
|
* it can return false or throw an exception for invalid file
|
||||||
|
* use anonymous function / closure in PHP 5.3 and above
|
||||||
|
* use function name in other cases
|
||||||
|
*
|
||||||
|
* @var Callable
|
||||||
|
*/
|
||||||
|
public static $customValidationFunction;
|
||||||
|
/**
|
||||||
|
* Since exceptions are triggered way before at the `get` stage
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public static $suppressExceptionsAsError = false;
|
||||||
|
|
||||||
|
protected static function checkFile(& $file, $doMimeCheck = false, $doSizeCheck = false)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($file['error']) {
|
||||||
|
//server is throwing an error
|
||||||
|
//assume that the error is due to maximum size limit
|
||||||
|
throw new RestException($file['error'] > 5 ? 500 : 413, static::$errors[$file['error']]);
|
||||||
|
}
|
||||||
|
$typeElements = explode('/', $file['type']);
|
||||||
|
$genericType = $typeElements[0].'/*';
|
||||||
|
if (
|
||||||
|
$doMimeCheck
|
||||||
|
&& !(
|
||||||
|
in_array($file['type'], self::$allowedMimeTypes)
|
||||||
|
|| in_array($genericType, self::$allowedMimeTypes)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new RestException(403, "File type ({$file['type']}) is not supported.");
|
||||||
|
}
|
||||||
|
if ($doSizeCheck && $file['size'] > self::$maximumFileSize) {
|
||||||
|
throw new RestException(413, "Uploaded file ({$file['name']}) is too big.");
|
||||||
|
}
|
||||||
|
if (self::$customValidationFunction) {
|
||||||
|
if (!call_user_func(self::$customValidationFunction, $file)) {
|
||||||
|
throw new RestException(403, "File ({$file['name']}) is not supported.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (RestException $e) {
|
||||||
|
if (static::$suppressExceptionsAsError) {
|
||||||
|
$file['error'] = $e->getCode() == 413 ? 1 : 6;
|
||||||
|
$file['exception'] = $e;
|
||||||
|
} else {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function encode($data, $humanReadable = false)
|
||||||
|
{
|
||||||
|
throw new RestException(500, 'UploadFormat is read only');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function decode($data)
|
||||||
|
{
|
||||||
|
$doMimeCheck = !empty(self::$allowedMimeTypes);
|
||||||
|
$doSizeCheck = self::$maximumFileSize ? TRUE : FALSE;
|
||||||
|
//validate
|
||||||
|
foreach ($_FILES as & $file) {
|
||||||
|
if (is_array($file['error'])) {
|
||||||
|
foreach ($file['error'] as $i => $error) {
|
||||||
|
$innerFile = array();
|
||||||
|
foreach ($file as $property => $value) {
|
||||||
|
$innerFile[$property] = $value[$i];
|
||||||
|
}
|
||||||
|
if ($innerFile['name'])
|
||||||
|
static::checkFile($innerFile, $doMimeCheck, $doSizeCheck);
|
||||||
|
|
||||||
|
if (isset($innerFile['exception'])) {
|
||||||
|
$file['error'][$i] = $innerFile['error'];
|
||||||
|
$file['exception'] = $innerFile['exception'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($file['name'])
|
||||||
|
static::checkFile($file, $doMimeCheck, $doSizeCheck);
|
||||||
|
if (isset($innerFile['exception'])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//sort file order if needed;
|
||||||
|
return UrlEncodedFormat::decoderTypeFix($_FILES + $_POST);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWritable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
58
htdocs/includes/restler/Format/UrlEncodedFormat.php
Normal file
58
htdocs/includes/restler/Format/UrlEncodedFormat.php
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL Encoded String Format
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage format
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class UrlEncodedFormat extends Format
|
||||||
|
{
|
||||||
|
const MIME = 'application/x-www-form-urlencoded';
|
||||||
|
const EXTENSION = 'post';
|
||||||
|
|
||||||
|
public function encode($data, $humanReadable = false)
|
||||||
|
{
|
||||||
|
return http_build_query(static::encoderTypeFix($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function decode($data)
|
||||||
|
{
|
||||||
|
parse_str($data, $r);
|
||||||
|
return self::decoderTypeFix($r);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function encoderTypeFix(array $data)
|
||||||
|
{
|
||||||
|
foreach ($data as $k => $v) {
|
||||||
|
if (is_bool($v)) {
|
||||||
|
$data[$k] = $v = $v ? 'true' : 'false';
|
||||||
|
} elseif (is_array($v)) {
|
||||||
|
$data[$k] = $v = static::decoderTypeFix($v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function decoderTypeFix(array $data)
|
||||||
|
{
|
||||||
|
foreach ($data as $k => $v) {
|
||||||
|
if ($v === 'true' || $v === 'false') {
|
||||||
|
$data[$k] = $v = $v === 'true';
|
||||||
|
} elseif (is_array($v)) {
|
||||||
|
$data[$k] = $v = static::decoderTypeFix($v);
|
||||||
|
} elseif (empty($v) && $v != 0) {
|
||||||
|
unset($data[$k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
348
htdocs/includes/restler/Format/XmlFormat.php
Normal file
348
htdocs/includes/restler/Format/XmlFormat.php
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
|
||||||
|
use Luracast\Restler\Data\Object;
|
||||||
|
use Luracast\Restler\RestException;
|
||||||
|
use SimpleXMLElement;
|
||||||
|
use XMLWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML Markup Format for Restler Framework
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage format
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class XmlFormat extends Format
|
||||||
|
{
|
||||||
|
const MIME = 'application/xml';
|
||||||
|
const EXTENSION = 'xml';
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// Properties related to reading/parsing/decoding xml
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
public static $importSettingsFromXml = false;
|
||||||
|
public static $parseAttributes = true;
|
||||||
|
public static $parseNamespaces = true;
|
||||||
|
public static $parseTextNodeAsProperty = true;
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// Properties related to writing/encoding xml
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
public static $useTextNodeProperty = true;
|
||||||
|
public static $useNamespaces = true;
|
||||||
|
public static $cdataNames = array();
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
//
|
||||||
|
// Common Properties
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
public static $attributeNames = array();
|
||||||
|
public static $textNodeName = 'text';
|
||||||
|
public static $namespaces = array();
|
||||||
|
public static $namespacedProperties = array();
|
||||||
|
/**
|
||||||
|
* Default name for the root node.
|
||||||
|
*
|
||||||
|
* @var string $rootNodeName
|
||||||
|
*/
|
||||||
|
public static $rootName = 'response';
|
||||||
|
public static $defaultTagName = 'item';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When you decode an XML its structure is copied to the static vars
|
||||||
|
* we can use this function to echo them out and then copy paste inside
|
||||||
|
* our service methods
|
||||||
|
*
|
||||||
|
* @return string PHP source code to reproduce the configuration
|
||||||
|
*/
|
||||||
|
public static function exportCurrentSettings()
|
||||||
|
{
|
||||||
|
$s = 'XmlFormat::$rootName = "' . (self::$rootName) . "\";\n";
|
||||||
|
$s .= 'XmlFormat::$attributeNames = ' .
|
||||||
|
(var_export(self::$attributeNames, true)) . ";\n";
|
||||||
|
$s .= 'XmlFormat::$defaultTagName = "' .
|
||||||
|
self::$defaultTagName . "\";\n";
|
||||||
|
$s .= 'XmlFormat::$parseAttributes = ' .
|
||||||
|
(self::$parseAttributes ? 'true' : 'false') . ";\n";
|
||||||
|
$s .= 'XmlFormat::$parseNamespaces = ' .
|
||||||
|
(self::$parseNamespaces ? 'true' : 'false') . ";\n";
|
||||||
|
if (self::$parseNamespaces) {
|
||||||
|
$s .= 'XmlFormat::$namespaces = ' .
|
||||||
|
(var_export(self::$namespaces, true)) . ";\n";
|
||||||
|
$s .= 'XmlFormat::$namespacedProperties = ' .
|
||||||
|
(var_export(self::$namespacedProperties, true)) . ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function encode($data, $humanReadable = false)
|
||||||
|
{
|
||||||
|
$data = Object::toArray($data);
|
||||||
|
$xml = new XMLWriter();
|
||||||
|
$xml->openMemory();
|
||||||
|
$xml->startDocument('1.0', $this->charset);
|
||||||
|
if ($humanReadable) {
|
||||||
|
$xml->setIndent(true);
|
||||||
|
$xml->setIndentString(' ');
|
||||||
|
}
|
||||||
|
static::$useNamespaces && isset(static::$namespacedProperties[static::$rootName])
|
||||||
|
?
|
||||||
|
$xml->startElementNs(
|
||||||
|
static::$namespacedProperties[static::$rootName],
|
||||||
|
static::$rootName,
|
||||||
|
static::$namespaces[static::$namespacedProperties[static::$rootName]]
|
||||||
|
)
|
||||||
|
:
|
||||||
|
$xml->startElement(static::$rootName);
|
||||||
|
if (static::$useNamespaces) {
|
||||||
|
foreach (static::$namespaces as $prefix => $ns) {
|
||||||
|
if (isset(static::$namespacedProperties[static::$rootName])
|
||||||
|
&& static::$namespacedProperties[static::$rootName] == $prefix
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
$prefix = 'xmlns' . (empty($prefix) ? '' : ':' . $prefix);
|
||||||
|
$xml->writeAttribute($prefix, $ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->write($xml, $data, static::$rootName);
|
||||||
|
$xml->endElement();
|
||||||
|
return $xml->outputMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write(XMLWriter $xml, $data, $parent)
|
||||||
|
{
|
||||||
|
$text = array();
|
||||||
|
if (is_array($data)) {
|
||||||
|
if (static::$useTextNodeProperty && isset($data[static::$textNodeName])) {
|
||||||
|
$text [] = $data[static::$textNodeName];
|
||||||
|
unset($data[static::$textNodeName]);
|
||||||
|
}
|
||||||
|
$attributes = array_flip(static::$attributeNames);
|
||||||
|
//make sure we deal with attributes first
|
||||||
|
$temp = array();
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
if (isset($attributes[$key])) {
|
||||||
|
$temp[$key] = $data[$key];
|
||||||
|
unset($data[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$data = array_merge($temp, $data);
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
if (is_numeric($key)) {
|
||||||
|
if (!is_array($value)) {
|
||||||
|
$text [] = $value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$key = static::$defaultTagName;
|
||||||
|
}
|
||||||
|
$useNS = static::$useNamespaces
|
||||||
|
&& !empty(static::$namespacedProperties[$key])
|
||||||
|
&& false === strpos($key, ':');
|
||||||
|
if (is_array($value)) {
|
||||||
|
if ($value == array_values($value)) {
|
||||||
|
//numeric array, create siblings
|
||||||
|
foreach ($value as $v) {
|
||||||
|
$useNS
|
||||||
|
? $xml->startElementNs(
|
||||||
|
static::$namespacedProperties[$key],
|
||||||
|
$key,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
: $xml->startElement($key);
|
||||||
|
$this->write($xml, $v, $key);
|
||||||
|
$xml->endElement();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$useNS
|
||||||
|
? $xml->startElementNs(
|
||||||
|
static::$namespacedProperties[$key],
|
||||||
|
$key,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
: $xml->startElement($key);
|
||||||
|
$this->write($xml, $value, $key);
|
||||||
|
$xml->endElement();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} elseif (is_bool($value)) {
|
||||||
|
$value = $value ? 'true' : 'false';
|
||||||
|
}
|
||||||
|
if (isset($attributes[$key])) {
|
||||||
|
$xml->writeAttribute($useNS ? static::$namespacedProperties[$key] . ':' . $key : $key, $value);
|
||||||
|
} else {
|
||||||
|
$useNS
|
||||||
|
?
|
||||||
|
$xml->startElementNs(
|
||||||
|
static::$namespacedProperties[$key],
|
||||||
|
$key,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
: $xml->startElement($key);
|
||||||
|
$this->write($xml, $value, $key);
|
||||||
|
$xml->endElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$text [] = (string)$data;
|
||||||
|
}
|
||||||
|
if (!empty($text)) {
|
||||||
|
if (count($text) == 1) {
|
||||||
|
in_array($parent, static::$cdataNames)
|
||||||
|
? $xml->writeCdata(implode('', $text))
|
||||||
|
: $xml->text(implode('', $text));
|
||||||
|
} else {
|
||||||
|
foreach ($text as $t) {
|
||||||
|
$xml->writeElement(static::$textNodeName, $t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function decode($data)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($data == '') {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
libxml_use_internal_errors(true);
|
||||||
|
$xml = simplexml_load_string($data,
|
||||||
|
"SimpleXMLElement", LIBXML_NOBLANKS | LIBXML_NOCDATA | LIBXML_COMPACT);
|
||||||
|
if (false === $xml) {
|
||||||
|
$error = libxml_get_last_error();
|
||||||
|
throw new RestException(400, 'Malformed XML. '
|
||||||
|
. trim($error->message, "\r\n") . ' at line ' . $error->line);
|
||||||
|
}
|
||||||
|
libxml_clear_errors();
|
||||||
|
if (static::$importSettingsFromXml) {
|
||||||
|
static::$attributeNames = array();
|
||||||
|
static::$namespacedProperties = array();
|
||||||
|
static::$namespaces = array();
|
||||||
|
static::$rootName = $xml->getName();
|
||||||
|
$namespaces = $xml->getNamespaces();
|
||||||
|
if (count($namespaces)) {
|
||||||
|
$p = strpos($data, $xml->getName());
|
||||||
|
if ($p && $data{$p - 1} == ':') {
|
||||||
|
$s = strpos($data, '<') + 1;
|
||||||
|
$prefix = substr($data, $s, $p - $s - 1);
|
||||||
|
static::$namespacedProperties[static::$rootName] = $prefix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$data = $this->read($xml);
|
||||||
|
if (count($data) == 1 && isset($data[static::$textNodeName]))
|
||||||
|
$data = $data[static::$textNodeName];
|
||||||
|
return $data;
|
||||||
|
} catch (\RuntimeException $e) {
|
||||||
|
throw new RestException(400,
|
||||||
|
"Error decoding request. " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read(SimpleXMLElement $xml, $namespaces = null)
|
||||||
|
{
|
||||||
|
$r = array();
|
||||||
|
$text = (string)$xml;
|
||||||
|
|
||||||
|
if (static::$parseAttributes) {
|
||||||
|
$attributes = $xml->attributes();
|
||||||
|
foreach ($attributes as $key => $value) {
|
||||||
|
if (static::$importSettingsFromXml
|
||||||
|
&& !in_array($key, static::$attributeNames)
|
||||||
|
) {
|
||||||
|
static::$attributeNames[] = $key;
|
||||||
|
}
|
||||||
|
$r[$key] = static::setType((string)$value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$children = $xml->children();
|
||||||
|
foreach ($children as $key => $value) {
|
||||||
|
if (isset($r[$key])) {
|
||||||
|
if (is_array($r[$key])) {
|
||||||
|
if ($r[$key] != array_values($r[$key]))
|
||||||
|
$r[$key] = array($r[$key]);
|
||||||
|
} else {
|
||||||
|
$r[$key] = array($r[$key]);
|
||||||
|
}
|
||||||
|
$r[$key][] = $this->read($value, $namespaces);
|
||||||
|
} else {
|
||||||
|
$r[$key] = $this->read($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static::$parseNamespaces) {
|
||||||
|
if (is_null($namespaces))
|
||||||
|
$namespaces = $xml->getDocNamespaces(true);
|
||||||
|
foreach ($namespaces as $prefix => $ns) {
|
||||||
|
static::$namespaces[$prefix] = $ns;
|
||||||
|
if (static::$parseAttributes) {
|
||||||
|
$attributes = $xml->attributes($ns);
|
||||||
|
foreach ($attributes as $key => $value) {
|
||||||
|
if (isset($r[$key])) {
|
||||||
|
$key = "{$prefix}:$key";
|
||||||
|
}
|
||||||
|
if (static::$importSettingsFromXml
|
||||||
|
&& !in_array($key, static::$attributeNames)
|
||||||
|
) {
|
||||||
|
static::$namespacedProperties[$key] = $prefix;
|
||||||
|
static::$attributeNames[] = $key;
|
||||||
|
}
|
||||||
|
$r[$key] = static::setType((string)$value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$children = $xml->children($ns);
|
||||||
|
foreach ($children as $key => $value) {
|
||||||
|
if (static::$importSettingsFromXml)
|
||||||
|
static::$namespacedProperties[$key] = $prefix;
|
||||||
|
if (isset($r[$key])) {
|
||||||
|
if (is_array($r[$key])) {
|
||||||
|
if ($r[$key] != array_values($r[$key]))
|
||||||
|
$r[$key] = array($r[$key]);
|
||||||
|
} else {
|
||||||
|
$r[$key] = array($r[$key]);
|
||||||
|
}
|
||||||
|
$r[$key][] = $this->read($value, $namespaces);
|
||||||
|
} else {
|
||||||
|
$r[$key] = $this->read($value, $namespaces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($text) && $text !== '0') {
|
||||||
|
if (empty($r)) return null;
|
||||||
|
} else {
|
||||||
|
empty($r)
|
||||||
|
? $r = static::setType($text)
|
||||||
|
: (
|
||||||
|
static::$parseTextNodeAsProperty
|
||||||
|
? $r[static::$textNodeName] = static::setType($text)
|
||||||
|
: $r[] = static::setType($text)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function setType($value)
|
||||||
|
{
|
||||||
|
if (empty($value) && $value !== '0')
|
||||||
|
return null;
|
||||||
|
if ($value == 'true')
|
||||||
|
return true;
|
||||||
|
if ($value == 'false')
|
||||||
|
return true;
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
36
htdocs/includes/restler/Format/YamlFormat.php
Normal file
36
htdocs/includes/restler/Format/YamlFormat.php
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Luracast\Restler\Data\Object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* YAML Format for Restler Framework
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage format
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class YamlFormat extends Format
|
||||||
|
{
|
||||||
|
const MIME = 'text/plain';
|
||||||
|
const EXTENSION = 'yaml';
|
||||||
|
|
||||||
|
public function encode($data, $humanReadable = false)
|
||||||
|
{
|
||||||
|
// require_once 'sfyaml.php';
|
||||||
|
return @Yaml::dump(Object::toArray($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function decode($data)
|
||||||
|
{
|
||||||
|
// require_once 'sfyaml.php';
|
||||||
|
return Yaml::parse($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
30
htdocs/includes/restler/Format/iDecodeStream.php
Normal file
30
htdocs/includes/restler/Format/iDecodeStream.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for creating formats that accept steams for decoding
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage format
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
interface iDecodeStream
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the given data stream
|
||||||
|
*
|
||||||
|
* @param string $stream A stream resource with data
|
||||||
|
* sent from client to the api
|
||||||
|
* in the given format.
|
||||||
|
*
|
||||||
|
* @return array associative array of the parsed data
|
||||||
|
*/
|
||||||
|
public function decodeStream($stream);
|
||||||
|
|
||||||
|
}
|
||||||
109
htdocs/includes/restler/Format/iFormat.php
Normal file
109
htdocs/includes/restler/Format/iFormat.php
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\Format;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for creating custom data formats
|
||||||
|
* like xml, json, yaml, amf etc
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage format
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
interface iFormat
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get MIME type => Extension mappings as an associative array
|
||||||
|
*
|
||||||
|
* @return array list of mime strings for the format
|
||||||
|
* @example array('application/json'=>'json');
|
||||||
|
*/
|
||||||
|
public function getMIMEMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected MIME type
|
||||||
|
*
|
||||||
|
* @param string $mime
|
||||||
|
* MIME type
|
||||||
|
*/
|
||||||
|
public function setMIME($mime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content-Type field of the HTTP header can send a charset
|
||||||
|
* parameter in the HTTP header to specify the character
|
||||||
|
* encoding of the document.
|
||||||
|
* This information is passed
|
||||||
|
* here so that Format class can encode data accordingly
|
||||||
|
* Format class may choose to ignore this and use its
|
||||||
|
* default character set.
|
||||||
|
*
|
||||||
|
* @param string $charset
|
||||||
|
* Example utf-8
|
||||||
|
*/
|
||||||
|
public function setCharset($charset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content-Type accepted by the Format class
|
||||||
|
*
|
||||||
|
* @return string $charset Example utf-8
|
||||||
|
*/
|
||||||
|
public function getCharset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get selected MIME type
|
||||||
|
*/
|
||||||
|
public function getMIME();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected file extension
|
||||||
|
*
|
||||||
|
* @param string $extension
|
||||||
|
* file extension
|
||||||
|
*/
|
||||||
|
public function setExtension($extension);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the selected file extension
|
||||||
|
*
|
||||||
|
* @return string file extension
|
||||||
|
*/
|
||||||
|
public function getExtension();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @return string encoded string
|
||||||
|
*/
|
||||||
|
public function encode($data, $humanReadable = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the given data from the format
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* data sent from client to
|
||||||
|
* the api in the given format.
|
||||||
|
* @return array associative array of the parsed data
|
||||||
|
*/
|
||||||
|
public function decode($data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return boolean is parsing the request supported?
|
||||||
|
*/
|
||||||
|
public function isReadable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return boolean is composing response supported?
|
||||||
|
*/
|
||||||
|
public function isWritable();
|
||||||
|
}
|
||||||
|
|
||||||
129
htdocs/includes/restler/HumanReadableCache.php
Normal file
129
htdocs/includes/restler/HumanReadableCache.php
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Cache that writes/reads human readable files for caching purpose
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class HumanReadableCache implements iCache
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string path of the folder to hold cache files
|
||||||
|
*/
|
||||||
|
public static $cacheDir;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if (is_null(self::$cacheDir)) {
|
||||||
|
self::$cacheDir = Defaults::$cacheDirectory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* store data in the cache
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param mixed $data
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
* @return boolean true if successful
|
||||||
|
*/
|
||||||
|
public function set($name, $data)
|
||||||
|
{
|
||||||
|
if (is_array($data)) {
|
||||||
|
$s = '$o = array();' . PHP_EOL . PHP_EOL;
|
||||||
|
$s .= '// ** THIS IS AN AUTO GENERATED FILE.'
|
||||||
|
. ' DO NOT EDIT MANUALLY ** ';
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$s .= PHP_EOL . PHP_EOL .
|
||||||
|
"//==================== $key ===================="
|
||||||
|
. PHP_EOL . PHP_EOL;
|
||||||
|
if (is_array($value)) {
|
||||||
|
$s .= '$o[\'' . $key . '\'] = array();';
|
||||||
|
foreach ($value as $ke => $va) {
|
||||||
|
$s .= PHP_EOL . PHP_EOL . "//==== $key $ke ===="
|
||||||
|
. PHP_EOL . PHP_EOL;
|
||||||
|
$s .= '$o[\'' . $key . '\'][\'' . $ke . '\'] = ' .
|
||||||
|
str_replace(' ', ' ',
|
||||||
|
var_export($va, true)) . ';';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$s .= '$o[\'' . $key . '\'] = '
|
||||||
|
. var_export($value, true) . ';';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$s .= PHP_EOL . 'return $o;';
|
||||||
|
} else {
|
||||||
|
$s = 'return ' . var_export($data, true) . ';';
|
||||||
|
}
|
||||||
|
$file = $this->_file($name);
|
||||||
|
$r = @file_put_contents($file, "<?php $s");
|
||||||
|
@chmod($file, 0777);
|
||||||
|
if ($r === false) {
|
||||||
|
$this->throwException();
|
||||||
|
}
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retrieve data from the cache
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param bool $ignoreErrors
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get($name, $ignoreErrors = false)
|
||||||
|
{
|
||||||
|
$file = $this->_file($name);
|
||||||
|
if (file_exists($file)) {
|
||||||
|
return include($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete data from the cache
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param bool $ignoreErrors
|
||||||
|
*
|
||||||
|
* @return boolean true if successful
|
||||||
|
*/
|
||||||
|
public function clear($name, $ignoreErrors = false)
|
||||||
|
{
|
||||||
|
return @unlink($this->_file($name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if the given name is cached
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return boolean true if cached
|
||||||
|
*/
|
||||||
|
public function isCached($name)
|
||||||
|
{
|
||||||
|
return file_exists($this->_file($name));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _file($name)
|
||||||
|
{
|
||||||
|
return self::$cacheDir . '/' . $name . '.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function throwException()
|
||||||
|
{
|
||||||
|
throw new \Exception(
|
||||||
|
'The cache directory `'
|
||||||
|
. self::$cacheDir . '` should exist with write permission.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
8
htdocs/includes/restler/README.md
Normal file
8
htdocs/includes/restler/README.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Luracast Restler Framework
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Restler is a simple and effective multi-format Web API Server written in PHP.
|
||||||
|
|
||||||
|
This repository contains just the framework files for installing the framework core using composer require statements.
|
||||||
|
|
||||||
|
For more information, usage examples, pull requests, and issues go to the [Main Repository](https://github.com/Luracast/Restler)
|
||||||
53
htdocs/includes/restler/Redirect.php
Normal file
53
htdocs/includes/restler/Redirect.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
use Luracast\Restler\Format\JsonFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static class for handling redirection
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class Redirect
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
1006
htdocs/includes/restler/Resources.php
Normal file
1006
htdocs/includes/restler/Resources.php
Normal file
File diff suppressed because it is too large
Load Diff
138
htdocs/includes/restler/RestException.php
Normal file
138
htdocs/includes/restler/RestException.php
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special Exception for raising API errors
|
||||||
|
* that can be used in API methods
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage exception
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
*/
|
||||||
|
|
||||||
|
class RestException extends Exception
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* HTTP status codes
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public static $codes = array(
|
||||||
|
100 => 'Continue',
|
||||||
|
101 => 'Switching Protocols',
|
||||||
|
200 => 'OK',
|
||||||
|
201 => 'Created',
|
||||||
|
202 => 'Accepted',
|
||||||
|
203 => 'Non-Authoritative Information',
|
||||||
|
204 => 'No Content',
|
||||||
|
205 => 'Reset Content',
|
||||||
|
206 => 'Partial Content',
|
||||||
|
300 => 'Multiple Choices',
|
||||||
|
301 => 'Moved Permanently',
|
||||||
|
302 => 'Found',
|
||||||
|
303 => 'See Other',
|
||||||
|
304 => 'Not Modified',
|
||||||
|
305 => 'Use Proxy',
|
||||||
|
306 => '(Unused)',
|
||||||
|
307 => 'Temporary Redirect',
|
||||||
|
400 => 'Bad Request',
|
||||||
|
401 => 'Unauthorized',
|
||||||
|
402 => 'Payment Required',
|
||||||
|
403 => 'Forbidden',
|
||||||
|
404 => 'Not Found',
|
||||||
|
405 => 'Method Not Allowed',
|
||||||
|
406 => 'Not Acceptable',
|
||||||
|
407 => 'Proxy Authentication Required',
|
||||||
|
408 => 'Request Timeout',
|
||||||
|
409 => 'Conflict',
|
||||||
|
410 => 'Gone',
|
||||||
|
411 => 'Length Required',
|
||||||
|
412 => 'Precondition Failed',
|
||||||
|
413 => 'Request Entity Too Large',
|
||||||
|
414 => 'Request-URI Too Long',
|
||||||
|
415 => 'Unsupported Media Type',
|
||||||
|
416 => 'Requested Range Not Satisfiable',
|
||||||
|
417 => 'Expectation Failed',
|
||||||
|
429 => 'Too Many Requests', //still in draft but used for rate limiting
|
||||||
|
500 => 'Internal Server Error',
|
||||||
|
501 => 'Not Implemented',
|
||||||
|
502 => 'Bad Gateway',
|
||||||
|
503 => 'Service Unavailable',
|
||||||
|
504 => 'Gateway Timeout',
|
||||||
|
505 => 'HTTP Version Not Supported'
|
||||||
|
);
|
||||||
|
private $details;
|
||||||
|
private $stage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $httpStatusCode http status code
|
||||||
|
* @param string|null $errorMessage error message
|
||||||
|
* @param array $details any extra detail about the exception
|
||||||
|
* @param Exception $previous previous exception if any
|
||||||
|
*/
|
||||||
|
public function __construct($httpStatusCode, $errorMessage = null, array $details = array(), Exception $previous = null)
|
||||||
|
{
|
||||||
|
$events = Scope::get('Restler')->getEvents();
|
||||||
|
if(count($events)<= 1){
|
||||||
|
$this->stage = 'setup';
|
||||||
|
} else {
|
||||||
|
$this->stage = $previous ? $events[count($events)-2] : end($events);
|
||||||
|
}
|
||||||
|
$this->details = $details;
|
||||||
|
parent::__construct($errorMessage, $httpStatusCode, $previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get extra details about the exception
|
||||||
|
*
|
||||||
|
* @return array details array
|
||||||
|
*/
|
||||||
|
public function getDetails()
|
||||||
|
{
|
||||||
|
return $this->details;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStage()
|
||||||
|
{
|
||||||
|
return $this->stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStages()
|
||||||
|
{
|
||||||
|
$e = Scope::get('Restler')->getEvents();
|
||||||
|
$i = array_search($this->stage, $e);
|
||||||
|
return array(
|
||||||
|
'success' => array_slice($e, 0, $i),
|
||||||
|
'failure' => array_slice($e, $i),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getErrorMessage()
|
||||||
|
{
|
||||||
|
$statusCode = $this->getCode();
|
||||||
|
$message = $this->getMessage();
|
||||||
|
if (isset(RestException::$codes[$statusCode])) {
|
||||||
|
$message = RestException::$codes[$statusCode] .
|
||||||
|
(empty($message) ? '' : ': ' . $message);
|
||||||
|
}
|
||||||
|
return $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSource()
|
||||||
|
{
|
||||||
|
$e = $this;
|
||||||
|
while ($e->getPrevious()) {
|
||||||
|
$e = $e->getPrevious();
|
||||||
|
}
|
||||||
|
return basename($e->getFile()) . ':'
|
||||||
|
. $e->getLine() . ' at '
|
||||||
|
. $this->getStage() . ' stage';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
1451
htdocs/includes/restler/Restler.php
Normal file
1451
htdocs/includes/restler/Restler.php
Normal file
File diff suppressed because it is too large
Load Diff
696
htdocs/includes/restler/Routes.php
Normal file
696
htdocs/includes/restler/Routes.php
Normal file
@@ -0,0 +1,696 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
use Luracast\Restler\Data\ApiMethodInfo;
|
||||||
|
use Luracast\Restler\Data\String;
|
||||||
|
use ReflectionClass;
|
||||||
|
use ReflectionMethod;
|
||||||
|
use ReflectionProperty;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Router class that routes the urls to api methods along with parameters
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class Routes
|
||||||
|
{
|
||||||
|
public static $prefixingParameterNames = array(
|
||||||
|
'id'
|
||||||
|
);
|
||||||
|
protected static $routes = array();
|
||||||
|
|
||||||
|
protected static $models = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route the public and protected methods of an Api class
|
||||||
|
*
|
||||||
|
* @param string $className
|
||||||
|
* @param string $resourcePath
|
||||||
|
* @param int $version
|
||||||
|
*
|
||||||
|
* @throws RestException
|
||||||
|
*/
|
||||||
|
public static function addAPIClass($className, $resourcePath = '', $version = 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mapping Rules
|
||||||
|
* =============
|
||||||
|
*
|
||||||
|
* - Optional parameters should not be mapped to URL
|
||||||
|
* - If a required parameter is of primitive type
|
||||||
|
* - If one of the self::$prefixingParameterNames
|
||||||
|
* - Map it to URL
|
||||||
|
* - Else If request method is POST/PUT/PATCH
|
||||||
|
* - Map it to body
|
||||||
|
* - Else If request method is GET/DELETE
|
||||||
|
* - Map it to body
|
||||||
|
* - If a required parameter is not primitive type
|
||||||
|
* - Do not include it in URL
|
||||||
|
*/
|
||||||
|
$class = new ReflectionClass($className);
|
||||||
|
try {
|
||||||
|
$classMetadata = CommentParser::parse($class->getDocComment());
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new RestException(500, "Error while parsing comments of `$className` class. " . $e->getMessage());
|
||||||
|
}
|
||||||
|
$classMetadata['scope'] = $scope = static::scope($class);
|
||||||
|
$methods = $class->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();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$metadata = CommentParser::parse($doc) + $classMetadata;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new RestException(500, "Error while parsing comments of `{$className}::{$method->getName()}` method. " . $e->getMessage());
|
||||||
|
}
|
||||||
|
//@access should not be private
|
||||||
|
if (isset($metadata['access'])
|
||||||
|
&& $metadata['access'] == 'private'
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$arguments = array();
|
||||||
|
$defaults = array();
|
||||||
|
$params = $method->getParameters();
|
||||||
|
$position = 0;
|
||||||
|
$pathParams = array();
|
||||||
|
$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();
|
||||||
|
}
|
||||||
|
if (isset($metadata['return']['type'])) {
|
||||||
|
if ($qualified = Scope::resolve($metadata['return']['type'], $scope))
|
||||||
|
list($metadata['return']['type'], $metadata['return']['children']) =
|
||||||
|
static::getTypeAndModel(new ReflectionClass($qualified), $scope);
|
||||||
|
} else {
|
||||||
|
//assume return type is array
|
||||||
|
$metadata['return']['type'] = 'array';
|
||||||
|
}
|
||||||
|
foreach ($params as $param) {
|
||||||
|
$children = array();
|
||||||
|
$type =
|
||||||
|
$param->isArray() ? 'array' : $param->getClass();
|
||||||
|
$arguments[$param->getName()] = $position;
|
||||||
|
$defaults[$position] = $param->isDefaultValueAvailable() ?
|
||||||
|
$param->getDefaultValue() : null;
|
||||||
|
if (!isset($metadata['param'][$position])) {
|
||||||
|
$metadata['param'][$position] = array();
|
||||||
|
}
|
||||||
|
$m = & $metadata ['param'] [$position];
|
||||||
|
$m ['name'] = $param->getName();
|
||||||
|
if (empty($m['label']))
|
||||||
|
$m['label'] = static::label($m['name']);
|
||||||
|
if (is_null($type) && isset($m['type'])) {
|
||||||
|
$type = $m['type'];
|
||||||
|
}
|
||||||
|
if ($m['name'] == 'email' && empty($m[CommentParser::$embeddedDataName]['type']) && $type == 'string')
|
||||||
|
$m[CommentParser::$embeddedDataName]['type'] = 'email';
|
||||||
|
$m ['default'] = $defaults [$position];
|
||||||
|
$m ['required'] = !$param->isOptional();
|
||||||
|
$contentType = Util::nestedValue(
|
||||||
|
$m,
|
||||||
|
CommentParser::$embeddedDataName,
|
||||||
|
'type'
|
||||||
|
);
|
||||||
|
if ($contentType && $qualified = Scope::resolve($contentType, $scope)) {
|
||||||
|
list($m[CommentParser::$embeddedDataName]['type'], $children) = static::getTypeAndModel(
|
||||||
|
new ReflectionClass($qualified), $scope
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($type instanceof ReflectionClass) {
|
||||||
|
list($type, $children) = static::getTypeAndModel($type, $scope);
|
||||||
|
} elseif ($type && is_string($type) && $qualified = Scope::resolve($type, $scope)) {
|
||||||
|
list($type, $children)
|
||||||
|
= static::getTypeAndModel(new ReflectionClass($qualified), $scope);
|
||||||
|
}
|
||||||
|
if (isset($type)) {
|
||||||
|
$m['type'] = $type;
|
||||||
|
}
|
||||||
|
$m['children'] = $children;
|
||||||
|
|
||||||
|
if ($m['name'] == Defaults::$fullRequestDataName) {
|
||||||
|
$from = 'body';
|
||||||
|
if (!isset($m['type'])) {
|
||||||
|
$type = $m['type'] = 'array';
|
||||||
|
}
|
||||||
|
|
||||||
|
} elseif (isset($m[CommentParser::$embeddedDataName]['from'])) {
|
||||||
|
$from = $m[CommentParser::$embeddedDataName]['from'];
|
||||||
|
} else {
|
||||||
|
if ((isset($type) && Util::isObjectOrArray($type))
|
||||||
|
) {
|
||||||
|
$from = 'body';
|
||||||
|
if (!isset($type)) {
|
||||||
|
$type = $m['type'] = 'array';
|
||||||
|
}
|
||||||
|
} elseif ($m['required'] && in_array($m['name'], static::$prefixingParameterNames)) {
|
||||||
|
$from = 'path';
|
||||||
|
} else {
|
||||||
|
$from = 'body';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$m[CommentParser::$embeddedDataName]['from'] = $from;
|
||||||
|
if (!isset($m['type'])) {
|
||||||
|
$type = $m['type'] = static::type($defaults[$position]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($allowAmbiguity || $from == 'path') {
|
||||||
|
$pathParams [] = $position;
|
||||||
|
}
|
||||||
|
$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(
|
||||||
|
'url' => null,
|
||||||
|
'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], '/');
|
||||||
|
//deep copy the call, as it may change for each @url
|
||||||
|
$copy = unserialize(serialize($call));
|
||||||
|
foreach ($copy['metadata']['param'] as $i => $p) {
|
||||||
|
$inPath =
|
||||||
|
strpos($url, '{' . $p['name'] . '}') ||
|
||||||
|
strpos($url, ':' . $p['name']);
|
||||||
|
if ($inPath) {
|
||||||
|
$copy['metadata']['param'][$i][CommentParser::$embeddedDataName]['from'] = 'path';
|
||||||
|
} elseif ($httpMethod == 'GET' || $httpMethod == 'DELETE') {
|
||||||
|
$copy['metadata']['param'][$i][CommentParser::$embeddedDataName]['from'] = 'query';
|
||||||
|
} elseif ($p[CommentParser::$embeddedDataName]['from'] == 'path') {
|
||||||
|
$copy['metadata']['param'][$i][CommentParser::$embeddedDataName]['from'] = 'body';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$url = preg_replace_callback('/{[^}]+}|:[^\/]+/',
|
||||||
|
function ($matches) use ($call) {
|
||||||
|
$match = trim($matches[0], '{}:');
|
||||||
|
$index = $call['arguments'][$match];
|
||||||
|
return '{' .
|
||||||
|
Routes::typeChar(isset(
|
||||||
|
$call['metadata']['param'][$index]['type'])
|
||||||
|
? $call['metadata']['param'][$index]['type']
|
||||||
|
: null)
|
||||||
|
. $index . '}';
|
||||||
|
}, $url);
|
||||||
|
static::addPath($url, $copy, $httpMethod, $version);
|
||||||
|
}
|
||||||
|
//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;
|
||||||
|
$lastPathParam = array_keys($pathParams);
|
||||||
|
$lastPathParam = end($lastPathParam);
|
||||||
|
for ($position = 0; $position < count($params); $position++) {
|
||||||
|
$from = $metadata['param'][$position][CommentParser::$embeddedDataName]['from'];
|
||||||
|
if ($from == 'body' && ($httpMethod == 'GET' ||
|
||||||
|
$httpMethod == 'DELETE')
|
||||||
|
) {
|
||||||
|
$call['metadata']['param'][$position][CommentParser::$embeddedDataName]['from']
|
||||||
|
= 'query';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($pathParams) || $allowAmbiguity) {
|
||||||
|
static::addPath($url, $call, $httpMethod, $version);
|
||||||
|
}
|
||||||
|
foreach ($pathParams as $position) {
|
||||||
|
if (!empty($url))
|
||||||
|
$url .= '/';
|
||||||
|
$url .= '{' .
|
||||||
|
static::typeChar(isset($call['metadata']['param'][$position]['type'])
|
||||||
|
? $call['metadata']['param'][$position]['type']
|
||||||
|
: null)
|
||||||
|
. $position . '}';
|
||||||
|
if ($allowAmbiguity || $position == $lastPathParam) {
|
||||||
|
static::addPath($url, $call, $httpMethod, $version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
public static function typeChar($type = null)
|
||||||
|
{
|
||||||
|
if (!$type) {
|
||||||
|
return 's';
|
||||||
|
}
|
||||||
|
switch ($type{0}) {
|
||||||
|
case 'i':
|
||||||
|
case 'f':
|
||||||
|
return 'n';
|
||||||
|
}
|
||||||
|
return 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function addPath($path, array $call,
|
||||||
|
$httpMethod = 'GET', $version = 1)
|
||||||
|
{
|
||||||
|
$call['url'] = preg_replace_callback(
|
||||||
|
"/\{\S(\d+)\}/",
|
||||||
|
function ($matches) use ($call) {
|
||||||
|
return '{' .
|
||||||
|
$call['metadata']['param'][$matches[1]]['name'] . '}';
|
||||||
|
},
|
||||||
|
$path
|
||||||
|
);
|
||||||
|
//check for wildcard routes
|
||||||
|
if (substr($path, -1, 1) == '*') {
|
||||||
|
$path = rtrim($path, '/*');
|
||||||
|
static::$routes["v$version"]['*'][$path][$httpMethod] = $call;
|
||||||
|
} else {
|
||||||
|
static::$routes["v$version"][$path][$httpMethod] = $call;
|
||||||
|
//create an alias with index if the method name is index
|
||||||
|
if ($call['methodName'] == 'index')
|
||||||
|
static::$routes["v$version"][ltrim("$path/index", '/')][$httpMethod] = $call;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the api method for the given url and http method
|
||||||
|
*
|
||||||
|
* @param string $path Requested url path
|
||||||
|
* @param string $httpMethod GET|POST|PUT|PATCH|DELETE etc
|
||||||
|
* @param int $version Api Version number
|
||||||
|
* @param array $data Data collected from the request
|
||||||
|
*
|
||||||
|
* @throws RestException
|
||||||
|
* @return ApiMethodInfo
|
||||||
|
*/
|
||||||
|
public static function find($path, $httpMethod,
|
||||||
|
$version = 1, array $data = array())
|
||||||
|
{
|
||||||
|
$p = Util::nestedValue(static::$routes, "v$version");
|
||||||
|
if (!$p) {
|
||||||
|
throw new RestException(
|
||||||
|
404,
|
||||||
|
$version == 1 ? '' : "Version $version is not supported"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$status = 404;
|
||||||
|
$message = null;
|
||||||
|
$methods = array();
|
||||||
|
if (isset($p[$path][$httpMethod])) {
|
||||||
|
//================== static routes ==========================
|
||||||
|
return static::populate($p[$path][$httpMethod], $data);
|
||||||
|
} elseif (isset($p['*'])) {
|
||||||
|
//================== wildcard routes ========================
|
||||||
|
uksort($p['*'], function ($a, $b) {
|
||||||
|
return strlen($b) - strlen($a);
|
||||||
|
});
|
||||||
|
foreach ($p['*'] as $key => $value) {
|
||||||
|
if (strpos($path, $key) === 0 && isset($value[$httpMethod])) {
|
||||||
|
//path found, convert rest of the path to parameters
|
||||||
|
$path = substr($path, strlen($key) + 1);
|
||||||
|
$call = ApiMethodInfo::__set_state($value[$httpMethod]);
|
||||||
|
$call->parameters = empty($path)
|
||||||
|
? array()
|
||||||
|
: explode('/', $path);
|
||||||
|
return $call;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//================== dynamic routes =============================
|
||||||
|
//add newline char if trailing slash is found
|
||||||
|
if (substr($path, -1) == '/')
|
||||||
|
$path .= PHP_EOL;
|
||||||
|
//if double slash is found fill in newline char;
|
||||||
|
$path = str_replace('//', '/' . PHP_EOL . '/', $path);
|
||||||
|
ksort($p);
|
||||||
|
foreach ($p as $key => $value) {
|
||||||
|
if (!isset($value[$httpMethod])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$regex = str_replace(array('{', '}'),
|
||||||
|
array('(?P<', '>[^/]+)'), $key);
|
||||||
|
if (preg_match_all(":^$regex$:i", $path, $matches, PREG_SET_ORDER)) {
|
||||||
|
$matches = $matches[0];
|
||||||
|
$found = true;
|
||||||
|
foreach ($matches as $k => $v) {
|
||||||
|
if (is_numeric($k)) {
|
||||||
|
unset($matches[$k]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$index = intval(substr($k, 1));
|
||||||
|
$details = $value[$httpMethod]['metadata']['param'][$index];
|
||||||
|
if ($k{0} == 's' || strpos($k, static::pathVarTypeOf($v)) === 0) {
|
||||||
|
//remove the newlines
|
||||||
|
$data[$details['name']] = trim($v, PHP_EOL);
|
||||||
|
} else {
|
||||||
|
$status = 400;
|
||||||
|
$message = 'invalid value specified for `'
|
||||||
|
. $details['name'] . '`';
|
||||||
|
$found = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($found) {
|
||||||
|
return static::populate($value[$httpMethod], $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($status == 404) {
|
||||||
|
//check if other methods are allowed
|
||||||
|
if (isset($p[$path])) {
|
||||||
|
$status = 405;
|
||||||
|
$methods = array_keys($p[$path]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($status == 405) {
|
||||||
|
header('Allow: ' . implode(', ', $methods));
|
||||||
|
}
|
||||||
|
throw new RestException($status, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates the parameter values
|
||||||
|
*
|
||||||
|
* @param array $call
|
||||||
|
* @param $data
|
||||||
|
*
|
||||||
|
* @return ApiMethodInfo
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
protected static function populate(array $call, $data)
|
||||||
|
{
|
||||||
|
$call['parameters'] = $call['defaults'];
|
||||||
|
$p = & $call['parameters'];
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
if (isset($call['arguments'][$key])) {
|
||||||
|
$p[$call['arguments'][$key]] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Defaults::$smartParameterParsing && 'post' != (string)Util::$restler->requestFormat) {
|
||||||
|
if (
|
||||||
|
count($p) == 1 &&
|
||||||
|
($m = Util::nestedValue($call, 'metadata', 'param', 0)) &&
|
||||||
|
!array_key_exists($m['name'], $data) &&
|
||||||
|
array_key_exists(Defaults::$fullRequestDataName, $data) &&
|
||||||
|
!is_null($d = $data[Defaults::$fullRequestDataName]) &&
|
||||||
|
isset($m['type']) &&
|
||||||
|
static::typeMatch($m['type'], $d)
|
||||||
|
) {
|
||||||
|
$p[0] = $d;
|
||||||
|
} else {
|
||||||
|
$bodyParamCount = 0;
|
||||||
|
$lastBodyParamIndex = -1;
|
||||||
|
$lastM = null;
|
||||||
|
foreach ($call['metadata']['param'] as $k => $m) {
|
||||||
|
if ($m[CommentParser::$embeddedDataName]['from'] == 'body') {
|
||||||
|
$bodyParamCount++;
|
||||||
|
$lastBodyParamIndex = $k;
|
||||||
|
$lastM = $m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
$bodyParamCount == 1 &&
|
||||||
|
!array_key_exists($lastM['name'], $data) &&
|
||||||
|
array_key_exists(Defaults::$fullRequestDataName, $data) &&
|
||||||
|
!is_null($d = $data[Defaults::$fullRequestDataName])
|
||||||
|
) {
|
||||||
|
$p[$lastBodyParamIndex] = $d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$r = ApiMethodInfo::__set_state($call);
|
||||||
|
$modifier = "_modify_{$r->methodName}_api";
|
||||||
|
if (method_exists($r->className, $modifier)) {
|
||||||
|
$stage = end(Scope::get('Restler')->getEvents());
|
||||||
|
if (empty($stage))
|
||||||
|
$stage = 'setup';
|
||||||
|
$r = Scope::get($r->className)->$modifier($r, $stage) ? : $r;
|
||||||
|
}
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
protected static function pathVarTypeOf($var)
|
||||||
|
{
|
||||||
|
if (is_numeric($var)) {
|
||||||
|
return 'n';
|
||||||
|
}
|
||||||
|
if ($var === 'true' || $var === 'false') {
|
||||||
|
return 'b';
|
||||||
|
}
|
||||||
|
return 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function typeMatch($type, $var)
|
||||||
|
{
|
||||||
|
switch ($type) {
|
||||||
|
case 'boolean':
|
||||||
|
case 'bool':
|
||||||
|
return is_bool($var);
|
||||||
|
case 'array':
|
||||||
|
case 'object':
|
||||||
|
return is_array($var);
|
||||||
|
case 'string':
|
||||||
|
case 'int':
|
||||||
|
case 'integer':
|
||||||
|
case 'float':
|
||||||
|
case 'number':
|
||||||
|
return is_scalar($var);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import previously created routes from cache
|
||||||
|
*
|
||||||
|
* @param array $routes
|
||||||
|
*/
|
||||||
|
public static function fromArray(array $routes)
|
||||||
|
{
|
||||||
|
static::$routes = $routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export current routes for cache
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function toArray()
|
||||||
|
{
|
||||||
|
return static::$routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function type($var)
|
||||||
|
{
|
||||||
|
if (is_object($var)) return get_class($var);
|
||||||
|
if (is_array($var)) return 'array';
|
||||||
|
if (is_bool($var)) return 'boolean';
|
||||||
|
if (is_numeric($var)) return is_float($var) ? 'float' : 'int';
|
||||||
|
return 'string';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a label from name of the parameter or property
|
||||||
|
*
|
||||||
|
* Convert `camelCase` style names into proper `Title Case` names
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function label($name)
|
||||||
|
{
|
||||||
|
return ucfirst(preg_replace(array('/(?<=[^A-Z])([A-Z])/', '/(?<=[^0-9])([0-9])/'), ' $0', $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function scope(ReflectionClass $class)
|
||||||
|
{
|
||||||
|
$namespace = $class->getNamespaceName();
|
||||||
|
$imports = array(
|
||||||
|
'*' => empty($namespace) ? '' : $namespace . '\\'
|
||||||
|
);
|
||||||
|
$file = file_get_contents($class->getFileName());
|
||||||
|
$tokens = token_get_all($file);
|
||||||
|
$namespace = '';
|
||||||
|
$alias = '';
|
||||||
|
$reading = false;
|
||||||
|
$last = 0;
|
||||||
|
foreach ($tokens as $token) {
|
||||||
|
if (is_string($token)) {
|
||||||
|
if ($reading && ',' == $token) {
|
||||||
|
//===== STOP =====//
|
||||||
|
$reading = false;
|
||||||
|
if (!empty($namespace))
|
||||||
|
$imports[$alias] = trim($namespace, '\\');
|
||||||
|
//===== START =====//
|
||||||
|
$reading = true;
|
||||||
|
$namespace = '';
|
||||||
|
$alias = '';
|
||||||
|
} else {
|
||||||
|
//===== STOP =====//
|
||||||
|
$reading = false;
|
||||||
|
if (!empty($namespace))
|
||||||
|
$imports[$alias] = trim($namespace, '\\');
|
||||||
|
}
|
||||||
|
} elseif (T_USE == $token[0]) {
|
||||||
|
//===== START =====//
|
||||||
|
$reading = true;
|
||||||
|
$namespace = '';
|
||||||
|
$alias = '';
|
||||||
|
} elseif ($reading) {
|
||||||
|
//echo token_name($token[0]) . ' ' . $token[1] . PHP_EOL;
|
||||||
|
switch ($token[0]) {
|
||||||
|
case T_WHITESPACE:
|
||||||
|
continue 2;
|
||||||
|
case T_STRING:
|
||||||
|
$alias = $token[1];
|
||||||
|
if (T_AS == $last) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//don't break;
|
||||||
|
case T_NS_SEPARATOR:
|
||||||
|
$namespace .= $token[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$last = $token[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $imports;
|
||||||
|
}
|
||||||
|
}
|
||||||
190
htdocs/includes/restler/Scope.php
Normal file
190
htdocs/includes/restler/Scope.php
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope resolution class, manages instantiation and acts as a dependency
|
||||||
|
* injection container
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class Scope
|
||||||
|
{
|
||||||
|
public static $classAliases = array(
|
||||||
|
|
||||||
|
//Core
|
||||||
|
'Restler' => 'Luracast\Restler\Restler',
|
||||||
|
|
||||||
|
//Format classes
|
||||||
|
'AmfFormat' => 'Luracast\Restler\Format\AmfFormat',
|
||||||
|
'JsFormat' => 'Luracast\Restler\Format\JsFormat',
|
||||||
|
'JsonFormat' => 'Luracast\Restler\Format\JsonFormat',
|
||||||
|
'HtmlFormat' => 'Luracast\Restler\Format\HtmlFormat',
|
||||||
|
'PlistFormat' => 'Luracast\Restler\Format\PlistFormat',
|
||||||
|
'UploadFormat' => 'Luracast\Restler\Format\UploadFormat',
|
||||||
|
'UrlEncodedFormat' => 'Luracast\Restler\Format\UrlEncodedFormat',
|
||||||
|
'XmlFormat' => 'Luracast\Restler\Format\XmlFormat',
|
||||||
|
'YamlFormat' => 'Luracast\Restler\Format\YamlFormat',
|
||||||
|
'CsvFormat' => 'Luracast\Restler\Format\CsvFormat',
|
||||||
|
'TsvFormat' => 'Luracast\Restler\Format\TsvFormat',
|
||||||
|
|
||||||
|
//Filter classes
|
||||||
|
'RateLimit' => 'Luracast\Restler\Filter\RateLimit',
|
||||||
|
|
||||||
|
//UI classes
|
||||||
|
'Forms' => 'Luracast\Restler\UI\Forms',
|
||||||
|
'Nav' => 'Luracast\Restler\UI\Nav',
|
||||||
|
'Emmet' => 'Luracast\Restler\UI\Emmet',
|
||||||
|
'T' => 'Luracast\Restler\UI\Tags',
|
||||||
|
|
||||||
|
//API classes
|
||||||
|
'Resources' => 'Luracast\Restler\Resources',
|
||||||
|
|
||||||
|
//Cache classes
|
||||||
|
'HumanReadableCache' => 'Luracast\Restler\HumanReadableCache',
|
||||||
|
'ApcCache' => 'Luracast\Restler\ApcCache',
|
||||||
|
|
||||||
|
//Utility classes
|
||||||
|
'Object' => 'Luracast\Restler\Data\Object',
|
||||||
|
'String' => 'Luracast\Restler\Data\String',
|
||||||
|
'Arr' => 'Luracast\Restler\Data\Arr',
|
||||||
|
|
||||||
|
//Exception
|
||||||
|
'RestException' => 'Luracast\Restler\RestException'
|
||||||
|
);
|
||||||
|
public static $properties = array();
|
||||||
|
protected static $instances = array();
|
||||||
|
protected static $registry = array();
|
||||||
|
|
||||||
|
public static function register($name, Callable $function, $singleton = true)
|
||||||
|
{
|
||||||
|
static::$registry[$name] = (object)compact('function', 'singleton');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function set($name, $instance)
|
||||||
|
{
|
||||||
|
static::$instances[$name] = (object)array('instance' => $instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get($name)
|
||||||
|
{
|
||||||
|
$r = null;
|
||||||
|
$initialized = false;
|
||||||
|
$properties = array();
|
||||||
|
if (array_key_exists($name, static::$instances)) {
|
||||||
|
$initialized = true;
|
||||||
|
$r = static::$instances[$name]->instance;
|
||||||
|
} elseif (!empty(static::$registry[$name])) {
|
||||||
|
$function = static::$registry[$name]->function;
|
||||||
|
$r = $function();
|
||||||
|
if (static::$registry[$name]->singleton)
|
||||||
|
static::$instances[$name] = (object)array('instance' => $r);
|
||||||
|
} else {
|
||||||
|
$fullName = $name;
|
||||||
|
if (isset(static::$classAliases[$name])) {
|
||||||
|
$fullName = static::$classAliases[$name];
|
||||||
|
}
|
||||||
|
if (class_exists($fullName)) {
|
||||||
|
$shortName = Util::getShortName($name);
|
||||||
|
$r = new $fullName();
|
||||||
|
static::$instances[$name] = (object)array('instance' => $r);
|
||||||
|
if ($name != 'Restler') {
|
||||||
|
$r->restler = static::get('Restler');
|
||||||
|
$m = Util::nestedValue($r->restler, 'apiMethodInfo', 'metadata');
|
||||||
|
if ($m) {
|
||||||
|
$properties = Util::nestedValue(
|
||||||
|
$m, 'class', $fullName,
|
||||||
|
CommentParser::$embeddedDataName
|
||||||
|
) ? : (Util::nestedValue(
|
||||||
|
$m, 'class', $shortName,
|
||||||
|
CommentParser::$embeddedDataName
|
||||||
|
) ? : array());
|
||||||
|
} else {
|
||||||
|
static::$instances[$name]->initPending = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
$r instanceof iUseAuthentication &&
|
||||||
|
static::get('Restler')->_authVerified &&
|
||||||
|
!isset(static::$instances[$name]->authVerified)
|
||||||
|
) {
|
||||||
|
static::$instances[$name]->authVerified = true;
|
||||||
|
$r->__setAuthenticationStatus
|
||||||
|
(static::get('Restler')->_authenticated);
|
||||||
|
}
|
||||||
|
if (isset(static::$instances[$name]->initPending)) {
|
||||||
|
$m = Util::nestedValue(static::get('Restler'), 'apiMethodInfo', 'metadata');
|
||||||
|
$fullName = $name;
|
||||||
|
if (class_exists($name)) {
|
||||||
|
$shortName = Util::getShortName($name);
|
||||||
|
} else {
|
||||||
|
$shortName = $name;
|
||||||
|
if (isset(static::$classAliases[$name]))
|
||||||
|
$fullName = static::$classAliases[$name];
|
||||||
|
}
|
||||||
|
if ($m) {
|
||||||
|
$properties = Util::nestedValue(
|
||||||
|
$m, 'class', $fullName,
|
||||||
|
CommentParser::$embeddedDataName
|
||||||
|
) ? : (Util::nestedValue(
|
||||||
|
$m, 'class', $shortName,
|
||||||
|
CommentParser::$embeddedDataName
|
||||||
|
) ? : array());
|
||||||
|
unset(static::$instances[$name]->initPending);
|
||||||
|
$initialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$initialized && is_object($r)) {
|
||||||
|
$properties += static::$properties;
|
||||||
|
$objectVars = get_object_vars($r);
|
||||||
|
$className = get_class($r);
|
||||||
|
foreach ($properties as $property => $value) {
|
||||||
|
if (property_exists($className, $property)) {
|
||||||
|
//if not a static property
|
||||||
|
array_key_exists($property, $objectVars)
|
||||||
|
? $r->{$property} = $value
|
||||||
|
: $r::$$property = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get fully qualified class name for the given scope
|
||||||
|
*
|
||||||
|
* @param string $className
|
||||||
|
* @param array $scope local scope
|
||||||
|
*
|
||||||
|
* @return string|boolean returns the class name or false
|
||||||
|
*/
|
||||||
|
public static function resolve($className, array $scope)
|
||||||
|
{
|
||||||
|
if (empty($className) || !is_string($className))
|
||||||
|
return false;
|
||||||
|
$divider = '\\';
|
||||||
|
$qualified = false;
|
||||||
|
if ($className{0} == $divider) {
|
||||||
|
$qualified = trim($className, $divider);
|
||||||
|
} elseif (array_key_exists($className, $scope)) {
|
||||||
|
$qualified = $scope[$className];
|
||||||
|
} else {
|
||||||
|
$qualified = $scope['*'] . $className;
|
||||||
|
}
|
||||||
|
if (class_exists($qualified))
|
||||||
|
return $qualified;
|
||||||
|
if (isset(static::$classAliases[$className])) {
|
||||||
|
$qualified = static::$classAliases[$className];
|
||||||
|
if (class_exists($qualified))
|
||||||
|
return $qualified;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
383
htdocs/includes/restler/UI/Emmet.php
Normal file
383
htdocs/includes/restler/UI/Emmet.php
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\UI;
|
||||||
|
|
||||||
|
use Luracast\Restler\UI\Tags as T;
|
||||||
|
use Luracast\Restler\Util;
|
||||||
|
|
||||||
|
class Emmet
|
||||||
|
{
|
||||||
|
const DELIMITERS = '.#*>+^[=" ]{$@-#}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
case '#':
|
||||||
|
$offsetTokens = array_values($tokens);
|
||||||
|
array_unshift($offsetTokens, '#');
|
||||||
|
$implicitTag();
|
||||||
|
$tag->id(
|
||||||
|
$parseText(
|
||||||
|
array_shift($tokens), $round, $total, $data
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
//attributes
|
||||||
|
case '[':
|
||||||
|
$offsetTokens = array_values($tokens);
|
||||||
|
array_unshift($offsetTokens, '[');
|
||||||
|
$implicitTag();
|
||||||
|
$parseAttributes(
|
||||||
|
$parseAttributes, $round, $total, $data
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
//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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function tokenize($string)
|
||||||
|
{
|
||||||
|
$r = array();
|
||||||
|
$f = strtok($string, static::DELIMITERS);
|
||||||
|
$pos = 0;
|
||||||
|
do {
|
||||||
|
$start = $pos;
|
||||||
|
$pos = strpos($string, $f, $start);
|
||||||
|
$tokens = array();
|
||||||
|
for ($i = $start; $i < $pos; $i++) {
|
||||||
|
$token = $string{$i};
|
||||||
|
if (('#' == $token || '.' == $token) &&
|
||||||
|
(!empty($tokens) || $i == 0)
|
||||||
|
) {
|
||||||
|
$r[] = '';
|
||||||
|
}
|
||||||
|
$r[] = $tokens[] = $token;
|
||||||
|
}
|
||||||
|
$pos += strlen($f);
|
||||||
|
$r[] = $f;
|
||||||
|
} while (false != ($f = strtok(static::DELIMITERS)));
|
||||||
|
for ($i = $pos; $i < strlen($string); $i++) {
|
||||||
|
$token = $string{$i};
|
||||||
|
$r[] = $tokens[] = $token;
|
||||||
|
}
|
||||||
|
return $r;
|
||||||
|
/* sample output produced by ".row*3>.col*3"
|
||||||
|
[0] => div
|
||||||
|
[1] => .
|
||||||
|
[2] => row
|
||||||
|
[3] => *
|
||||||
|
[4] => 3
|
||||||
|
[5] => >
|
||||||
|
[6] => div
|
||||||
|
[7] => .
|
||||||
|
[8] => col
|
||||||
|
[9] => *
|
||||||
|
[10] => 4
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
59
htdocs/includes/restler/UI/FormStyles.php
Normal file
59
htdocs/includes/restler/UI/FormStyles.php
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\UI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for providing preset styles for html forms
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class FormStyles
|
||||||
|
{
|
||||||
|
public static $html = array(
|
||||||
|
'form' => 'form[role=form id=$id# name=$name# method=$method# action=$action# enctype=$enctype#]',
|
||||||
|
'input' => '.row>section>label{$label#}^input[name=$name# value=$value# type=$type# required=$required# autofocus=$autofocus# placeholder=$default# accept=$accept#]',
|
||||||
|
'textarea' => '.row>label{$label#}^textarea[name=$name# required=$required# autofocus=$autofocus# placeholder=$default# rows=3]{$value#}',
|
||||||
|
'radio' => '.row>section>label{$label#}^span>label*options>input[name=$name# value=$value# type=radio checked=$selected# required=$required#]+{ $text#}',
|
||||||
|
'select' => '.row>label{$label#}^select[name=$name# required=$required#]>option[value]+option[value=$value# selected=$selected#]{$text#}*options',
|
||||||
|
'submit' => '.row>label{ }^button[type=submit]{$label#}',
|
||||||
|
'fieldset' => 'fieldset>legend{$label#}',
|
||||||
|
'checkbox' => '.row>label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{$label#}',
|
||||||
|
//------------- TYPE BASED STYLES ---------------------//
|
||||||
|
'checkbox-array' => 'fieldset>legend{$label#}+section*options>label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{ $text#}',
|
||||||
|
'select-array' => 'label{$label#}+select[name=$name# required=$required# multiple style="height: auto;background-image: none; outline: inherit;"]>option[value=$value# selected=$selected#]{$text#}*options',
|
||||||
|
);
|
||||||
|
public static $bootstrap3 = array(
|
||||||
|
'form' => 'form[role=form id=$id# name=$name# method=$method# action=$action# enctype=$enctype#]',
|
||||||
|
'input' => '.form-group>label{$label#}+input.form-control[name=$name# value=$value# type=$type# required=$required# autofocus=$autofocus# placeholder=$default# accept=$accept#]',
|
||||||
|
'textarea' => '.form-group>label{$label#}+textarea.form-control[name=$name# required=$required# autofocus=$autofocus# placeholder=$default# rows=3]{$value#}',
|
||||||
|
'radio' => 'fieldset>legend{$label#}>.radio*options>label>input.radio[name=$name# value=$value# type=radio checked=$selected# required=$required#]{$text#}',
|
||||||
|
'select' => '.form-group>label{$label#}+select.form-control[name=$name# multiple=$multiple# required=$required#]>option[value]+option[value=$value# selected=$selected#]{$text#}*options',
|
||||||
|
'submit' => 'button.btn.btn-primary[type=submit]{$label#}',
|
||||||
|
'fieldset' => 'fieldset>legend{$label#}',
|
||||||
|
'checkbox' => '.checkbox>label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{$label#}',
|
||||||
|
//------------- TYPE BASED STYLES ---------------------//
|
||||||
|
'checkbox-array' => 'fieldset>legend{$label#}>.checkbox*options>label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required#]{$text#}',
|
||||||
|
'select-array' => '.form-group>label{$label#}+select.form-control[name=$name# multiple=$multiple# required=$required#] size=$options#>option[value=$value# selected=$selected#]{$text#}*options',
|
||||||
|
//------------- CUSTOM STYLES ---------------------//
|
||||||
|
'radio-inline' => '.form-group>label{$label# : }+label.radio-inline*options>input.radio[name=$name# value=$value# type=radio checked=$selected# required=$required#]+{$text#}',
|
||||||
|
);
|
||||||
|
public static $foundation5 = array(
|
||||||
|
'form' => 'form[id=$id# name=$name# method=$method# action=$action# enctype=$enctype#]',
|
||||||
|
'input' => 'label{$label#}+input[name=$name# value=$value# type=$type# required=$required# autofocus=$autofocus# placeholder=$default# accept=$accept#]',
|
||||||
|
'textarea' => 'label{$label#}+textarea[name=$name# required=$required# autofocus=$autofocus# placeholder=$default# rows=3]{$value#}',
|
||||||
|
'radio' => 'label{$label# : }+label.radio-inline*options>input.radio[name=$name# value=$value# type=radio checked=$selected# required=$required#]+{$text#}',
|
||||||
|
'select' => 'label{$label#}+select[name=$name# required=$required#]>option[value]+option[value=$value# selected=$selected#]{$text#}*options',
|
||||||
|
'submit' => 'button.button[type=submit]{$label#}',
|
||||||
|
'fieldset' => 'fieldset>legend{$label#}',
|
||||||
|
'checkbox' => 'label>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{ $label#}',
|
||||||
|
//------------- TYPE BASED STYLES ---------------------//
|
||||||
|
'checkbox-array' => 'fieldset>legend{$label#}+label*options>input[name=$name# value=$value# type=checkbox checked=$selected# required=$required# autofocus=$autofocus# accept=$accept#]+{ $text#}',
|
||||||
|
'select-array' => 'label{$label#}+select[name=$name# required=$required# multiple style="height: auto;background-image: none; outline: inherit;"]>option[value=$value# selected=$selected#]{$text#}*options',
|
||||||
|
//------------- CUSTOM STYLES ---------------------//
|
||||||
|
);
|
||||||
|
}
|
||||||
434
htdocs/includes/restler/UI/Forms.php
Normal file
434
htdocs/includes/restler/UI/Forms.php
Normal file
@@ -0,0 +1,434 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\UI;
|
||||||
|
|
||||||
|
use Luracast\Restler\CommentParser;
|
||||||
|
use Luracast\Restler\Data\ApiMethodInfo;
|
||||||
|
use Luracast\Restler\Data\String;
|
||||||
|
use Luracast\Restler\Data\ValidationInfo;
|
||||||
|
use Luracast\Restler\Defaults;
|
||||||
|
use Luracast\Restler\Format\UploadFormat;
|
||||||
|
use Luracast\Restler\Format\UrlEncodedFormat;
|
||||||
|
use Luracast\Restler\iFilter;
|
||||||
|
use Luracast\Restler\RestException;
|
||||||
|
use Luracast\Restler\Restler;
|
||||||
|
use Luracast\Restler\Routes;
|
||||||
|
use Luracast\Restler\Scope;
|
||||||
|
use Luracast\Restler\UI\Tags as T;
|
||||||
|
use Luracast\Restler\User;
|
||||||
|
use Luracast\Restler\Util;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for automatically generating forms for the given http method
|
||||||
|
* and api url
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class Forms implements iFilter
|
||||||
|
{
|
||||||
|
const FORM_KEY = 'form_key';
|
||||||
|
public static $filterFormRequestsOnly = false;
|
||||||
|
|
||||||
|
public static $excludedPaths = array();
|
||||||
|
|
||||||
|
public static $style;
|
||||||
|
/**
|
||||||
|
* @var bool should we fill up the form using given data?
|
||||||
|
*/
|
||||||
|
public static $preFill = true;
|
||||||
|
/**
|
||||||
|
* @var ValidationInfo
|
||||||
|
*/
|
||||||
|
public static $validationInfo = null;
|
||||||
|
protected static $inputTypes = array(
|
||||||
|
'hidden',
|
||||||
|
'password',
|
||||||
|
'button',
|
||||||
|
'image',
|
||||||
|
'file',
|
||||||
|
'reset',
|
||||||
|
'submit',
|
||||||
|
'search',
|
||||||
|
'checkbox',
|
||||||
|
'radio',
|
||||||
|
'email',
|
||||||
|
'text',
|
||||||
|
'color',
|
||||||
|
'date',
|
||||||
|
'datetime',
|
||||||
|
'datetime-local',
|
||||||
|
'email',
|
||||||
|
'month',
|
||||||
|
'number',
|
||||||
|
'range',
|
||||||
|
'search',
|
||||||
|
'tel',
|
||||||
|
'time',
|
||||||
|
'url',
|
||||||
|
'week',
|
||||||
|
);
|
||||||
|
protected static $fileUpload = false;
|
||||||
|
private static $key = array();
|
||||||
|
/**
|
||||||
|
* @var ApiMethodInfo;
|
||||||
|
*/
|
||||||
|
private static $info;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the form
|
||||||
|
*
|
||||||
|
* @param string $method http method to submit the form
|
||||||
|
* @param string $action relative path from the web root. When set to null
|
||||||
|
* it uses the current api method's path
|
||||||
|
* @param bool $dataOnly if you want to render the form yourself use this
|
||||||
|
* option
|
||||||
|
* @param string $prefix used for adjusting the spacing in front of
|
||||||
|
* form elements
|
||||||
|
* @param string $indent used for adjusting indentation
|
||||||
|
*
|
||||||
|
* @return array|T
|
||||||
|
*
|
||||||
|
* @throws \Luracast\Restler\RestException
|
||||||
|
*/
|
||||||
|
public static function get($method = 'POST', $action = null, $dataOnly = false, $prefix = '', $indent = ' ')
|
||||||
|
{
|
||||||
|
if (!static::$style)
|
||||||
|
static::$style = FormStyles::$html;
|
||||||
|
|
||||||
|
try {
|
||||||
|
/** @var Restler $restler */
|
||||||
|
$restler = Scope::get('Restler');
|
||||||
|
if (is_null($action))
|
||||||
|
$action = $restler->url;
|
||||||
|
|
||||||
|
$info = $restler->url == $action
|
||||||
|
&& Util::getRequestMethod() == $method
|
||||||
|
? $restler->apiMethodInfo
|
||||||
|
: Routes::find(
|
||||||
|
trim($action, '/'),
|
||||||
|
$method,
|
||||||
|
$restler->getRequestedApiVersion(),
|
||||||
|
static::$preFill ||
|
||||||
|
($restler->requestMethod == $method &&
|
||||||
|
$restler->url == $action)
|
||||||
|
? $restler->getRequestData()
|
||||||
|
: array()
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (RestException $e) {
|
||||||
|
//echo $e->getErrorMessage();
|
||||||
|
$info = false;
|
||||||
|
}
|
||||||
|
if (!$info)
|
||||||
|
throw new RestException(500, 'invalid action path for form `' . $method . ' ' . $action . '`');
|
||||||
|
static::$info = $info;
|
||||||
|
$m = $info->metadata;
|
||||||
|
$r = static::fields($dataOnly);
|
||||||
|
if ($method != 'GET' && $method != 'POST') {
|
||||||
|
if (empty(Defaults::$httpMethodOverrideProperty))
|
||||||
|
throw new RestException(
|
||||||
|
500,
|
||||||
|
'Forms require `Defaults::\$httpMethodOverrideProperty`' .
|
||||||
|
"for supporting HTTP $method"
|
||||||
|
);
|
||||||
|
if ($dataOnly) {
|
||||||
|
$r[] = array(
|
||||||
|
'tag' => 'input',
|
||||||
|
'name' => Defaults::$httpMethodOverrideProperty,
|
||||||
|
'type' => 'hidden',
|
||||||
|
'value' => 'method',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$r[] = T::input()
|
||||||
|
->name(Defaults::$httpMethodOverrideProperty)
|
||||||
|
->value($method)
|
||||||
|
->type('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
$method = 'POST';
|
||||||
|
}
|
||||||
|
if (session_id() != '') {
|
||||||
|
$form_key = static::key($method, $action);
|
||||||
|
if ($dataOnly) {
|
||||||
|
$r[] = array(
|
||||||
|
'tag' => 'input',
|
||||||
|
'name' => static::FORM_KEY,
|
||||||
|
'type' => 'hidden',
|
||||||
|
'value' => 'hidden',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$key = T::input()
|
||||||
|
->name(static::FORM_KEY)
|
||||||
|
->type('hidden')
|
||||||
|
->value($form_key);
|
||||||
|
$r[] = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$s = array(
|
||||||
|
'tag' => 'button',
|
||||||
|
'type' => 'submit',
|
||||||
|
'label' =>
|
||||||
|
Util::nestedValue($m, 'return', CommentParser::$embeddedDataName, 'label')
|
||||||
|
? : 'Submit'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$dataOnly)
|
||||||
|
$s = Emmet::make(static::style('submit', $m), $s);
|
||||||
|
$r[] = $s;
|
||||||
|
$t = array(
|
||||||
|
'action' => $restler->getBaseUrl() . '/' . rtrim($action, '/'),
|
||||||
|
'method' => $method,
|
||||||
|
);
|
||||||
|
if (static::$fileUpload) {
|
||||||
|
static::$fileUpload = false;
|
||||||
|
$t['enctype'] = 'multipart/form-data';
|
||||||
|
}
|
||||||
|
if (!$dataOnly) {
|
||||||
|
$t = Emmet::make(static::style('form', $m), $t);
|
||||||
|
$t->prefix = $prefix;
|
||||||
|
$t->indent = $indent;
|
||||||
|
$t[] = $r;
|
||||||
|
} else {
|
||||||
|
$t['fields'] = $r;
|
||||||
|
}
|
||||||
|
return $t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function style($name, array $metadata, $type = '')
|
||||||
|
{
|
||||||
|
return isset($metadata[CommentParser::$embeddedDataName][$name])
|
||||||
|
? $metadata[CommentParser::$embeddedDataName][$name]
|
||||||
|
: (!empty($type) && isset(static::$style["$name-$type"])
|
||||||
|
? static::$style["$name-$type"]
|
||||||
|
: (isset(static::$style[$name])
|
||||||
|
? static::$style[$name]
|
||||||
|
: null
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ValidationInfo $p
|
||||||
|
*
|
||||||
|
* @param bool $dataOnly
|
||||||
|
*
|
||||||
|
* @return array|T
|
||||||
|
*/
|
||||||
|
public static function field(ValidationInfo $p, $dataOnly = false)
|
||||||
|
{
|
||||||
|
if (is_string($p->value)) {
|
||||||
|
//prevent XSS attacks
|
||||||
|
$p->value = htmlspecialchars($p->value, ENT_QUOTES | ENT_HTML401, 'UTF-8');
|
||||||
|
}
|
||||||
|
$type = $p->field ? : static::guessFieldType($p);
|
||||||
|
$tag = in_array($type, static::$inputTypes)
|
||||||
|
? 'input' : $type;
|
||||||
|
$options = array();
|
||||||
|
$name = $p->name;
|
||||||
|
$multiple = null;
|
||||||
|
if ($p->type == 'array' && $p->contentType != 'associative') {
|
||||||
|
$name .= '[]';
|
||||||
|
$multiple = true;
|
||||||
|
}
|
||||||
|
if ($p->choice) {
|
||||||
|
foreach ($p->choice as $i => $choice) {
|
||||||
|
$option = array('name' => $name, 'value' => $choice);
|
||||||
|
$option['text'] = isset($p->rules['select'][$i])
|
||||||
|
? $p->rules['select'][$i]
|
||||||
|
: $choice;
|
||||||
|
if ($choice == $p->value)
|
||||||
|
$option['selected'] = true;
|
||||||
|
$options[] = $option;
|
||||||
|
}
|
||||||
|
} elseif ($p->type == 'boolean' || $p->type == 'bool') {
|
||||||
|
if (String::beginsWith($type, 'radio')) {
|
||||||
|
$options[] = array('name' => $p->name, 'text' => ' Yes ',
|
||||||
|
'value' => 'true');
|
||||||
|
$options[] = array('name' => $p->name, 'text' => ' No ',
|
||||||
|
'value' => 'false');
|
||||||
|
if ($p->value || $p->default)
|
||||||
|
$options[0]['selected'] = true;
|
||||||
|
} else {
|
||||||
|
$r = array(
|
||||||
|
'tag' => $tag,
|
||||||
|
'name' => $name,
|
||||||
|
'type' => $type,
|
||||||
|
'label' => $p->label,
|
||||||
|
'value' => 'true',
|
||||||
|
'default' => $p->default,
|
||||||
|
);
|
||||||
|
$r['text'] = 'Yes';
|
||||||
|
if ($p->default) {
|
||||||
|
$r['selected'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($r)) {
|
||||||
|
$r = array(
|
||||||
|
'tag' => $tag,
|
||||||
|
'name' => $name,
|
||||||
|
'type' => $type,
|
||||||
|
'label' => $p->label,
|
||||||
|
'value' => $p->value,
|
||||||
|
'default' => $p->default,
|
||||||
|
'options' => & $options,
|
||||||
|
'multiple' => $multiple,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($type == 'file') {
|
||||||
|
static::$fileUpload = true;
|
||||||
|
$r['accept'] = implode(', ', UploadFormat::$allowedMimeTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true === $p->required)
|
||||||
|
$r['required'] = true;
|
||||||
|
if (isset($p->rules['autofocus']))
|
||||||
|
$r['autofocus'] = true;
|
||||||
|
/*
|
||||||
|
echo "<pre>";
|
||||||
|
print_r($r);
|
||||||
|
echo "</pre>";
|
||||||
|
*/
|
||||||
|
if ($dataOnly)
|
||||||
|
return $r;
|
||||||
|
if (isset($p->rules['form']))
|
||||||
|
return Emmet::make($p->rules['form'], $r);
|
||||||
|
$m = static::$info->metadata;
|
||||||
|
$t = Emmet::make(static::style($type, $m, $p->type) ? : static::style($tag, $m, $p->type), $r);
|
||||||
|
return $t;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function guessFieldType(ValidationInfo $p, $type = 'type')
|
||||||
|
{
|
||||||
|
if (in_array($p->$type, static::$inputTypes))
|
||||||
|
return $p->$type;
|
||||||
|
if ($p->choice)
|
||||||
|
return $p->type == 'array' ? 'checkbox' : 'select';
|
||||||
|
switch ($p->$type) {
|
||||||
|
case 'boolean':
|
||||||
|
return 'radio';
|
||||||
|
case 'int':
|
||||||
|
case 'number':
|
||||||
|
case 'float':
|
||||||
|
return 'number';
|
||||||
|
case 'array':
|
||||||
|
return static::guessFieldType($p, 'contentType');
|
||||||
|
}
|
||||||
|
if ($p->name == 'password')
|
||||||
|
return 'password';
|
||||||
|
return 'text';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the form key
|
||||||
|
*
|
||||||
|
* @param string $method http method for form key
|
||||||
|
* @param string $action relative path from the web root. When set to null
|
||||||
|
* it uses the current api method's path
|
||||||
|
*
|
||||||
|
* @return string generated form key
|
||||||
|
*/
|
||||||
|
public static function key($method = 'POST', $action = null)
|
||||||
|
{
|
||||||
|
if (is_null($action))
|
||||||
|
$action = Scope::get('Restler')->url;
|
||||||
|
$target = "$method $action";
|
||||||
|
if (empty(static::$key[$target]))
|
||||||
|
static::$key[$target] = md5($target . User::getIpAddress() . uniqid(mt_rand()));
|
||||||
|
$_SESSION[static::FORM_KEY] = static::$key;
|
||||||
|
return static::$key[$target];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
208
htdocs/includes/restler/UI/Nav.php
Normal file
208
htdocs/includes/restler/UI/Nav.php
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\UI;
|
||||||
|
|
||||||
|
use Luracast\Restler\CommentParser;
|
||||||
|
use Luracast\Restler\Defaults;
|
||||||
|
use Luracast\Restler\Restler;
|
||||||
|
use Luracast\Restler\Routes;
|
||||||
|
use Luracast\Restler\Scope;
|
||||||
|
use Luracast\Restler\Util;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for automatically creating data to build an navigation interface
|
||||||
|
* based on available routes that are accessible by the current user
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class Nav
|
||||||
|
{
|
||||||
|
public static $root = 'home';
|
||||||
|
/**
|
||||||
|
* @var null|callable if the api methods are under access control mechanism
|
||||||
|
* you can attach a function here that returns true or false to determine
|
||||||
|
* visibility of a protected api method. this function will receive method
|
||||||
|
* info as the only parameter.
|
||||||
|
*/
|
||||||
|
public static $accessControlFunction = null;
|
||||||
|
/**
|
||||||
|
* @var array all paths beginning with any of the following will be excluded
|
||||||
|
* from documentation. if an empty string is given it will exclude the root
|
||||||
|
*/
|
||||||
|
public static $excludedPaths = array('');
|
||||||
|
/**
|
||||||
|
* @var array prefix additional menu items with one of the following syntax
|
||||||
|
* [$path => $text]
|
||||||
|
* [$path]
|
||||||
|
* [$path => ['text' => $text, 'url' => $url]]
|
||||||
|
*/
|
||||||
|
public static $prepends = array();
|
||||||
|
/**
|
||||||
|
* @var array suffix additional menu items with one of the following syntax
|
||||||
|
* [$path => $text]
|
||||||
|
* [$path]
|
||||||
|
* [$path => ['text' => $text, 'url' => $url]]
|
||||||
|
*/
|
||||||
|
public static $appends = array();
|
||||||
|
|
||||||
|
public static $addExtension = true;
|
||||||
|
|
||||||
|
protected static $extension = '';
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function build(&$tree, $path,
|
||||||
|
$url = null, $text = null, $activeUrl = null)
|
||||||
|
{
|
||||||
|
$parts = explode('/', $path);
|
||||||
|
if (count($parts) == 1 && empty($parts[0]))
|
||||||
|
$parts = array(static::$root);
|
||||||
|
$p = & $tree;
|
||||||
|
$end = end($parts);
|
||||||
|
foreach ($parts as $part) {
|
||||||
|
if (!isset($p[$part])) {
|
||||||
|
$p[$part] = array(
|
||||||
|
'href' => '#',
|
||||||
|
'text' => static::title($part)
|
||||||
|
);
|
||||||
|
if ($part == $end) {
|
||||||
|
$p[$part]['class'] = $part;
|
||||||
|
if ($text)
|
||||||
|
$p[$part]['text'] = $text;
|
||||||
|
if (is_null($url)) {
|
||||||
|
if (empty($path) && !empty(static::$extension))
|
||||||
|
$path = 'index';
|
||||||
|
$p[$part]['href'] = Util::$restler->getBaseUrl()
|
||||||
|
. '/' . $path . static::$extension;
|
||||||
|
} else {
|
||||||
|
if (empty($url) && !empty(static::$extension))
|
||||||
|
$url = 'index';
|
||||||
|
$p[$part]['href'] = $url . static::$extension;
|
||||||
|
}
|
||||||
|
if ($path == $activeUrl) {
|
||||||
|
$p[$part]['active'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$p[$part]['children'] = array();
|
||||||
|
|
||||||
|
}
|
||||||
|
$p = & $p[$part]['children'];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function title($name)
|
||||||
|
{
|
||||||
|
if (empty($name)) {
|
||||||
|
$name = static::$root;
|
||||||
|
} else {
|
||||||
|
$name = ltrim($name, '#');
|
||||||
|
}
|
||||||
|
return ucfirst(preg_replace(array('/(?<=[^A-Z])([A-Z])/', '/(?<=[^0-9])([0-9])/'), ' $0', $name));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
282
htdocs/includes/restler/UI/Tags.php
Normal file
282
htdocs/includes/restler/UI/Tags.php
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler\UI;
|
||||||
|
|
||||||
|
use ArrayAccess;
|
||||||
|
use Countable;
|
||||||
|
use Luracast\Restler\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for generating html tags in an object oriented way
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*
|
||||||
|
* ============================ magic properties ==============================
|
||||||
|
* @property Tags parent parent tag
|
||||||
|
* ============================== magic methods ===============================
|
||||||
|
* @method Tags name(string $value) name attribute
|
||||||
|
* @method Tags action(string $value) action attribute
|
||||||
|
* @method Tags placeholder(string $value) placeholder attribute
|
||||||
|
* @method Tags value(string $value) value attribute
|
||||||
|
* @method Tags required(boolean $value) required attribute
|
||||||
|
* @method Tags class(string $value) required attribute
|
||||||
|
*
|
||||||
|
* =========================== static magic methods ============================
|
||||||
|
* @method static Tags form() creates a html form
|
||||||
|
* @method static Tags input() creates a html input element
|
||||||
|
* @method static Tags button() creates a html button element
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class Tags implements ArrayAccess, Countable
|
||||||
|
{
|
||||||
|
public static $humanReadable = true;
|
||||||
|
public static $initializer = null;
|
||||||
|
protected static $instances = array();
|
||||||
|
public $prefix = '';
|
||||||
|
public $indent = ' ';
|
||||||
|
public $tag;
|
||||||
|
protected $attributes = array();
|
||||||
|
protected $children = array();
|
||||||
|
protected $_parent;
|
||||||
|
|
||||||
|
public function __construct($name = null, array $children = array())
|
||||||
|
{
|
||||||
|
$this->tag = $name;
|
||||||
|
$c = array();
|
||||||
|
foreach ($children as $child) {
|
||||||
|
is_array($child)
|
||||||
|
? $c = array_merge($c, $child)
|
||||||
|
: $c [] = $child;
|
||||||
|
}
|
||||||
|
$this->markAsChildren($c);
|
||||||
|
$this->children = $c;
|
||||||
|
if (static::$initializer)
|
||||||
|
call_user_func_array(static::$initializer, array(& $this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $name
|
||||||
|
* @param array $children
|
||||||
|
*
|
||||||
|
* @return Tags
|
||||||
|
*/
|
||||||
|
public static function __callStatic($name, array $children)
|
||||||
|
{
|
||||||
|
return new static($name, $children);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toString($prefix = '', $indent = ' ')
|
||||||
|
{
|
||||||
|
$this->prefix = $prefix;
|
||||||
|
$this->indent = $indent;
|
||||||
|
return $this->__toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
$children = '';
|
||||||
|
if (static::$humanReadable) {
|
||||||
|
$lineBreak = false;
|
||||||
|
foreach ($this->children as $key => $child) {
|
||||||
|
$prefix = $this->prefix;
|
||||||
|
if (!is_null($this->tag))
|
||||||
|
$prefix .= $this->indent;
|
||||||
|
if ($child instanceof $this) {
|
||||||
|
$child->prefix = $prefix;
|
||||||
|
$child->indent = $this->indent;
|
||||||
|
$children .= PHP_EOL . $child;
|
||||||
|
$lineBreak = true;
|
||||||
|
} else {
|
||||||
|
$children .= $child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($lineBreak)
|
||||||
|
$children .= PHP_EOL . $this->prefix;
|
||||||
|
} else {
|
||||||
|
$children = implode('', $this->children);
|
||||||
|
}
|
||||||
|
if (is_null($this->tag))
|
||||||
|
return $children;
|
||||||
|
$attributes = '';
|
||||||
|
foreach ($this->attributes as $attribute => &$value)
|
||||||
|
$attributes .= " $attribute=\"$value\"";
|
||||||
|
|
||||||
|
if (count($this->children))
|
||||||
|
return static::$humanReadable
|
||||||
|
? "$this->prefix<{$this->tag}{$attributes}>"
|
||||||
|
. "$children"
|
||||||
|
. "</{$this->tag}>"
|
||||||
|
: "<{$this->tag}{$attributes}>$children</{$this->tag}>";
|
||||||
|
|
||||||
|
return "$this->prefix<{$this->tag}{$attributes}/>";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray()
|
||||||
|
{
|
||||||
|
$r = array();
|
||||||
|
$r['attributes'] = $this->attributes;
|
||||||
|
$r['tag'] = $this->tag;
|
||||||
|
$children = array();
|
||||||
|
foreach ($this->children as $key => $child) {
|
||||||
|
$children[$key] = $child instanceof $this
|
||||||
|
? $child->toArray()
|
||||||
|
: $child;
|
||||||
|
}
|
||||||
|
$r['children'] = $children;
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($name)
|
||||||
|
{
|
||||||
|
if ('parent' == $name)
|
||||||
|
return $this->_parent;
|
||||||
|
if (isset($this->attributes[$name]))
|
||||||
|
return $this->attributes[$name];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($name, $value)
|
||||||
|
{
|
||||||
|
if ('parent' == $name) {
|
||||||
|
if ($this->_parent) {
|
||||||
|
unset($this->_parent[array_search($this, $this->_parent->children)]);
|
||||||
|
}
|
||||||
|
if (!empty($value)) {
|
||||||
|
$value[] = $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __isset($name)
|
||||||
|
{
|
||||||
|
return isset($this->attributes[$name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $attribute
|
||||||
|
* @param $value
|
||||||
|
*
|
||||||
|
* @return Tags
|
||||||
|
*/
|
||||||
|
public function __call($attribute, $value)
|
||||||
|
{
|
||||||
|
if (is_null($value)) {
|
||||||
|
return isset($this->attributes[$attribute])
|
||||||
|
? $this->attributes[$attribute]
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
$value = $value[0];
|
||||||
|
if (is_null($value)) {
|
||||||
|
unset($this->attributes[$attribute]);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
$this->attributes[$attribute] = is_bool($value)
|
||||||
|
? ($value ? 'true' : 'false')
|
||||||
|
: @(string)$value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetGet($index)
|
||||||
|
{
|
||||||
|
if ($this->offsetExists($index)) {
|
||||||
|
return $this->children[$index];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetExists($index)
|
||||||
|
{
|
||||||
|
return isset($this->children[$index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetSet($index, $value)
|
||||||
|
{
|
||||||
|
if ($index) {
|
||||||
|
$this->children[$index] = $value;
|
||||||
|
} elseif (is_array($value)) {
|
||||||
|
$c = array();
|
||||||
|
foreach ($value as $child) {
|
||||||
|
is_array($child)
|
||||||
|
? $c = array_merge($c, $child)
|
||||||
|
: $c [] = $child;
|
||||||
|
}
|
||||||
|
$this->markAsChildren($c);
|
||||||
|
$this->children += $c;
|
||||||
|
} else {
|
||||||
|
$c = array($value);
|
||||||
|
$this->markAsChildren($c);
|
||||||
|
$this->children[] = $value;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetUnset($index)
|
||||||
|
{
|
||||||
|
$this->children[$index]->_parent = null;
|
||||||
|
unset($this->children[$index]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContents()
|
||||||
|
{
|
||||||
|
return $this->children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function count()
|
||||||
|
{
|
||||||
|
return count($this->children);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function markAsChildren(& $children)
|
||||||
|
{
|
||||||
|
foreach ($children as $i => $child) {
|
||||||
|
if (is_string($child))
|
||||||
|
continue;
|
||||||
|
if (!is_object($child)) {
|
||||||
|
unset($children[$i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//echo $child;
|
||||||
|
if (isset($child->_parent) && $child->_parent != $this) {
|
||||||
|
//remove from current parent
|
||||||
|
unset($child->_parent[array_search($child, $child->_parent->children)]);
|
||||||
|
}
|
||||||
|
$child->_parent = $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
100
htdocs/includes/restler/User.php
Normal file
100
htdocs/includes/restler/User.php
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
/**
|
||||||
|
* Information gathered about the api user is kept here using static methods
|
||||||
|
* and properties for other classes to make use of them.
|
||||||
|
* Typically Authentication classes populate them
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class User implements iIdentifyUser
|
||||||
|
{
|
||||||
|
private static $initialized = false;
|
||||||
|
public static $id = null;
|
||||||
|
public static $cacheId = null;
|
||||||
|
public static $ip;
|
||||||
|
public static $browser = '';
|
||||||
|
public static $platform = '';
|
||||||
|
|
||||||
|
public static function init()
|
||||||
|
{
|
||||||
|
static::$initialized = true;
|
||||||
|
static::$ip = static::getIpAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getUniqueIdentifier($includePlatform = false)
|
||||||
|
{
|
||||||
|
if (!static::$initialized) static::init();
|
||||||
|
return static::$id ? : base64_encode('ip:' . ($includePlatform
|
||||||
|
? static::$ip . '-' . static::$platform
|
||||||
|
: static::$ip
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getIpAddress($ignoreProxies = false)
|
||||||
|
{
|
||||||
|
foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR',
|
||||||
|
'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP',
|
||||||
|
'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED',
|
||||||
|
'REMOTE_ADDR') as $key) {
|
||||||
|
if (array_key_exists($key, $_SERVER) === true) {
|
||||||
|
foreach (explode(',', $_SERVER[$key]) as $ip) {
|
||||||
|
$ip = trim($ip); // just to be safe
|
||||||
|
|
||||||
|
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4
|
||||||
|
| FILTER_FLAG_NO_PRIV_RANGE
|
||||||
|
| FILTER_FLAG_NO_RES_RANGE) !== false
|
||||||
|
) {
|
||||||
|
return $ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authentication classes should call this method
|
||||||
|
*
|
||||||
|
* @param string $id user id as identified by the authentication classes
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function setUniqueIdentifier($id)
|
||||||
|
{
|
||||||
|
static::$id = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User identity to be used for caching purpose
|
||||||
|
*
|
||||||
|
* When the dynamic cache service places an object in the cache, it needs to
|
||||||
|
* label it with a unique identifying string known as a cache ID. This
|
||||||
|
* method gives that identifier
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function getCacheIdentifier()
|
||||||
|
{
|
||||||
|
return static::$cacheId ?: static::$id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User identity for caching purpose
|
||||||
|
*
|
||||||
|
* In a role based access control system this will be based on role
|
||||||
|
*
|
||||||
|
* @param $id
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function setCacheIdentifier($id)
|
||||||
|
{
|
||||||
|
static::$cacheId = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
201
htdocs/includes/restler/Util.php
Normal file
201
htdocs/includes/restler/Util.php
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
/**
|
||||||
|
* Describe the purpose of this class/interface/trait
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
class Util
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Restler instance injected at runtime
|
||||||
|
*/
|
||||||
|
public static $restler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* verify if the given data type string is scalar or not
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*
|
||||||
|
* @param string $type data type as string
|
||||||
|
*
|
||||||
|
* @return bool true or false
|
||||||
|
*/
|
||||||
|
public static function isObjectOrArray($type)
|
||||||
|
{
|
||||||
|
if (is_array($type)) {
|
||||||
|
foreach ($type as $t) {
|
||||||
|
if (static::isObjectOrArray($t)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !(boolean)strpos('|bool|boolean|int|float|string|', $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value deeply nested inside an array / object
|
||||||
|
*
|
||||||
|
* Using isset() to test the presence of nested value can give a false positive
|
||||||
|
*
|
||||||
|
* This method serves that need
|
||||||
|
*
|
||||||
|
* When the deeply nested property is found its value is returned, otherwise
|
||||||
|
* false is returned.
|
||||||
|
*
|
||||||
|
* @param array $from array to extract the value from
|
||||||
|
* @param string|array $key ... pass more to go deeply inside the array
|
||||||
|
* alternatively you can pass a single array
|
||||||
|
*
|
||||||
|
* @return null|mixed null when not found, value otherwise
|
||||||
|
*/
|
||||||
|
public static function nestedValue($from, $key/**, $key2 ... $key`n` */)
|
||||||
|
{
|
||||||
|
if (is_array($key)) {
|
||||||
|
$keys = $key;
|
||||||
|
} else {
|
||||||
|
$keys = func_get_args();
|
||||||
|
array_shift($keys);
|
||||||
|
}
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if (is_array($from) && isset($from[$key])) {
|
||||||
|
$from = $from[$key];
|
||||||
|
continue;
|
||||||
|
} elseif (is_object($from) && isset($from->{$key})) {
|
||||||
|
$from = $from->{$key};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return $from;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getResourcePath($className,
|
||||||
|
$resourcePath = null,
|
||||||
|
$prefix = '')
|
||||||
|
{
|
||||||
|
if (is_null($resourcePath)) {
|
||||||
|
if (Defaults::$autoRoutingEnabled) {
|
||||||
|
$resourcePath = strtolower($className);
|
||||||
|
if (false !== ($index = strrpos($className, '\\')))
|
||||||
|
$resourcePath = substr($resourcePath, $index + 1);
|
||||||
|
if (false !== ($index = strrpos($resourcePath, '_')))
|
||||||
|
$resourcePath = substr($resourcePath, $index + 1);
|
||||||
|
} else {
|
||||||
|
$resourcePath = '';
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
$resourcePath = trim($resourcePath, '/');
|
||||||
|
if (strlen($resourcePath) > 0)
|
||||||
|
$resourcePath .= '/';
|
||||||
|
return $prefix . $resourcePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two strings and remove the common
|
||||||
|
* sub string from the first string and return it
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*
|
||||||
|
* @param string $fromPath
|
||||||
|
* @param string $usingPath
|
||||||
|
* @param string $char
|
||||||
|
* optional, set it as
|
||||||
|
* blank string for char by char comparison
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function removeCommonPath($fromPath, $usingPath, $char = '/')
|
||||||
|
{
|
||||||
|
if (empty($fromPath))
|
||||||
|
return '';
|
||||||
|
$fromPath = explode($char, $fromPath);
|
||||||
|
$usingPath = explode($char, $usingPath);
|
||||||
|
while (count($usingPath)) {
|
||||||
|
if (count($fromPath) && $fromPath[0] == $usingPath[0]) {
|
||||||
|
array_shift($fromPath);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
array_shift($usingPath);
|
||||||
|
}
|
||||||
|
return implode($char, $fromPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the request to figure out the http request type
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*
|
||||||
|
* @return string which will be one of the following
|
||||||
|
* [GET, POST, PUT, PATCH, DELETE]
|
||||||
|
* @example GET
|
||||||
|
*/
|
||||||
|
public static function getRequestMethod()
|
||||||
|
{
|
||||||
|
$method = $_SERVER['REQUEST_METHOD'];
|
||||||
|
if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
|
||||||
|
$method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
|
||||||
|
} elseif (
|
||||||
|
!empty(Defaults::$httpMethodOverrideProperty)
|
||||||
|
&& isset($_REQUEST[Defaults::$httpMethodOverrideProperty])
|
||||||
|
) {
|
||||||
|
// support for exceptional clients who can't set the header
|
||||||
|
$m = strtoupper($_REQUEST[Defaults::$httpMethodOverrideProperty]);
|
||||||
|
if ($m == 'PUT' || $m == 'DELETE' ||
|
||||||
|
$m == 'POST' || $m == 'PATCH'
|
||||||
|
) {
|
||||||
|
$method = $m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// support for HEAD request
|
||||||
|
if ($method == 'HEAD') {
|
||||||
|
$method = 'GET';
|
||||||
|
}
|
||||||
|
return $method;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass any content negotiation header such as Accept,
|
||||||
|
* Accept-Language to break it up and sort the resulting array by
|
||||||
|
* the order of negotiation.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*
|
||||||
|
* @param string $accept header value
|
||||||
|
*
|
||||||
|
* @return array sorted by the priority
|
||||||
|
*/
|
||||||
|
public static function sortByPriority($accept)
|
||||||
|
{
|
||||||
|
$acceptList = array();
|
||||||
|
$accepts = explode(',', strtolower($accept));
|
||||||
|
if (!is_array($accepts)) {
|
||||||
|
$accepts = array($accepts);
|
||||||
|
}
|
||||||
|
foreach ($accepts as $pos => $accept) {
|
||||||
|
$parts = explode(';q=', trim($accept));
|
||||||
|
$type = array_shift($parts);
|
||||||
|
$quality = count($parts) ?
|
||||||
|
floatval(array_shift($parts)) :
|
||||||
|
(1000 - $pos) / 1000;
|
||||||
|
$acceptList[$type] = $quality;
|
||||||
|
}
|
||||||
|
arsort($acceptList);
|
||||||
|
return $acceptList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getShortName($className)
|
||||||
|
{
|
||||||
|
$className = explode('\\', $className);
|
||||||
|
return end($className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
10
htdocs/includes/restler/compatibility/iAuthenticate.php
Normal file
10
htdocs/includes/restler/compatibility/iAuthenticate.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface iAuthenticate only exists for compatibility mode for Restler 2 and below, it should
|
||||||
|
* not be used otherwise.
|
||||||
|
*/
|
||||||
|
interface iAuthenticate
|
||||||
|
{
|
||||||
|
public function __isAuthenticated();
|
||||||
|
}
|
||||||
14
htdocs/includes/restler/compatibility/restler1.php
Normal file
14
htdocs/includes/restler/compatibility/restler1.php
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Restler 1 compatibility mode enabler
|
||||||
|
*/
|
||||||
|
use Luracast\Restler\Defaults;
|
||||||
|
|
||||||
|
//changes in iAuthenticate
|
||||||
|
Defaults::$authenticationMethod = 'isAuthenticated';
|
||||||
|
include __DIR__ . '/iAuthenticate.php';
|
||||||
|
|
||||||
|
//changes in routing
|
||||||
|
Defaults::$autoRoutingEnabled = false;
|
||||||
|
Defaults::$smartParameterParsing = false;
|
||||||
|
Defaults::$autoValidationEnabled = false;
|
||||||
40
htdocs/includes/restler/compatibility/restler2.php
Normal file
40
htdocs/includes/restler/compatibility/restler2.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Restler 2 compatibility mode enabler
|
||||||
|
*/
|
||||||
|
use Luracast\Restler\Defaults;
|
||||||
|
use Luracast\Restler\AutoLoader;
|
||||||
|
use Luracast\Restler\CommentParser;
|
||||||
|
|
||||||
|
//changes in auto loading
|
||||||
|
$classMap = array();
|
||||||
|
//find lowercase php files representing a class/interface
|
||||||
|
foreach (explode(PATH_SEPARATOR, get_include_path()) as $path)
|
||||||
|
foreach (new DirectoryIterator($path) as $fileInfo)
|
||||||
|
if ($fileInfo->isFile()
|
||||||
|
&& 'php' === $fileInfo->getExtension()
|
||||||
|
&& ctype_lower($fileInfo->getBasename('.php'))
|
||||||
|
&& preg_match(
|
||||||
|
'/^ *(class|interface|abstract +class)'
|
||||||
|
. ' +([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/m',
|
||||||
|
file_get_contents($fileInfo->getPathname()),
|
||||||
|
$matches
|
||||||
|
)
|
||||||
|
)
|
||||||
|
$classMap[$matches[2]] = $fileInfo->getPathname();
|
||||||
|
|
||||||
|
AutoLoader::seen($classMap);
|
||||||
|
|
||||||
|
//changes in iAuthenticate
|
||||||
|
Defaults::$authenticationMethod = '__isAuthenticated';
|
||||||
|
|
||||||
|
include __DIR__ . '/iAuthenticate.php';
|
||||||
|
|
||||||
|
//changes in auto routing
|
||||||
|
Defaults::$smartAutoRouting = false;
|
||||||
|
Defaults::$smartParameterParsing = false;
|
||||||
|
Defaults::$autoValidationEnabled = false;
|
||||||
|
|
||||||
|
//changes in parsing embedded data in comments
|
||||||
|
CommentParser::$embeddedDataPattern = '/\((\S+)\)/ms';
|
||||||
|
CommentParser::$embeddedDataIndex = 1;
|
||||||
70
htdocs/includes/restler/composer.json
Normal file
70
htdocs/includes/restler/composer.json
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"name":"restler/framework",
|
||||||
|
"description":"Just the Restler Framework without the tests and examples",
|
||||||
|
"type":"library",
|
||||||
|
"keywords":["server","api","framework","REST"],
|
||||||
|
"homepage":"http://luracast.com/products/restler/",
|
||||||
|
"license":"LGPL-2.1",
|
||||||
|
"authors":[
|
||||||
|
{
|
||||||
|
"name":"Luracast",
|
||||||
|
"email":"arul@luracast.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Nick nickl- Lombard",
|
||||||
|
"email":"github@jigsoft.co.za"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"extra":{
|
||||||
|
"branch-alias":{
|
||||||
|
"master":"v3.0.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"suggest":{
|
||||||
|
"luracast/explorer":"Restler's very own api explorer (see require-dev for details)",
|
||||||
|
"rodneyrehm/plist":"Restler supports tho Apple plist xml format (see require-dev for details)",
|
||||||
|
"zendframework/zendamf":"Support for the amf document format (see require-dev for details)",
|
||||||
|
"symfony/yaml":"Restler can produce content in yaml format as well (see require-dev for details)",
|
||||||
|
"twig/twig":"Restler can render HtmlView using twig templates (see require-dev for details)",
|
||||||
|
"mustache/mustache":"Restler can render HtmlView using mustache/handlebar templates (see require-dev for details)",
|
||||||
|
"bshaffer/oauth2-server-php":"Restler can provide OAuth2 authentication using this library (see require-dev for details)"
|
||||||
|
},
|
||||||
|
"require":{
|
||||||
|
"php":">=5.3.0"
|
||||||
|
},
|
||||||
|
"require-dev":{
|
||||||
|
"luracast/explorer":"*",
|
||||||
|
"rodneyrehm/plist":"dev-master",
|
||||||
|
"zendframework/zendamf":"dev-master",
|
||||||
|
"symfony/yaml":"*",
|
||||||
|
"mustache/mustache": "dev-master",
|
||||||
|
"twig/twig": "v1.13.0",
|
||||||
|
"bshaffer/oauth2-server-php":"v1.0"
|
||||||
|
},
|
||||||
|
"repositories":[
|
||||||
|
{
|
||||||
|
"type":"vcs",
|
||||||
|
"url":"https://github.com/zendframework/ZendAmf.git"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type":"package",
|
||||||
|
"package":{
|
||||||
|
"name":"luracast/explorer",
|
||||||
|
"version":"v3.0.0",
|
||||||
|
"dist":{
|
||||||
|
"type":"zip",
|
||||||
|
"url":"https://github.com/Luracast/Restler-API-Explorer/zipball/v3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"autoload":{
|
||||||
|
"psr-0":{
|
||||||
|
"Luracast\\Restler":""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"target-dir": "Luracast/Restler",
|
||||||
|
"replace": {
|
||||||
|
"luracast/restler":"3.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
23
htdocs/includes/restler/composer.lock
generated
Normal file
23
htdocs/includes/restler/composer.lock
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"hash": "ee84444dcf34101555d20a813d528c44",
|
||||||
|
"packages": [],
|
||||||
|
"packages-dev": null,
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": {
|
||||||
|
"rodneyrehm/plist": 20,
|
||||||
|
"zendframework/zendamf": 20,
|
||||||
|
"mustache/mustache": 20
|
||||||
|
},
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": {
|
||||||
|
"php": ">=5.3.0"
|
||||||
|
},
|
||||||
|
"platform-dev": []
|
||||||
|
}
|
||||||
25
htdocs/includes/restler/iAuthenticate.php
Normal file
25
htdocs/includes/restler/iAuthenticate.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for creating authentication classes
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage auth
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
interface iAuthenticate extends iFilter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string string to be used with WWW-Authenticate header
|
||||||
|
* @example Basic
|
||||||
|
* @example Digest
|
||||||
|
* @example OAuth
|
||||||
|
*/
|
||||||
|
public function __getWWWAuthenticateString();
|
||||||
|
}
|
||||||
63
htdocs/includes/restler/iCache.php
Executable file
63
htdocs/includes/restler/iCache.php
Executable file
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
/**
|
||||||
|
* Interface for the cache system that manages caching of given data
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
interface iCache
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* store data in the cache
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param mixed $data
|
||||||
|
*
|
||||||
|
* @return boolean true if successful
|
||||||
|
*/
|
||||||
|
public function set($name, $data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retrieve data from the cache
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param bool $ignoreErrors
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get($name, $ignoreErrors = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete data from the cache
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param bool $ignoreErrors
|
||||||
|
*
|
||||||
|
* @return boolean true if successful
|
||||||
|
*/
|
||||||
|
public function clear($name, $ignoreErrors = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if the given name is cached
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return boolean true if cached
|
||||||
|
*/
|
||||||
|
public function isCached($name);
|
||||||
|
}
|
||||||
|
|
||||||
36
htdocs/includes/restler/iCompose.php
Normal file
36
htdocs/includes/restler/iCompose.php
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for composing response
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage result
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
interface iCompose {
|
||||||
|
/**
|
||||||
|
* Result of an api call is passed to this method
|
||||||
|
* to create a standard structure for the data
|
||||||
|
*
|
||||||
|
* @param mixed $result can be a primitive or array or object
|
||||||
|
*/
|
||||||
|
public function response($result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the api call results in RestException this method
|
||||||
|
* will be called to return the error message
|
||||||
|
*
|
||||||
|
* @param RestException $exception exception that has reasons for failure
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public function message(RestException $exception);
|
||||||
|
}
|
||||||
30
htdocs/includes/restler/iFilter.php
Normal file
30
htdocs/includes/restler/iFilter.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for creating classes that perform authentication/access
|
||||||
|
* verification
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @subpackage auth
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
interface iFilter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Access verification method.
|
||||||
|
*
|
||||||
|
* API access will be denied when this method returns false
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @return boolean true when api access is allowed false otherwise
|
||||||
|
*/
|
||||||
|
public function __isAllowed();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
63
htdocs/includes/restler/iIdentifyUser.php
Normal file
63
htdocs/includes/restler/iIdentifyUser.php
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to identify the user
|
||||||
|
*
|
||||||
|
* When the user is known we will be able to monitor, rate limit and do more
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
interface iIdentifyUser
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A way to uniquely identify the current api consumer
|
||||||
|
*
|
||||||
|
* When his user id is known it should be used otherwise ip address
|
||||||
|
* can be used
|
||||||
|
*
|
||||||
|
* @param bool $includePlatform Should we consider user alone or should
|
||||||
|
* consider the application/platform/device
|
||||||
|
* as well for generating unique id
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function getUniqueIdentifier($includePlatform = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User identity to be used for caching purpose
|
||||||
|
*
|
||||||
|
* When the dynamic cache service places an object in the cache, it needs to
|
||||||
|
* label it with a unique identifying string known as a cache ID. This
|
||||||
|
* method gives that identifier
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function getCacheIdentifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authentication classes should call this method
|
||||||
|
*
|
||||||
|
* @param string $id user id as identified by the authentication classes
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function setUniqueIdentifier($id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User identity for caching purpose
|
||||||
|
*
|
||||||
|
* In a role based access control system this will be based on role
|
||||||
|
*
|
||||||
|
* @param $id
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function setCacheIdentifier($id);
|
||||||
|
}
|
||||||
11
htdocs/includes/restler/iProvideMultiVersionApi.php
Normal file
11
htdocs/includes/restler/iProvideMultiVersionApi.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
|
||||||
|
interface iProvideMultiVersionApi {
|
||||||
|
/**
|
||||||
|
* Maximum api version supported by the api class
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function __getMaximumSupportedVersion();
|
||||||
|
}
|
||||||
32
htdocs/includes/restler/iUseAuthentication.php
Normal file
32
htdocs/includes/restler/iUseAuthentication.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
namespace Luracast\Restler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Api classes or filter classes can implement this interface to know about
|
||||||
|
* authentication status
|
||||||
|
*
|
||||||
|
* @category Framework
|
||||||
|
* @package Restler
|
||||||
|
* @author R.Arul Kumaran <arul@luracast.com>
|
||||||
|
* @copyright 2010 Luracast
|
||||||
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
|
* @link http://luracast.com/products/restler/
|
||||||
|
* @version 3.0.0rc5
|
||||||
|
*/
|
||||||
|
interface iUseAuthentication
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This method will be called first for filter classes and api classes so
|
||||||
|
* that they can respond accordingly for filer method call and api method
|
||||||
|
* calls
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*
|
||||||
|
* @param bool $isAuthenticated passes true when the authentication is
|
||||||
|
* done false otherwise
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function __setAuthenticationStatus($isAuthenticated=false);
|
||||||
|
}
|
||||||
|
|
||||||
7
htdocs/includes/restler/vendor/autoload.php
vendored
Normal file
7
htdocs/includes/restler/vendor/autoload.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
require_once __DIR__ . '/composer' . '/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInite65e15efc7e9ea1f2b7ba7fa697ba485::getLoader();
|
||||||
413
htdocs/includes/restler/vendor/composer/ClassLoader.php
vendored
Normal file
413
htdocs/includes/restler/vendor/composer/ClassLoader.php
vendored
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClassLoader implements a PSR-0 class loader
|
||||||
|
*
|
||||||
|
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
|
||||||
|
*
|
||||||
|
* $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
*
|
||||||
|
* // register classes with namespaces
|
||||||
|
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||||
|
* $loader->add('Symfony', __DIR__.'/framework');
|
||||||
|
*
|
||||||
|
* // activate the autoloader
|
||||||
|
* $loader->register();
|
||||||
|
*
|
||||||
|
* // to enable searching the include path (eg. for PEAR packages)
|
||||||
|
* $loader->setUseIncludePath(true);
|
||||||
|
*
|
||||||
|
* In this example, if you try to use a class in the Symfony\Component
|
||||||
|
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||||
|
* the autoloader will first look for the class under the component/
|
||||||
|
* directory, and it will then fallback to the framework/ directory if not
|
||||||
|
* found before giving up.
|
||||||
|
*
|
||||||
|
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*/
|
||||||
|
class ClassLoader
|
||||||
|
{
|
||||||
|
// PSR-4
|
||||||
|
private $prefixLengthsPsr4 = array();
|
||||||
|
private $prefixDirsPsr4 = array();
|
||||||
|
private $fallbackDirsPsr4 = array();
|
||||||
|
|
||||||
|
// PSR-0
|
||||||
|
private $prefixesPsr0 = array();
|
||||||
|
private $fallbackDirsPsr0 = array();
|
||||||
|
|
||||||
|
private $useIncludePath = false;
|
||||||
|
private $classMap = array();
|
||||||
|
|
||||||
|
private $classMapAuthoritative = false;
|
||||||
|
|
||||||
|
public function getPrefixes()
|
||||||
|
{
|
||||||
|
if (!empty($this->prefixesPsr0)) {
|
||||||
|
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrefixesPsr4()
|
||||||
|
{
|
||||||
|
return $this->prefixDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFallbackDirs()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFallbackDirsPsr4()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClassMap()
|
||||||
|
{
|
||||||
|
return $this->classMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $classMap Class to filename map
|
||||||
|
*/
|
||||||
|
public function addClassMap(array $classMap)
|
||||||
|
{
|
||||||
|
if ($this->classMap) {
|
||||||
|
$this->classMap = array_merge($this->classMap, $classMap);
|
||||||
|
} else {
|
||||||
|
$this->classMap = $classMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix, either
|
||||||
|
* appending or prepending to the ones previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param array|string $paths The PSR-0 root directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*/
|
||||||
|
public function add($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->fallbackDirsPsr0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$this->fallbackDirsPsr0,
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first = $prefix[0];
|
||||||
|
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($prepend) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->prefixesPsr0[$first][$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$this->prefixesPsr0[$first][$prefix],
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace, either
|
||||||
|
* appending or prepending to the ones previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param array|string $paths The PSR-0 base directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function addPsr4($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
// Register directories for the root namespace.
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->fallbackDirsPsr4
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$this->fallbackDirsPsr4,
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||||
|
// Register directories for a new namespace.
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
} elseif ($prepend) {
|
||||||
|
// Prepend directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->prefixDirsPsr4[$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Append directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$this->prefixDirsPsr4[$prefix],
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix,
|
||||||
|
* replacing any others previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param array|string $paths The PSR-0 base directories
|
||||||
|
*/
|
||||||
|
public function set($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr0 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace,
|
||||||
|
* replacing any others previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param array|string $paths The PSR-4 base directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function setPsr4($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr4 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on searching the include path for class files.
|
||||||
|
*
|
||||||
|
* @param bool $useIncludePath
|
||||||
|
*/
|
||||||
|
public function setUseIncludePath($useIncludePath)
|
||||||
|
{
|
||||||
|
$this->useIncludePath = $useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to check if the autoloader uses the include path to check
|
||||||
|
* for classes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getUseIncludePath()
|
||||||
|
{
|
||||||
|
return $this->useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns off searching the prefix and fallback directories for classes
|
||||||
|
* that have not been registered with the class map.
|
||||||
|
*
|
||||||
|
* @param bool $classMapAuthoritative
|
||||||
|
*/
|
||||||
|
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||||
|
{
|
||||||
|
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should class lookup fail if not found in the current class map?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClassMapAuthoritative()
|
||||||
|
{
|
||||||
|
return $this->classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @param bool $prepend Whether to prepend the autoloader or not
|
||||||
|
*/
|
||||||
|
public function register($prepend = false)
|
||||||
|
{
|
||||||
|
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters this instance as an autoloader.
|
||||||
|
*/
|
||||||
|
public function unregister()
|
||||||
|
{
|
||||||
|
spl_autoload_unregister(array($this, 'loadClass'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given class or interface.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
* @return bool|null True if loaded, null otherwise
|
||||||
|
*/
|
||||||
|
public function loadClass($class)
|
||||||
|
{
|
||||||
|
if ($file = $this->findFile($class)) {
|
||||||
|
includeFile($file);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the path to the file where the class is defined.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
*
|
||||||
|
* @return string|false The path if found, false otherwise
|
||||||
|
*/
|
||||||
|
public function findFile($class)
|
||||||
|
{
|
||||||
|
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
|
||||||
|
if ('\\' == $class[0]) {
|
||||||
|
$class = substr($class, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// class map lookup
|
||||||
|
if (isset($this->classMap[$class])) {
|
||||||
|
return $this->classMap[$class];
|
||||||
|
}
|
||||||
|
if ($this->classMapAuthoritative) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->findFileWithExtension($class, '.php');
|
||||||
|
|
||||||
|
// Search for Hack files if we are running on HHVM
|
||||||
|
if ($file === null && defined('HHVM_VERSION')) {
|
||||||
|
$file = $this->findFileWithExtension($class, '.hh');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($file === null) {
|
||||||
|
// Remember that this class does not exist.
|
||||||
|
return $this->classMap[$class] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findFileWithExtension($class, $ext)
|
||||||
|
{
|
||||||
|
// PSR-4 lookup
|
||||||
|
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
|
||||||
|
$first = $class[0];
|
||||||
|
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||||
|
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
|
||||||
|
if (0 === strpos($class, $prefix)) {
|
||||||
|
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
|
||||||
|
if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-4 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||||
|
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 lookup
|
||||||
|
if (false !== $pos = strrpos($class, '\\')) {
|
||||||
|
// namespaced class name
|
||||||
|
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||||
|
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||||
|
} else {
|
||||||
|
// PEAR-like class name
|
||||||
|
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->prefixesPsr0[$first])) {
|
||||||
|
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||||
|
if (0 === strpos($class, $prefix)) {
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||||
|
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 include paths.
|
||||||
|
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope isolated include.
|
||||||
|
*
|
||||||
|
* Prevents access to $this/self from included files.
|
||||||
|
*/
|
||||||
|
function includeFile($file)
|
||||||
|
{
|
||||||
|
include $file;
|
||||||
|
}
|
||||||
9
htdocs/includes/restler/vendor/composer/autoload_classmap.php
vendored
Normal file
9
htdocs/includes/restler/vendor/composer/autoload_classmap.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_classmap.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
);
|
||||||
10
htdocs/includes/restler/vendor/composer/autoload_namespaces.php
vendored
Normal file
10
htdocs/includes/restler/vendor/composer/autoload_namespaces.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_namespaces.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Luracast\\Restler' => array($baseDir . '/'),
|
||||||
|
);
|
||||||
9
htdocs/includes/restler/vendor/composer/autoload_psr4.php
vendored
Normal file
9
htdocs/includes/restler/vendor/composer/autoload_psr4.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
);
|
||||||
70
htdocs/includes/restler/vendor/composer/autoload_real.php
vendored
Normal file
70
htdocs/includes/restler/vendor/composer/autoload_real.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_real.php @generated by Composer
|
||||||
|
|
||||||
|
class ComposerAutoloaderInite65e15efc7e9ea1f2b7ba7fa697ba485
|
||||||
|
{
|
||||||
|
private static $loader;
|
||||||
|
|
||||||
|
public static function loadClassLoader($class)
|
||||||
|
{
|
||||||
|
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||||
|
require __DIR__ . '/ClassLoader.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getLoader()
|
||||||
|
{
|
||||||
|
if (null !== self::$loader) {
|
||||||
|
return self::$loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_autoload_register(array('ComposerAutoloaderInite65e15efc7e9ea1f2b7ba7fa697ba485', 'loadClassLoader'), true, true);
|
||||||
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
spl_autoload_unregister(array('ComposerAutoloaderInite65e15efc7e9ea1f2b7ba7fa697ba485', 'loadClassLoader'));
|
||||||
|
|
||||||
|
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||||
|
foreach ($map as $namespace => $path) {
|
||||||
|
$loader->set($namespace, $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
$map = require __DIR__ . '/autoload_psr4.php';
|
||||||
|
foreach ($map as $namespace => $path) {
|
||||||
|
$loader->setPsr4($namespace, $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||||
|
if ($classMap) {
|
||||||
|
$loader->addClassMap($classMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_autoload_register(array('ComposerAutoloaderInite65e15efc7e9ea1f2b7ba7fa697ba485', 'autoload'), true, true);
|
||||||
|
|
||||||
|
$loader->register(true);
|
||||||
|
|
||||||
|
return $loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function autoload($class)
|
||||||
|
{
|
||||||
|
$dir = dirname(dirname(__DIR__)) . '/';
|
||||||
|
$prefixes = array('Luracast\\Restler');
|
||||||
|
foreach ($prefixes as $prefix) {
|
||||||
|
if (0 !== strpos($class, $prefix)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$path = $dir . implode('/', array_slice(explode('\\', $class), 2)).'.php';
|
||||||
|
if (!$path = stream_resolve_include_path($path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
require $path;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function composerRequiree65e15efc7e9ea1f2b7ba7fa697ba485($file)
|
||||||
|
{
|
||||||
|
require $file;
|
||||||
|
}
|
||||||
1
htdocs/includes/restler/vendor/composer/installed.json
vendored
Normal file
1
htdocs/includes/restler/vendor/composer/installed.json
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
441
htdocs/includes/restler/views/debug.css
Normal file
441
htdocs/includes/restler/views/debug.css
Normal file
File diff suppressed because one or more lines are too long
169
htdocs/includes/restler/views/debug.php
Normal file
169
htdocs/includes/restler/views/debug.php
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
<?php
|
||||||
|
use Luracast\Restler\Restler;
|
||||||
|
use Luracast\Restler\Util;
|
||||||
|
|
||||||
|
$template_vars = $data;//get_defined_vars();
|
||||||
|
|
||||||
|
unset($template_vars['response']);
|
||||||
|
unset($template_vars['api']);
|
||||||
|
unset($template_vars['request']);
|
||||||
|
unset($template_vars['stages']);
|
||||||
|
$template_vars['request'] = $data['request'];
|
||||||
|
$template_vars['stages'] = $data['stages'];
|
||||||
|
|
||||||
|
$call_trace = '';
|
||||||
|
|
||||||
|
function exceptions()
|
||||||
|
{
|
||||||
|
global $call_trace;
|
||||||
|
$r = Util::$restler;
|
||||||
|
$source = $r->_exceptions;
|
||||||
|
if (count($source)) {
|
||||||
|
$source = end($source);
|
||||||
|
$traces = array();
|
||||||
|
do {
|
||||||
|
$traces += $source->getTrace();
|
||||||
|
} while ($source = $source->getPrevious());
|
||||||
|
$traces += debug_backtrace();
|
||||||
|
$call_trace
|
||||||
|
= parse_backtrace($traces, 0);
|
||||||
|
} else {
|
||||||
|
$call_trace
|
||||||
|
= parse_backtrace(debug_backtrace());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
exceptions();
|
||||||
|
|
||||||
|
function parse_backtrace($raw, $skip = 1)
|
||||||
|
{
|
||||||
|
$output = "";
|
||||||
|
foreach ($raw as $entry) {
|
||||||
|
if ($skip-- > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//$output .= print_r($entry, true) . "\n";
|
||||||
|
$output .= "\nFile: " . $entry['file'] . " (Line: " . $entry['line'] . ")\n";
|
||||||
|
if (isset($entry['class']))
|
||||||
|
$output .= $entry['class'] . "::";
|
||||||
|
$output .= $entry['function']
|
||||||
|
. "( " . json_encode($entry['args']) . " )\n";
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//print_r(get_defined_vars());
|
||||||
|
//print_r($response);
|
||||||
|
$icon;
|
||||||
|
if ($success && isset($api)) {
|
||||||
|
$arguments = implode(', ', $api->parameters);
|
||||||
|
$icon = "<icon class=\"success\"></icon>";
|
||||||
|
$title = "{$api->className}::"
|
||||||
|
. "{$api->methodName}({$arguments})";
|
||||||
|
} else {
|
||||||
|
if (isset($response['error']['message'])) {
|
||||||
|
$icon = '<icon class="denied"></icon>';
|
||||||
|
$title = end(explode(':',$response['error']['message']));
|
||||||
|
} else {
|
||||||
|
$icon = '<icon class="warning"></icon>';
|
||||||
|
$title = 'No Matching Resource';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function render($data, $shadow=true)
|
||||||
|
{
|
||||||
|
$r = '';
|
||||||
|
if (empty($data))
|
||||||
|
return $r;
|
||||||
|
$r .= $shadow ? "<ul class=\"shadow\">\n": "<ul>\n";
|
||||||
|
if (is_array($data)) {
|
||||||
|
// field name
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$r .= '<li>';
|
||||||
|
$r .= is_numeric($key)
|
||||||
|
? "<strong>[$key]</strong> "
|
||||||
|
: "<strong>$key: </strong>";
|
||||||
|
$r .= '<span>';
|
||||||
|
if (is_array($value)) {
|
||||||
|
// recursive
|
||||||
|
$r .= render($value,false);
|
||||||
|
} else {
|
||||||
|
// value, with hyperlinked hyperlinks
|
||||||
|
if (is_bool($value)) {
|
||||||
|
$value = $value ? 'true' : 'false';
|
||||||
|
}
|
||||||
|
$value = htmlentities($value, ENT_COMPAT, 'UTF-8');
|
||||||
|
if (strpos($value, 'http://') === 0) {
|
||||||
|
$r .= '<a href="' . $value . '">' . $value . '</a>';
|
||||||
|
} else {
|
||||||
|
$r .= $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$r .= "</span></li>\n";
|
||||||
|
}
|
||||||
|
} elseif (is_bool($data)) {
|
||||||
|
$r .= '<li>' . ($data ? 'true' : 'false') . '</li>';
|
||||||
|
} else {
|
||||||
|
$r .= "<li><strong>$data</strong></li>";
|
||||||
|
}
|
||||||
|
$r .= "</ul>\n";
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
$reqHeadersArr = array();
|
||||||
|
$requestHeaders = $_SERVER['REQUEST_METHOD'] . ' ' . $_SERVER['REQUEST_URI'] . ' ' . $_SERVER['SERVER_PROTOCOL'] . PHP_EOL;
|
||||||
|
foreach ($reqHeadersArr as $key => $value) {
|
||||||
|
if ($key == 'Host')
|
||||||
|
continue;
|
||||||
|
$requestHeaders .= "$key: $value" . PHP_EOL;
|
||||||
|
}
|
||||||
|
// $requestHeaders = $this->encode(apache_request_headers(), FALSE,
|
||||||
|
// FALSE);
|
||||||
|
$responseHeaders = implode(PHP_EOL, headers_list()).PHP_EOL.'Status: HTTP/1.1 ';
|
||||||
|
$responseHeaders .= Util::$restler->responseCode.' '.\Luracast\Restler\RestException::$codes[Util::$restler->responseCode];
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><?php echo $title?></title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||||
|
<style>
|
||||||
|
<?php include __DIR__.'/debug.css'; ?>
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="breadcrumbs-one">
|
||||||
|
<?php
|
||||||
|
if(Util::$restler->exception){
|
||||||
|
$stages = Util::$restler->exception->getStages();
|
||||||
|
$curStage = Util::$restler->exception->getStage();
|
||||||
|
foreach($stages['success'] as $stage){
|
||||||
|
echo "<a href=\"#\">$stage</a>";
|
||||||
|
}
|
||||||
|
foreach($stages['failure'] as $stage){
|
||||||
|
echo '<a href="#" class="failure">'
|
||||||
|
. $stage
|
||||||
|
. ($stage==$curStage ? ' <span class="state"/> ' : '')
|
||||||
|
. '</a>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach(Util::$restler->_events as $stage){
|
||||||
|
echo "<a href=\"#\">$stage</a>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<header>
|
||||||
|
<h1><?php echo $title ?></h1>
|
||||||
|
</header>
|
||||||
|
<article>
|
||||||
|
|
||||||
|
<h2>Response:<right><?php echo $icon;?></right></h2>
|
||||||
|
<pre class="header"><?php echo $responseHeaders ?></pre>
|
||||||
|
<?php echo render($response); ?>
|
||||||
|
<h2>Additional Template Data:</h2>
|
||||||
|
<?php echo render($template_vars); ?>
|
||||||
|
<p>Restler v<?php echo Restler::VERSION?></p>
|
||||||
|
</article>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user