/** * 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; }
/** * 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); }
/** * 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; }