/**
  * @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());
 }
Exemple #2
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;
 }
Exemple #3
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 #4
0
 /**
  * Instead of chaining together .alias().demand().default(),
  * you can specify keys in opt for each of the chainable methods.
  *
  * Optionally options() can take an object that maps
  * keys to opt parameters.
  */
 public function options($key, $opt)
 {
     $key = Utility::wrapAssoc($key);
     foreach ($key as $_key) {
         foreach ($opt as $option => $value) {
             if (method_exists($this, $option)) {
                 $this->{$option}($_key, $value);
             }
         }
     }
     return $this;
     // chainable
 }
Exemple #5
0
/**
 * This differs from propIn() only when target property
 * is an array, this returns true when at least one of
 * the contents in targert property matches $values,
 * while propIn() does full array equality comparison.
 *
 * @param {string} $prop Target property.
 * @param {array} $values Array of values to match against.
 * @param {bool} $strict Whether to perform a strict comparison or not.
 *
 * @returns {Closure} A function that returns true on
 *                    at least one matches, false othereise.
 */
function propHas($prop, array $values, $strict = false)
{
    return function ($object) use($prop, $values, $strict) {
        $prop = Utility::wrapAssoc(@$object[$prop]);
        $prop = array_map(function ($prop) use($values, $strict) {
            return in_array($prop, $values, $strict);
        }, $prop);
        return in_array(true, $prop, true);
    };
}