Example #1
0
 /**
  * Dispatches a URI for internal processing.
  * Usually called by a front controller.
  * @method dispatch
  * @static
  * @param {mixed} [$uri=null] You can pass a custom URI to dispatch. Otherwise, Qbix will attempt
  *  to route the requested URL, if any.
  * @param {array} [$check=array('accessible')] Pass array() to skip checking whether the URI can be obtained
  *  as a result of routing some URL.
  * @return {boolean}
  * @throws {Q_Exception_MethodNotSupported}
  * @throws {Q_Exception_Recursion}
  * @throws {Q_Exception_DispatcherErrors}
  * @throws {Q_Exception_DispatcherForward}
  */
 static function dispatch($uri = null, $check = array('accessible'))
 {
     if (!is_array($check)) {
         $check = array('accessible');
     }
     if (isset($uri)) {
         if (in_array('accessible', $check)) {
             if (!Q_Uri::url($uri)) {
                 // We shouldn't dispatch to this URI
                 $uri = Q_Uri::from(array());
             }
         }
         self::$uri = Q_Uri::from($uri);
     } else {
         $request_uri = Q_Request::uri();
         self::$uri = clone $request_uri;
     }
     // if file or dir is requested, try to serve it
     $served = false;
     $skip = Q_Config::get('Q', 'dispatcherSkipFilename', false);
     $filename = $skip ? false : Q_Request::filename();
     if ($filename) {
         if (is_dir($filename)) {
             /**
              * @event Q/dir
              * @param {string} filename
              * @param {string} routed_uri
              * @return {boolean}
              */
             $served = Q::event("Q/dir", compact('filename', 'routed_uri'));
             $dir_was_served = true;
         } else {
             /**
              * @event Q/file
              * @param {string} filename
              * @param {string} routed_uri
              * @return {boolean}
              */
             $served = Q::event("Q/file", compact('filename', 'routed_uri'));
             $dir_was_served = false;
         }
     }
     // if response was served, then return
     if ($served) {
         self::result($dir_was_served ? "Dir served" : "File served");
         return true;
     }
     // This loop is for forwarding
     $max_forwards = Q_Config::get('Q', 'maxForwards', 10);
     for ($try = 0; $try < $max_forwards; ++$try) {
         // Make an array from the routed URI
         $routed_uri_array = array();
         if (self::$uri instanceof Q_Uri) {
             $routed_uri_array = self::$uri->toArray();
         }
         // If no module was found, then respond with noModule and return
         if (!isset(self::$uri->module)) {
             /**
              * @event Q/noModule
              * @param {array} $routed_uri_array
              */
             Q::event("Q/noModule", $routed_uri_array);
             // should echo things
             self::result("No module");
             return false;
         }
         $module = self::$uri->module;
         try {
             // Implement restricting of modules we are allowed to access
             $routed_modules = Q_Config::get('Q', 'routedModules', null);
             if (isset($routed_modules)) {
                 if (!in_array($module, $routed_modules)) {
                     /**
                      * @event Q/notFound
                      * @param {array} $routed_uri_array
                      */
                     Q::event('Q/notFound', $routed_uri_array);
                     // should echo things
                     self::result("Unknown module");
                     return false;
                 }
             } else {
                 if (!Q::realPath("handlers/{$module}")) {
                     Q::event('Q/notFound', $routed_uri_array);
                     // should echo things
                     self::result("Unknown module");
                     return false;
                 }
             }
             // Implement notFound if action was not found
             if (empty(self::$uri->action)) {
                 Q::event('Q/notFound', $routed_uri_array);
                 // should echo things
                 self::result("Unknown action");
                 return false;
             }
             // Fire a pure event, for aggregation etc
             if (!isset(self::$skip['Q/prepare'])) {
                 /**
                  * @event Q/prepare
                  * @param {array} $routed_uri_array
                  */
                 Q::event('Q/prepare', $routed_uri_array, true);
             }
             // Perform validation
             if (!isset(self::$skip['Q/validate'])) {
                 /**
                  * @event Q/validate
                  * @param {array} $routed_uri_array
                  */
                 Q::event('Q/validate', $routed_uri_array);
                 if (!isset(self::$skip['Q/errors'])) {
                     // Check if any errors accumulated
                     if (Q_Response::getErrors()) {
                         // There were validation errors -- render a response
                         self::result('Validation errors');
                         self::errors(null, $module, null);
                         return false;
                     }
                 }
             }
             // Time to instantiate some app objects from the request
             if (!isset(self::$skip['Q/objects'])) {
                 /**
                  * @event Q/objects
                  * @param {array} $routed_uri_array
                  */
                 Q::event('Q/objects', $routed_uri_array, true);
             }
             // We might want to reroute the request
             if (!isset(self::$skip['Q/reroute'])) {
                 /**
                  * @event Q/reroute
                  * @param {array} $routed_uri_array
                  * @return {boolean} whether to stop the dispatch
                  */
                 $stop_dispatch = Q::event('Q/reroute', $routed_uri_array, true);
                 if ($stop_dispatch) {
                     self::result("Stopped dispatch");
                     return false;
                 }
             }
             // Make some changes to server state, possibly
             $method = Q_Request::method();
             if ($method != 'GET') {
                 $methods = Q_Config::get('Q', 'methods', array('POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD'));
                 if (!in_array($method, $methods)) {
                     throw new Q_Exception_MethodNotSupported(compact('method'));
                 }
                 $method_event = 'Q/' . strtolower($method);
                 if (!isset(self::$skip['Q/method']) and !isset(self::$skip[$method_event])) {
                     if (!Q::canHandle($method_event)) {
                         throw new Q_Exception_MethodNotSupported(compact('method'));
                     }
                     Q::event($method_event);
                 }
             }
             // You can calculate some analytics here, and store them somewhere
             if (!isset(self::$skip['Q/analytics'])) {
                 /**
                  * @event Q/analytics
                  * @param {array} $routed_uri_array
                  */
                 Q::event('Q/analytics', $routed_uri_array, true);
             }
             if (!isset(self::$skip['Q/errors'])) {
                 // Check if any errors accumulated
                 if (Q_Response::getErrors()) {
                     // There were processing errors -- render a response
                     self::result('Processing errors');
                     self::errors(null, $module, null);
                     return false;
                 }
             }
             // When handling all further events, you should probably
             // refrain from changing server state, and only do reading.
             // That is because GET in HTTP is not supposed to have side effects
             // for which the client is responsible.
             // Start buffering the response, unless otherwise requested
             $handler = Q_Response::isBuffered();
             if ($handler !== false) {
                 $ob = new Q_OutputBuffer($handler);
             }
             // Generate and render a response
             /**
              * @event Q/response
              * @param {array} $routed_uri_array
              */
             self::$response_started = true;
             Q::event("Q/response", $routed_uri_array);
             if (!empty($ob)) {
                 $ob->endFlush();
             }
             self::result("Served response");
             return true;
         } catch (Q_Exception_DispatcherForward $e) {
             if (!empty($ob)) {
                 $ob->getClean();
             }
             self::handleForwardException($e);
         } catch (Q_Exception_DispatcherErrors $e) {
             if (!empty($ob)) {
                 $partial_response = $ob->getClean();
             } else {
                 $partial_response = null;
             }
             self::errors(null, $module, $partial_response);
             self::result("Rendered errors");
             return true;
         } catch (Exception $exception) {
             if (!empty($ob)) {
                 $partial_response = $ob->getClean();
             } else {
                 $partial_response = null;
             }
             $message = $exception->getMessage();
             $file = $exception->getFile();
             $line = $exception->getLine();
             if (is_callable(array($exception, 'getTraceAsStringEx'))) {
                 $trace_string = $exception->getTraceAsStringEx();
             } else {
                 $trace_string = $exception->getTraceAsString();
             }
             $colored = Q_Exception::coloredString($message, $file, $line, $trace_string);
             self::result("Exception occurred:\n\n{$colored}");
             try {
                 self::errors($exception, $module, $partial_response);
             } catch (Exception $e) {
                 if (!empty($forwarding_to_error_action)) {
                     // Looks like there were errors in the error action
                     // So show the default one with the original exception
                     throw $exception;
                 }
                 if (get_class($e) === 'Q_Exception_DispatcherForward') {
                     $forwarding_to_error_action = true;
                     self::handleForwardException($e);
                     continue;
                 } else {
                     throw $e;
                 }
             }
             return false;
         }
     }
     // If we are here, we have done forwarding too much
     throw new Q_Exception_Recursion(array('function_name' => 'Dispatcher::forward()'));
 }
Example #2
0
 /**
  * Dumps the result as a table in text mode
  * @method __toString
  */
 function __toString()
 {
     try {
         $ob = new Q_OutputBuffer();
         $results = array();
         foreach ($this->fields as $key => $value) {
             $results[] = array('Field name:' => $key, 'Field value:' => $value, 'Field modified:' => $this->wasModified($key) ? 'Yes' : 'No');
         }
         Db_Utils::dump_table($results);
         return $ob->getClean();
     } catch (Exception $e) {
         return $e->getMessage();
     }
 }
Example #3
0
 /**
  * Renders a particular view
  * @method view
  * @static
  * @param {string} $viewName
  *  The full name of the view
  * @param {array} $params=array()
  *  Parameters to pass to the view
  * @return {string}
  *  The rendered content of the view
  * @throws {Q_Exception_MissingFile}
  */
 static function view($viewName, $params = array())
 {
     require_once Q_CLASSES_DIR . DS . 'Q' . DS . 'Exception' . DS . 'MissingFile.php';
     if (empty($params)) {
         $params = array();
     }
     $viewName = implode(DS, explode('/', $viewName));
     $fields = Q_Config::get('Q', 'views', 'fields', null);
     if ($fields) {
         $params = array_merge($fields, $params);
     }
     /**
      * @event {before} Q/view
      * @param {string} viewName
      * @param {string} params
      * @return {string}
      *  Optional. If set, override method return
      */
     $result = self::event('Q/view', compact('viewName', 'params'), 'before');
     if (isset($result)) {
         return $result;
     }
     try {
         $ob = new Q_OutputBuffer();
         self::includeFile('views' . DS . $viewName, $params);
         return $ob->getClean();
     } catch (Q_Exception_MissingFile $e) {
         if (basename($e->params['filename']) != basename($viewName)) {
             throw $e;
         }
         $ob->flushHigherBuffers();
         /**
          * Renders 'Missing View' page
          * @event Q/missingView
          * @param {string} viewName
          * @return {string}
          */
         return self::event('Q/missingView', compact('viewName'));
     }
 }
Example #4
0
 /**
  * Returns a &lt;style&gt; tag with the content of all the stylesheets included inline
  * @method stylesheetsInline
  * @static
  * @param {array} [$styles=array()] If not empty, this associative array contains styles which will be
  * included at the end of the generated &lt;style&gt; tag.
  * @param {string} [$slotName=null] If provided, returns only the stylesheets added while filling this slot.
  * @return {string} the style tags and their contents inline
  */
 static function stylesheetsInline($styles = array(), $slotName = null)
 {
     if (empty(self::$stylesheets)) {
         return '';
     }
     $sheets = self::stylesheetsArray($slotName, false);
     $sheets_for_slots = array();
     if (!empty($sheets)) {
         foreach ($sheets as $stylesheet) {
             $href = '';
             $media = 'screen, print';
             $type = 'text/css';
             extract($stylesheet, EXTR_IF_EXISTS);
             $ob = new Q_OutputBuffer();
             if (Q_Valid::url($href)) {
                 try {
                     include $href;
                 } catch (Exception $e) {
                 }
             } else {
                 list($href, $filename) = Q_Html::themedUrlAndFilename($href);
                 try {
                     Q::includeFile($filename);
                 } catch (Exception $e) {
                 }
             }
             $sheets_for_slots[$stylesheet['slot']][] = "\n/* Included inline from {$href} */\n" . $ob->getClean();
         }
     }
     $parts = array();
     foreach ($sheets_for_slots as $slot => $texts) {
         $parts[] = Q_Html::tag('style', array('data-slot' => $slot), implode("\n\n", $texts));
     }
     return implode("", $parts);
 }
Example #5
0
/**
 * The default implementation.
 */
function Q_errors($params)
{
    extract($params);
    /**
     * @var Exception $exception
     * @var boolean $startedResponse
     */
    if (!empty($exception)) {
        Q_Response::addError($exception);
    }
    $errors = Q_Response::getErrors();
    $errors_array = Q_Exception::toArray($errors);
    // Simply return the errors, if this was an AJAX request
    if ($is_ajax = Q_Request::isAjax()) {
        try {
            $errors_json = @Q::json_encode($errors_array);
        } catch (Exception $e) {
            $errors_array = array_slice($errors_array, 0, 1);
            unset($errors_array[0]['trace']);
            $errors_json = @Q::json_encode($errors_array);
        }
        $json = "{\"errors\": {$errors_json}}";
        $callback = Q_Request::callback();
        switch (strtolower($is_ajax)) {
            case 'iframe':
                if (!Q_Response::$batch) {
                    header("Content-type: text/html");
                }
                echo <<<EOT
<!doctype html><html lang=en>
<head><meta charset=utf-8><title>Q Result</title></head>
<body>
<script type="text/javascript">
window.result = function () { return {$json} };
</script>
</body>
</html>
EOT;
                break;
            case 'json':
            default:
                header("Content-type: " . ($callback ? "application/javascript" : "application/json"));
                echo $callback ? "{$callback}({$json})" : $json;
        }
        return;
    }
    // Forward internally, if it was requested
    if ($onErrors = Q_Request::special('onErrors', null)) {
        $uri1 = Q_Dispatcher::uri();
        $uri2 = Q_Uri::from($onErrors);
        $url2 = $uri2->toUrl();
        if (!isset($uri2)) {
            throw new Q_Exception_WrongValue(array('field' => 'onErrors', 'range' => 'an internal URI reachable from a URL'));
        }
        if ($uri1->toUrl() !== $url2) {
            Q_Dispatcher::forward($uri2);
            return;
            // we don't really need this, but it's here anyway
        }
    }
    $params2 = compact('errors', 'exception', 'errors_array', 'exception_array');
    if (Q::eventStack('Q/response')) {
        // Errors happened while rendering response. Just render errors view.
        return Q::view('Q/errors.php', $params2);
    }
    if (!$startedResponse) {
        try {
            // Try rendering the response, expecting it to
            // display the errors along with the rest.
            $ob = new Q_OutputBuffer();
            Q::event('Q/response', $params2);
            $ob->endFlush();
            return;
        } catch (Exception $e) {
            if (get_class($e) === 'Q_Exception_DispatcherForward') {
                throw $e;
                // if forwarding was requested, do it
                // for all other errors, continue trying other things
            }
            $output = $ob->getClean();
        }
    }
    if ($errors) {
        // Try rendering the app's errors response, if any.
        $app = Q::app();
        if (Q::canHandle("{$app}/errors/response/content")) {
            Q_Dispatcher::forward("{$app}/errors");
        } else {
            echo Q::view("Q/errors.php", compact('errors'));
        }
    }
    if (!empty($e)) {
        return Q::event('Q/exception', array('exception' => $e));
    }
}
Example #6
0
 /**
  * Returns a response to the client.
  * @param {boolean} [$closeConnection=false] Whether to send headers to close the connection
  * @method response
  * @static
  */
 static function response($closeConnection = false)
 {
     if (self::$servedResponse) {
         return;
         // response was served, and no new dispatch started
     }
     // Start buffering the response, unless otherwise requested
     $handler = Q_Response::isBuffered();
     if ($handler !== false) {
         $ob = new Q_OutputBuffer($handler);
     }
     if (!empty($_GET['Q_ct'])) {
         Q_Response::setCookie('Q_ct', $_GET['Q_ct']);
     }
     if (!empty($_GET['Q_cordova'])) {
         Q_Response::setCookie('Q_cordova', $_GET['Q_cordova']);
     }
     Q_Response::sendCookieHeaders();
     // Generate and render a response
     /**
      * Gives the app a chance to generate a response.
      * You should not change the server state when handling this event.
      * @event Q/response
      * @param {array} $routed
      */
     self::$startedResponse = true;
     Q::event("Q/response", self::$routed);
     if ($closeConnection) {
         header("Connection: close");
         header("Content-Length: " . $ob->getLength());
     }
     if (!empty($ob)) {
         $ob->endFlush();
     }
     if ($closeConnection) {
         ob_end_flush();
         flush();
     }
     self::$servedResponse = true;
     self::result("Served response");
     return true;
 }
Example #7
0
 /**
  * Dumps the result as a table in text mode
  * Side effect, though: can't fetch anymore until the cursor is closed.
  * @method __toString
  * @return {string}
  */
 function __toString()
 {
     return "Db_Result";
     try {
         $ob = new Q_OutputBuffer();
         $rows = $this->fetchAll(PDO::FETCH_ASSOC);
         Db::dump_table($rows);
         return $ob->getClean();
     } catch (Exception $e) {
         return $e->getMessage();
     }
 }