/**
  * @constructor
  *
  * @param {array}  $rules Redirect rules
  * @param {callable|string} $rules[][source] Regex, plain string startsWith() or callback matcher func,
  * @param {string} $rules[][target] String for redirection, can use backreference on regex,
  * @param {?int}   $rules[][options] Redirection $options, or internal by default,
  * @param {?string} $options[source] Base path to match against requests, defaults to root.
  * @param {string|callable} $options[target] Redirects to a static target, or function($request) returns a string;
  */
 public function __construct($rules)
 {
     // rewrite all URLs
     if (is_string($rules)) {
         $rules = array('*' => $rules);
     }
     $rules = util::wrapAssoc($rules);
     $this->rules = array_reduce($rules, function ($result, $rule) {
         $rule = array_select($rule, array('source', 'target', 'options'));
         // note: make sure source is callback
         if (is_string($rule['source'])) {
             // regex
             if (@preg_match($rule['source'], null) !== false) {
                 $rule['source'] = matches($rule['source']);
                 if (is_string($rule['target'])) {
                     $rule['target'] = compose(invokes('uri', array('path')), replaces($rule['source'], $rule['target']));
                 }
             } else {
                 if (!is_callable($rule['source'])) {
                     $rule['source'] = startsWith($rule['source']);
                     if (is_string($rule['target'])) {
                         $rule['target'] = compose(invokes('uri', array('path')), replaces('/^' . preg_quote($rule['source']) . '/', $rule['target']));
                     }
                 }
             }
         }
         if (!is_callable($rule['source'])) {
             throw new InvalidArgumentException('Source must be string, regex or callable.');
         }
         $result[] = $rule;
         return $result;
     }, array());
 }
 function process()
 {
     $path = (array) @$this->taskInstance()->settings;
     $path = $path['path'];
     $store = $this->dataStore();
     Log::info(sprintf('Value of (%s) in current data store.', $path), array('path' => $path, 'value' => util::deepVal($path, $store)));
 }
Exemple #3
0
 /**
  * Shorthand access to common filter types.
  */
 static function &commonFilters()
 {
     static $filters;
     if (!$filters) {
         $filters = array('raw' => array('filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_NULL_ON_FAILURE), 'rawS' => array('filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_REQUIRE_SCALAR | FILTER_NULL_ON_FAILURE), 'rawA' => array('filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FORCE_ARRAY | FILTER_NULL_ON_FAILURE), 'boolS' => array('filter' => FILTER_VALIDATE_BOOLEAN, 'flags' => FILTER_REQUIRE_SCALAR | FILTER_NULL_ON_FAILURE), 'intS' => array('filter' => FILTER_VALIDATE_INT, 'flags' => FILTER_REQUIRE_SCALAR | FILTER_NULL_ON_FAILURE), 'intA' => array('filter' => FILTER_VALIDATE_INT, 'flags' => FILTER_FORCE_ARRAY | FILTER_NULL_ON_FAILURE), 'floatS' => array('filter' => FILTER_VALIDATE_FLOAT, 'flags' => FILTER_REQUIRE_SCALAR | FILTER_NULL_ON_FAILURE), 'floatA' => array('filter' => FILTER_VALIDATE_FLOAT, 'flags' => FILTER_FORCE_ARRAY | FILTER_NULL_ON_FAILURE), 'strS' => array('filter' => FILTER_SANITIZE_STRING, 'flags' => FILTER_REQUIRE_SCALAR | FILTER_NULL_ON_FAILURE | FILTER_FLAG_NO_ENCODE_QUOTES), 'strA' => array('filter' => FILTER_SANITIZE_STRING, 'flags' => FILTER_FORCE_ARRAY | FILTER_NULL_ON_FAILURE | FILTER_FLAG_NO_ENCODE_QUOTES), 'urlS' => array('filter' => FILTER_VALIDATE_URL, 'flags' => FILTER_REQUIRE_SCALAR | FILTER_NULL_ON_FAILURE), 'urlA' => array('filter' => FILTER_VALIDATE_URL, 'flags' => FILTER_FORCE_ARRAY | FILTER_NULL_ON_FAILURE), 'date' => array('filter' => FILTER_CALLBACK, 'flags' => FILTER_NULL_ON_FAILURE, 'options' => '\\core\\Utility::validateDateTime'), 'dateS' => array('filter' => FILTER_CALLBACK, 'flags' => FILTER_REQUIRE_SCALAR | FILTER_NULL_ON_FAILURE, 'options' => '\\core\\Utility::validateDateTime'), 'priceS' => array('filter' => FILTER_CALLBACK, 'options' => function ($input) {
             return preg_match('/[+-]?\\d+(?:\\.\\d+)?(?:\\:\\w+)?/', trim(Utility::unwrapAssoc($input))) ? $input : null;
         }), 'regex' => function ($pattern) {
             return array('filter' => FILTER_CALLBACK, 'options' => function ($input) use($pattern) {
                 return preg_match($pattern, $input) ? $input : null;
             });
         });
     }
     return $filters;
 }
Exemple #4
0
 public function validate(array &$errors = array())
 {
     $schema = $this->schema();
     if (!Jsv4::isValid($this->data, $schema)) {
         // try to coerce on initial failure
         $result = Jsv4::coerce($this->data, $schema);
         if ($result->value) {
             $this->data = $result->value;
         }
         // return errors if exists
         if (!empty($result->errors)) {
             $errors = array_merge(util::objectToArray($result->errors));
         }
     }
     return parent::validate();
 }
Exemple #5
0
 public function resolve(Request $request, Response $response)
 {
     global $argv;
     // Debug access log
     if (System::environment() == 'debug') {
         switch ($request->client('type')) {
             case 'cli':
                 $message = implode(' ', array($request->client('type'), $request->uri()));
                 break;
             default:
                 $message = implode(' ', array($request->client('version'), strtoupper($request->method()), $request->uri('path')));
                 @Log::debug($message, array_filter(array('origin' => $request->client('referer'), 'userAgent' => util::cascade(@$request->client('userAgent'), 'Unknown'), 'timeElapsed' => round(microtime(1) - $request->timestamp(), 4) . ' secs')));
                 break;
         }
     }
 }
Exemple #6
0
 /**
  * @param $handlers An array of template handler options, with the following format:
  *                 [{ render: // Template rendering function: function($template, $resource);
  *                  , extensions: // File extensions to match against handling templates.
  *                  }]
  */
 public function __construct($handlers = array())
 {
     $handlers = Utility::wrapAssoc($handlers);
     foreach ($handlers as &$handler) {
         if (!is_callable($handler['render'])) {
             throw new FrameworkException('Please specify a valid render function.');
         }
         if (!@$handler['extensions']) {
             throw new FrameworkException('Please specify file extensions this handler handles.');
         }
         if (is_string($handler['extensions'])) {
             $handler['extensions'] = preg_split('/\\s+/', $handler['extensions']);
         }
         $handler['extensions'] = Utility::wrapAssoc($handler['extensions']);
     }
     $this->handlers = $handlers;
 }
 public function __invoke(array $record)
 {
     if ($record['level'] >= $this->level) {
         // note: file and line should be one level above the called class.
         $backtrace = util::getCallee($this->backtraceLevel - 1);
         if (isset($backtrace['file'])) {
             $record['extra']['file'] = str_replace(getcwd(), '', basename($backtrace['file'], '.php'));
         }
         if (isset($backtrace['line'])) {
             $record['extra']['line'] = $backtrace['line'];
         }
         $backtrace = util::getCallee($this->backtraceLevel);
         $action = @"{$backtrace['class']}{$backtrace['type']}{$backtrace['function']}";
         if ($action) {
             $record['extra']['action'] = $action;
         }
         unset($action);
     }
     return $record;
 }
 public function render($path)
 {
     $res = $this->response();
     $mime = util::getInfo($path);
     if (preg_match('/^text\\//', $mime)) {
         $res->header('Content-Type', "{$mime}; charset=utf-8");
     } else {
         $res->header('Content-Type', $mime);
     }
     unset($mime);
     // note; during developement everything must be revalidated
     if (System::environment() == System::ENV_DEVELOPMENT) {
         $res->isVirtual = true;
     }
     parent::render($path);
     // Ouptut the file
     if ($res->status() < 300) {
         $res->header('Content-Length', filesize($path));
         $res->send($path);
     }
 }
Exemple #9
0
 /**
  * Open an image file.
  */
 function open($file)
 {
     $stat = Utility::getInfo($file, FILEINFO_MIME_TYPE);
     switch ($stat) {
         case 'image/jpeg':
         case 'image/jpg':
             $image = imagecreatefromjpeg($file);
             break;
         case 'image/gif':
             $image = imagecreatefromgif($file);
             break;
         case 'image/png':
             $image = imagecreatefrompng($file);
             imagealphablending($image, true);
             imagesavealpha($image, true);
             break;
         case 'image/bmp':
             // $image = imagecreatefromwbmp($file);
             $image = self::importBMP($file);
             break;
         case 'image/vnd.wap.wbmp':
             $image = imagecreatefromwbmp($file);
             break;
         case 'image/tif':
         case 'image/tiff':
         default:
             $image = null;
             break;
     }
     if (!$image) {
         throw new exceptions\CoreException("Invalid image format \"{$stat}\", this class supports a limited set of image formats. Read the code for more details.");
     }
     imageinterlace($image, 1);
     $this->image = $image;
     $this->mime = $stat;
 }
Exemple #10
0
function triggerDeprecate($successor = '')
{
    $message = Utility::getCallee();
    $message = implode('::', array_filter([@$message['class'], @$message['function']]));
    $message = "Function {$message}() has been deprecated";
    if ($successor) {
        $message .= ", use its successor {$successor} instead";
    }
    trigger_error("{$message}.", E_USER_DEPRECATED);
}
Exemple #11
0
 /**
  * Perform cURL requests and throw appropriate exceptions.
  *
  * An array of parameters used in a curl_setopt_array,
  * multiple calls can be passed in.
  *
  * This function make use of curl_multi no matter it is
  * single request or not.
  *
  * Callbacks are used to handle results inside the array.
  *
  * $option['callbacks'] = array(
  *   'progress' => [Function]
  * , 'success' => [Function]
  * , 'failure' => [Function]
  * , 'always'  => [Function]
  * );
  *
  * @return void
  */
 public static function curlRequest($options)
 {
     $options = Utility::wrapAssoc(array_values((array) $options));
     $multiHandle = curl_multi_init();
     // Initialize cUrl options
     array_walk($options, function (&$option) {
         // 1. Request headers
         $option['response'] = array('headers' => '');
         $option[CURLOPT_HEADERFUNCTION] = function ($curl, $data) use(&$option) {
             $option['response']['headers'] .= $data;
             return strlen($data);
         };
         // 2. Progress function
         $progressCallback =& $option['callbacks']['progress'];
         if ($progressCallback) {
             $option[CURLOPT_NOPROGRESS] = false;
             $option[CURLOPT_PROGRESSFUNCTION] = function () use(&$progressCallback) {
                 if (func_num_args() == 4) {
                     list($dSize, $dLen, $uSize, $uLen) = func_get_args();
                 } else {
                     list($req, $dSize, $dLen, $uSize, $uLen) = func_get_args();
                 }
                 if ($dSize || $dLen) {
                     static $_dLen = 0;
                     if ($_dLen != $dLen) {
                         $_dLen = $dLen;
                         /*! Note by Vicary @ 2.Oct.2012
                          *  Total download size is often 0 if server doesn't
                          *  response with a Content-Length header.
                          *
                          *  Total size guessing logic:
                          *  1. if $dLen < 1M, assume 1M.
                          *  2. if $dLen < 10M, assume 10M.
                          *  3. if $dLen < 100M, assume 100M.
                          *  4. if $dLen < 1G, assume 1G.
                          */
                         if (!$dSize) {
                             // Do not assume when size under 1K
                             if ($dLen < 5000) {
                                 return;
                             } elseif ($dLen < 10000000) {
                                 $dSize = 20000000;
                             } elseif ($dLen < 100000000) {
                                 $dSize = 200000000;
                             } elseif ($dLen < 1000000000) {
                                 $dSize = 2000000000;
                             } else {
                                 $dSize = 20000000000;
                             }
                             // $dSize = $dLen / .05;
                         }
                         // Download progress, from 0 to 1.
                         $progressArgs = array($dLen / $dSize, $dLen, $dSize);
                     }
                 } else {
                     if ($uSize) {
                         static $_uLen = 0;
                         if ($_uLen != $uLen) {
                             $_uLen = $uLen;
                             $uSize *= -1;
                             $uLen += $uSize;
                             // Upload progress, from -1 to 0.
                             $progressArgs = array($uLen / $uSize, $uLen, $uSize);
                         }
                     }
                 }
                 // Fire the event for each µSeconds.
                 static $_tOffset = 0;
                 $tOffset = microtime(1);
                 if (isset($progressArgs) && $tOffset - $_tOffset > self::progressInterval()) {
                     $_tOffset = $tOffset;
                     Utility::forceInvoke($progressCallback, $progressArgs);
                 }
             };
         }
         unset($progressCallback);
         // 3. Apply cUrl options, numeric keys only.
         $option['handle'] = curl_init();
         curl_setopt_array($option['handle'], array_filter_keys($option, 'is_int'));
     });
     $requestIndex = 0;
     while ($requestIndex < self::$maximumRequests && isset($options[$requestIndex])) {
         curl_multi_add_handle($multiHandle, $options[$requestIndex++]['handle']);
     }
     // Start the multi request
     do {
         $status = curl_multi_exec($multiHandle, $active);
         /* Added by Vicary @ 6.Nov.2012
               Blocks until there is a message arrives.
            */
         curl_multi_select($multiHandle);
         do {
             $info = curl_multi_info_read($multiHandle, $queueLength);
             if ($info === FALSE) {
                 continue;
             }
             $optionIndex = array_search($info['handle'], array_map(prop('handle'), $options));
             if ($optionIndex === FALSE) {
                 continue;
             }
             $curlOption =& $options[$optionIndex];
             $callbacks =& $curlOption['callbacks'];
             // Success handler
             if ($info['result'] === CURLE_OK) {
                 // Fire a 100% downloaded event.
                 if (@$callbacks['progress']) {
                     Utility::forceInvoke($callbacks['progress'], array(1, 1, 1));
                     usleep(self::progressInterval() * 1000000);
                 }
                 // Append HTTP status code
                 $curlOption['status'] = curl_getinfo($info['handle'], CURLINFO_HTTP_CODE);
                 Utility::forceInvoke(@$callbacks['success'], array(curl_multi_getcontent($info['handle']), $curlOption));
             } else {
                 $errorNumber = curl_errno($info['handle']);
                 $errorMessage = curl_error($info['handle']);
                 // libcurl errors, try to parse it.
                 if ($errorNumber === 0) {
                     if (preg_match('/errno: (\\d+)/', $errorMessage, $matches)) {
                         $errorNumber = (int) $matches[1];
                         $curlErrors = unserialize(FRAMEWORK_NET_CURL_ERRORS);
                         if (isset($curlErrors[$errorNumber])) {
                             $errorMessage = $curlErrors[$errorNumber];
                         }
                     }
                 }
                 Utility::forceInvoke(@$callbacks['failure'], array($errorNumber, $errorMessage, $curlOption));
                 unset($errorNumber, $errorMessage);
             }
             // Always handler
             Utility::forceInvoke(@$callbacks['always'], array($curlOption));
             if (isset($options[$requestIndex])) {
                 curl_multi_add_handle($multiHandle, $options[$requestIndex++]['handle']);
                 // Keep the loop alive.
                 $active = TRUE;
             }
             curl_multi_remove_handle($multiHandle, $info['handle']);
             curl_close($info['handle']);
             unset($info, $callbacks, $curlOption, $options[$optionIndex], $optionIndex);
         } while ($queueLength > 0);
     } while ($status === CURLM_CALL_MULTI_PERFORM || $active);
     curl_multi_close($multiHandle);
 }
Exemple #12
0
 /**
  * Process next task with POST data.
  */
 public function process()
 {
     // note: Some tasks can work without post data, but request method must be POST.
     if (!$this->__isSuperUser && $this->__request->method() != 'post') {
         $this->__response->status(405);
         // Method not allowed
         return;
     }
     // WorkInstance
     if (!$this->identity()) {
         $this->__response->status(404);
         // WorkInstance not found
         return;
     }
     // TaskInstance
     $instance = $this->nextTask();
     if (!$instance) {
         $this->__response->status(404);
         // TaskInstance not foudn
         return;
     }
     // release mutable lock for work instance initialization.
     $this->_immutable = false;
     // remove tasks to prevent unwanted change.
     $tasks = $this->tasks;
     unset($this->tasks);
     // creates $this->dataStore if not yet.
     if (empty($this->dataStore)) {
         $this->dataStore = array();
     }
     unset($this->lastError);
     // immutable marker to prevent direct modifications to the internal data.
     $this->_immutable = true;
     // note: Send bayeux message to groups with permission to this task.
     $userGroups = $instance->userGroups();
     try {
         // Note: Since $this->dataStore is an array, it is mutable itself.
         $promise = $instance->process();
     } catch (\Exception $e) {
         Log::warning('Task process exception.', array_filter(array('message' => $e->getMessage(), 'code' => $e->getCode(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'trace' => $e->getTrace())));
         $lastError = array('message' => $this->__response->__($e->getMessage()), 'code' => $e->getCode());
         // note: Failure on Headless tasks will revert to previous task.
         if (@$instance->type == 'Headless') {
             $deferred = new Deferred();
             $deferred->reject($lastError['message'], $lastError['code']);
             $promise = $deferred->promise();
             unset($deferred);
         } else {
             $this->_immutable = false;
             $this->lastError = $lastError;
         }
         unset($lastError);
     }
     $this->_immutable = false;
     $result = array();
     $saveFunc = function () use(&$result) {
         unset($this->timestamp);
         $this->save($result);
     };
     if (isset($promise)) {
         // note: rejection here means revert to previous task
         $promise->fail(function ($error, $code = null) use($instance, $tasks) {
             $this->lastError = array_filter(array('message' => $error, 'code' => $code));
             // revert to previous task
             $prevTask = array_search($instance->identity(), array_map(invokes('identity'), $tasks));
             $prevTask = @$tasks[$prevTask - 1];
             // fallback to the first task
             if (!$prevTask) {
                 $prevTask = reset($tasks);
             }
             $this->nextTask = util::packUuid($prevTask->identity());
         });
         // note: resolution always advances to next task
         $promise->done(function () use($instance, $tasks) {
             $nextTask = array_search($instance->identity(), array_map(invokes('identity'), $tasks));
             $nextTask = @$tasks[$nextTask + 1];
             if ($nextTask) {
                 $this->nextTask = util::packUuid($nextTask->identity());
             } else {
                 $this->state = static::STATE_CLOSE;
                 $this->nextTask = null;
             }
         });
         // note: controller script must call resolve() or reject() to make this happen.
         $promise->always($saveFunc);
     } else {
         $saveFunc();
     }
     unset($saveFunc);
     // note: Merge user groups before and after task processing
     if ($this->nextTask) {
         $userGroups = array_unique(array_merge($userGroups, $this->nextTask()->userGroups()));
     }
     foreach ($userGroups as $userGroup) {
         Bayeux::sendMessage("/group/{$userGroup}", array('action' => 'update', '@collection' => 'WorkInstance', 'timestamp' => $this->timestamp));
     }
     if (@$result['error']) {
         $this->__response->status(500);
         return $result;
     } else {
         // note; User might no longer has access to the updated work instance.
         if ($this->data()) {
             $this->__response->status(200);
             return $this;
         } else {
             $this->__response->status(204);
         }
     }
 }
Exemple #13
0
          AND `pid` IS NOT NULL
          AND `pid` NOT IN (' . Utility::fillArray($pids) . ')', $pids);
        if ($res) {
            $affectedRows += $res->rowCount();
        }
        // Delete cron process only when current time is ahead of start_time
        $res = Database::query('DELETE FROM `' . FRAMEWORK_COLLECTION_PROCESS . '`
        WHERE `type` = \'cron\'
          AND `pid` = 0
          AND `pid` NOT IN (' . Utility::fillArray($pids) . ')', $pids);
        if ($res) {
            $affectedRows += $res->rowCount();
        }
        // Clear pid of dead permanent process
        $res = Database::query('UPDATE `' . FRAMEWORK_COLLECTION_PROCESS . '` SET `pid` = NULL
        WHERE `type` = \'permanent\' AND `pid` IS NOT NULL AND `pid` NOT IN (' . Utility::fillArray($pids) . ')', $pids);
        if ($res) {
            $affectedRows += $res->rowCount();
        }
    }
    unset($res, $pids);
    if ($affectedRows) {
        Log::debug(sprintf('Process cleanup, %d processes removed.', $affectedRows));
    }
    die;
}
// Cron processes
if (@$opts['cron']) {
    Log::debug('Cron started process.');
    $scheduler = function ($schedule) {
        $nextTime = CronExpression::factory($schedule['schedule'])->getNextRunDate()->format('Y-m-d H:i:s');
Exemple #14
0
 /**
  * @protected
  *
  * Pack UUID for delete filters.
  */
 function beforeDelete(array &$filter = array())
 {
     if (isset($filter[$this->primaryKey()])) {
         $filter[$this->primaryKey()] = util::packUuid($filter[$this->primaryKey()]);
     }
     return $this;
 }
Exemple #15
0
 /**
  * @constructor
  *
  * @param {?string|array} $options An array of request options, or the URI string.
  * @param {?string} $options['prefix'] Parameters keys start with this prefix
  *                                     will be treated as meta-parameters and
  *                                     not returned in param() related functions.
  *
  *                                     Parameters values start with this prefix
  *                                     will be tried to parsed as constants and
  *                                     booleans.
  *
  *                                     This defaults to the "@" character.
  *
  * @param {?string} $options['uri'] The request uri, defaults to $_SERVER['REQUEST_URI'].
  * @param {?string} $options['method'] Request method, defaults to $_SERVER['REQUEST_METHOD'].
  * @param {?array} $options['headers'] Request headers, defaults to the contents of getallhheaders()
  *                                     if the function is available.
  * @param {?array} $options['client'] Request client details, defaults to everything from $_SERVER.
  * @param {?array} $options['get'] GET parameters in array format.
  * @param {?array} $options['post'] POST parameters in array format.
  * @param {?array} $options['cookies'] COOKIES in array format.
  * @param {?array} $options['files'] Upload files along with this request. (Use with care)
  *                                   When doing CURL requests, files array must compatible with CURL.
  *                                   Otherwise this must obey the resolver-request format.
  * @param {?string} $options['locale'] Requesting locale, defaults to en_US.
  */
 public function __construct($options = array())
 {
     global $argv;
     if ($options instanceof Resolver) {
         $this->resolver = $options;
         $options = array();
     }
     if (@$options) {
         if (is_string($options)) {
             $options = array('uri' => $options);
         }
         // Special parameter prefix
         if (!empty($options['prefix'])) {
             $this->metaPrefix = $options['prefix'];
         }
         // Request URI
         if (empty($options['uri'])) {
             throw new FrameworkException('Request URI is required.');
         } else {
             $this->setUri($options['uri']);
         }
         // Request method
         if (isset($options['method'])) {
             $this->method = strtolower($options['method']);
         } else {
             $this->method = 'get';
         }
         // Request headers
         if (isset($options['headers'])) {
             $this->headers = (array) $options['headers'];
         }
         // Request client
         if (!empty($options['client'])) {
             $this->client = (array) $options['client'];
         }
         // Request parameters GET
         if (isset($options['get'])) {
             $this->paramCache['get'] = (array) $options['get'];
         }
         // Request parameters POST
         if (!empty($options['post'])) {
             $this->paramCache['post'] = (array) $options['post'];
         }
         // Cookies
         if (isset($options['cookies'])) {
             $this->paramCache['cookies'] = (array) $options['cookies'];
         }
         // File uploads
         if (isset($options['files'])) {
             $this->paramCache['files'] = (array) $options['files'];
         }
         // Request locale
         if (!empty($options['locale'])) {
             $this->locale = (string) $options['locale'];
         }
     } else {
         // Request client
         switch (constant('PHP_SAPI')) {
             case 'cli':
                 $this->client = array('type' => 'cli', 'host' => gethostname(), 'user' => get_current_user());
                 break;
             default:
                 $this->client = array_filter(array('type' => 'http', 'secure' => @$_SERVER['HTTPS'] && strtolower($_SERVER['HTTPS']) != 'off', 'address' => @$_SERVER['REMOTE_ADDR'], 'host' => @$_SERVER['REMOTE_HOST'], 'port' => @$_SERVER['REMOTE_PORT'], 'user' => @$_SERVER['REMOTE_USER'], 'referer' => @$_SERVER['HTTP_REFERER'], 'version' => @$_SERVER['SERVER_PROTOCOL'], 'userAgent' => @$_SERVER['HTTP_USER_AGENT'], 'forwarder' => @$_SERVER['HTTP_X_FORWARDED_FOR'], 'isAjax' => strtolower(@$_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'), compose('not', 'is_null'));
                 break;
         }
         // Request method
         switch ($this->client('type')) {
             case 'cli':
                 $this->method = 'cli';
                 break;
             default:
                 $this->method = strtolower(@$_SERVER['REQUEST_METHOD']);
                 break;
         }
         // Request headers
         switch ($this->client('type')) {
             case 'cli':
                 break;
             default:
                 $this->headers = getallheaders();
                 break;
         }
         // Request parameters
         switch ($this->client('type')) {
             case 'cli':
                 $this->paramCache['cli'] = new Optimist();
                 break;
             default:
                 // Request parameters GET
                 $this->paramCache['get'] = $_GET;
                 // Request parameters POST
                 if (preg_match('/^application\\/json/', $this->header('Content-Type'))) {
                     $this->paramCache['post'] = ContentDecoder::json(file_get_contents('php://input'), true);
                 } else {
                     $this->paramCache['post'] = $_POST;
                 }
                 // Cookies
                 $this->paramCache['cookies'] =& $_COOKIE;
                 // File uploads
                 if ($this->method() == 'put') {
                     $this->paramCache['files'] = new RequestPutFile($this->header('Content-Type'));
                 } else {
                     util::filesFix();
                     $parseFile = function ($file) use(&$parseFile) {
                         if (!is_array($file)) {
                             return $file;
                         }
                         if (util::isAssoc($file)) {
                             switch ($file['error']) {
                                 case UPLOAD_ERR_OK:
                                     return new RequestPostFile($file);
                                 case UPLOAD_ERR_NO_FILE:
                                     // Skip it.
                                     break;
                                 default:
                                     return $file['error'];
                             }
                         } else {
                             return array_mapdef($file, $parseFile);
                         }
                     };
                     $this->paramCache['files'] = array_mapdef(array_filter_keys($_FILES, compose('not', startsWith('@'))), $parseFile);
                     unset($parseFile);
                 }
                 break;
         }
         // Request URI
         // CLI requires request parameters
         switch ($this->client('type')) {
             case 'cli':
                 /*! Note @ 9 May, 2015
                  *  Usage: node-cli [OPTIONS] COMMAND
                  *  Only one command is supported, simply shift it out.
                  */
                 $this->uri = @$this->paramCache['cli']['_'][0];
                 if (!$this->uri) {
                     $this->uri = $argv[1];
                 }
                 break;
             default:
                 $uri = array('scheme' => $this->client('secure') ? 'https' : 'http', 'user' => @$_SERVER['REMOTE_USER'], 'host' => @$_SERVER['SERVER_NAME'], 'port' => @$_SERVER['SERVER_PORT'], 'path' => @$_SERVER['REQUEST_URI'], 'query' => $_GET);
                 if (empty($uri['user'])) {
                     $uri['user'] = @$_SERVER['PHP_AUTH_USER'];
                 }
                 $this->setUri(array_filter($uri));
                 // = parse_url(http_build_url($this->uri));
                 break;
         }
         // Parse special parameter values
         array_walk_recursive($this->paramCache, function (&$value) {
             if (is_string($value) && strpos($value, $this->metaPrefix) === 0) {
                 $_value = substr($value, strlen($this->metaPrefix));
                 switch (strtolower($_value)) {
                     case 'true':
                         $value = true;
                         break;
                     case 'false':
                         $value = false;
                         break;
                     default:
                         if (defined($_value)) {
                             $value = constant($_value);
                         }
                         break;
                 }
             }
         });
         // Request timestamp
         $this->timestamp = (double) @$_SERVER['REQUEST_TIME_FLOAT'];
     }
     // Unified params ($_REQUEST mimic)
     switch ($this->client('type')) {
         case 'cli':
             // $this->paramCache['request'] = $this->paramCache['cli'];
             break;
         default:
             $this->paramCache['request'] = array_merge((array) @$this->paramCache['cookies'], (array) @$this->paramCache['get'], (array) @$this->paramCache['post']);
             break;
     }
     // Failover in case of request time not exists.
     if (!$this->timestamp) {
         $this->timestamp = microtime(1);
     }
 }
 public function resolve(Request $req, Response $res)
 {
     $auth = $this->paths;
     $pathNodes = trim($req->uri('path'), '/');
     if ($pathNodes) {
         $pathNodes = explode('/', $pathNodes);
     } else {
         $pathNodes = ['/'];
     }
     $lastWildcard = @$auth['*'];
     foreach ($pathNodes as $index => $pathNode) {
         if (!util::isAssoc($auth)) {
             break;
             // No more definitions, break out.
         }
         if (isset($auth['*'])) {
             $lastWildcard = $auth['*'];
         }
         if (isset($auth[$pathNode])) {
             $auth = $auth[$pathNode];
         } else {
             unset($auth);
             break;
         }
     }
     if (!isset($auth) || !is_bool($auth) && (!is_array($auth) || util::isAssoc($auth))) {
         if (empty($lastWildcard)) {
             throw new FrameworkException('Unable to resolve authentication chain from request URI.');
         } else {
             $auth = $lastWildcard;
         }
     }
     unset($pathNodes, $lastWildcard);
     // Numeric array
     if (is_array($auth) && !util::isAssoc($auth)) {
         $auth = array_reduce($auth, function ($result, $auth) use($req) {
             if (!$result) {
                 return $result;
             }
             if (is_callable($auth)) {
                 $auth = $auth($req);
             } else {
                 if (is_string($auth)) {
                     if (strpos($auth, '/') === false) {
                         $auth = "authenticators\\{$auth}";
                     }
                     if (is_a($auth, 'framework\\interfaces\\IAuthenticator', true)) {
                         $result = $result && $auth::authenticate($req);
                     } else {
                         throw new FrameworkException('Unknown authenticator type, must be ' . 'instance of IAuthenticator or callable.');
                     }
                 } else {
                     throw new FrameworkException('Unknown authenticator type, must be ' . 'instance of IAuthenticator or callable.');
                 }
             }
             return $result && $auth;
         }, true);
     }
     // Boolean
     if (is_bool($auth) && !$auth) {
         $res->status($this->statusCode);
     }
     // TODO: Mark allowed or denied according to the new resolver mechanism.
 }
Exemple #17
0
function invokes($name, array $args = array())
{
    return function ($object) use($name, $args) {
        if (Utility::isAssoc($object)) {
            $func = @$object[$name];
        } else {
            if (method_exists($object, $name)) {
                $func = array($object, $name);
            } else {
                if (isset($object->{$name}) && is_callable($object->{$name})) {
                    $func = $object->{$name};
                } else {
                    if (is_object($object)) {
                        $object = get_class($object);
                    }
                    if (is_array($object)) {
                        $object = 'input array';
                    }
                    trigger_error("No callable {$name}() found in {$object}.", E_USER_WARNING);
                    unset($object);
                }
            }
        }
        return call_user_func_array($func, $args);
    };
}
Exemple #18
0
 /**
  * Retrieve process related info by specified property $name.
  *
  * @param {string} $name Target property in process object, omit this to get the whole object.
  */
 public static function get($name = null)
 {
     if (constant('PHP_SAPI') != 'cli' || !function_exists('posix_getppid')) {
         return null;
     }
     $processData =& self::$_processData;
     if (!$processData) {
         $processData = Node::get(array(Node::FIELD_COLLECTION => FRAMEWORK_COLLECTION_PROCESS, 'pid' => [getmypid(), posix_getppid()]));
         $processData = util::unwrapAssoc($processData);
     }
     if (is_null($name)) {
         return $processData;
     } else {
         return @$processData[$name];
     }
 }
Exemple #19
0
 /**
  * Generate a one-time authentication token string for additional
  * security for AJAX service calls.
  *
  * Each additional call to this function overwrites the token generated last time.
  *
  * @return One-time token string, or null on invalid session.
  */
 static function generateToken($sid = null)
 {
     $res = static::ensure($sid);
     if ($res !== true) {
         return $res;
     }
     $res =& static::$currentSession;
     $res['token'] = Database::fetchField("SELECT UNHEX(REPLACE(UUID(),'-',''));");
     unset($res['timestamp']);
     if (Node::set($res) === false) {
         return null;
     }
     return util::unpackUuid($res['token']);
 }
<?php

ob_start();
//error_reporting(-1);
//ini_set('display_errors','1');
use core\Utility as U;
use core\Router;
require_once './core/Utility.php';
U::init();
require_once U::$APP . 'routes.php';
Router::execute($_GET['route'], $_POST);
exit;
Exemple #21
0
 /**
  * Returns the fileinfo expression of current file.
  *
  * @param {int} $type One of the FILEINFO_* constants.
  * @return {array|string|boolean} Result of finfo_file($type), or false when not applicable.
  */
 public function getInfo($type = FILEINFO_MIME_TYPE)
 {
     return \core\Utility::getInfo($this->getRealPath(), $type);
 }
Exemple #22
0
 /**
  * Primary task when including PHP is that we need
  * to change $_SERVER variables to match target file.
  */
 private function handle($path, $request, $response)
 {
     $context = array('request' => $request, 'response' => $response);
     $mime = Utility::getInfo($path, FILEINFO_MIME_TYPE);
     if (strpos($mime, ';') !== false) {
         $mime = substr($mime, 0, strpos($mime, ';'));
     }
     switch ($mime) {
         // note; special case, need content encoding header here. fall over to static file.
         case 'image/svg+xml':
             if (pathinfo($path, PATHINFO_EXTENSION) == 'svgz') {
                 $response->header('Content-Encoding: gzip');
             }
             // mime-types that we output directly.
         // mime-types that we output directly.
         case 'application/pdf':
         case 'application/octect-stream':
         case 'image/jpeg':
         case 'image/jpg':
         case 'image/gif':
         case 'image/png':
         case 'image/bmp':
         case 'image/vnd.wap.wbmp':
         case 'image/tif':
         case 'image/tiff':
         case 'text/plain':
         case 'text/html':
         default:
             $renderer = new StaticFileRenderer($context);
             break;
         case 'application/x-php':
             $renderer = new IncludeRenderer($context);
             break;
     }
     $renderer->render($path);
 }
Exemple #23
0
 public function resolve(Request $request, Response $response)
 {
     $path = $this->srcPath . $request->uri('path') . '.url';
     // Check if target file is a proxy.
     if (!is_file($path)) {
         return;
     }
     $cacheTarget = parse_ini_file($path);
     $cacheTarget = @$cacheTarget['URL'];
     unset($path);
     if (!$cacheTarget) {
         Log::warning('Proxy file has not URL parameter.', array('requestUri' => $request->uri(), 'proxyFile' => $request->uri('path') . '.uri'));
         $response->status(502);
         // Bad Gateway
         return;
     }
     /*! Cache Header Notes
      *
      *  # Cache-Control
      *  [public | private] Cacheable when public, otherwise the client is responsible for caching.
      *  [no-cache( \w+)?]  When no fields are specified, the whole thing must revalidate everytime,
      *                     otherwise cache it except specified fields.
      *  [no-store] 				 Ignore caching and pipe into output.
      *  [max-age=\d+] 		 Seconds before this cache is meant to expire, this overrides Expires header.
      *  [s-maxage=\d+] 		 Overrides max-age and Expires header, behaves just like max-age.
      *                 	   (This is for CDN and we are using it.)
      *  [must-revalidate]  Tells those CDNs which are intended to serve stale contents to revalidate every time.
      *  [proxy-revalidate] Like the "s-" version of max-age, a "must-revalidate" override only for CDN.
      *  [no-transform]     Some CDNs will optimize images and other formats, this "opt-out" of it.
      *
      *  # Expires
      *  RFC timestamp for an absolute cache expiration, overridden by Cache-Control header.
      *
      *  # ETag
      *  Hash of anything, weak ETags is not supported at this moment.
      *
      *  # vary
      *  Too much fun inside and we are too serious about caching, ignore this.
      *
      *  # pragma
      *  This guy is too old to recognize.
      *  [no-cache] Only this is known nowadays and is already succeed by Cache-Control: no-cache.
      *
      */
     // note; Use "cache-meta://" scheme for header and cache meta info, for performance.
     // 1. Check if cache exists.
     $cache = (array) Cache::get("cache-meta://{$cacheTarget}");
     // Cache expiration, in seconds.
     // expires = ( s-maxage || max-age || Expires );
     if (@$cache['expires'] && time() > $cache['expires']) {
         Cache::delete("cache-meta://{$cacheTarget}");
         Cache::delete("cache://{$cacheTarget}");
         $cache = null;
     }
     // - If not exists, make normal request to remote server.
     // - If exists, make conditional request to remote server.
     //   - Revalidation, we can skip this request and serve the content if false.
     //     revalidates = ( Cache-Control:proxy-revalidate || Cache-Control:must-revalidate )
     if (!$cache || @$cache['revalidates']) {
         $_request = array('uri' => $cacheTarget);
         if ($cache) {
             // Last-Modified
             if (@$cache['headers']['Last-Modified']) {
                 $_request['headers']['If-Modified-Since'] = $cache['Last-Modified'];
             }
             // Entity-Tag
             if (@$cache['headers']['ETag'] && strpos($cache['headers']['ETag'], 'W\\') !== 0) {
                 $_request['headers']['If-None-Match'] = $cache['ETag'];
             }
         } else {
             $cache = array();
         }
         // Make the request
         $_response = new Response(array('autoOutput' => false));
         (new Request($_request))->send(null, $_response);
         unset($_request);
         // parse headers into cache settings.
         if (in_array($_response->status(), array(200, 304))) {
             $res = preg_split('/\\s*,\\s*/', util::unwrapAssoc($_response->header('Cache-Control')));
             $res = array_reduce($res, function ($res, $value) {
                 // todo; Take care of no-cache with field name.
                 if (strpos($value, '=') > 0) {
                     $value = explode('=', $value);
                     $res[$value[0]] = $value[1];
                 } else {
                     $res[$value] = true;
                 }
                 return $res;
             }, array());
             // private, no-store, no-cache
             if (@$res['private'] || @$res['no-store'] || @$res['no-cache']) {
                 // note; in case the upstream server change this to uncacheable
                 Cache::delete("cache-meta://{$cacheTarget}");
                 Cache::delete("cache://{$cacheTarget}");
                 $_response->clearBody();
             }
             if ($_response->status() == 200 && $_response->body()) {
                 $cache['contents'] = $_response->body();
             }
             // expires = ( s-maxage || max-age || Expires );
             if (@$res['s-maxage']) {
                 $cache['expires'] = time() + $res['s-maxage'];
             } elseif (@$res['max-age']) {
                 $cache['expires'] = time() + $res['max-age'];
             } else {
                 $res = util::unwrapAssoc($_response->header('Expires'));
                 if ($res) {
                     $cache['expires'] = strtotime($res);
                 }
             }
             // revalidates = ( Cache-Control:proxy-revalidate || Cache-Control:must-revalidate )
             if (@$res['proxy-revalidate'] || @$res['must-revalidate']) {
                 $cache['revalidates'] = true;
             }
             unset($res);
         }
         $cache['headers'] = array_map('core\\Utility::unwrapAssoc', $_response->header());
         // PHP does not support chunked, skip this one.
         unset($cache['headers']['Transfer-Encoding']);
         // note; If cache is to be ignored, the $cacheTarget variable will be already unset().
         if (isset($cacheTarget)) {
             if (@$cache['contents']) {
                 Cache::set("cache://{$cacheTarget}", $cache['contents']);
             }
             Cache::set("cache-meta://{$cacheTarget}", array_filter_keys($cache, isNot('contents')));
         }
         unset($_response);
     }
     // note; Send cache headers regardless of the request condition.
     if (@$cache['headers']) {
         $response->clearHeaders();
         foreach ($cache['headers'] as $name => $value) {
             $response->header($name, $value, true);
         }
         unset($name, $value);
     }
     // note; Handles conditional request
     $ch = array_map('core\\Utility::unwrapAssoc', (array) @$cache['headers']);
     $mtime = @$ch['Last-Modified'] ? strtotime($ch['Last-Modified']) : false;
     // Request headr: If-Modified-Since
     if (@$ch['Last-Modified'] && $mtime) {
         if (strtotime($request->header('If-Modified-Since')) >= $mtime) {
             return $response->status(304);
         }
     }
     // Request header: If-Range
     if ($request->header('If-Range')) {
         // Entity tag
         if (strpos(substr($request->header('If-Range'), 0, 2), '"') !== false && @$ch['ETag']) {
             if ($this->compareETags(@$ch['ETag'], $request->header('If-Range'))) {
                 return $this->response()->status(304);
             }
         } elseif (strtotime($request->header('If-Range')) === $mtime) {
             return $this->response()->status(304);
         }
     }
     unset($mtime);
     // Request header: If-None-Match
     if (!$request->header('If-Modified-Since') && $request->header('If-None-Match')) {
         // Exists but not GET or HEAD
         switch ($request->method()) {
             case 'get':
             case 'head':
                 break;
             default:
                 return $this->response()->status(412);
         }
         /*! Note by Vicary @ 24 Jan, 2013
          *  If-None-Match means 304 when target resources exists.
          */
         if ($request->header('If-None-Match') === '*' && @$ch['ETag']) {
             return $this->response()->status(304);
         }
         if ($this->compareETags(@$ch['ETag'], preg_split('/\\s*,\\s*/', $request->header('If-None-Match')))) {
             return $this->response()->status(304);
         }
     }
     // Request header: If-Match
     if (!$request->header('If-Modified-Since') && $request->header('If-Match')) {
         // Exists but not GET or HEAD
         switch ($request->method()) {
             case 'get':
             case 'head':
                 break;
             default:
                 return $this->response()->status(412);
         }
         if ($request->header('If-Match') === '*' && !@$ch['ETag']) {
             return $this->response()->status(412);
         }
         preg_match_all('/(?:^\\*$|(:?"([^\\*"]+)")(?:\\s*,\\s*(:?"([^\\*"]+)")))$/', $request->header('If-Match'), $eTags);
         // 412 Precondition Failed when nothing matches.
         if (@$eTags[1] && !in_array($eTag, (array) $eTags[1])) {
             return $this->response()->status(412);
         }
     }
     if ($cacheTarget && empty($cache['contents'])) {
         $cache['contents'] = Cache::get("cache://{$cacheTarget}");
     }
     // Output the cahce content
     $response->send($cache['contents'], 200);
 }
Exemple #24
0
 /**
  * Get a list of data models from the collection.
  */
 function find(array $filter = array())
 {
     if ($filter && !util::isAssoc($filter)) {
         $filter = array($this->_primaryKey => $filter);
     }
     $filter[Node::FIELD_COLLECTION] = self::collectionName();
     $collection = array();
     Node::getAsync($filter, function ($data) use(&$collection) {
         // create a new instance for retrieved data
         $model = get_called_class();
         $model = new $model($data);
         if (isset($this->__request)) {
             $model->__request = $this->__request;
         }
         if (isset($this->__response)) {
             $model->__response = $this->__response;
         }
         // force invoke internal function
         util::forceInvoke(array($model, 'afterLoad'));
         // add if model still has data
         if ($model->data()) {
             $collection[] = $model;
         }
     });
     return $collection;
 }
Exemple #25
0
 private static function walkArray($node, &$writer, $ns = NULL, $currentNode = NULL)
 {
     $ns = (array) $ns;
     // Text value
     if (!is_array($node)) {
         if (strlen($node) > self::CDATA_THRESHOLD) {
             $writer->writeCData($node);
         } else {
             $writer->text($node);
         }
         return;
     }
     $nodeStarted = FALSE;
     if ($currentNode !== NULL) {
         //------------------------------
         //  startElement()
         //------------------------------
         // 1. Metadata exists
         // 2. Not numeric array
         if (Utility::isAssoc($node)) {
             self::startElement($currentNode, $writer, $ns);
             //------------------------------
             //  Metadata
             //-----------------------------
             // @attributes
             if (isset($node['@attributes'])) {
                 foreach ($node['@attributes'] as $key => $value) {
                     if (preg_match('/^(xmlns\\:?)(.*)$/', $key, $matches)) {
                         $ns[$matches[2]] = $value;
                     }
                     $key = explode(':', $key);
                     if (count($key) == 2) {
                         $writer->writeAttributeNS($key[0], $key[1], $ns[$key[0]], $value);
                     } else {
                         $writer->writeAttribute($key[0], $value);
                     }
                 }
                 unset($node['@attributes']);
             }
             // @comments
             if (isset($node['@comments'])) {
                 foreach ((array) $node['@comments'] as $value) {
                     $writer->writeComment($value);
                 }
                 unset($node['@comments']);
             }
             $nodeStarted = TRUE;
         }
     }
     if (isset($node['@value'])) {
         $node = (array) $node['@value'];
     }
     //------------------------------
     //  Children
     //------------------------------
     if (Utility::isAssoc($node)) {
         foreach ($node as $key => $value) {
             if (!is_array($value)) {
                 self::writeElement($key, $writer, $ns, $value);
             } else {
                 self::walkArray($value, $writer, $ns, $key);
             }
         }
     } else {
         foreach ($node as $value) {
             if (!$nodeStarted && !is_array($value)) {
                 self::writeElement($currentNode, $writer, $ns, $value);
             } else {
                 self::walkArray($value, $writer, $ns, $currentNode);
             }
         }
     }
     if ($nodeStarted) {
         $writer->endElement();
     }
 }
Exemple #26
0
 private function processArgs()
 {
     if ($this->result) {
         return;
     }
     $argv = $this->argv;
     // Remove until the script itself.
     if (@$_SERVER['PHP_SELF'] && in_array($_SERVER['PHP_SELF'], $argv)) {
         $argv = array_slice($argv, array_search($_SERVER['PHP_SELF'], $argv) + 1);
     }
     $args = array();
     $currentNode =& $args['_'];
     foreach ($argv as $value) {
         // Match flag style, then assign initial value.
         if (preg_match('/^\\-\\-?([\\w\\.]+)(?:=(.*))?$/', $value, $matches)) {
             $nodePath = explode('.', $matches[1]);
             $currentNode =& $args[array_shift($nodePath)];
             while ($nodePath) {
                 $currentNode =& $currentNode[array_shift($nodePath)];
             }
             $value = isset($matches[3]) ? $matches[3] : TRUE;
             // Value exists
             if ($currentNode) {
                 if (!is_array($currentNode)) {
                     $currentNode = array($currentNode);
                 }
                 $currentNode[] = $value;
             } else {
                 $currentNode = $value;
             }
             unset($matches, $nodePath);
         } else {
             if (isset($currentNode)) {
                 if ($currentNode === TRUE) {
                     $currentNode = $value;
                 } else {
                     array_pop($currentNode);
                     $currentNode[] = $value;
                 }
             } else {
                 @($args['_'][] = $value);
             }
             unset($currentNode);
         }
     }
     unset($value);
     if (!$args['_']) {
         unset($args['_']);
     }
     //------------------------------
     //  Alias
     //------------------------------
     foreach ($this->alias as $key => $alias) {
         // Alias: type
         if (isset($this->types[$key])) {
             $this->types += array_fill_keys($alias, $this->types[$key]);
         }
         // Alias: defaults
         if (isset($this->defaults[$key])) {
             $this->defaults += array_fill_keys($alias, $this->defaults[$key]);
         }
     }
     unset($key, $alias);
     // Alias: value
     foreach ($args as $option => $value) {
         $target = (array) @$this->alias[$option];
         foreach ($target as $alias) {
             $args[$alias] = $value;
         }
         unset($alias);
     }
     unset($option, $value, $target);
     if (@$args['_']) {
         foreach ($args['_'] as $value) {
             $target = (array) @$this->alias[$value];
             foreach ($target as $alias) {
                 $args['_'][] = $alias;
             }
             unset($alias);
         }
         $args['_'] = array_unique($args['_']);
     }
     unset($value, $target);
     //------------------------------
     //  Demands
     //------------------------------
     if (is_numeric($this->demand)) {
         if (count((array) @$args['_']) < $this->demand) {
             $this->showError("It requires at least {$this->demand} args to run.");
             die;
         }
     } else {
         $missingKeys = array();
         foreach ($this->demand as $key => $value) {
             if (!isset($args[$key])) {
                 $missingKeys[] = $key;
             }
         }
         if ($missingKeys) {
             $this->showError("Missing required options: " . implode(', ', $missingKeys));
             die;
         }
     }
     //------------------------------
     //  Type-casting
     //------------------------------
     $args = Utility::flattenArray($args);
     array_walk($args, function (&$value, $key) {
         if (isset($this->types[$key])) {
             if (!settype($value, $this->types[$key])) {
                 $this->showError("Unable to cast {$key} into type {$type}.");
                 die;
             }
         } elseif (is_numeric($value)) {
             $value = doubleval($value);
         }
     });
     $args += (array) $this->defaults;
     return $this->result = Utility::unflattenArray($args);
 }