Пример #1
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;
 }
Пример #2
0
 /**
  * Send HTTP request to a URL.
  *
  * The format is purposedly copied as much as possible from jQuery.ajax() function.
  *
  * To initiate multiple requests, pass it as an array and wrapping parameters of
  * each single request as an array.
  *
  * @param {string} $options['url'] Target request url
  * @param {?string} $options['type'] Request method, defaults to GET.
  * @param {?array|string} $options['data'] Either raw string or array to be encoded,
  *                                         to be sent as query string on GET, HEAD, DELETE and OPTION
  *                                         or message body on POST or PUT.
  * @param {?array} $options['headers'] Request headers to be sent.
  * @param {?callable} $options['progress'] Callback function for progress ticks.
  *                                         function($progress, $current, $maximum);
  * @param {?callable} $options['success'] Callback function on successful request.
  *                                        function($responseText, $curlOptions);
  * @param {?callable} $options['failure'] Callback function on request failure, with curl errors as parameters.
  *                                        function($errorNumber, $errorMessage, $curlOptions);
  * @param {?array} $options['__curlOpts'] Curl options to be passed directly to curl_setopt_array.
  *
  * @return void
  */
 public static function httpRequest($options)
 {
     $options = Utility::wrapAssoc((array) $options);
     $options = array_map(function (&$option) {
         if (is_string($option)) {
             $option = array('url' => $option);
         } else {
             if (!@$option['url']) {
                 throw new exceptions\CoreException('No URL set!');
             }
         }
         // Auto prepend http, default protocol.
         if (preg_match('/^(\\/\\/)/', $option['url'])) {
             $option['url'] = "http:" . $option['url'];
         }
         $curlOption = array(CURLOPT_URL => $option['url'], CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_CAINFO => null, CURLOPT_CAPATH => null, CURLOPT_FOLLOWLOCATION => true);
         // Request method: 'GET', 'POST', 'PUT', 'HEAD', 'DELETE'
         if (!isset($option['type']) && is_array(@$option['data']) || preg_match('/^post$/i', @$option['type'])) {
             $curlOption[CURLOPT_POST] = true;
             $curlOption[CURLOPT_CUSTOMREQUEST] = 'POST';
         } elseif (preg_match('/^put$/i', @$option['type'])) {
             if (!@$option['file'] || !is_file($option['file'])) {
                 throw new exceptions\CoreException('Please specify the \'file\' option when using PUT method.');
             }
             $curlOption[CURLOPT_PUT] = true;
             $curlOption[CURLOPT_CUSTOMREQUEST] = 'PUT';
             $curlOption[CURLOPT_UPLOAD] = true;
             $curlOption[CURLOPT_INFILE] = fopen($option['file'], 'r');
             $curlOption[CURLOPT_INFILESIZE] = filesize($option['file']);
         } elseif (preg_match('/^head$/i', @$option['type'])) {
             $curlOption[CURLOPT_NOBODY] = true;
             $curlOption[CURLOPT_CUSTOMREQUEST] = 'HEAD';
         } elseif (preg_match('/^delete$/i', @$option['type'])) {
             $curlOption[CURLOPT_CUSTOMREQUEST] = 'DELETE';
         } else {
             $curlOption[CURLOPT_CUSTOMREQUEST] = 'GET';
         }
         // Query data, applicable for all request methods.
         if (@$option['data']) {
             $data = $option['data'];
             // The data contains traditional file POST value: "@/foo/bar"
             $hasPostFile = is_array($data) && array_reduce($data, function ($ret, $val) {
                 return $ret || is_a($val, 'CurlFile') || is_string($val) && strpos($val, '@') === 0 && file_exists(Utility::unwrapAssoc(explode(';', substr($val, 1))));
             }, false);
             // Build query regardless if file exists on PHP < 5.2.0, otherwise
             // only build when there is NOT files to be POSTed.
             // Skip the whole build if $data is not array or object.
             if ((version_compare(PHP_VERSION, '5.2.0', '<') || !$hasPostFile) && (is_array($data) || is_object($data))) {
                 $data = http_build_query($data);
             }
             if (version_compare(PHP_VERSION, '5.5.0', '>=') && $hasPostFile) {
                 array_walk_recursive($data, function (&$value, $key) {
                     if (is_string($value) && strpos($value, '@') === 0) {
                         @(list($path, $type) = explode(';', substr($value, 1)));
                         if (!$type) {
                             $type = Utility::getInfo($path, FILEINFO_MIME_TYPE);
                         }
                         $value = curl_file_create($path, $type, $key);
                     }
                 });
             }
             if (@$curlOption[CURLOPT_POST] === true) {
                 $curlOption[CURLOPT_POSTFIELDS] = $data;
             } else {
                 $url =& $curlOption[CURLOPT_URL];
                 $url .= (strpos($url, '?') === false ? '?' : '&') . $data;
             }
         }
         // HTTP Headers
         if (isset($option['headers'])) {
             $curlOption[CURLOPT_HTTPHEADER] =& $option['headers'];
         }
         // Data type converting
         if (isset($option['success'])) {
             $originalSuccess = @$option['success'];
             switch (@$option['dataType']) {
                 case 'json':
                     $option['success'] = function ($response, $curlOptions) use($option, $originalSuccess) {
                         $result = @ContentEncoder::json($response);
                         if ($result === false && $response) {
                             Utility::forceInvoke(@$option['failure'], array(3, 'Malformed JSON string returned.', $curlOptions));
                         } else {
                             Utility::forceInvoke(@$originalSuccess, array($result, $curlOptions));
                         }
                     };
                     break;
                 case 'xml':
                     $option['success'] = function ($response, $curlOptions) use($option, $originalSuccess) {
                         try {
                             $result = XMLConverter::fromXML($response);
                         } catch (\Exception $e) {
                             $result = NULL;
                         }
                         if ($result === NULL && $response) {
                             Utility::forceInvoke(@$option['failure'], array(2, 'Malformed XML string returned.', $curlOptions));
                         } else {
                             Utility::forceInvoke(@$originalSuccess, array($result, $curlOptions));
                         }
                     };
                     break;
             }
             unset($originalSuccess);
         }
         $curlOption['callbacks'] = array_filter(array('progress' => @$option['progress'], 'success' => @$option['success'], 'failure' => @$option['failure'], 'complete' => @$option['complete']));
         $curlOption = (array) @$option['__curlOpts'] + $curlOption;
         if (System::environment() == 'debug') {
             Log::debug('Net ' . $curlOption[CURLOPT_CUSTOMREQUEST] . ' to ' . $curlOption[CURLOPT_URL], $curlOption);
         }
         return $curlOption;
     }, $options);
     return self::curlRequest($options);
 }
Пример #3
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);
 }
Пример #4
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];
     }
 }