Вход Регистрация
Файл: library/XenForo/FrontController.php
Строк: 983
<?php

/**
* Class to manage most of the flow of a request to a XenForo page.
*
* This class: resolves a URL to a route, loads a specified controller, executes an action
* in that controller, loads the view, renders the view, and outputs the response.
*
* Most dependent objects can be injected
*
* @package XenForo_Mvc
*/
class XenForo_FrontController
{
    
/**
    * An object that is able to load the dependencies needed to use this front controller.
    *
    * @var XenForo_Dependencies_Abstract
    */
    
protected $_dependencies;

    
/**
    * Request object.
    *
    * @see setRequest()
    * @var Zend_Controller_Request_Http
    */
    
protected $_request;

    
/**
    * Response object.
    *
    * @see setResponse()
    * @var Zend_Controller_Response_Http
    */
    
protected $_response;

    
/**
    * Controls whether calling {@link run()} prints the response via the {@link $_response} object.
    * If set to false, the response is returned instead.
    *
    * @see setSendResponse()
    * @var boolean
    */
    
protected $_sendResponse true;

    
/**
    * Constructor. Sets up dependencies.
    *
    * @param XenForo_Dependencies_Abstract
    */
    
public function __construct(XenForo_Dependencies_Abstract $dependencies)
    {
        
$this->_dependencies $dependencies;
    }

    
/**
    * Setter for {@link $_request}.
    *
    * @param Zend_Controller_Request_Http
    */
    
public function setRequest(Zend_Controller_Request_Http $request)
    {
        
$this->_request $request;
    }

    
/**
    * Setter for {@link $_response}.
    *
    * @param Zend_Controller_Response_Http
    */
    
public function setResponse(Zend_Controller_Response_Http $response)
    {
        
$this->_response $response;
    }

    
/**
     * @return Zend_Controller_Request_Http
     */
    
public function getRequest()
    {
        return 
$this->_request;
    }

    
/**
     * @return Zend_Controller_Response_Http
     */
    
public function getResponse()
    {
        return 
$this->_response;
    }

    
/**
     * @return XenForo_Dependencies_Abstract
     */
    
public function getDependencies()
    {
        return 
$this->_dependencies;
    }

    
/**
    * Setter for {@link $_sendResponse}.
    *
    * @param boolean
    */
    
public function setSendResponse($sendResponse)
    {
        
$this->_sendResponse = (bool)$sendResponse;
    }

    
/**
    * Runs the request, handling from routing straight through to response output.
    * Primary method to be used by the external API.
    *
    * @return string|null Returns a string if {@link $_sendResponse} is false
    */
    
public function run()
    {
        
ob_start();

        
XenForo_Application::set('fc'$this);

        
$this->setup();
        
$this->setRequestPaths();
        
$showDebugOutput $this->showDebugOutput();

        
$this->_dependencies->preLoadData();

        
XenForo_CodeEvent::fire('front_controller_pre_route', array($this));
        
$routeMatch $this->route();

        
XenForo_CodeEvent::fire('front_controller_pre_dispatch', array($this, &$routeMatch));

        
$controllerResponse $this->dispatch($routeMatch);
        if (!
$controllerResponse)
        {
            
XenForo_Error::noControllerResponse($routeMatch$this->_request);
            exit;
        }

        
$viewRenderer $this->_getViewRenderer($routeMatch->getResponseType());
        if (!
$viewRenderer)
        {
            
// note: should only happen if there's an error getting the default renderer, which should never happen :)
            
XenForo_Error::noViewRenderer($this->_request);
            exit;
        }

        
$containerParams = array(
            
'majorSection' => $routeMatch->getMajorSection(),
            
'minorSection' => $routeMatch->getMinorSection()
        );

        
XenForo_CodeEvent::fire('front_controller_pre_view',
            array(
$this, &$controllerResponse, &$viewRenderer, &$containerParams)
        );

        
$content $this->renderView($controllerResponse$viewRenderer$containerParams);

        if (
$showDebugOutput)
        {
            
$content $this->renderDebugOutput($content);
        }

        
$bufferedContents ob_get_contents();
        
ob_end_clean();
        if (
$bufferedContents !== '' && is_string($content))
        {
            if (
preg_match('#<body[^>]*>#sU'$content$match))
            {
                
$content str_replace($match[0], $match[0] . $bufferedContents$content);
            }
            else
            {
                
$content $bufferedContents $content;
            }
        }

        
XenForo_CodeEvent::fire('front_controller_post_view', array($this, &$content));

        if (
$this->_sendResponse)
        {
            
$headers $this->_response->getHeaders();
            
$isText false;
            foreach (
$headers AS $header)
            {
                if (
$header['name'] == 'Content-Type')
                {
                    if (
strpos($header['value'], 'text/') === 0)
                    {
                        
$isText true;
                    }
                    break;
                }
            }
            if (
$isText && is_string($content) && $content)
            {
                
$extraHeaders XenForo_Application::gzipContentIfSupported($content);
                foreach (
$extraHeaders AS $extraHeader)
                {
                    
$this->_response->setHeader($extraHeader[0], $extraHeader[1], $extraHeader[2]);
                }
            }

            if (
is_string($content) && $content && !ob_get_level() && XenForo_Application::get('config')->enableContentLength)
            {
                if (
$this->_response->getHttpResponseCode() >= 400
                    
&& strpos($this->_request->getServer('HTTP_USER_AGENT'''), 'IEMobile') !== false
                
)
                {
                    
// Windows mobile bug - 400+ errors cause the standard browser error
                    // to be output if a content length is sent. ...Err, what?
                
}
                else
                {
                    
$this->_response->setHeader('Content-Length'strlen($content), true);
                }
            }

            
$this->_response->sendHeaders();

            if (
$content instanceof XenForo_FileOutput)
            {
                
$content->output();
            }
            else
            {
                echo 
$content;
            }
        }
        else
        {
            return 
$content;
        }
    }

    
/**
    * Sets up the default version of objects needed to run.
    */
    
public function setup()
    {
        if (!
$this->_request)
        {
            
$this->_request = new Zend_Controller_Request_Http();
        }

        if (!
$this->_response)
        {
            
$this->_response = new Zend_Controller_Response_Http();
        }
    }

    
/**
     * Sets the request paths for this request.
     */
    
public function setRequestPaths()
    {
        if (!
XenForo_Application::isRegistered('requestPaths'))
        {
            
$requestPaths XenForo_Application::getRequestPaths($this->_request);
            
XenForo_Application::set('requestPaths'$requestPaths);
        }
    }

    
/**
     * Determines if the full debug output show be shown. This usually depends
     * on application configuration and a request param.
     *
     * @return boolean
     */
    
public function showDebugOutput()
    {
        return (
$this->_request->getParam('_debug') && XenForo_Application::debugMode());
    }

    
/**
     * Sends the request to the router for routing.
     *
     * @param string|null $routePath
     *
     * @return XenForo_RouteMatch
     */
    
public function route($routePath null)
    {
        
$return $this->_dependencies->route($this->_request$routePath);
        if (!
$return || !$return->getControllerName())
        {
            list(
$controllerName$action) = $this->_dependencies->getNotFoundErrorRoute();
            if (!
$return)
            {
                
$return = new XenForo_RouteMatch();
            }
            
$return->setControllerName($controllerName);
            
$return->setAction($action);
        }

        return 
$return;
    }

    
/**
    * Executes the controller dispatch loop.
    *
    * @param XenForo_RouteMatch $routeMatch
    *
    * @return XenForo_ControllerResponse_Abstract|null Null will only occur if error handling is broken
    */
    
public function dispatch(XenForo_RouteMatch &$routeMatch)
    {
        
$reroute = array(
            
'controllerName' => $routeMatch->getControllerName(),
            
'action' => $routeMatch->getAction()
        );

        
$allowReroute true;

        do
        {
            if (isset(
$reroute['path']))
            {
                
$routeMatch $this->route($reroute['path']);
                
$reroute = array(
                    
'controllerName' => $routeMatch->getControllerName(),
                    
'action' => $routeMatch->getAction()
                );
            }

            
$controllerResponse null;
            
$controllerName $reroute['controllerName'];

            
$action str_replace(array('-''/'), ' 'strtolower($reroute['action']));
            
$action str_replace(' '''ucwords($action));
            if (
$action === '')
            {
                
$action 'Index';
            }

            
$reroute false;

            
$controller $this->_getValidatedController($controllerName$action$routeMatch);
            if (
$controller)
            {
                try
                {
                    try
                    {
                        
$controller->preDispatch($action$controllerName);
                        
$controllerResponse $controller->{'action' $action}();
                    }
                    catch (
XenForo_ControllerResponse_Exception $e)
                    {
                        
$controllerResponse $e->getControllerResponse();
                    }

                    
$controller->postDispatch($controllerResponse$controllerName$action);
                    
$reroute $this->_handleControllerResponse($controllerResponse$controllerName$action);
                }
                catch (
Exception $e)
                {
                    
// this is a bit hacky, but it's a selective catch so it's a strange case
                    
if ($e instanceof XenForo_Exception && $e->isUserPrintable())
                    {
                        
$controllerResponse $this->_getErrorResponseFromException($e);
                        
$controller->postDispatch($controllerResponse$controllerName$action);
                    }
                    else
                    {
                        if (!
$allowReroute)
                        {
                            break;
                        }

                        
$reroute $this->_rerouteServerError($e);
                        
$allowReroute false;

                        
XenForo_Error::logException($e);
                    }
                }

                
$responseType $controller->getResponseType();
                
$this->_dependencies->mergeViewStateChanges($controller->getViewStateChanges());
            }
            else
            {
                if (!
$allowReroute)
                {
                    break;
                }

                
$reroute $this->_rerouteNotFound($controllerName$action);
                
$allowReroute false;
            }
        }
        while (
$reroute);

        if (
$controllerResponse instanceof XenForo_ControllerResponse_Abstract)
        {
            
$controllerResponse->controllerName $controllerName;
            
$controllerResponse->controllerAction $action;
        }

        return 
$controllerResponse;
    }

    
/**
    * Called when a printable exception occurs, to get the controller response object.
    *
    * @param Exception Exception that occurred
    *
    * @return XenForo_ControllerResponse_Abstract
    */
    
protected function _getErrorResponseFromException(Exception $e)
    {
        if (
method_exists($e'getMessages'))
        {
            
$message $e->getMessages();
        }
        else
        {
            
$message $e->getMessage();
        }

        
$controllerResponse = new XenForo_ControllerResponse_Error();
        
$controllerResponse->errorText $message;

        return 
$controllerResponse;
    }

    
/**
    * Loads the controller only if it and the specified action have been validated as callable.
    *
    * @param string Name of the controller to load
    * @param string Name of the action to run
    * @param XenForo_RouteMatch Route match for this request (may not match controller)
    *
    * @return XenForo_Controller|null
    */
    
protected function _getValidatedController($controllerName$actionXenForo_RouteMatch $routeMatch)
    {
        
$newControllerName XenForo_Application::resolveDynamicClass($controllerName'controller');
        if (
$newControllerName)
        {
            
$controller = new $newControllerName($this->_request$this->_response$routeMatch);
            if (
method_exists($controller'action' $action) && $this->_dependencies->allowControllerDispatch($controller$action))
            {
                return 
$controller;
            }
        }

        return 
null;
    }

    
/**
    * Handles a controller response to determine if something failed or a reroute is needed.
    *
    * @param mixed  Exceptions will be thrown if is not {@link XenForo_ControllerResponse_Abstract}
    * @param string Name of the controller that generated this response
    * @param string Name of the action that generated this response
    *
    * @return false|array False if no reroute is needed. Array with keys controllerName and action/path if needed.
    */
    
protected function _handleControllerResponse($controllerResponse$controllerName$action)
    {
        if (!
$controllerResponse)
        {
            throw new 
XenForo_Exception("No controller response from $controllerName::action$action");
        }
        else if (!(
$controllerResponse instanceof XenForo_ControllerResponse_Abstract))
        {
            throw new 
XenForo_Exception("Invalid controller response from $controllerName::action$action");
        }
        else if (
$controllerResponse instanceof XenForo_ControllerResponse_Reroute)
        {
            if (
$controllerResponse->controllerName == $controllerName && strtolower($controllerResponse->action) == strtolower($action))
            {
                throw new 
XenForo_Exception("Cannot reroute controller to itself ($controllerName::action$action)");
            }

            return array(
                
'controllerName' => $controllerResponse->controllerName,
                
'action' => $controllerResponse->action
            
);
        }
        else if (
$controllerResponse instanceof XenForo_ControllerResponse_ReroutePath)
        {
            if (!
$controllerResponse->path)
            {
                throw new 
XenForo_Exception("Cannot reroute to path '{$controllerResponse->path}''");
            }

            return array(
                
'path' => $controllerResponse->path
            
);
        }

        return 
false;
    }

    
/**
    * Returns information about how to reroute if a server error occurs.
    *
    * @param Exception Exception object that triggered the error
    *
    * @return array Reroute array
    */
    
protected function _rerouteServerError(Exception $e)
    {
        
$this->_request->setParam('_exception'$e);

        list(
$controllerName$action) = $this->_dependencies->getServerErrorRoute();
        return array(
            
'controllerName' => $controllerName,
            
'action' => $action
        
);
    }

    
/**
    * Returns information about how to reroute if a page not found error occurs.
    *
    * @param string Controller name
    * @param string Action
    *
    * @return array Reroute array
    */
    
protected function _rerouteNotFound($controllerName$action)
    {
        
$this->_request->setParams(array(
            
'_controllerName' => $controllerName,
            
'_action' => $action
        
));

        list(
$controllerName$action) = $this->_dependencies->getNotFoundErrorRoute();
        return array(
            
'controllerName' => $controllerName,
            
'action' => $action
        
);
    }

    
/**
    * Gets the view renderer for the specified response type.
    *
    * @param string Response type (eg, html, xml, json)
    *
    * @return XenForo_ViewRenderer_Abstract
    */
    
protected function _getViewRenderer($responseType)
    {
        return 
$this->_dependencies->getViewRenderer($this->_response$responseType$this->_request);
    }

    
/**
    * Renders the view.
    *
    * @param XenForo_ControllerResponse_Abstract Controller response object from last controller
    * @param XenForo_ViewRenderer_Abstract       View renderer for specified response type
    * @param array                            Extra container params (probably "sections" from routing)
    *
    * @return string View output
    */
    
public function renderView(XenForo_ControllerResponse_Abstract $controllerResponseXenForo_ViewRenderer_Abstract $viewRenderer, array $containerParams = array())
    {
        
$this->_dependencies->preRenderView($controllerResponse);
        
$this->_response->setHeader('Last-Modified'gmdate('D, d M Y H:i:s') . ' GMT'true);
        if (
$controllerResponse->responseCode)
        {
            
$this->_response->setHttpResponseCode($controllerResponse->responseCode);
            if (!empty(
$controllerResponse->containerParams['allowHeader']))
            {
                
$this->_response->setHeader('Allow'$controllerResponse->containerParams['allowHeader']);
            }
        }

        if (
$controllerResponse instanceof XenForo_ControllerResponse_Error)
        {
            
$innerContent $viewRenderer->renderError($controllerResponse->errorText);
        }
        else if (
$controllerResponse instanceof XenForo_ControllerResponse_Message)
        {
            
$innerContent $viewRenderer->renderMessage($controllerResponse->message);
        }
        else if (
$controllerResponse instanceof XenForo_ControllerResponse_View)
        {
            
$viewRenderer->addJsonParams($controllerResponse->jsonParams);
            
$innerContent $viewRenderer->renderView(
                
$controllerResponse->viewName$controllerResponse->params$controllerResponse->templateName,
                
$controllerResponse->subView
            
);
        }
        else if (
$controllerResponse instanceof XenForo_ControllerResponse_Redirect)
        {
            
$target XenForo_Link::convertUriToAbsoluteUri($controllerResponse->redirectTargettrue);

            
$innerContent $viewRenderer->renderRedirect(
                
$controllerResponse->redirectType,
                
$target,
                
$controllerResponse->redirectMessage,
                
$controllerResponse->redirectParams
            
);
        }
        else
        {
            
// generally shouldn't happen
            
$innerContent false;
        }

        if (
$innerContent === false || $innerContent === null)
        {
            
$innerContent $viewRenderer->renderUnrepresentable();
        }

        if (
$viewRenderer->getNeedsContainer())
        {
            
$specificContainerParams XenForo_Application::mapMerge(
                
$containerParams,
                
$controllerResponse->containerParams
            
);
            
$containerParams $this->_dependencies->getEffectiveContainerParams($specificContainerParams$this->_request);

            return 
$viewRenderer->renderContainer($innerContent$containerParams);
        }
        else
        {
            return 
$innerContent;
        }
    }

    
/**
     * Renders page-level debugging output and replaces the original view content
     * with it. Alternatively, it could inject itself into the view content.
     *
     * @param string $originalContent Original, rendered view content
     *
     * @return string Replacement rendered view content
     */
    
public function renderDebugOutput($originalContent)
    {
        
$this->_response->clearHeaders();
        
$this->_response->setHttpResponseCode(200);
        
$this->_response->setHeader('Content-Type''text/html; charset=UTF-8'true);
        
$this->_response->setHeader('Last-Modified'gmdate('D, d M Y H:i:s') . ' GMT'true);

        return 
XenForo_Debug::getDebugPageWrapperHtml(
            
XenForo_Debug::getDebugHtml()
        );
    }
}
Онлайн: 0
Реклама