/** * Use the new resolving mechanism to call for local services. */ static function call($service, $method, $parameters = array(), $options = array()) { $hostname = System::getHostname('service'); if (!$hostname) { throw new ServiceException('Service hostname undefined.'); } if (is_string($options)) { $options = array('method' => $options); } if (isset($options['type']) && empty($options['method'])) { $options['method'] = $options['type']; } $options = (array) $options + self::$defaultOptions; $prefix = conf::get('web::resolvers.service.prefix', '/service'); $options['uri'] = array('scheme' => (bool) @$options['secure'] ? 'https' : 'http', 'host' => $hostname, 'path' => "{$prefix}/{$service}/{$method}/" . implode('/', array_map('urlencode', (array) $parameters))); unset($prefix); // Customizable Resolver if (@$options['resolver'] instanceof Resolver) { $serviceResolver = $options['resolver']; } else { $serviceResolver = Resolver::getActiveInstance(); } unset($options['resolver']); if (@$options['response'] instanceof Response) { $serviceResponse = $options['response']; } else { $serviceResponse = new Response(); } unset($options['response']); $serviceRequest = new Request($options); // Explicitly force this request to be local. $serviceRequest->__local = true; return $serviceRequest->send($serviceResolver, $serviceResponse); }
function http_build_url($url = null, $parts = array()) { if (is_array($url)) { $parts = $url; $url = null; } if ($url) { $url = parse_url($url); } $url = $parts + (array) $url; if (empty($url['scheme'])) { $url['scheme'] = 'http://'; } else { if (substr($url['scheme'], -3) != '://') { $url['scheme'] .= '://'; } } // Build the url according to parts. if (!empty($url['user'])) { if (!empty($url['pass'])) { $url['user'] = "******"; $url['pass'] = "******"; } else { $url['user'] = "******"; } } if (empty($url['host'])) { $url['host'] = System::getHostname(); } if (!empty($url['port']) && $url['port'] != 80) { $url['port'] = ":{$url['port']}"; } else { unset($url['port']); } if (substr(@$url['path'], 0, 1) != '/') { $url['path'] = @"/{$url['path']}"; } if (isset($url['query'])) { if (is_array($url['query'])) { $url['query'] = http_build_query($url['query']); } if ($url['query'] && $url['query'][0] != '?') { $url['query'] = "?{$url['query']}"; } } if (!empty($url['fragment']) && $url['fragment'][0] != '#') { $url['fragment'] = "#{$url['fragment']}"; } return @"{$url['scheme']}{$url['user']}{$url['pass']}{$url['host']}{$url['port']}{$url['path']}{$url['query']}{$url['fragment']}"; }
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; } } }
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); } }
function __autoload($name) { System::__autoload($name); }
/** * 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); }
//-------------------------------------------------- require_once 'scripts/Initialize.php'; //-------------------------------------------------- // // Resolve the request // //-------------------------------------------------- // Resolver chain $resolver = new framework\Resolver(); // Maintenance resolver // Simply don't put it into chain when disabled. if (conf::get('system::maintenance.enable')) { $resolver->registerResolver(new resolvers\MaintenanceResolver(array('templatePath' => conf::get('system::maintenance.templatePath'), 'whitelist' => (array) conf::get('system::maintenance.whitelist'))), 999); } // Session authentication $resolver->registerResolver(new resolvers\UserContextResolver(array('setup' => System::environment() != System::ENV_PRODUCTION)), 100); // Access rules and policy $resolver->registerResolver(new resolvers\AuthenticationResolver(array('paths' => conf::get('web::auth.paths'), 'statusCode' => conf::get('web::auth.statusCode'))), 80); // Locale $resolver->registerResolver(new resolvers\LocaleResolver(array('default' => conf::get('system::i18n.localeDefault', 'en_US'))), 70); // JSONP Headers $resolver->registerResolver(new resolvers\JsonpResolver(array('defaultCallback' => conf::get('web::resolvers.jsonp.defaultCallback', 'callback'))), 65); // URI Rewrite $resolver->registerResolver(new resolvers\UriRewriteResolver(array(array('source' => function ($uri) { return preg_match('/^\\/task\\/\\w{32,32}\\//', $uri); }, 'target' => function ($request, $response) { // note; This pattern does not work: '/^\/task\/(\w{32,32})(?:\/(\w+)\/(\w+))?$/', // PHP fails to match "/task/:hash/foo" with the last optional group. preg_match('/^\\/task\\/(\\w{32,32})\\/(.+)?$/', $request->uri('path'), $matches); // note; search task by UUID $instance = (new models\WorkInstance())->loadByNextTask($matches[1]);
public static function handleException($e) { if (error_reporting() == 0) { return; } while (ob_get_level() > 0) { ob_end_clean(); } $eS = $e->getMessage(); $eN = $e->getCode(); $eC = $e->getTrace(); // Put current context into stack trace array_unshift($eC, array('file' => $e->getFile(), 'line' => $e->getLine())); if ($e instanceof ErrorException) { switch ($e->getSeverity()) { case E_ERROR: case E_PARSE: case E_CORE_ERROR: case E_USER_ERROR: default: $logType = LogLevel::CRITICAL; break; case E_WARNING: case E_CORE_WARNING: case E_USER_WARNING: $logType = LogLevel::WARNING; break; case E_DEPRECATED: case E_NOTICE: case E_USER_DEPRECATED: case E_USER_NOTICE: $logType = LogLevel::NOTICE; break; case E_STRICT: $logType = LogLevel::INFO; break; } $exceptionType = 'error'; } else { $exceptionType = get_class($e); if (strpos($exceptionType, '\\') !== false) { $exceptionType = substr(strrchr($exceptionType, '\\'), 1); } $logType = LogLevel::ERROR; } $logString = sprintf('[Gateway] Uncaught %s#%d with message: "%s".', $exceptionType, $eN, $eS); unset($exceptionType); // Current request context $resolver = Resolver::getActiveInstance(); if ($resolver) { if ($resolver->request()) { $client = $resolver->request()->client(); } $response = $resolver->response(); } unset($resolver); // Prevent recursive errors on logging when database fails to connect. if (Database::isConnected()) { // Release table locks of current session. @Database::unlockTables(false); if (Database::inTransaction()) { @Database::rollback(); } } $logContext = array_filter(array('errorContext' => $eC, 'client' => @$client)); // Log the error try { @Log::log($logType, $logString, $logContext); } catch (\Exception $e) { } unset($logContext); // Send the error to output $output = array('error' => $eS, 'code' => $eN); if (System::environment(false) != System::ENV_PRODUCTION) { $output['trace'] = $eC; } // Display error message if (isset($response) && @$client['type'] != 'cli') { // Do i18n when repsonse context is available if ($e instanceof GeneralException) { $errorMessage = $response->__($eS, $logType); if ($errorMessage) { $output['error'] = $errorMessage; } } if ($e instanceof ErrorException) { $statusCode = 500; } else { $statusCode = 400; } if ($e instanceof ValidationException) { $output['errors'] = $e->getErrors(); } $response->clearHeaders(); $response->header('Content-Type', 'application/json; charset=utf-8'); $response->send($output, $statusCode); } else { header('Content-Type: text/plain', true, 500); echo "{$logString}\n"; // Debug stack trace if (System::environment(false) != System::ENV_PRODUCTION) { echo "Trace:\n"; array_walk($eC, function ($stack, $index) { $trace = $index + 1 . '.'; $function = implode('->', array_filter(array(@$stack['class'], @$stack['function']))); if ($function) { $trace .= " {$function}()"; } unset($function); if (@$stack['file']) { $trace .= " {$stack['file']}"; if (@$stack['line']) { $trace .= ":{$stack['line']}"; } } echo "{$trace}\n"; }); } } // CLI exit code on Exceptions and Errors if (in_array($logType, array(LogLevel::ERROR, LogLevel::CRITICAL, LogLevel::ALERT, LogLevel::EMERGENCY))) { $exitCode = $e->getCode(); if ($exitCode <= 0) { $exitCode = 1; } die($exitCode); } }
/** * Last step of task process. Responsible of taking request parameters and/or * any external input and process them. Tasks might also update the data store * in work instance to for reference of further tasks. * * @param {Request} $request The HTTP request parameter for post data, null for headless tasks. * * @return {Promise} The promise object about whether this task is successfully processed. */ public function process() { $this->__deferred = new Deferred(); // // note; execute $this->processScript. // $f = tmpfile(); fwrite($f, '<?php ' . $this->processScript); // $i = stream_get_meta_data($f); $i = $i['uri']; // call_user_func( // function($request) { include(func_get_arg(1)); }, // $request, $i); // fclose($f); unset($f, $i); if (empty($this->extra['endpoint']['controller'])) { throw new FrameworkException('Task instance has no endpoint defined.'); } else { // load the specified controller $controller = $this->extra['endpoint']['controller']; $basePath = ".private/modules/{$this->name}"; require_once "{$basePath}/controllers/{$controller}.php"; // note; change working directory to task based chdir($basePath); unset($basePath); $controller = new $controller($this); if (!$controller instanceof \prefw\ITaskProcessor) { throw new FrameworkException('Controller must implement ITaskProcessor interface!'); } else { $ret = $controller->process(); /*! note; * Three kinds of acceptible return value, * 1. null type resolves immediately * 2. Exceptions rejects immediately * 3. Promise objects pipes results */ if (@$ret === null) { $this->resolve(); } else { if ($ret instanceof \core\Promise) { $ret->then(array($this, 'resolve'), array($this, 'reject')); } } } // note; revert back to project working directory chdir(System::getPathname()); } return $this->__deferred->promise(); }
/** * Sends the body to output buffer. */ public function flushBody() { $body = $this->body(); if ($body instanceof \SplFileObject) { $body->fpassthru(); } else { if (is_resource($body)) { fpassthru($body); } else { if (is_string($body) && @is_file($body)) { $path = realpath(System::getPathname() . '/' . $body); if ($path && is_readable($path)) { readfile($path); } } else { echo $this->contentEncode($body); } } } return $this; }
public function setUri($uri) { if (is_string($uri)) { $uri = parse_url($uri); $uri = parse_url(http_build_url($uri)); } // note; fallback to system default hostname if (empty($uri['host'])) { $uri['host'] = System::getHostname(); } $this->uri = $uri; }
/** * Autoload PHP classes */ public static function __autoload($name) { // Namespace path fix $name = str_replace('\\', '/', ltrim($name, '\\')); // Classname path fix // Note: Partially abide to PSR-0, ignoring starting and trailing underscores. $name = dirname($name) . '/' . preg_replace('/(\\w+)_(\\w+)/', '$1/$2', basename($name)); // Look up current folder if (file_exists("./{$name}.php")) { require_once "./{$name}.php"; } else { $lookupPaths = (array) self::$pathPrefixes; // Assumption: wildcards are always shorter than exact matches because "*" only has one character. $prefix = array_reduce(array_keys($lookupPaths), function ($result, $prefix) use(&$name) { // Wildcards if (strpos($prefix, '*') !== false) { if (!preg_match('/^' . preg_replace('/\\\\\\*/', '.*', preg_quote($prefix)) . '/', $name)) { unset($prefix); } } else { if (strpos($name, $prefix) === false) { unset($prefix); } } if (isset($prefix) && strlen($result) < strlen($prefix)) { $result = $prefix; } return $result; }); if (!$prefix) { return; } // Exact matches should remove prefix portion (PSR-4) if (strpos($prefix, '*') === false) { $name = trim(substr($name, strlen($prefix)), '/'); } $lookupPaths = (array) @$lookupPaths[$prefix]; foreach ($lookupPaths as $lookupPath) { $lookupPath = System::getPathname() . "/{$lookupPath}/{$name}.php"; if (file_exists($lookupPath)) { require_once $lookupPath; } } } }