Example #1
0
 /**
  * Encodes the content base on current headers.
  */
 protected function contentEncode($message)
 {
     $contentTypes = (array) @$this->headers['Content-Type'];
     if (preg_grep('/json/i', $contentTypes)) {
         $message = ContentEncoder::json($message);
         // note; check if this is a JSONP request, convert the response if so.
         $callback = $this->header('X-JSONP-CALLBACK');
         if ($callback) {
             $this->header('Content-Type', 'application/javascript', true);
             $message = "{$callback}({$message})";
         }
         unset($callback);
     } else {
         if (preg_grep('/xml/i', $contentTypes)) {
             $message = ContentEncoder::xml($message);
         } else {
             if (preg_grep('/php.serialize/i', $contentTypes)) {
                 $message = ContentEncoder::serialize($message);
             } else {
                 if (preg_grep('/php.dump/i', $contentTypes)) {
                     $message = ContentEncoder::dump($message);
                 } else {
                     if (preg_grep('/php.export/i', $contentTypes)) {
                         $message = ContentEncoder::export($message);
                     }
                 }
             }
         }
     }
     return $message;
 }
Example #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);
 }
Example #3
0
 /**
  * Put the specified command into process queue, optionally spawn a daemon to run it.
  *
  * @param {string} $command Command line to be run by the daemon.
  *
  * @param {array} $options Array with the following properties:
  *                $options[$spawn] {bool} Whether to spawn a worker daemon immediately, default true.
  *                $options[$singleton] {bool} Whether to skip the queuing when there is already an exact same command
  *                                           in the process list, default false.
  *                $options[$requeue] {bool} True to remove any previous inactive identical commands before pushing into
  *                                         queue, default false.
  *                $options[$kill] {int} When provided, a signal to be sent to all active identical commands.
  *                $options[$type] {string} Identifier of command queue groups, commands will be drawn and run randomly
  *                                        among groups, one at a time, by the daemon.
  *                $options[$weight] {int} The likeliness of a command to be drawn within the same group, default 1.
  *                $options[$capacity] {float} Percentage of occupation within the same group. To limit maximum active
  *                                           processes within the same group to be 10, set this to 0.1. Default 0.2.
  *                $options[$env] {array} Associative array of properties that will be available when target command starts.
  *                $options[...] Any other values will be set into the process object, which will be accessible by spawn
  *                              processes with Process::get() method.
  */
 public static function enqueue($command, $options = array())
 {
     // For backward-compatibility, this parameter is originally $spawnProcess.
     if (is_bool($options)) {
         $options = array('$spawn' => $options);
     } else {
         $options = (array) $options;
     }
     $options = array_filter($options, compose('not', 'is_null'));
     $options += self::$defaultOptions;
     $process = array(Node::FIELD_COLLECTION => FRAMEWORK_COLLECTION_PROCESS, 'command' => $command) + array_select($options, array_filter(array_keys($options), compose('not', startsWith('$'))));
     // Remove identical inactive commands
     if ($options['$requeue']) {
         Node::delete(array(Node::FIELD_COLLECTION => FRAMEWORK_COLLECTION_PROCESS, 'command' => $command, 'pid' => null));
     }
     // Sends the specified signal to all active identical commands
     if (is_int(@$options['$kill'])) {
         if (!function_exists('posix_kill')) {
             throw new ProcessException('Platform does not support posix_kill command.', ERR_SUPRT);
         }
         $activeProcesses = Node::get(array(Node::FIELD_COLLECTION => FRAMEWORK_COLLECTION_PROCESS, 'command' => $command, 'pid' => '!=null'));
         foreach ($activeProcesses as $process) {
             posix_kill($process['pid'], $options['$kill']);
         }
         unset($activeProcesses);
     }
     // Only pushes the command into queue when there are no identical process.
     if ($options['$singleton']) {
         $identicalProcesses = Node::get(array(Node::FIELD_COLLECTION => FRAMEWORK_COLLECTION_PROCESS, 'command' => $command));
         // Process object will be updated
         if ($identicalProcesses) {
             $process['id'] = $identicalProcesses[0]['id'];
         }
         unset($identicalProcesses);
     }
     // Copy process related fields.
     foreach (['type', 'weight', 'capacity'] as $field) {
         if (isset($options["\${$field}"])) {
             $process[$field] = $options["\${$field}"];
         }
     }
     // Default start time to now
     if (empty($process['start_time']) || !strtotime($process['start_time'])) {
         $process['start_time'] = date('c');
     }
     // Push or updates target process.
     $res = Node::set($process);
     if ($res === false) {
         throw new ProcessException('Unable to enqueue process.', self::ERR_ENQUE);
     }
     if (is_numeric($res)) {
         $process['id'] = $res;
     }
     unset($res);
     $env = (array) @$options['$env'];
     if ($env) {
         $env = array('env' => ContentEncoder::json($env));
     }
     unset($options['$env']);
     // Only spawn a worker if target process is not already working.
     if (@$options['$spawn'] && !@$process['pid'] && !self::spawnWorker($env)) {
         throw new ProcessException('Unable to spawn daemon worker.', self::ERR_SPAWN);
     }
     return $process;
 }