Example #1
0
 /**
  * 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);
 }
Example #2
0
 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']}";
 }
Example #3
0
 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;
         }
     }
 }
Example #4
0
 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);
     }
 }
Example #5
0
 function __autoload($name)
 {
     System::__autoload($name);
 }
Example #6
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 #7
0
//--------------------------------------------------
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]);
Example #8
0
 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);
     }
 }
Example #9
0
 /**
  * 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();
 }
Example #10
0
 /**
  * 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;
 }
Example #11
0
 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;
 }
Example #12
0
 /**
  * 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;
             }
         }
     }
 }